Files
upstream-corteza/server/system/envoy/yaml_decode.gen.go
upstream-mirror 7e385fc126
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
2026-09-18 01:22:23 +05:00

3780 lines
94 KiB
Go
Generated

package envoy
// This file is auto-generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
import (
"context"
"io"
"os"
"strings"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/pkg/rbac"
"github.com/cortezaproject/corteza/server/pkg/y7s"
"github.com/cortezaproject/corteza/server/system/types"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
"github.com/pkg/errors"
"github.com/spf13/cast"
"golang.org/x/text/language"
"gopkg.in/yaml.v3"
)
type (
// YamlDecoder is responsible for decoding YAML documents into Corteza resources
// which are then managed by envoy and imported via an encoder.
YamlDecoder struct{}
// documentContext provides a bit of metadata to the decoder such as
// root-level reference definitions (such as the namespace)
documentContext struct {
// references holds any references defined on the root of the document
//
// Example of defining a namespace reference:
// namespace: test_import_namespace
// modules:
// - name: Test Import Module
// handle: test_import_module
references map[string]string
// parentIdent holds the identifier of the parent resource (if nested).
// The parent ident can be handy if the resource requires the info before the
// original handling is finished (such as record datasource nodes).
parentIdent envoyx.Identifiers
}
// auxYamlDoc is a helper struct that registers custom YAML decoders
// to assist the package
auxYamlDoc struct {
nodes envoyx.NodeSet
}
)
const (
paramsKeyStream = "stream"
)
// CanFile returns true if the provided file can be decoded with this decoder
func (d YamlDecoder) CanFile(f *os.File) (ok bool) {
// @todo improve this check; for now a simple extension check should do the trick
return d.CanExt(f.Name())
}
// CanExt returns true if the provided file extension can be decoded with this decoder
func (d YamlDecoder) CanExt(name string) (ok bool) {
var (
pt = strings.Split(name, ".")
ext = strings.TrimSpace(pt[len(pt)-1])
)
return ext == "yaml" || ext == "yml"
}
// Decode returns a set of envoy nodes based on the provided params
//
// YamlDecoder expects the DecodeParam of `stream` which conforms
// to the io.Reader interface.
func (d YamlDecoder) Decode(ctx context.Context, p envoyx.DecodeParams) (out envoyx.NodeSet, err error) {
r, err := d.getReader(ctx, p)
if err != nil {
return
}
// Offload decoding to the aux document
doc := &auxYamlDoc{}
err = yaml.NewDecoder(r).Decode(doc)
if err != nil {
err = errors.Wrap(err, "system yaml decoder: failed to decode document")
return
}
return doc.nodes, nil
}
func (d *auxYamlDoc) UnmarshalYAML(n *yaml.Node) (err error) {
// Get the document context from the root level
dctx, err := d.getDocumentContext(n)
if err != nil {
return
}
var aux envoyx.NodeSet
return y7s.EachMap(n, func(k, v *yaml.Node) error {
kv := strings.ToLower(k.Value)
switch kv {
// Decode all resources under the system component
case "application", "applications", "apps":
if y7s.IsMapping(v) {
aux, err = d.unmarshalApplicationMap(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if y7s.IsSeq(v) {
aux, err = d.unmarshalApplicationSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: application")
}
return err
case "apigwroute", "endpoints":
if y7s.IsMapping(v) {
aux, err = d.unmarshalApigwRouteMap(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if y7s.IsSeq(v) {
aux, err = d.unmarshalApigwRouteSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: apigwRoute")
}
return err
case "apigwfilter":
// @note apigwFilter doesn't support mapped inputs. This can be
// changed in the .cue definition under the
// .envoy.yaml.supportMappedInput field.
if y7s.IsSeq(v) {
aux, err = d.unmarshalApigwFilterSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: apigwFilter")
}
return err
case "authclient", "authclients":
if y7s.IsMapping(v) {
aux, err = d.unmarshalAuthClientMap(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if y7s.IsSeq(v) {
aux, err = d.unmarshalAuthClientSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: authClient")
}
return err
case "queue":
if y7s.IsMapping(v) {
aux, err = d.unmarshalQueueMap(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if y7s.IsSeq(v) {
aux, err = d.unmarshalQueueSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: queue")
}
return err
case "report", "reports":
if y7s.IsMapping(v) {
aux, err = d.unmarshalReportMap(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if y7s.IsSeq(v) {
aux, err = d.unmarshalReportSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: report")
}
return err
case "role", "roles":
if y7s.IsMapping(v) {
aux, err = d.unmarshalRoleMap(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if y7s.IsSeq(v) {
aux, err = d.unmarshalRoleSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: role")
}
return err
case "template", "templates":
if y7s.IsMapping(v) {
aux, err = d.unmarshalTemplateMap(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if y7s.IsSeq(v) {
aux, err = d.unmarshalTemplateSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: template")
}
return err
case "user", "users", "usr":
if y7s.IsMapping(v) {
aux, err = d.unmarshalUserMap(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if y7s.IsSeq(v) {
aux, err = d.unmarshalUserSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: user")
}
return err
case "dalconnection", "connection", "connections":
if y7s.IsMapping(v) {
aux, err = d.unmarshalDalConnectionMap(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if y7s.IsSeq(v) {
aux, err = d.unmarshalDalConnectionSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: dalConnection")
}
return err
case "dalsensitivitylevel", "sensitivity_level":
if y7s.IsMapping(v) {
aux, err = d.unmarshalDalSensitivityLevelMap(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if y7s.IsSeq(v) {
aux, err = d.unmarshalDalSensitivityLevelSeq(dctx, v)
d.nodes = append(d.nodes, aux...)
}
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: dalSensitivityLevel")
}
return err
// Decode access control nodes
case "allow":
aux, err = unmarshalAllowNode(v)
d.nodes = append(d.nodes, aux...)
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: RBAC allow")
}
case "deny":
aux, err = unmarshalDenyNode(v)
d.nodes = append(d.nodes, aux...)
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: RBAC deny")
}
// Resource translation nodes
case "locale", "translation", "translations", "i18n":
aux, err = unmarshalLocaleNode(v)
d.nodes = append(d.nodes, aux...)
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node: locale")
}
// Offload to custom handlers
default:
aux, err = d.unmarshalYAML(kv, v)
d.nodes = append(d.nodes, aux...)
if err != nil {
err = errors.Wrap(err, "failed to unmarshal node")
}
}
return nil
})
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource application
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalApplicationSeq unmarshals Application when provided as a sequence node
func (d *auxYamlDoc) unmarshalApplicationSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalApplicationNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalApplicationMap unmarshals Application when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
func (d *auxYamlDoc) unmarshalApplicationMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
aux, err = d.unmarshalApplicationNode(dctx, n, k)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalApplicationNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalApplicationNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.Application
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
// When a resource supports mapped input, the key is passed as meta which
// needs to be registered as an identifier (since it is)
if len(meta) > 0 {
y7s.DecodeScalar(meta[0], "Name", &r.Name)
ii = ii.Add(r.Name)
}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
rbacNodes envoyx.NodeSet
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "ownerid", "owner":
// Handle references
err = y7s.DecodeScalar(n, "ownerID", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["OwnerID"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
// Handle RBAC rules
case "allow":
auxOut, err = unmarshalAllowNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "deny":
auxOut, err = unmarshalDenyNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under application such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["ApplicationID"]; !ok {
a.References["ApplicationID"] = envoyx.Ref{
ResourceType: types.ApplicationResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.ApplicationResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Update RBAC resource nodes with references regarding the resource
rres := r.RbacResource()
for _, rn := range rbacNodes {
aux := rn.Resource.(*rbac.Rule)
if aux.Resource == "" {
aux.Resource = rres
}
// Since the rule belongs to the resource, it will have the same
// subset of references as the parent resource.
rn.References = envoyx.MergeRefs(rn.References, a.References)
// The RBAC rule's most specific identifier is the resource itself.
// Using this we can hardcode it to point to the location after the parent resource.
//
// @todo consider using a more descriptive identifier for the position
// such as `index-%d`.
rn.References["0"] = envoyx.Ref{
ResourceType: a.ResourceType,
Identifiers: a.Identifiers,
Scope: scope,
}
for _, r := range rn.References {
if r.Scope.IsEmpty() {
continue
}
rn.Scope = r.Scope
break
}
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
out = append(out, rbacNodes...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource apigwRoute
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalApigwRouteSeq unmarshals ApigwRoute when provided as a sequence node
func (d *auxYamlDoc) unmarshalApigwRouteSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalApigwRouteNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalApigwRouteMap unmarshals ApigwRoute when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
func (d *auxYamlDoc) unmarshalApigwRouteMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
aux, err = d.unmarshalApigwRouteNode(dctx, n, k)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalFiltersExtendedSeq unmarshals Filters when provided as a sequence node
func (d *auxYamlDoc) unmarshalExtendedFiltersSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalFiltersExtendedNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalApigwRouteNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalApigwRouteNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.ApigwRoute
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
// When a resource supports mapped input, the key is passed as meta which
// needs to be registered as an identifier (since it is)
if len(meta) > 0 {
y7s.DecodeScalar(meta[0], "Endpoint", &r.Endpoint)
ii = ii.Add(r.Endpoint)
}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
rbacNodes envoyx.NodeSet
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "createdby":
// Handle references
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["CreatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "deletedby":
// Handle references
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["DeletedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "group":
// Handle references
err = y7s.DecodeScalar(n, "group", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["Group"] = envoyx.Ref{
ResourceType: "corteza::system:apigw-group",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "updatedby":
// Handle references
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["UpdatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
// Handle RBAC rules
case "allow":
auxOut, err = unmarshalAllowNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "deny":
auxOut, err = unmarshalDenyNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under apigwRoute such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
case "filters":
if y7s.IsSeq(n) {
nestedNodes, err = d.unmarshalExtendedFiltersSeq(dctx, n)
if err != nil {
return err
}
}
break
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["ApigwRouteID"]; !ok {
a.References["ApigwRouteID"] = envoyx.Ref{
ResourceType: types.ApigwRouteResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.ApigwRouteResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Update RBAC resource nodes with references regarding the resource
rres := r.RbacResource()
for _, rn := range rbacNodes {
aux := rn.Resource.(*rbac.Rule)
if aux.Resource == "" {
aux.Resource = rres
}
// Since the rule belongs to the resource, it will have the same
// subset of references as the parent resource.
rn.References = envoyx.MergeRefs(rn.References, a.References)
// The RBAC rule's most specific identifier is the resource itself.
// Using this we can hardcode it to point to the location after the parent resource.
//
// @todo consider using a more descriptive identifier for the position
// such as `index-%d`.
rn.References["0"] = envoyx.Ref{
ResourceType: a.ResourceType,
Identifiers: a.Identifiers,
Scope: scope,
}
for _, r := range rn.References {
if r.Scope.IsEmpty() {
continue
}
rn.Scope = r.Scope
break
}
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
out = append(out, rbacNodes...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource apigwFilter
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalApigwFilterSeq unmarshals ApigwFilter when provided as a sequence node
func (d *auxYamlDoc) unmarshalApigwFilterSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalApigwFilterNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalApigwFilterMap unmarshals ApigwFilter when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
// @note this resource does not support map encoding.
// Refer to the corresponding definition files to adjust if needed.
// unmarshalApigwFilterNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalApigwFilterNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.ApigwFilter
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "createdby":
// Handle references
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["CreatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "deletedby":
// Handle references
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["DeletedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "route":
// Handle references
err = y7s.DecodeScalar(n, "route", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["Route"] = envoyx.Ref{
ResourceType: "corteza::system:apigw-route",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "updatedby":
// Handle references
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["UpdatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under apigwFilter such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["ApigwFilterID"]; !ok {
a.References["ApigwFilterID"] = envoyx.Ref{
ResourceType: types.ApigwFilterResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.ApigwFilterResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource authClient
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalAuthClientSeq unmarshals AuthClient when provided as a sequence node
func (d *auxYamlDoc) unmarshalAuthClientSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalAuthClientNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalAuthClientMap unmarshals AuthClient when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
func (d *auxYamlDoc) unmarshalAuthClientMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
aux, err = d.unmarshalAuthClientNode(dctx, n, k)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalAuthClientNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalAuthClientNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.AuthClient
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
// When a resource supports mapped input, the key is passed as meta which
// needs to be registered as an identifier (since it is)
if len(meta) > 0 {
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
ii = ii.Add(r.Handle)
}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
rbacNodes envoyx.NodeSet
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "createdby":
// Handle references
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["CreatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "deletedby":
// Handle references
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["DeletedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "handle":
// Handle identifiers
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "ownedby":
// Handle references
err = y7s.DecodeScalar(n, "ownedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["OwnedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "security":
// Handle custom node decoder
//
// The decoder may update the passed resource with arbitrary values
// as well as provide additional references and identifiers for the node.
var (
auxRefs map[string]envoyx.Ref
auxIdents envoyx.Identifiers
)
auxRefs, auxIdents, err = d.unmarshalAuthClientSecurityNode(r, n)
if err != nil {
return err
}
refs = envoyx.MergeRefs(refs, auxRefs)
ii = ii.Merge(auxIdents)
break
case "updatedby":
// Handle references
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["UpdatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
// Handle RBAC rules
case "allow":
auxOut, err = unmarshalAllowNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "deny":
auxOut, err = unmarshalDenyNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under authClient such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["AuthClientID"]; !ok {
a.References["AuthClientID"] = envoyx.Ref{
ResourceType: types.AuthClientResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.AuthClientResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Update RBAC resource nodes with references regarding the resource
rres := r.RbacResource()
for _, rn := range rbacNodes {
aux := rn.Resource.(*rbac.Rule)
if aux.Resource == "" {
aux.Resource = rres
}
// Since the rule belongs to the resource, it will have the same
// subset of references as the parent resource.
rn.References = envoyx.MergeRefs(rn.References, a.References)
// The RBAC rule's most specific identifier is the resource itself.
// Using this we can hardcode it to point to the location after the parent resource.
//
// @todo consider using a more descriptive identifier for the position
// such as `index-%d`.
rn.References["0"] = envoyx.Ref{
ResourceType: a.ResourceType,
Identifiers: a.Identifiers,
Scope: scope,
}
for _, r := range rn.References {
if r.Scope.IsEmpty() {
continue
}
rn.Scope = r.Scope
break
}
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
out = append(out, rbacNodes...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource queue
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalQueueSeq unmarshals Queue when provided as a sequence node
func (d *auxYamlDoc) unmarshalQueueSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalQueueNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalQueueMap unmarshals Queue when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
func (d *auxYamlDoc) unmarshalQueueMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
aux, err = d.unmarshalQueueNode(dctx, n, k)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalQueueNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalQueueNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.Queue
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
// When a resource supports mapped input, the key is passed as meta which
// needs to be registered as an identifier (since it is)
if len(meta) > 0 {
y7s.DecodeScalar(meta[0], "Queue", &r.Queue)
ii = ii.Add(r.Queue)
}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
rbacNodes envoyx.NodeSet
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "createdby":
// Handle references
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["CreatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "deletedby":
// Handle references
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["DeletedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "queue":
// Handle identifiers
err = y7s.DecodeScalar(n, "queue", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "updatedby":
// Handle references
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["UpdatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
// Handle RBAC rules
case "allow":
auxOut, err = unmarshalAllowNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "deny":
auxOut, err = unmarshalDenyNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under queue such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["QueueID"]; !ok {
a.References["QueueID"] = envoyx.Ref{
ResourceType: types.QueueResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.QueueResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Update RBAC resource nodes with references regarding the resource
rres := r.RbacResource()
for _, rn := range rbacNodes {
aux := rn.Resource.(*rbac.Rule)
if aux.Resource == "" {
aux.Resource = rres
}
// Since the rule belongs to the resource, it will have the same
// subset of references as the parent resource.
rn.References = envoyx.MergeRefs(rn.References, a.References)
// The RBAC rule's most specific identifier is the resource itself.
// Using this we can hardcode it to point to the location after the parent resource.
//
// @todo consider using a more descriptive identifier for the position
// such as `index-%d`.
rn.References["0"] = envoyx.Ref{
ResourceType: a.ResourceType,
Identifiers: a.Identifiers,
Scope: scope,
}
for _, r := range rn.References {
if r.Scope.IsEmpty() {
continue
}
rn.Scope = r.Scope
break
}
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
out = append(out, rbacNodes...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource report
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalReportSeq unmarshals Report when provided as a sequence node
func (d *auxYamlDoc) unmarshalReportSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalReportNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalReportMap unmarshals Report when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
func (d *auxYamlDoc) unmarshalReportMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
aux, err = d.unmarshalReportNode(dctx, n, k)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalReportNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalReportNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.Report
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
// When a resource supports mapped input, the key is passed as meta which
// needs to be registered as an identifier (since it is)
if len(meta) > 0 {
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
ii = ii.Add(r.Handle)
}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
rbacNodes envoyx.NodeSet
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "createdby":
// Handle references
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["CreatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "deletedby":
// Handle references
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["DeletedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "handle":
// Handle identifiers
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "ownedby":
// Handle references
err = y7s.DecodeScalar(n, "ownedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["OwnedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "updatedby":
// Handle references
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["UpdatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
// Handle RBAC rules
case "allow":
auxOut, err = unmarshalAllowNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "deny":
auxOut, err = unmarshalDenyNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under report such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["ReportID"]; !ok {
a.References["ReportID"] = envoyx.Ref{
ResourceType: types.ReportResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.ReportResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Update RBAC resource nodes with references regarding the resource
rres := r.RbacResource()
for _, rn := range rbacNodes {
aux := rn.Resource.(*rbac.Rule)
if aux.Resource == "" {
aux.Resource = rres
}
// Since the rule belongs to the resource, it will have the same
// subset of references as the parent resource.
rn.References = envoyx.MergeRefs(rn.References, a.References)
// The RBAC rule's most specific identifier is the resource itself.
// Using this we can hardcode it to point to the location after the parent resource.
//
// @todo consider using a more descriptive identifier for the position
// such as `index-%d`.
rn.References["0"] = envoyx.Ref{
ResourceType: a.ResourceType,
Identifiers: a.Identifiers,
Scope: scope,
}
for _, r := range rn.References {
if r.Scope.IsEmpty() {
continue
}
rn.Scope = r.Scope
break
}
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
out = append(out, rbacNodes...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource role
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalRoleSeq unmarshals Role when provided as a sequence node
func (d *auxYamlDoc) unmarshalRoleSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalRoleNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalRoleMap unmarshals Role when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
func (d *auxYamlDoc) unmarshalRoleMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
aux, err = d.unmarshalRoleNode(dctx, n, k)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalRoleNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalRoleNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.Role
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
// When a resource supports mapped input, the key is passed as meta which
// needs to be registered as an identifier (since it is)
if len(meta) > 0 {
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
ii = ii.Add(r.Handle)
}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
rbacNodes envoyx.NodeSet
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "handle":
// Handle identifiers
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
// Handle RBAC rules
case "allow":
auxOut, err = unmarshalAllowNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "deny":
auxOut, err = unmarshalDenyNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under role such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["RoleID"]; !ok {
a.References["RoleID"] = envoyx.Ref{
ResourceType: types.RoleResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.RoleResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Update RBAC resource nodes with references regarding the resource
rres := r.RbacResource()
for _, rn := range rbacNodes {
aux := rn.Resource.(*rbac.Rule)
if aux.Resource == "" {
aux.Resource = rres
}
// Since the rule belongs to the resource, it will have the same
// subset of references as the parent resource.
rn.References = envoyx.MergeRefs(rn.References, a.References)
// The RBAC rule's most specific identifier is the resource itself.
// Using this we can hardcode it to point to the location after the parent resource.
//
// @todo consider using a more descriptive identifier for the position
// such as `index-%d`.
rn.References["0"] = envoyx.Ref{
ResourceType: a.ResourceType,
Identifiers: a.Identifiers,
Scope: scope,
}
for _, r := range rn.References {
if r.Scope.IsEmpty() {
continue
}
rn.Scope = r.Scope
break
}
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
out = append(out, rbacNodes...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource template
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalTemplateSeq unmarshals Template when provided as a sequence node
func (d *auxYamlDoc) unmarshalTemplateSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalTemplateNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalTemplateMap unmarshals Template when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
func (d *auxYamlDoc) unmarshalTemplateMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
aux, err = d.unmarshalTemplateNode(dctx, n, k)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalTemplateNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalTemplateNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.Template
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
// When a resource supports mapped input, the key is passed as meta which
// needs to be registered as an identifier (since it is)
if len(meta) > 0 {
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
ii = ii.Add(r.Handle)
}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
rbacNodes envoyx.NodeSet
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "handle":
// Handle identifiers
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "ownerid":
// Handle references
err = y7s.DecodeScalar(n, "ownerID", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["OwnerID"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
// Handle RBAC rules
case "allow":
auxOut, err = unmarshalAllowNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "deny":
auxOut, err = unmarshalDenyNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under template such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["TemplateID"]; !ok {
a.References["TemplateID"] = envoyx.Ref{
ResourceType: types.TemplateResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.TemplateResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Update RBAC resource nodes with references regarding the resource
rres := r.RbacResource()
for _, rn := range rbacNodes {
aux := rn.Resource.(*rbac.Rule)
if aux.Resource == "" {
aux.Resource = rres
}
// Since the rule belongs to the resource, it will have the same
// subset of references as the parent resource.
rn.References = envoyx.MergeRefs(rn.References, a.References)
// The RBAC rule's most specific identifier is the resource itself.
// Using this we can hardcode it to point to the location after the parent resource.
//
// @todo consider using a more descriptive identifier for the position
// such as `index-%d`.
rn.References["0"] = envoyx.Ref{
ResourceType: a.ResourceType,
Identifiers: a.Identifiers,
Scope: scope,
}
for _, r := range rn.References {
if r.Scope.IsEmpty() {
continue
}
rn.Scope = r.Scope
break
}
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
out = append(out, rbacNodes...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource user
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalUserSeq unmarshals User when provided as a sequence node
func (d *auxYamlDoc) unmarshalUserSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalUserNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalUserMap unmarshals User when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
func (d *auxYamlDoc) unmarshalUserMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
aux, err = d.unmarshalUserNode(dctx, n, k)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalUserNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalUserNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.User
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
// When a resource supports mapped input, the key is passed as meta which
// needs to be registered as an identifier (since it is)
if len(meta) > 0 {
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
ii = ii.Add(r.Handle)
}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
rbacNodes envoyx.NodeSet
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "handle":
// Handle identifiers
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "roles":
// Handle custom node decoder
//
// The decoder may update the passed resource with arbitrary values
// as well as provide additional references and identifiers for the node.
var (
auxRefs map[string]envoyx.Ref
auxIdents envoyx.Identifiers
)
auxRefs, auxIdents, err = d.unmarshalUserRolesNode(r, n)
if err != nil {
return err
}
refs = envoyx.MergeRefs(refs, auxRefs)
ii = ii.Merge(auxIdents)
break
case "usergroupid", "usergroup", "user_group", "user_group_id", "group":
// Handle references
err = y7s.DecodeScalar(n, "userGroupID", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["UserGroupID"] = envoyx.Ref{
ResourceType: "corteza::system:user-group",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
// Handle RBAC rules
case "allow":
auxOut, err = unmarshalAllowNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "deny":
auxOut, err = unmarshalDenyNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under user such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["UserID"]; !ok {
a.References["UserID"] = envoyx.Ref{
ResourceType: types.UserResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.UserResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Update RBAC resource nodes with references regarding the resource
rres := r.RbacResource()
for _, rn := range rbacNodes {
aux := rn.Resource.(*rbac.Rule)
if aux.Resource == "" {
aux.Resource = rres
}
// Since the rule belongs to the resource, it will have the same
// subset of references as the parent resource.
rn.References = envoyx.MergeRefs(rn.References, a.References)
// The RBAC rule's most specific identifier is the resource itself.
// Using this we can hardcode it to point to the location after the parent resource.
//
// @todo consider using a more descriptive identifier for the position
// such as `index-%d`.
rn.References["0"] = envoyx.Ref{
ResourceType: a.ResourceType,
Identifiers: a.Identifiers,
Scope: scope,
}
for _, r := range rn.References {
if r.Scope.IsEmpty() {
continue
}
rn.Scope = r.Scope
break
}
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
out = append(out, rbacNodes...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource dalConnection
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalDalConnectionSeq unmarshals DalConnection when provided as a sequence node
func (d *auxYamlDoc) unmarshalDalConnectionSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalDalConnectionNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalDalConnectionMap unmarshals DalConnection when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
func (d *auxYamlDoc) unmarshalDalConnectionMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
aux, err = d.unmarshalDalConnectionNode(dctx, n, k)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalDalConnectionNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalDalConnectionNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.DalConnection
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
// When a resource supports mapped input, the key is passed as meta which
// needs to be registered as an identifier (since it is)
if len(meta) > 0 {
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
ii = ii.Add(r.Handle)
}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
rbacNodes envoyx.NodeSet
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "createdby":
// Handle references
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["CreatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "deletedby":
// Handle references
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["DeletedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "handle":
// Handle identifiers
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "updatedby":
// Handle references
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["UpdatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
// Handle RBAC rules
case "allow":
auxOut, err = unmarshalAllowNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "deny":
auxOut, err = unmarshalDenyNode(n)
if err != nil {
return err
}
rbacNodes = append(rbacNodes, auxOut...)
auxOut = nil
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under dalConnection such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["DalConnectionID"]; !ok {
a.References["DalConnectionID"] = envoyx.Ref{
ResourceType: types.DalConnectionResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.DalConnectionResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Update RBAC resource nodes with references regarding the resource
rres := r.RbacResource()
for _, rn := range rbacNodes {
aux := rn.Resource.(*rbac.Rule)
if aux.Resource == "" {
aux.Resource = rres
}
// Since the rule belongs to the resource, it will have the same
// subset of references as the parent resource.
rn.References = envoyx.MergeRefs(rn.References, a.References)
// The RBAC rule's most specific identifier is the resource itself.
// Using this we can hardcode it to point to the location after the parent resource.
//
// @todo consider using a more descriptive identifier for the position
// such as `index-%d`.
rn.References["0"] = envoyx.Ref{
ResourceType: a.ResourceType,
Identifiers: a.Identifiers,
Scope: scope,
}
for _, r := range rn.References {
if r.Scope.IsEmpty() {
continue
}
rn.Scope = r.Scope
break
}
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
out = append(out, rbacNodes...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Functions for resource dalSensitivityLevel
// // // // // // // // // // // // // // // // // // // // // // // // //
// unmarshalDalSensitivityLevelSeq unmarshals DalSensitivityLevel when provided as a sequence node
func (d *auxYamlDoc) unmarshalDalSensitivityLevelSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachSeq(n, func(n *yaml.Node) error {
aux, err = d.unmarshalDalSensitivityLevelNode(dctx, n)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalDalSensitivityLevelMap unmarshals DalSensitivityLevel when provided as a mapping node
//
// When map encoded, the map key is used as a preset identifier.
// The identifier is passed to the node function as a meta node
func (d *auxYamlDoc) unmarshalDalSensitivityLevelMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
var aux envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
aux, err = d.unmarshalDalSensitivityLevelNode(dctx, n, k)
if err != nil {
return err
}
out = append(out, aux...)
return nil
})
return
}
// unmarshalDalSensitivityLevelNode is a cookie-cutter function to unmarshal
// the yaml node into the corresponding Corteza type & Node
func (d *auxYamlDoc) unmarshalDalSensitivityLevelNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
var r *types.DalSensitivityLevel
// @todo we're omitting errors because there will be a bunch due to invalid
// resource field types. This might be a bit unstable as other errors may
// also get ignored.
//
// A potential fix would be to firstly unmarshal into an any, check errors
// and then unmarshal into the resource while omitting errors.
n.Decode(&r)
// Identifiers are determined manually when iterating the yaml node.
// This is to help assure there are no duplicates and everything
// was accounted for especially when working with aliases such as
// user_name instead of userName.
ii := envoyx.Identifiers{}
// When a resource supports mapped input, the key is passed as meta which
// needs to be registered as an identifier (since it is)
if len(meta) > 0 {
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
ii = ii.Add(r.Handle)
}
var (
refs = make(map[string]envoyx.Ref)
auxOut envoyx.NodeSet
nestedNodes envoyx.NodeSet
scope envoyx.Scope
envoyConfig envoyx.EnvoyConfig
)
_ = auxOut
_ = refs
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
var auxNodeValue any
_ = auxNodeValue
switch strings.ToLower(k.Value) {
case "createdby":
// Handle references
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["CreatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "deletedby":
// Handle references
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["DeletedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "handle":
// Handle identifiers
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "id":
// Handle identifiers
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
if err != nil {
return err
}
ii = ii.Add(auxNodeValue)
break
case "updatedby":
// Handle references
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
if err != nil {
return err
}
// Omit if not defined
tmp := cast.ToString(auxNodeValue)
if tmp == "0" || tmp == "" {
break
}
refs["UpdatedBy"] = envoyx.Ref{
ResourceType: "corteza::system:user",
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
}
break
case "(envoy)":
envoyConfig = d.decodeEnvoyConfig(n)
}
return nil
})
if err != nil {
return
}
// Make parent identifiers available through the dctx
dctx.parentIdent = ii
// Apply the scope to all of the references of the same type
for k, ref := range refs {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
refs[k] = ref
}
// Handle any resources that could be inserted under dalSensitivityLevel such as a module inside a namespace
//
// This operation is done in the second pass of the document so we have
// the complete context of the current resource; such as the identifier,
// references, and scope.
var auxNestedNodes envoyx.NodeSet
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
nestedNodes = nil
switch strings.ToLower(k.Value) {
}
// Iterate nested nodes and update their reference to the current resource
//
// Any reference to the parent resource from the child resource is overwritten
// to avoid potential user-error edge cases.
for _, a := range nestedNodes {
// @note all nested resources fall under the same component and the same scope.
// Simply assign the same scope to all -- if it shouldn't be scoped
// the parent won't have it (saving CPU ticks :)
a.Scope = scope
if a.References == nil {
a.References = make(map[string]envoyx.Ref)
}
if _, ok := a.References["DalSensitivityLevelID"]; !ok {
a.References["DalSensitivityLevelID"] = envoyx.Ref{
ResourceType: types.DalSensitivityLevelResourceType,
Identifiers: ii,
Scope: scope,
}
}
for f, ref := range a.References {
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
continue
}
ref.Scope = scope
a.References[f] = ref
}
for f, ref := range refs {
// Only inherit root references
// @todo improve; this is a hack
if strings.Contains(f, ".") {
continue
}
// Only assume refs if they're not yet set
if _, ok := a.References[f]; ok {
continue
}
a.References[f] = ref
}
}
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
return nil
})
if err != nil {
return
}
out = append(out, auxNestedNodes...)
a := &envoyx.Node{
Resource: r,
ResourceType: types.DalSensitivityLevelResourceType,
Identifiers: ii,
References: refs,
Config: envoyConfig,
}
// Put it all together...
out = append(out, a)
out = append(out, auxOut...)
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// RBAC unmarshal logic
// // // // // // // // // // // // // // // // // // // // // // // // //
func unmarshalAllowNode(n *yaml.Node) (out envoyx.NodeSet, err error) {
return unmarshalRBACNode(n, rbac.Allow)
}
func unmarshalDenyNode(n *yaml.Node) (out envoyx.NodeSet, err error) {
return unmarshalRBACNode(n, rbac.Deny)
}
func unmarshalRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
if y7s.IsMapping(n.Content[1]) {
out, err = unmarshalFlatRBACNode(n, acc)
if err != nil {
return
}
} else {
out, err = unmarshalNestedRBACNode(n, acc)
if err != nil {
return
}
}
for _, o := range out {
for _, r := range o.References {
if r.Scope.IsEmpty() {
continue
}
o.Scope = r.Scope
break
}
}
return
}
// unmarshalNestedRBACNode handles RBAC rules when they are nested inside a resource
//
// The edge-case exists since the node doesn't explicitly specify the resource
// it belongs to.
//
// Example:
//
// modules:
//
// module1:
// name: "module 1"
// fields: ...
// allow:
// role1:
// - read
// - delete
func unmarshalNestedRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
return out, y7s.EachMap(n, func(role, op *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &rbac.Rule{
Operation: op.Value,
Access: acc,
},
ResourceType: rbac.RuleResourceType,
References: map[string]envoyx.Ref{
"RoleID": {
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:role",
Identifiers: envoyx.MakeIdentifiers(role.Value),
},
},
})
return nil
})
}
// unmarshalFlatRBACNode handles RBAC rules when they are provided on the root level
//
// Example:
//
// allow:
//
// role1:
// corteza::system/:
// - users.search
// - users.create
func unmarshalFlatRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
// Handles role
return out, y7s.EachMap(n, func(role, perm *yaml.Node) error {
// Handles operations
return y7s.EachMap(perm, func(res, ops *yaml.Node) error {
// Handle operation (one RBAC rule per op)
return y7s.EachSeq(ops, func(op *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &rbac.Rule{
Resource: res.Value,
Operation: op.Value,
Access: acc,
},
ResourceType: rbac.RuleResourceType,
References: envoyx.MergeRefs(
map[string]envoyx.Ref{"RoleID": {
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:role",
Identifiers: envoyx.MakeIdentifiers(role.Value),
}},
envoyx.SplitResourceIdentifier(res.Value),
),
})
return nil
})
})
})
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// i18n unmarshal logic
// // // // // // // // // // // // // // // // // // // // // // // // //
func unmarshalLocaleNode(n *yaml.Node) (out envoyx.NodeSet, err error) {
err = y7s.EachMap(n, func(lang, loc *yaml.Node) error {
langTag := systemTypes.Lang{Tag: language.Make(lang.Value)}
return y7s.EachMap(loc, func(res, kv *yaml.Node) error {
return y7s.EachMap(kv, func(k, msg *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &systemTypes.ResourceTranslation{
Resource: res.Value,
Lang: langTag,
K: k.Value,
Message: msg.Value,
},
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:resource-translation",
References: envoyx.SplitResourceIdentifier(res.Value),
})
return nil
})
})
})
if err != nil {
return
}
for _, o := range out {
for _, r := range o.References {
if r.Scope.IsEmpty() {
continue
}
o.Scope = r.Scope
break
}
}
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Envoy config unmarshal logic
// // // // // // // // // // // // // // // // // // // // // // // // //
func (d *auxYamlDoc) decodeEnvoyConfig(n *yaml.Node) (out envoyx.EnvoyConfig) {
y7s.EachMap(n, func(k, v *yaml.Node) (err error) {
switch strings.ToLower(k.Value) {
case "skipif", "skip":
return y7s.DecodeScalar(v, "decode skip if", &out.SkipIf)
case "onexisting", "mergealg":
out.MergeAlg = envoyx.CastMergeAlg(v.Value)
}
return nil
})
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Utilities
// // // // // // // // // // // // // // // // // // // // // // // // //
func (d YamlDecoder) getReader(ctx context.Context, p envoyx.DecodeParams) (r io.Reader, err error) {
aux, ok := p.Params[paramsKeyStream]
if ok {
r, ok = aux.(io.Reader)
if ok {
return
}
}
// @todo consider adding support for managing files from a location
err = errors.Errorf("YAML decoder expects a stream conforming to io.Reader interface")
return
}
func (d *auxYamlDoc) getDocumentContext(n *yaml.Node) (dctx documentContext, err error) {
dctx = documentContext{
references: make(map[string]string),
}
err = y7s.EachMap(n, func(k, v *yaml.Node) error {
// @todo expand when needed. The previous implementation only supported
// namespaces on the root of the document.
if y7s.IsKind(v, yaml.ScalarNode) {
dctx.references[k.Value] = v.Value
}
return nil
})
return
}