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

Fix backend init #524

Merged
merged 8 commits into from
Jun 21, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

Unreleased changes are available as `avenga/couper:edge` container.

* **Fixed**
* configuration related panic while loading backends with oauth2-block which depends on other defined backends ([#524](https://github.com/avenga/couper/pull/524))
malud marked this conversation as resolved.
Show resolved Hide resolved

---

## [1.9.1](https://github.com/avenga/couper/releases/tag/v1.9.1)
Expand Down
10 changes: 6 additions & 4 deletions config/configload/endpoint_sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import (

"github.com/avenga/couper/config"
"github.com/avenga/couper/config/body"
"github.com/avenga/couper/config/sequence"
"github.com/avenga/couper/eval"
)

// buildSequences collects possible dependencies from 'backend_responses' variable.
func buildSequences(names map[string]hcl.Body, endpoint *config.Endpoint) (err error) {
sequences := map[string]*config.Sequence{}
sequences := map[string]*sequence.Item{}

defer func() {
if rc := recover(); rc != nil {
Expand All @@ -35,14 +37,14 @@ func buildSequences(names map[string]hcl.Body, endpoint *config.Endpoint) (err e

seq, exist := sequences[name]
if !exist {
seq = &config.Sequence{Name: name, BodyRange: getRange(b)}
seq = &sequence.Item{Name: name, BodyRange: getRange(b)}
sequences[name] = seq
}

for _, r := range refs {
ref, ok := sequences[r]
if !ok {
ref = &config.Sequence{Name: r, BodyRange: getRange(b)}
ref = &sequence.Item{Name: r, BodyRange: getRange(b)}
sequences[r] = ref
}
// Do not add ourselves
Expand All @@ -68,7 +70,7 @@ func responseReferences(b hcl.Body) []string {

for _, expr := range body.CollectExpressions(b) {
for _, traversal := range expr.Variables() {
if traversal.RootName() != "backend_responses" || len(traversal) < 2 {
if traversal.RootName() != eval.BackendResponses || len(traversal) < 2 {
continue
}

Expand Down
111 changes: 109 additions & 2 deletions config/configload/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import (

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclsyntax"

"github.com/avenga/couper/config"
"github.com/avenga/couper/config/sequence"
"github.com/avenga/couper/errors"
"github.com/avenga/couper/eval"
)

Expand Down Expand Up @@ -69,7 +72,13 @@ func (h *helper) addBackend(block *hcl.Block) error {
}

func (h *helper) configureDefinedBackends() error {
for name, b := range h.defsBackends {
backendNames, err := h.resolveBackendDeps()
if err != nil {
return err
}

for _, name := range backendNames {
b := h.defsBackends[name]
be, err := PrepareBackend(h, "_init", "", &config.Backend{Name: name, Remain: b})
if err != nil {
return err
Expand All @@ -81,7 +90,7 @@ func (h *helper) configureDefinedBackends() error {

h.defsBackends[name] = be
}
return nil
return err
}

func (h *helper) configureACBackends() error {
Expand All @@ -106,3 +115,101 @@ func (h *helper) configureACBackends() error {
}
return nil
}

// resolveBackendDeps returns defined backends ordered by reference. Referenced ones needs to configured first.
func (h *helper) resolveBackendDeps() (uniqueItems []string, err error) {
malud marked this conversation as resolved.
Show resolved Hide resolved
// collect referenced backends
refs := make(map[string][]string)
h.collectBackendDeps(refs)
// built up deps
refPtr := map[string]*sequence.Item{}
for name := range refs {
parent := &sequence.Item{Name: name}
refPtr[name] = parent
}

defer func() {
if p := recover(); p != nil { // since we use sequence related logic, replace wording due to backend context here
err = errors.Configuration.Message(strings.Replace(fmt.Sprintf("%s", p), "sequence ", "", 1))
}
}()

var defs sequence.List
for parent, ref := range refs {
for _, r := range ref {
p, _ := refPtr[parent]
if be, exist := refPtr[r]; exist {
p.Add(be)
} else {
p.Add(&sequence.Item{Name: r})
}
defs = append(defs, p)
}
}

items := sequence.Dependencies(defs)
//fmt.Printf("%v\n", items)
malud marked this conversation as resolved.
Show resolved Hide resolved

// do not forget the other ones
var standalone []string
for def, _ := range h.defsBackends {
standalone = append(standalone, def)
}
items = append(items, standalone)

// unique by name /w score (sort?) // TODO: MAY refine with scoring of appearance
unique := make(map[string]int)
for _, seqItem := range items {
for _, name := range seqItem {
if _, exist := unique[name]; !exist {
unique[name] = 1
uniqueItems = append(uniqueItems, name)
} else {
unique[name]++
}
}
}

//fmt.Printf("%v\n", uniqueItems)
return uniqueItems, err
malud marked this conversation as resolved.
Show resolved Hide resolved
}

func (h *helper) collectBackendDeps(refs map[string][]string) {
for name, b := range h.defsBackends {
refs[name] = nil
content, _, _ := b.PartialContent(&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{{Type: oauth2}}},
)
oaBlocks := content.Blocks.OfType(oauth2)
for _, ob := range oaBlocks {
osb, ok := ob.Body.(*hclsyntax.Body)
if !ok {
continue
}

for _, be := range osb.Attributes {
if be.Name == backend {
val, _ := be.Expr.Value(envContext)
refs[name] = append(refs[name], val.AsString())
break
}
}

for _, block := range osb.Blocks {
if block.Type != backend {
continue
}
if len(block.Labels) > 0 {
refs[name] = append(refs[name], block.Labels[0])
}

for _, subBlock := range block.Body.Blocks {
if subBlock.Type == oauth2 {
h.collectBackendDeps(refs)
break
}
}
}
}
}
}
3 changes: 2 additions & 1 deletion config/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/hashicorp/hcl/v2/gohcl"

"github.com/avenga/couper/config/meta"
"github.com/avenga/couper/config/sequence"
)

var _ Inline = &Endpoint{}
Expand All @@ -25,7 +26,7 @@ type Endpoint struct {
Proxies Proxies
Requests Requests
RequiredPermission hcl.Expression
Sequences Sequences
Sequences sequence.List
}

// Endpoints represents a list of <Endpoint> objects.
Expand Down
18 changes: 9 additions & 9 deletions config/oauth2ra.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ type OAuth2ReqAuth struct {
}

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

// HCLBody implements the <Inline> interface.
func (oa OAuth2ReqAuth) HCLBody() hcl.Body {
func (oa *OAuth2ReqAuth) HCLBody() hcl.Body {
return oa.Remain
}

// Inline implements the <Inline> interface.
func (oa OAuth2ReqAuth) Inline() interface{} {
func (oa *OAuth2ReqAuth) Inline() interface{} {
type Inline struct {
Backend *Backend `hcl:"backend,block"`
}
Expand All @@ -55,7 +55,7 @@ func (oa OAuth2ReqAuth) Inline() interface{} {
}

// Schema implements the <Inline> interface.
func (oa OAuth2ReqAuth) Schema(inline bool) *hcl.BodySchema {
func (oa *OAuth2ReqAuth) Schema(inline bool) *hcl.BodySchema {
if !inline {
schema, _ := gohcl.ImpliedBodySchema(oa)
return schema
Expand All @@ -78,25 +78,25 @@ func SchemaWithOAuth2RA(schema *hcl.BodySchema) *hcl.BodySchema {
return schema
}

func (oa OAuth2ReqAuth) GetClientID() string {
func (oa *OAuth2ReqAuth) GetClientID() string {
return oa.ClientID
}

func (oa OAuth2ReqAuth) GetClientSecret() string {
func (oa *OAuth2ReqAuth) GetClientSecret() string {
return oa.ClientSecret
}

func (oa OAuth2ReqAuth) GetScope() string {
func (oa *OAuth2ReqAuth) GetScope() string {
if oa.Scope == nil {
return ""
}
return *oa.Scope
}

func (oa OAuth2ReqAuth) GetTokenEndpoint() (string, error) {
func (oa *OAuth2ReqAuth) GetTokenEndpoint() (string, error) {
return oa.TokenEndpoint, nil
}

func (oa OAuth2ReqAuth) GetTokenEndpointAuthMethod() *string {
func (oa *OAuth2ReqAuth) GetTokenEndpointAuthMethod() *string {
return oa.TokenEndpointAuthMethod
}
12 changes: 11 additions & 1 deletion config/runtime/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/avenga/couper/backend"
"github.com/avenga/couper/cache"
"github.com/avenga/couper/config"
"github.com/avenga/couper/errors"
"github.com/avenga/couper/eval"
"github.com/avenga/couper/handler/transport"
"github.com/avenga/couper/handler/validation"
Expand Down Expand Up @@ -122,7 +123,16 @@ func newAuthBackend(evalCtx *hcl.EvalContext, beConf *config.Backend, blocks hcl
return nil, diags
}

innerBackend := innerContent.Blocks.OfType("backend")[0] // backend block is set by configload
backendBlocks := innerContent.Blocks.OfType("backend")
if len(backendBlocks) == 0 {
r := beConf.OAuth2.Remain.MissingItemRange()
diag := &hcl.Diagnostics{&hcl.Diagnostic{
Subject: &r,
Summary: "missing backend initialization",
}}
return nil, errors.Configuration.Label("unexpected").With(diag)
}
innerBackend := backendBlocks[0] // backend block is set by configload
authBackend, authErr := NewBackend(evalCtx, innerBackend.Body, log, conf, memStore)
if authErr != nil {
return nil, authErr
Expand Down
40 changes: 5 additions & 35 deletions config/runtime/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/avenga/couper/cache"
"github.com/avenga/couper/config"
"github.com/avenga/couper/config/runtime/server"
"github.com/avenga/couper/config/sequence"
"github.com/avenga/couper/errors"
"github.com/avenga/couper/eval"
"github.com/avenga/couper/handler"
Expand Down Expand Up @@ -193,16 +194,9 @@ func newEndpointOptions(confCtx *hcl.EvalContext, endpointConf *config.Endpoint,
// newSequences lookups any request related dependency and sort them into a sequence.
// Also return left-overs for parallel usage.
func newSequences(proxies map[string]*producer.Proxy, requests map[string]*producer.Request,
items ...*config.Sequence) (producer.Sequences, producer.Requests, producer.Proxies) {

// just collect for filtering
var allDeps [][]string
for _, item := range items {
deps := make([]string, 0)
seen := make([]string, 0)
resolveSequence(item, &deps, &seen)
allDeps = append(allDeps, deps)
}
items ...*sequence.Item) (producer.Sequences, producer.Requests, producer.Proxies) {

allDeps := sequence.Dependencies(items)

var reqs producer.Requests
var ps producer.Proxies
Expand Down Expand Up @@ -240,7 +234,7 @@ reqLeftovers:
return seqs, reqs, ps
}

func newSequence(seq *config.Sequence,
func newSequence(seq *sequence.Item,
proxies map[string]*producer.Proxy,
requests map[string]*producer.Request) producer.Roundtrip {

Expand Down Expand Up @@ -290,27 +284,3 @@ func newSequenceItem(name, previous string,
}
return nil
}

func resolveSequence(item *config.Sequence, resolved, seen *[]string) {
name := item.Name
*seen = append(*seen, name)
for _, dep := range item.Deps() {
if !containsString(resolved, dep.Name) {
if !containsString(seen, dep.Name) {
resolveSequence(dep, resolved, seen)
continue
}
}
}

*resolved = append(*resolved, name)
}

func containsString(slice *[]string, needle string) bool {
for _, n := range *slice {
if n == needle {
return true
}
}
return false
}
Loading