Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate couper configuration #296

Merged
merged 46 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2bd6281
Draft
Aug 20, 2021
cc91211
Merge branch 'master' into validate-config
Aug 27, 2021
9258fcb
Merge branch 'master' into validate-config
Aug 27, 2021
e4d68f9
Merge branch 'master' into validate-config
Aug 30, 2021
5fac64b
Merge branch 'master' into validate-config
Aug 31, 2021
3d53cee
Fix test
Sep 1, 2021
334be1c
Prepare schema validation
Sep 1, 2021
902440c
Fix schema validation
Sep 1, 2021
5ccda04
Resport meta.Attributes
Sep 1, 2021
abcb14f
Cleanup, reduce complexity
Sep 2, 2021
3a466c2
Merge branch 'master' into validate-config
Sep 2, 2021
e6bed97
Add 'verify' command
Sep 2, 2021
cd8c524
Docu
Sep 2, 2021
324bc6c
Changelog
Sep 2, 2021
3dabfa5
Validate error_handler, remove redundant errors
Sep 8, 2021
8b4532d
Fix name-fetch-regex
Sep 9, 2021
a8f4256
Fix test name
Sep 10, 2021
ab08bf6
Merge branch 'master' into validate-config
Sep 27, 2021
9e3702e
Fix tests
Sep 28, 2021
993db1b
Implement a new Method to be able to access Inline member
Sep 28, 2021
84ad658
Implement recursive checks and checks for error_handler
Sep 29, 2021
b0f82ca
Merge branch 'master' into validate-config
Sep 29, 2021
6bfa174
Fix test
Sep 29, 2021
45a4a77
Fix review notices
Sep 29, 2021
b95998c
Fix tests
Sep 29, 2021
3c1b57b
Check for oauth2 in a backend block
Sep 29, 2021
eb6a9ae
Merge branch 'master' into validate-config
Sep 30, 2021
cac7b84
Fix tests, add inline interface to jwt
Sep 30, 2021
12bd3a1
Do not return empty errors
Sep 30, 2021
a7722cf
AccessControlSetter only with right context blocks
Sep 30, 2021
b78854a
Merge branch 'master' into validate-config
Sep 30, 2021
a3dd262
Merge branch 'master' into validate-config
Oct 18, 2021
8c84746
Merge branch 'master' into validate-config
Oct 20, 2021
66fc9ae
Merge branch 'master' into validate-config
Oct 27, 2021
970cd37
Merge branch 'master' into validate-config
Oct 28, 2021
b6471cc
Merge branch 'master' into validate-config
Oct 28, 2021
fe6debc
Fix test
Oct 28, 2021
58ca13b
Merge branch 'master' into validate-config
Nov 3, 2021
ebb0000
Fix merge conflicts
Nov 3, 2021
e5ec307
Fix tests
Nov 3, 2021
475d614
Merge branch 'master' into validate-config
Nov 9, 2021
ca2659a
Merge branch 'master' into validate-config
Nov 9, 2021
bc0b766
Merge branch 'master' into validate-config
Nov 16, 2021
2146b8b
Merge branch 'master' into validate-config
Nov 17, 2021
26f7620
Remove debug code
Nov 17, 2021
d629dc4
Merge branch 'master' into validate-config
Nov 17, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Unreleased changes are available as `avenga/couper:edge` container.

