3
0
Tomaž Jerman f661206a79 Tweak envoy config structs
Have each node define it's own configs where the decoder may
define the wanted values and the global config populates missing
values in the Bake step.
2023-03-17 10:58:46 +01:00

260 lines
5.4 KiB
Go

package envoyx
import (
"context"
"fmt"
"strings"
)
type (
service struct {
decoders map[decodeType][]Decoder
encoders map[encodeType][]Encoder
preparers map[encodeType][]Preparer
}
// Traverser provides a structure which can be used to traverse the node's deps
Traverser interface {
// ParentForRef returns the parent of the provided node which matches the ref
//
// If no parent is found, nil is returned.
ParentForRef(*Node, Ref) *Node
// ChildrenForResourceType returns the children of the provided node which
// match the provided resource type
ChildrenForResourceType(*Node, string) NodeSet
// Children returns all of the children of the provided node
Children(*Node) NodeSet
}
Preparer interface {
// Prepare performs generic preprocessing on the provided nodes
//
// The function is called for every resource type where all of the nodes of
// that resource type are passed as the argument.
Prepare(context.Context, EncodeParams, string, NodeSet) error
}
Encoder interface {
// Encode encodes the data
//
// The function receives a set of root-level nodes (with no parent dependencies)
// and a Traverser it can use to handle all of the child nodes.
Encode(context.Context, EncodeParams, string, NodeSet, Traverser) (err error)
}
PrepareEncoder interface {
Preparer
Encoder
}
Decoder interface {
// Decode returns a set of Nodes extracted based on the provided definition
Decode(ctx context.Context, p DecodeParams) (out NodeSet, err error)
}
DecodeParams struct {
Type decodeType
Params map[string]any
Config DecoderConfig
Filter map[string]ResourceFilter
}
DecoderConfig struct{}
EncodeParams struct {
Type encodeType
Params map[string]any
Envoy EnvoyConfig
Encoder EncoderConfig
}
EncoderConfig struct {
PreferredTimeLayout string
PreferredTimezone string
}
ResourceFilter struct {
Identifiers Identifiers
Refs map[string]Ref
Limit uint
Scope Scope
}
decodeType string
encodeType string
mergeAlg int
)
var (
global *service
)
const (
OnConflictDefault mergeAlg = iota
OnConflictReplace
OnConflictSkip
OnConflictPanic
// OnConflictMergeLeft mergeAlg = "mergeLeft"
// OnConflictMergeRight mergeAlg = "mergeRight"
DecodeTypeURI decodeType = "uri"
DecodeTypeStore decodeType = "store"
EncodeTypeURI encodeType = "uri"
EncodeTypeStore encodeType = "store"
EncodeTypeIo encodeType = "io"
)
// New initializes a new Envoy service
func New() *service {
return &service{}
}
// SetGlobal sets the global envoy service
func SetGlobal(n *service) {
global = n
}
// Service gets the global envoy service
func Service() *service {
if global == nil {
panic("global service not defined")
}
return global
}
// Decode returns a set of envoy Nodes based on the given decode params
func (svc *service) Decode(ctx context.Context, p DecodeParams) (nn NodeSet, err error) {
err = p.validate()
if err != nil {
return
}
switch p.Type {
case DecodeTypeURI:
return svc.decodeUri(ctx, p)
case DecodeTypeStore:
return svc.decodeStore(ctx, p)
default:
err = fmt.Errorf("unsupported decoder type %s", p.Type)
}
return
}
func (svc *service) Bake(ctx context.Context, p EncodeParams, nodes ...*Node) (err error) {
err = svc.bakeEnvoyConfig(p.Envoy, nodes...)
return
}
// Encode encodes Corteza resources bases on the provided encode params
//
// use the BuildDepGraph function to build the default dependency graph.
func (svc *service) Encode(ctx context.Context, p EncodeParams, dg *depGraph) (err error) {
err = p.validate()
if err != nil {
return
}
switch p.Type {
case EncodeTypeStore:
return svc.encodeStore(ctx, dg, p)
case EncodeTypeIo:
return svc.encodeIo(ctx, dg, p)
default:
err = fmt.Errorf("unsupported encoder type %s", p.Type)
}
return
}
func (svc *service) AddDecoder(t decodeType, dd ...Decoder) {
if svc.decoders == nil {
svc.decoders = make(map[decodeType][]Decoder)
}
svc.decoders[t] = append(svc.decoders[t], dd...)
}
func (svc *service) AddEncoder(t encodeType, ee ...Encoder) {
if svc.encoders == nil {
svc.encoders = make(map[encodeType][]Encoder)
}
svc.encoders[t] = append(svc.encoders[t], ee...)
}
func (svc *service) AddPreparer(t encodeType, pp ...Preparer) {
if svc.preparers == nil {
svc.preparers = make(map[encodeType][]Preparer)
}
svc.preparers[t] = append(svc.preparers[t], pp...)
}
// Utility functions
func (p DecodeParams) validate() (err error) {
switch p.Type {
case DecodeTypeURI:
_, ok := p.Params["uri"]
if !ok {
return fmt.Errorf("uhoh, no uri provided")
}
case DecodeTypeStore:
}
// @todo...
return
}
func (p EncodeParams) validate() (err error) {
switch p.Type {
// @todo...
}
// @todo...
return
}
func CastMergeAlg(v string) (mergeAlg mergeAlg) {
switch strings.ToLower(v) {
case "replace", "mergeleft":
mergeAlg = OnConflictReplace
case "skip", "mergeright":
mergeAlg = OnConflictSkip
case "panic", "error":
mergeAlg = OnConflictPanic
}
return
}
func (svc *service) bakeEnvoyConfig(dft EnvoyConfig, nodes ...*Node) (err error) {
for _, n := range nodes {
n.Config = svc.mergeEnvoyConfigs(n.Config, dft)
}
return
}
func (svc *service) mergeEnvoyConfigs(a, b EnvoyConfig) (c EnvoyConfig) {
c = a
if c.MergeAlg == OnConflictDefault {
c.MergeAlg = b.MergeAlg
}
if c.MergeAlg == OnConflictDefault {
c.MergeAlg = OnConflictReplace
}
// @todo pre eval this?
if c.SkipIf == "" {
c.SkipIf = b.SkipIf
}
return
}