Skip to content

Commit c8557dc

Browse files
authored
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.
1 parent 1b453dd commit c8557dc

File tree

7 files changed

+40
-54
lines changed

7 files changed

+40
-54
lines changed

caddyconfig/caddyfile/adapter.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package caddyfile
1616

1717
import (
18+
"bytes"
1819
"encoding/json"
1920
"fmt"
2021

@@ -51,11 +52,17 @@ func (a Adapter) Adapt(body []byte, options map[string]interface{}) ([]byte, []c
5152
return nil, warnings, err
5253
}
5354

54-
marshalFunc := json.Marshal
55-
if options["pretty"] == "true" {
56-
marshalFunc = caddyconfig.JSONIndent
55+
// lint check: see if input was properly formatted; sometimes messy files files parse
56+
// successfully but result in logical errors because the Caddyfile is a bad format
57+
// TODO: also perform this check on imported files
58+
if !bytes.Equal(Format(body), body) {
59+
warnings = append(warnings, caddyconfig.Warning{
60+
File: filename,
61+
Message: "file is not formatted with 'caddy fmt'",
62+
})
5763
}
58-
result, err := marshalFunc(cfg)
64+
65+
result, err := json.Marshal(cfg)
5966

6067
return result, warnings, err
6168
}

caddyconfig/configadapters.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,6 @@ func JSONModuleObject(val interface{}, fieldName, fieldVal string, warnings *[]W
9393
return result
9494
}
9595

96-
// JSONIndent is used to JSON-marshal the final resulting Caddy
97-
// configuration in a consistent, human-readable way.
98-
func JSONIndent(val interface{}) ([]byte, error) {
99-
return json.MarshalIndent(val, "", "\t")
100-
}
101-
10296
// RegisterAdapter registers a config adapter with the given name.
10397
// This should usually be done at init-time. It panics if the
10498
// adapter cannot be registered successfully.

caddyconfig/httpcaddyfile/builtins_test.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@ import (
1010
func TestLogDirectiveSyntax(t *testing.T) {
1111
for i, tc := range []struct {
1212
input string
13-
expectWarn bool
1413
expectError bool
1514
}{
1615
{
1716
input: `:8080 {
1817
log
1918
}
2019
`,
21-
expectWarn: false,
2220
expectError: false,
2321
},
2422
{
@@ -28,7 +26,6 @@ func TestLogDirectiveSyntax(t *testing.T) {
2826
}
2927
}
3028
`,
31-
expectWarn: false,
3229
expectError: false,
3330
},
3431
{
@@ -38,7 +35,6 @@ func TestLogDirectiveSyntax(t *testing.T) {
3835
}
3936
}
4037
`,
41-
expectWarn: false,
4238
expectError: true,
4339
},
4440
} {
@@ -47,12 +43,7 @@ func TestLogDirectiveSyntax(t *testing.T) {
4743
ServerType: ServerType{},
4844
}
4945

50-
_, warnings, err := adapter.Adapt([]byte(tc.input), nil)
51-
52-
if len(warnings) > 0 != tc.expectWarn {
53-
t.Errorf("Test %d warning expectation failed Expected: %v, got %v", i, tc.expectWarn, warnings)
54-
continue
55-
}
46+
_, _, err := adapter.Adapt([]byte(tc.input), nil)
5647

5748
if err != nil != tc.expectError {
5849
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)

caddyconfig/httpcaddyfile/httptype_test.go

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
func TestMatcherSyntax(t *testing.T) {
1010
for i, tc := range []struct {
1111
input string
12-
expectWarn bool
1312
expectError bool
1413
}{
1514
{
@@ -18,7 +17,6 @@ func TestMatcherSyntax(t *testing.T) {
1817
query showdebug=1
1918
}
2019
`,
21-
expectWarn: false,
2220
expectError: false,
2321
},
2422
{
@@ -27,7 +25,6 @@ func TestMatcherSyntax(t *testing.T) {
2725
query bad format
2826
}
2927
`,
30-
expectWarn: false,
3128
expectError: true,
3229
},
3330
{
@@ -38,7 +35,6 @@ func TestMatcherSyntax(t *testing.T) {
3835
}
3936
}
4037
`,
41-
expectWarn: false,
4238
expectError: false,
4339
},
4440
{
@@ -47,14 +43,12 @@ func TestMatcherSyntax(t *testing.T) {
4743
not path /somepath*
4844
}
4945
`,
50-
expectWarn: false,
5146
expectError: false,
5247
},
5348
{
5449
input: `http://localhost
5550
@debug not path /somepath*
5651
`,
57-
expectWarn: false,
5852
expectError: false,
5953
},
6054
{
@@ -63,7 +57,6 @@ func TestMatcherSyntax(t *testing.T) {
6357
}
6458
http://localhost
6559
`,
66-
expectWarn: false,
6760
expectError: true,
6861
},
6962
} {
@@ -72,12 +65,7 @@ func TestMatcherSyntax(t *testing.T) {
7265
ServerType: ServerType{},
7366
}
7467

75-
_, warnings, err := adapter.Adapt([]byte(tc.input), nil)
76-
77-
if len(warnings) > 0 != tc.expectWarn {
78-
t.Errorf("Test %d warning expectation failed Expected: %v, got %v", i, tc.expectWarn, warnings)
79-
continue
80-
}
68+
_, _, err := adapter.Adapt([]byte(tc.input), nil)
8169

8270
if err != nil != tc.expectError {
8371
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
@@ -119,7 +107,6 @@ func TestSpecificity(t *testing.T) {
119107
func TestGlobalOptions(t *testing.T) {
120108
for i, tc := range []struct {
121109
input string
122-
expectWarn bool
123110
expectError bool
124111
}{
125112
{
@@ -129,7 +116,6 @@ func TestGlobalOptions(t *testing.T) {
129116
}
130117
:80
131118
`,
132-
expectWarn: false,
133119
expectError: false,
134120
},
135121
{
@@ -139,7 +125,6 @@ func TestGlobalOptions(t *testing.T) {
139125
}
140126
:80
141127
`,
142-
expectWarn: false,
143128
expectError: false,
144129
},
145130
{
@@ -149,7 +134,6 @@ func TestGlobalOptions(t *testing.T) {
149134
}
150135
:80
151136
`,
152-
expectWarn: false,
153137
expectError: false,
154138
},
155139
{
@@ -161,7 +145,6 @@ func TestGlobalOptions(t *testing.T) {
161145
}
162146
:80
163147
`,
164-
expectWarn: false,
165148
expectError: true,
166149
},
167150
{
@@ -174,7 +157,6 @@ func TestGlobalOptions(t *testing.T) {
174157
}
175158
:80
176159
`,
177-
expectWarn: false,
178160
expectError: false,
179161
},
180162
{
@@ -187,7 +169,6 @@ func TestGlobalOptions(t *testing.T) {
187169
}
188170
:80
189171
`,
190-
expectWarn: false,
191172
expectError: false,
192173
},
193174
{
@@ -200,7 +181,6 @@ func TestGlobalOptions(t *testing.T) {
200181
}
201182
:80
202183
`,
203-
expectWarn: false,
204184
expectError: true,
205185
},
206186
{
@@ -213,7 +193,6 @@ func TestGlobalOptions(t *testing.T) {
213193
}
214194
:80
215195
`,
216-
expectWarn: false,
217196
expectError: true,
218197
},
219198
} {
@@ -222,12 +201,7 @@ func TestGlobalOptions(t *testing.T) {
222201
ServerType: ServerType{},
223202
}
224203

225-
_, warnings, err := adapter.Adapt([]byte(tc.input), nil)
226-
227-
if len(warnings) > 0 != tc.expectWarn {
228-
t.Errorf("Test %d warning expectation failed Expected: %v, got %v", i, tc.expectWarn, warnings)
229-
continue
230-
}
204+
_, _, err := adapter.Adapt([]byte(tc.input), nil)
231205

232206
if err != nil != tc.expectError {
233207
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)

caddytest/caddytest.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,14 +336,21 @@ func CompareAdapt(t *testing.T, rawConfig string, adapterName string, expectedRe
336336
}
337337

338338
options := make(map[string]interface{})
339-
options["pretty"] = "true"
340339

341340
result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
342341
if err != nil {
343342
t.Logf("adapting config using %s adapter: %v", adapterName, err)
344343
return false
345344
}
346345

346+
// prettify results to keep tests human-manageable
347+
var prettyBuf bytes.Buffer
348+
err = json.Indent(&prettyBuf, result, "", "\t")
349+
if err != nil {
350+
return false
351+
}
352+
result = prettyBuf.Bytes()
353+
347354
if len(warnings) > 0 {
348355
for _, w := range warnings {
349356
t.Logf("warning: directive: %s : %s", w.Directive, w.Message)

cmd/commandfuncs.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -463,17 +463,22 @@ func cmdAdaptConfig(fl Flags) (int, error) {
463463
fmt.Errorf("reading input file: %v", err)
464464
}
465465

466-
opts := make(map[string]interface{})
467-
if adaptCmdPrettyFlag {
468-
opts["pretty"] = "true"
469-
}
470-
opts["filename"] = adaptCmdInputFlag
466+
opts := map[string]interface{}{"filename": adaptCmdInputFlag}
471467

472468
adaptedConfig, warnings, err := cfgAdapter.Adapt(input, opts)
473469
if err != nil {
474470
return caddy.ExitCodeFailedStartup, err
475471
}
476472

473+
if adaptCmdPrettyFlag {
474+
var prettyBuf bytes.Buffer
475+
err = json.Indent(&prettyBuf, adaptedConfig, "", "\t")
476+
if err != nil {
477+
return caddy.ExitCodeFailedStartup, err
478+
}
479+
adaptedConfig = prettyBuf.Bytes()
480+
}
481+
477482
// print warnings to stderr
478483
for _, warn := range warnings {
479484
msg := warn.Message

modules/caddyhttp/reverseproxy/caddyfile.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package reverseproxy
1616

1717
import (
18+
"log"
1819
"net"
1920
"net/http"
2021
"net/url"
@@ -497,6 +498,13 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
497498
case 1:
498499
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], "", "")
499500
case 2:
501+
// some lint checks, I guess
502+
if strings.EqualFold(args[0], "host") && (args[1] == "{hostport}" || args[1] == "{http.request.hostport}") {
503+
log.Printf("[WARNING] Unnecessary header_up ('Host' field): the reverse proxy's default behavior is to pass headers to the upstream")
504+
}
505+
if strings.EqualFold(args[0], "x-forwarded-proto") && (args[1] == "{scheme}" || args[1] == "{http.request.scheme}") {
506+
log.Printf("[WARNING] Unnecessary header_up ('X-Forwarded-Proto' field): the reverse proxy's default behavior is to pass headers to the upstream")
507+
}
500508
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], "")
501509
case 3:
502510
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], args[2])

0 commit comments

Comments
 (0)