* **Added**
* `Accept: application/json` request header to the OAuth2 token request, in order to make the Github token endpoint respond with a JSON token response ([#307](https://github.com/avenga/couper/pull/307))
* [`verify`](docs/CLI.md) command to be able to check the syntax of a configuration file w/o starting the server ([#296](https://github.com/avenga/couper/pull/296)), ([#168](https://github.com/avenga/couper/issues/168)), ([#188](https://github.com/avenga/couper/issues/188))
* Documentation of [logs](docs/LOGS.md) ([#310](https://github.com/avenga/couper/pull/310))
* `signing_ttl` and `signing_key`/`signing_key_file` to [`jwt` block](./docs/REFERENCE.md#jwt-block) for use with [`jwt_sign()` function](#functions) ([#309](https://github.com/avenga/couper/pull/309))
* `jwks_url` and `jwks_ttl` to [`jwt` block](./docs/REFERENCE.md#jwt-block) ([#312](https://github.com/avenga/couper/pull/312))
Expand Down
2 changes: 1 addition & 1 deletion accesscontrol/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ func TestJwtConfig(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(subT *testing.T) {
conf, err := configload.LoadBytes([]byte(tt.hcl), "couper.hcl")
conf, err := configload.LoadBytes([]byte(tt.hcl), "couper.hcl", false)
if conf != nil {
_, err = runtime.NewServerConfiguration(conf, log.WithContext(context.TODO()), nil)
}
Expand Down
2 changes: 2 additions & 0 deletions command/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ func NewCommand(ctx context.Context, cmd string) Cmd {
return NewHelp(ctx)
case "version":
return NewVersion()
case "verify":
return NewVerify()
default:
return nil
}
Expand Down
2 changes: 2 additions & 0 deletions command/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ Couper usage:
Available commands:
help Usage for given command.
run Start the server with given configuration file.
verify Verify the syntax of the given configuration file.
version Print the current version and build information.

Examples:
couper run
couper run -f couper.hcl
couper run -watch -log-format json -log-pretty -p 3000
couper verify -f couper.hcl
`)
}

Expand Down
4 changes: 2 additions & 2 deletions command/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func TestNewRun(t *testing.T) {
return
}

couperFile, err := configload.LoadFile(filepath.Join(wd, "testdata/settings", tt.file))
couperFile, err := configload.LoadFile(filepath.Join(wd, "testdata/settings", tt.file), false)
if err != nil {
subT.Error(err)
}
Expand Down Expand Up @@ -185,7 +185,7 @@ func TestAcceptForwarded(t *testing.T) {
return
}

couperFile, err := configload.LoadFile(filepath.Join(wd, "testdata/settings", tt.file))
couperFile, err := configload.LoadFile(filepath.Join(wd, "testdata/settings", tt.file), false)
if err != nil {
subT.Error(err)
}
Expand Down
44 changes: 44 additions & 0 deletions command/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package command

import (
"fmt"

"github.com/avenga/couper/config"
"github.com/avenga/couper/config/configload"
"github.com/hashicorp/hcl/v2"
"github.com/sirupsen/logrus"
)

var _ Cmd = &Verify{}

type Verify struct{}

func NewVerify() *Verify {
return &Verify{}
}

func (v Verify) Execute(args Args, _ *config.Couper, logger *logrus.Entry) error {
if len(args) != 1 {
v.Usage()

err := fmt.Errorf("invalid number of arguments given")
logger.WithError(err).Error()

return err
}

_, err := configload.LoadFile(args[0], true)
if diags, ok := err.(hcl.Diagnostics); ok {
for _, diag := range diags {
logger.WithError(diag).Error()
}

return diags
}

return err
}

func (v Verify) Usage() {
println("Usage of verify:\n verify [-f <file>] Verify the syntax of the given configuration file.")
}
61 changes: 33 additions & 28 deletions config/ac_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import (
"github.com/hashicorp/hcl/v2/gohcl"
)

// Internally used for 'error_handler'.
var _ Body = &JWT{}
var _ Inline = &JWT{}

// Claims represents the <Claims> object.
type Claims hcl.Expression
Expand Down Expand Up @@ -41,11 +40,42 @@ type JWT struct {
Backend hcl.Body
}

// HCLBody implements the <Body> interface. Internally used for 'error_handler'.
// Reference implements the <BackendReference> interface.
func (j *JWT) Reference() string {
return j.BackendName
}

// HCLBody implements the <Body> interface.
func (j *JWT) HCLBody() hcl.Body {
return j.Remain
}

// Inline implements the <Inline> interface.
func (j *JWT) Inline() interface{} {
type Inline struct {
Backend *Backend `hcl:"backend,block"`
}

return &Inline{}
}

// Schema implements the <Inline> interface.
func (j *JWT) Schema(inline bool) *hcl.BodySchema {
if !inline {
schema, _ := gohcl.ImpliedBodySchema(j)
return schema
}

schema, _ := gohcl.ImpliedBodySchema(j.Inline())

// A backend reference is defined, backend block is not allowed.
if j.BackendName != "" {
schema.Blocks = nil
}

return newBackendSchema(schema, j.HCLBody())
}

func (j *JWT) Check() error {
if j.BackendName != "" && j.Backend != nil {
return errors.New("backend must be either block or attribute")
Expand Down Expand Up @@ -75,28 +105,3 @@ func (j *JWT) Check() error {

return nil
}

// Reference implements the <BackendReference> interface.
func (j *JWT) Reference() string {
return j.BackendName
}

func (j *JWT) Schema(inline bool) *hcl.BodySchema {
if !inline {
schema, _ := gohcl.ImpliedBodySchema(j)
return schema
}

type Inline struct {
Backend *Backend `hcl:"backend,block"`
}

schema, _ := gohcl.ImpliedBodySchema(&Inline{})

// A backend reference is defined, backend block is not allowed.
if j.BackendName != "" {
schema.Blocks = nil
}

return newBackendSchema(schema, j.HCLBody())
}
19 changes: 12 additions & 7 deletions config/ac_oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,25 @@ func (oa OAuth2AC) HCLBody() hcl.Body {
return oa.Remain
}

// Inline implements the <Inline> interface.
func (oa OAuth2AC) Inline() interface{} {
type Inline struct {
Backend *Backend `hcl:"backend,block"`
RedirectURI string `hcl:"redirect_uri"`
VerifierValue string `hcl:"verifier_value"`
}

return &Inline{}
}

// Schema implements the <Inline> interface.
func (oa OAuth2AC) Schema(inline bool) *hcl.BodySchema {
if !inline {
schema, _ := gohcl.ImpliedBodySchema(oa)
return schema
}

type Inline struct {
Backend *Backend `hcl:"backend,block"`
RedirectURI string `hcl:"redirect_uri"`
VerifierValue string `hcl:"verifier_value"`
}

schema, _ := gohcl.ImpliedBodySchema(&Inline{})
schema, _ := gohcl.ImpliedBodySchema(oa.Inline())

// A backend reference is defined, backend block is not allowed.
if oa.BackendName != "" {
Expand Down
14 changes: 14 additions & 0 deletions config/ac_setter.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
package config

import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
)

var ACSetterSchema, _ = gohcl.ImpliedBodySchema(&AccessControlSetter{})

type AccessControlSetter struct {
ErrorHandler []*ErrorHandler `hcl:"error_handler,block"`
}

func (acs *AccessControlSetter) Set(ehConf *ErrorHandler) {
acs.ErrorHandler = append(acs.ErrorHandler, ehConf)
}

func SchemaWithACSetter(schema *hcl.BodySchema) *hcl.BodySchema {
schema.Attributes = append(schema.Attributes, ACSetterSchema.Attributes...)
schema.Blocks = append(schema.Blocks, ACSetterSchema.Blocks...)

return schema
}
21 changes: 13 additions & 8 deletions config/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type API struct {
DisableAccessControl []string `hcl:"disable_access_control,optional"`
Endpoints Endpoints `hcl:"endpoint,block"`
ErrorFile string `hcl:"error_file,optional"`
Name string
Name string `hcl:"name,label"`
Remain hcl.Body `hcl:",remain"`
Scope cty.Value `hcl:"beta_scope,optional"`

Expand All @@ -32,20 +32,25 @@ func (a API) HCLBody() hcl.Body {
return a.Remain
}

// Inline implements the <Inline> interface.
func (a API) Inline() interface{} {
type Inline struct {
AddResponseHeaders map[string]string `hcl:"add_response_headers,optional"`
DelResponseHeaders []string `hcl:"remove_response_headers,optional"`
SetResponseHeaders map[string]string `hcl:"set_response_headers,optional"`
}

return &Inline{}
}

// Schema implements the <Inline> interface.
func (a API) Schema(inline bool) *hcl.BodySchema {
if !inline {
schema, _ := gohcl.ImpliedBodySchema(a)
return schema
}

type Inline struct {
AddResponseHeaders map[string]string `hcl:"add_response_headers,optional"`
DelResponseHeaders []string `hcl:"remove_response_headers,optional"`
SetResponseHeaders map[string]string `hcl:"set_response_headers,optional"`
}

schema, _ := gohcl.ImpliedBodySchema(&Inline{})
schema, _ := gohcl.ImpliedBodySchema(a.Inline())

return schema
}
24 changes: 15 additions & 9 deletions config/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,8 @@ func (b Backend) HCLBody() hcl.Body {
return b.Remain
}

// Schema implements the <Inline> interface.
func (b Backend) Schema(inline bool) *hcl.BodySchema {
schema, _ := gohcl.ImpliedBodySchema(b)
if !inline {
return schema
}

// Inline implements the <Inline> interface.
func (b Backend) Inline() interface{} {
type Inline struct {
meta.Attributes
BasicAuth string `hcl:"basic_auth,optional"`
Expand All @@ -58,8 +53,19 @@ func (b Backend) Schema(inline bool) *hcl.BodySchema {
ResponseStatus *uint8 `hcl:"set_response_status,optional"`
}

schema, _ = gohcl.ImpliedBodySchema(&Inline{})
return schema
return &Inline{}
}

// Schema implements the <Inline> interface.
func (b Backend) Schema(inline bool) *hcl.BodySchema {
schema, _ := gohcl.ImpliedBodySchema(b)
if !inline {
return schema
}

schema, _ = gohcl.ImpliedBodySchema(b.Inline())

return meta.SchemaWithAttributes(schema)
}

func newBackendSchema(schema *hcl.BodySchema, body hcl.Body) *hcl.BodySchema {
Expand Down
14 changes: 9 additions & 5 deletions config/configload/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func SetWorkingDirectory(configFile string) (string, error) {
return os.Getwd()
}

func LoadFile(filePath string) (*config.Couper, error) {
func LoadFile(filePath string, verifyOnly bool) (*config.Couper, error) {
_, err := SetWorkingDirectory(filePath)
if err != nil {
return nil, err
Expand All @@ -72,19 +72,23 @@ func LoadFile(filePath string) (*config.Couper, error) {
return nil, fmt.Errorf("failed to load configuration: %w", err)
}

return LoadBytes(src, filename)
return LoadBytes(src, filename, verifyOnly)
}

func LoadBytes(src []byte, filename string) (*config.Couper, error) {
func LoadBytes(src []byte, filename string, verifyOnly bool) (*config.Couper, error) {
hclBody, diags := parser.Load(src, filename)
if diags.HasErrors() {
return nil, diags
}

return LoadConfig(hclBody, src, filename)
return LoadConfig(hclBody, src, filename, verifyOnly)
}

func LoadConfig(body hcl.Body, src []byte, filename string) (*config.Couper, error) {
func LoadConfig(body hcl.Body, src []byte, filename string, verifyOnly bool) (*config.Couper, error) {
if diags := ValidateConfigSchema(body, &config.Couper{}); diags.HasErrors() || verifyOnly {
return nil, diags
}

defaultsBlock := &config.DefaultsBlock{}
if diags := gohcl.DecodeBody(body, nil, defaultsBlock); diags.HasErrors() {
return nil, diags
Expand Down
2 changes: 1 addition & 1 deletion config/configload/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func TestLabels(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(subT *testing.T) {
conf, err := LoadBytes([]byte(tt.hcl), "couper.hcl")
conf, err := LoadBytes([]byte(tt.hcl), "couper.hcl", false)
if conf != nil {
_, err = runtime.NewServerConfiguration(conf, log, nil)
}
Expand Down
Loading