mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-26 21:53:48 +03:00
caddyfile: Introduce basic linting and fmt check (#3923)
* caddyfile: Introduce basic linting and fmt check This will help encourage people to keep their Caddyfiles tidy. * Remove unrelated tests I am not sure that testing the output of warnings here is quite the right idea; these tests are just for syntax and parsing success.
This commit is contained in:
parent
1b453dd4fb
commit
c8557dc00b
7 changed files with 40 additions and 54 deletions
|
@ -15,6 +15,7 @@
|
||||||
package caddyfile
|
package caddyfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
@ -51,11 +52,17 @@ func (a Adapter) Adapt(body []byte, options map[string]interface{}) ([]byte, []c
|
||||||
return nil, warnings, err
|
return nil, warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
marshalFunc := json.Marshal
|
// lint check: see if input was properly formatted; sometimes messy files files parse
|
||||||
if options["pretty"] == "true" {
|
// successfully but result in logical errors because the Caddyfile is a bad format
|
||||||
marshalFunc = caddyconfig.JSONIndent
|
// TODO: also perform this check on imported files
|
||||||
|
if !bytes.Equal(Format(body), body) {
|
||||||
|
warnings = append(warnings, caddyconfig.Warning{
|
||||||
|
File: filename,
|
||||||
|
Message: "file is not formatted with 'caddy fmt'",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
result, err := marshalFunc(cfg)
|
|
||||||
|
result, err := json.Marshal(cfg)
|
||||||
|
|
||||||
return result, warnings, err
|
return result, warnings, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,12 +93,6 @@ func JSONModuleObject(val interface{}, fieldName, fieldVal string, warnings *[]W
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONIndent is used to JSON-marshal the final resulting Caddy
|
|
||||||
// configuration in a consistent, human-readable way.
|
|
||||||
func JSONIndent(val interface{}) ([]byte, error) {
|
|
||||||
return json.MarshalIndent(val, "", "\t")
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterAdapter registers a config adapter with the given name.
|
// RegisterAdapter registers a config adapter with the given name.
|
||||||
// This should usually be done at init-time. It panics if the
|
// This should usually be done at init-time. It panics if the
|
||||||
// adapter cannot be registered successfully.
|
// adapter cannot be registered successfully.
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
func TestLogDirectiveSyntax(t *testing.T) {
|
func TestLogDirectiveSyntax(t *testing.T) {
|
||||||
for i, tc := range []struct {
|
for i, tc := range []struct {
|
||||||
input string
|
input string
|
||||||
expectWarn bool
|
|
||||||
expectError bool
|
expectError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -18,7 +17,6 @@ func TestLogDirectiveSyntax(t *testing.T) {
|
||||||
log
|
log
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -28,7 +26,6 @@ func TestLogDirectiveSyntax(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -38,7 +35,6 @@ func TestLogDirectiveSyntax(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
@ -47,12 +43,7 @@ func TestLogDirectiveSyntax(t *testing.T) {
|
||||||
ServerType: ServerType{},
|
ServerType: ServerType{},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, warnings, err := adapter.Adapt([]byte(tc.input), nil)
|
_, _, err := adapter.Adapt([]byte(tc.input), nil)
|
||||||
|
|
||||||
if len(warnings) > 0 != tc.expectWarn {
|
|
||||||
t.Errorf("Test %d warning expectation failed Expected: %v, got %v", i, tc.expectWarn, warnings)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil != tc.expectError {
|
if err != nil != tc.expectError {
|
||||||
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
|
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
func TestMatcherSyntax(t *testing.T) {
|
func TestMatcherSyntax(t *testing.T) {
|
||||||
for i, tc := range []struct {
|
for i, tc := range []struct {
|
||||||
input string
|
input string
|
||||||
expectWarn bool
|
|
||||||
expectError bool
|
expectError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -18,7 +17,6 @@ func TestMatcherSyntax(t *testing.T) {
|
||||||
query showdebug=1
|
query showdebug=1
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -27,7 +25,6 @@ func TestMatcherSyntax(t *testing.T) {
|
||||||
query bad format
|
query bad format
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -38,7 +35,6 @@ func TestMatcherSyntax(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -47,14 +43,12 @@ func TestMatcherSyntax(t *testing.T) {
|
||||||
not path /somepath*
|
not path /somepath*
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `http://localhost
|
input: `http://localhost
|
||||||
@debug not path /somepath*
|
@debug not path /somepath*
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -63,7 +57,6 @@ func TestMatcherSyntax(t *testing.T) {
|
||||||
}
|
}
|
||||||
http://localhost
|
http://localhost
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
@ -72,12 +65,7 @@ func TestMatcherSyntax(t *testing.T) {
|
||||||
ServerType: ServerType{},
|
ServerType: ServerType{},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, warnings, err := adapter.Adapt([]byte(tc.input), nil)
|
_, _, err := adapter.Adapt([]byte(tc.input), nil)
|
||||||
|
|
||||||
if len(warnings) > 0 != tc.expectWarn {
|
|
||||||
t.Errorf("Test %d warning expectation failed Expected: %v, got %v", i, tc.expectWarn, warnings)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil != tc.expectError {
|
if err != nil != tc.expectError {
|
||||||
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
|
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
|
||||||
|
@ -119,7 +107,6 @@ func TestSpecificity(t *testing.T) {
|
||||||
func TestGlobalOptions(t *testing.T) {
|
func TestGlobalOptions(t *testing.T) {
|
||||||
for i, tc := range []struct {
|
for i, tc := range []struct {
|
||||||
input string
|
input string
|
||||||
expectWarn bool
|
|
||||||
expectError bool
|
expectError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -129,7 +116,6 @@ func TestGlobalOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
:80
|
:80
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -139,7 +125,6 @@ func TestGlobalOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
:80
|
:80
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -149,7 +134,6 @@ func TestGlobalOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
:80
|
:80
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -161,7 +145,6 @@ func TestGlobalOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
:80
|
:80
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -174,7 +157,6 @@ func TestGlobalOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
:80
|
:80
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -187,7 +169,6 @@ func TestGlobalOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
:80
|
:80
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -200,7 +181,6 @@ func TestGlobalOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
:80
|
:80
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -213,7 +193,6 @@ func TestGlobalOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
:80
|
:80
|
||||||
`,
|
`,
|
||||||
expectWarn: false,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
@ -222,12 +201,7 @@ func TestGlobalOptions(t *testing.T) {
|
||||||
ServerType: ServerType{},
|
ServerType: ServerType{},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, warnings, err := adapter.Adapt([]byte(tc.input), nil)
|
_, _, err := adapter.Adapt([]byte(tc.input), nil)
|
||||||
|
|
||||||
if len(warnings) > 0 != tc.expectWarn {
|
|
||||||
t.Errorf("Test %d warning expectation failed Expected: %v, got %v", i, tc.expectWarn, warnings)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil != tc.expectError {
|
if err != nil != tc.expectError {
|
||||||
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
|
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
|
||||||
|
|
|
@ -336,7 +336,6 @@ func CompareAdapt(t *testing.T, rawConfig string, adapterName string, expectedRe
|
||||||
}
|
}
|
||||||
|
|
||||||
options := make(map[string]interface{})
|
options := make(map[string]interface{})
|
||||||
options["pretty"] = "true"
|
|
||||||
|
|
||||||
result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
|
result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -344,6 +343,14 @@ func CompareAdapt(t *testing.T, rawConfig string, adapterName string, expectedRe
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prettify results to keep tests human-manageable
|
||||||
|
var prettyBuf bytes.Buffer
|
||||||
|
err = json.Indent(&prettyBuf, result, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
result = prettyBuf.Bytes()
|
||||||
|
|
||||||
if len(warnings) > 0 {
|
if len(warnings) > 0 {
|
||||||
for _, w := range warnings {
|
for _, w := range warnings {
|
||||||
t.Logf("warning: directive: %s : %s", w.Directive, w.Message)
|
t.Logf("warning: directive: %s : %s", w.Directive, w.Message)
|
||||||
|
|
|
@ -463,17 +463,22 @@ func cmdAdaptConfig(fl Flags) (int, error) {
|
||||||
fmt.Errorf("reading input file: %v", err)
|
fmt.Errorf("reading input file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := make(map[string]interface{})
|
opts := map[string]interface{}{"filename": adaptCmdInputFlag}
|
||||||
if adaptCmdPrettyFlag {
|
|
||||||
opts["pretty"] = "true"
|
|
||||||
}
|
|
||||||
opts["filename"] = adaptCmdInputFlag
|
|
||||||
|
|
||||||
adaptedConfig, warnings, err := cfgAdapter.Adapt(input, opts)
|
adaptedConfig, warnings, err := cfgAdapter.Adapt(input, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, err
|
return caddy.ExitCodeFailedStartup, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if adaptCmdPrettyFlag {
|
||||||
|
var prettyBuf bytes.Buffer
|
||||||
|
err = json.Indent(&prettyBuf, adaptedConfig, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return caddy.ExitCodeFailedStartup, err
|
||||||
|
}
|
||||||
|
adaptedConfig = prettyBuf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
// print warnings to stderr
|
// print warnings to stderr
|
||||||
for _, warn := range warnings {
|
for _, warn := range warnings {
|
||||||
msg := warn.Message
|
msg := warn.Message
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package reverseproxy
|
package reverseproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -497,6 +498,13 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
case 1:
|
case 1:
|
||||||
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], "", "")
|
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], "", "")
|
||||||
case 2:
|
case 2:
|
||||||
|
// some lint checks, I guess
|
||||||
|
if strings.EqualFold(args[0], "host") && (args[1] == "{hostport}" || args[1] == "{http.request.hostport}") {
|
||||||
|
log.Printf("[WARNING] Unnecessary header_up ('Host' field): the reverse proxy's default behavior is to pass headers to the upstream")
|
||||||
|
}
|
||||||
|
if strings.EqualFold(args[0], "x-forwarded-proto") && (args[1] == "{scheme}" || args[1] == "{http.request.scheme}") {
|
||||||
|
log.Printf("[WARNING] Unnecessary header_up ('X-Forwarded-Proto' field): the reverse proxy's default behavior is to pass headers to the upstream")
|
||||||
|
}
|
||||||
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], "")
|
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], "")
|
||||||
case 3:
|
case 3:
|
||||||
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], args[2])
|
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], args[2])
|
||||||
|
|
Loading…
Reference in a new issue