Rework envoy node marshaling
This commit is contained in:
@@ -9,26 +9,27 @@ import (
|
||||
type (
|
||||
// ComposeModuleNode represents a ComposeModule
|
||||
ComposeModuleNode struct {
|
||||
Mod *types.Module
|
||||
Module *types.Module
|
||||
|
||||
// Related namespace
|
||||
Ns *types.Namespace
|
||||
RefNamespaceSlug string
|
||||
RefNamespaceID uint64
|
||||
}
|
||||
)
|
||||
|
||||
func (n *ComposeModuleNode) Identifiers() NodeIdentifiers {
|
||||
ii := make(NodeIdentifiers, 0)
|
||||
|
||||
if n.Mod.Handle != "" {
|
||||
ii = ii.Add(n.Mod.Handle)
|
||||
if n.Module.Handle != "" {
|
||||
ii = ii.Add(n.Module.Handle)
|
||||
}
|
||||
|
||||
if n.Mod.Name != "" {
|
||||
ii = ii.Add(n.Mod.Name)
|
||||
if n.Module.Name != "" {
|
||||
ii = ii.Add(n.Module.Name)
|
||||
}
|
||||
|
||||
if n.Mod.ID > 0 {
|
||||
ii = ii.Add(strconv.FormatUint(n.Mod.ID, 10))
|
||||
if n.Module.ID > 0 {
|
||||
ii = ii.Add(strconv.FormatUint(n.Module.ID, 10))
|
||||
}
|
||||
|
||||
return ii
|
||||
@@ -51,19 +52,17 @@ func (n *ComposeModuleNode) Relations() NodeRelationships {
|
||||
|
||||
// Related namespace
|
||||
nsr := types.NamespaceRBACResource.String()
|
||||
if n.Ns.Slug != "" {
|
||||
rel.Add(nsr, n.Ns.Slug)
|
||||
if n.RefNamespaceSlug != "" {
|
||||
rel.Add(nsr, n.RefNamespaceSlug)
|
||||
}
|
||||
if n.Ns.Name != "" {
|
||||
rel.Add(nsr, n.Ns.Name)
|
||||
}
|
||||
if n.Ns.ID > 0 {
|
||||
rel.Add(nsr, strconv.FormatUint(n.Ns.ID, 10))
|
||||
|
||||
if n.RefNamespaceID > 0 {
|
||||
rel.Add(nsr, strconv.FormatUint(n.RefNamespaceID, 10))
|
||||
}
|
||||
|
||||
// Related modules via Record module fields
|
||||
mdr := types.ModuleRBACResource.String()
|
||||
for _, f := range n.Mod.Fields {
|
||||
for _, f := range n.Module.Fields {
|
||||
// @todo should a missing module property raise an error?
|
||||
if f.Kind == "Record" && f.Options.String("module") != "" {
|
||||
rel.Add(mdr, f.Options.String("module"))
|
||||
@@ -86,25 +85,24 @@ func (n *ComposeModuleNode) updateNamespace(m Node) {
|
||||
}
|
||||
|
||||
mn := m.(*ComposeNamespaceNode)
|
||||
n.Ns = mn.Ns
|
||||
n.Module.NamespaceID = mn.Ns.ID
|
||||
}
|
||||
|
||||
func (n *ComposeModuleNode) updateRecFields(m Node) {
|
||||
if m.Resource() != types.ModuleRBACResource.String() {
|
||||
return
|
||||
}
|
||||
|
||||
mn := m.(*ComposeModuleNode)
|
||||
|
||||
// Check what record module field we can link this to
|
||||
for _, f := range n.Mod.Fields {
|
||||
for _, f := range n.Module.Fields {
|
||||
if f.Kind != "Record" {
|
||||
continue
|
||||
}
|
||||
|
||||
if mn.Identifiers().HasAny(f.Options.String("module")) {
|
||||
f.Options["module"] = strconv.FormatUint(mn.Mod.ID, 10)
|
||||
f.Options["module"] = strconv.FormatUint(mn.Module.ID, 10)
|
||||
}
|
||||
}
|
||||
|
||||
n.Ns = mn.Ns
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@ func (n *ComposeNamespaceNode) Identifiers() NodeIdentifiers {
|
||||
if n.Ns.Slug != "" {
|
||||
ii = ii.Add(n.Ns.Slug)
|
||||
}
|
||||
if n.Ns.Name != "" {
|
||||
ii = ii.Add(n.Ns.Name)
|
||||
}
|
||||
|
||||
if n.Ns.ID > 0 {
|
||||
ii = ii.Add(strconv.FormatUint(n.Ns.ID, 10))
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ type (
|
||||
ComposeRecordNode struct {
|
||||
Walk RecordIterator
|
||||
|
||||
// Metafields for relationship management
|
||||
Mod *types.Module
|
||||
RefModuleHandle string
|
||||
}
|
||||
)
|
||||
|
||||
func (n *ComposeRecordNode) Identifiers() NodeIdentifiers {
|
||||
return identifiersForModule(n.Mod)
|
||||
//return identifiersForModule(n.Mod)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *ComposeRecordNode) Resource() string {
|
||||
@@ -36,29 +36,29 @@ func (n *ComposeRecordNode) Relations() NodeRelationships {
|
||||
// This omits the namespace rel. as it's transitively implied via the modules
|
||||
|
||||
rel := make(NodeRelationships)
|
||||
mdr := types.ModuleRBACResource.String()
|
||||
rrr := "compose:record:"
|
||||
//mdr := types.ModuleRBACResource.String()
|
||||
//rrr := "compose:record:"
|
||||
//
|
||||
//if n.Mod == nil {
|
||||
// return rel
|
||||
//}
|
||||
|
||||
if n.Mod == nil {
|
||||
return rel
|
||||
}
|
||||
|
||||
// Original module
|
||||
mIdentifiers := identifiersForModule(n.Mod)
|
||||
rel.Add(mdr, mIdentifiers...)
|
||||
|
||||
// Field relationships
|
||||
for _, f := range n.Mod.Fields {
|
||||
if f.Kind == "Record" {
|
||||
modID := f.Options.String("module")
|
||||
// For the module
|
||||
rel.Add(mdr, modID)
|
||||
|
||||
// For the records.
|
||||
// Since this record depends on another module, it also depends on those records.
|
||||
rel.Add(rrr, modID)
|
||||
}
|
||||
}
|
||||
//// Original module
|
||||
//mIdentifiers := identifiersForModule(n.Mod)
|
||||
//rel.Add(mdr, mIdentifiers...)
|
||||
//
|
||||
//// Field relationships
|
||||
//for _, f := range n.Mod.Fields {
|
||||
// if f.Kind == "Record" {
|
||||
// modID := f.Options.String("module")
|
||||
// // For the module
|
||||
// rel.Add(mdr, modID)
|
||||
//
|
||||
// // For the records.
|
||||
// // Since this record depends on another module, it also depends on those records.
|
||||
// rel.Add(rrr, modID)
|
||||
// }
|
||||
//}
|
||||
|
||||
return rel
|
||||
}
|
||||
@@ -69,8 +69,8 @@ func (n *ComposeRecordNode) Update(mm ...Node) {
|
||||
case types.ModuleRBACResource.String():
|
||||
mn, _ := m.(*ComposeModuleNode)
|
||||
// Direct module dependency
|
||||
if n.Matches(n.Resource(), identifiersForModule(mn.Mod)...) {
|
||||
n.Mod = mn.Mod
|
||||
if n.Matches(n.Resource(), identifiersForModule(mn.Module)...) {
|
||||
//n.Mod = mn.Module
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package envoy
|
||||
|
||||
import "fmt"
|
||||
|
||||
type (
|
||||
Marshaller interface {
|
||||
MarshalEnvoy() ([]Node, error)
|
||||
}
|
||||
)
|
||||
|
||||
// MarshalMerge takes one or more nodes and Marshals and merges all nodes
|
||||
//
|
||||
//
|
||||
func Merge(nn []Node, ii ...interface{}) ([]Node, error) {
|
||||
for _, i := range ii {
|
||||
switch c := i.(type) {
|
||||
case Node:
|
||||
nn = append(nn, c)
|
||||
|
||||
case Marshaller:
|
||||
if tmp, err := c.(Marshaller).MarshalEnvoy(); err != nil {
|
||||
println(err)
|
||||
return nil, err
|
||||
} else {
|
||||
tmp = append(nn, tmp...)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("failed to merge %T; expecting Node or Marshaller interface", i)
|
||||
}
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
+55
-51
@@ -26,30 +26,28 @@ func NewStoreEncoder(s store.Storer) *StoreEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
func (se *StoreEncoder) Encode(ctx context.Context, s store.Storer, nn ...envoy.Node) error {
|
||||
return store.Tx(ctx, se.s, func(ctx context.Context, s store.Storer) error {
|
||||
func (se *StoreEncoder) Encode(ctx context.Context, nn ...envoy.Node) error {
|
||||
return store.Tx(ctx, se.s, func(ctx context.Context, s store.Storer) (err error) {
|
||||
for _, n := range nn {
|
||||
switch n.Resource() {
|
||||
case types.NamespaceRBACResource.String():
|
||||
ns := n.(*envoy.ComposeNamespaceNode)
|
||||
_, err := se.encodeNamespace(ctx, s, ns)
|
||||
if err != nil {
|
||||
return err
|
||||
if ns.Ns, err = se.encodeNamespace(ctx, s, ns); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
case types.ModuleRBACResource.String():
|
||||
mod := n.(*envoy.ComposeModuleNode)
|
||||
_, err := se.encodeModule(ctx, s, mod)
|
||||
if err != nil {
|
||||
return err
|
||||
if mod.Module, err = se.encodeModule(ctx, s, mod); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
case "compose:record:":
|
||||
rec := n.(*envoy.ComposeRecordNode)
|
||||
err := se.encodeRecord(ctx, s, rec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//case "compose:record:":
|
||||
// rec := n.(*envoy.ComposeRecordNode)
|
||||
//
|
||||
// if err = se.encodeRecord(ctx, s, rec); err != nil {
|
||||
// return
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +58,8 @@ func (se *StoreEncoder) Encode(ctx context.Context, s store.Storer, nn ...envoy.
|
||||
func (se *StoreEncoder) encodeNamespace(ctx context.Context, s store.Storer, n *envoy.ComposeNamespaceNode) (*types.Namespace, error) {
|
||||
cns := n.Ns
|
||||
|
||||
// @todo this should probably be refactored (together with services)
|
||||
// so that core logic is handled in one place
|
||||
cns.ID = nextID()
|
||||
if cns.CreatedAt.IsZero() {
|
||||
cns.CreatedAt = time.Now()
|
||||
@@ -73,11 +73,11 @@ func (se *StoreEncoder) encodeNamespace(ctx context.Context, s store.Storer, n *
|
||||
}
|
||||
|
||||
func (se *StoreEncoder) encodeModule(ctx context.Context, s store.Storer, m *envoy.ComposeModuleNode) (*types.Module, error) {
|
||||
mod := m.Mod
|
||||
cns := m.Ns
|
||||
mod := m.Module
|
||||
|
||||
// @todo this should probably be refactored (together with services)
|
||||
// so that core logic is handled in one place
|
||||
mod.ID = nextID()
|
||||
mod.NamespaceID = cns.ID
|
||||
|
||||
if mod.CreatedAt.IsZero() {
|
||||
mod.CreatedAt = time.Now()
|
||||
@@ -104,40 +104,44 @@ func (se *StoreEncoder) encodeModule(ctx context.Context, s store.Storer, m *env
|
||||
return mod, nil
|
||||
}
|
||||
|
||||
func (se *StoreEncoder) encodeRecord(ctx context.Context, s store.Storer, m *envoy.ComposeRecordNode) error {
|
||||
mod := m.Mod
|
||||
|
||||
return m.Walk(func(rec *types.Record) error {
|
||||
rec.ID = nextID()
|
||||
rec.ModuleID = mod.ID
|
||||
rec.NamespaceID = mod.NamespaceID
|
||||
|
||||
if rec.CreatedAt.IsZero() {
|
||||
rec.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
rec.Values = make(types.RecordValueSet, 0, 100)
|
||||
|
||||
// Process record values
|
||||
for _, crv := range rec.Values {
|
||||
crv.RecordID = rec.ID
|
||||
rec.Values = append(rec.Values, crv)
|
||||
}
|
||||
rec.Values.SetUpdatedFlag(true)
|
||||
rec.Values = se.setDefaultComposeRecordValues(mod, rec.Values)
|
||||
rec.Values = rvSanitizer.Run(mod, rec.Values)
|
||||
err := rvValidator.Run(ctx, s, mod, rec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := store.CreateComposeRecord(ctx, s, m.Mod, rec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
//func (se *StoreEncoder) encodeRecord(ctx context.Context, s store.Storer, m *envoy.ComposeRecordNode) error {
|
||||
// mod := m.Mod
|
||||
//
|
||||
// // @todo a bit less ad-hoc-ish solution
|
||||
// return m.Walk(func(rec *types.Record) error {
|
||||
// // @todo this should probably be refactored (together with services)
|
||||
// // so that core logic is handled in one place
|
||||
//
|
||||
// rec.ID = nextID()
|
||||
// rec.ModuleID = mod.ID
|
||||
// rec.NamespaceID = mod.NamespaceID
|
||||
//
|
||||
// if rec.CreatedAt.IsZero() {
|
||||
// rec.CreatedAt = time.Now()
|
||||
// }
|
||||
//
|
||||
// rec.Values = make(types.RecordValueSet, 0, 100)
|
||||
//
|
||||
// // Process record values
|
||||
// for _, crv := range rec.Values {
|
||||
// crv.RecordID = rec.ID
|
||||
// rec.Values = append(rec.Values, crv)
|
||||
// }
|
||||
// rec.Values.SetUpdatedFlag(true)
|
||||
// rec.Values = se.setDefaultComposeRecordValues(mod, rec.Values)
|
||||
// rec.Values = rvSanitizer.Run(mod, rec.Values)
|
||||
// err := rvValidator.Run(ctx, s, mod, rec)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// if err := store.CreateComposeRecord(ctx, s, m.Mod, rec); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
// })
|
||||
//}
|
||||
|
||||
// @note this method is coppied over from the compose/service/record.
|
||||
// Would it be better to unify the two methods?
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -20,19 +21,74 @@ func (c *compose) UnmarshalYAML(n *yaml.Node) error {
|
||||
return nodeErr(n, "expecting mapping node")
|
||||
}
|
||||
|
||||
return iterator(n, func(k, v *yaml.Node) error {
|
||||
var (
|
||||
nsRef string
|
||||
err error
|
||||
)
|
||||
|
||||
// 1st pass: handle doc-level references
|
||||
err = iterator(n, func(k, v *yaml.Node) error {
|
||||
switch k.Value {
|
||||
case "namespace":
|
||||
c.namespaces = ComposeNamespaceSet{&ComposeNamespace{}}
|
||||
return v.Decode(&c.namespaces[0])
|
||||
if def := findKeyNode(n, "namespaces"); def != nil {
|
||||
return nodeErr(def, "cannot combine namespace reference and namespaces definition")
|
||||
}
|
||||
|
||||
if err := decodeScalar(v, "namespace ref", &nsRef); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2nd pass: handle definitions
|
||||
return iterator(n, func(k, v *yaml.Node) error {
|
||||
switch k.Value {
|
||||
case "namespaces":
|
||||
return v.Decode(&c.namespaces)
|
||||
|
||||
case "modules":
|
||||
return v.Decode(&c.modules)
|
||||
if err = v.Decode(&c.modules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.modules.setNamespaceRef(nsRef)
|
||||
|
||||
case "records":
|
||||
return v.Decode(&c.records)
|
||||
if err = v.Decode(&c.records); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.records.setNamespaceRef(nsRef)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (wrap compose) MarshalEnvoy() ([]envoy.Node, error) {
|
||||
nn := make([]envoy.Node, 0, 100)
|
||||
|
||||
if wrap.namespaces != nil {
|
||||
if tmp, err := wrap.namespaces.MarshalEnvoy(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
nn = append(nn, tmp...)
|
||||
}
|
||||
}
|
||||
|
||||
if wrap.modules != nil {
|
||||
if tmp, err := wrap.modules.MarshalEnvoy(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
nn = append(nn, tmp...)
|
||||
}
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
@@ -3,25 +3,27 @@ package yaml
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/handle"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
ComposeModule struct {
|
||||
res *types.Module
|
||||
Rbac `yaml:",inline"`
|
||||
res *types.Module
|
||||
refNamespace string
|
||||
*rbacRules
|
||||
}
|
||||
ComposeModuleSet []*ComposeModule
|
||||
|
||||
ComposeModuleField struct {
|
||||
res *types.ModuleField `yaml:",inline"`
|
||||
Rbac `yaml:",inline"`
|
||||
res *types.ModuleField `yaml:",inline"`
|
||||
*rbacRules
|
||||
}
|
||||
ComposeModuleFieldSet []*ComposeModuleField
|
||||
)
|
||||
|
||||
func (set *ComposeModuleSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
func (wset *ComposeModuleSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
return iterator(n, func(k, v *yaml.Node) (err error) {
|
||||
var (
|
||||
wrap = &ComposeModule{}
|
||||
@@ -48,11 +50,38 @@ func (set *ComposeModuleSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
wrap.res.Name = k.Value
|
||||
}
|
||||
|
||||
*set = append(*set, wrap)
|
||||
*wset = append(*wset, wrap)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func (wset ComposeModuleSet) setNamespaceRef(ref string) error {
|
||||
for _, res := range wset {
|
||||
if res.refNamespace != "" && ref != res.refNamespace {
|
||||
return fmt.Errorf("cannot override namespace reference %s with %s", res.refNamespace, ref)
|
||||
}
|
||||
|
||||
res.refNamespace = ref
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wset ComposeModuleSet) MarshalEnvoy() ([]envoy.Node, error) {
|
||||
// namespace usually have bunch of sub-resources defined
|
||||
nn := make([]envoy.Node, 0, len(wset)*10)
|
||||
|
||||
for _, res := range wset {
|
||||
if tmp, err := res.MarshalEnvoy(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
nn = append(nn, tmp...)
|
||||
}
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (wrap *ComposeModule) UnmarshalYAML(n *yaml.Node) error {
|
||||
if wrap.res == nil {
|
||||
wrap.res = &types.Module{}
|
||||
@@ -65,7 +94,8 @@ func (wrap *ComposeModule) UnmarshalYAML(n *yaml.Node) error {
|
||||
return iterator(n, func(k, v *yaml.Node) (err error) {
|
||||
switch k.Value {
|
||||
case "name":
|
||||
return decodeScalar(v, &wrap.res.Name)
|
||||
return decodeScalar(v, "module name", &wrap.res.Name)
|
||||
|
||||
case "fields":
|
||||
if !isKind(v, yaml.MappingNode) {
|
||||
return nodeErr(n, "field definition must be a map")
|
||||
@@ -81,12 +111,23 @@ func (wrap *ComposeModule) UnmarshalYAML(n *yaml.Node) error {
|
||||
|
||||
wrap.res.Fields = aux.set()
|
||||
return nil
|
||||
|
||||
case "allow", "deny":
|
||||
return decodeAccessRoleOps(wrap.rbacRules, types.ModuleRBACResource, k, v)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (wrap ComposeModule) MarshalEnvoy() ([]envoy.Node, error) {
|
||||
nn := make([]envoy.Node, 0, 16)
|
||||
nn = append(nn, &envoy.ComposeModuleNode{Module: wrap.res})
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (set *ComposeModuleFieldSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
return iterator(n, func(k, v *yaml.Node) (err error) {
|
||||
var (
|
||||
@@ -138,25 +179,25 @@ func (wrap *ComposeModuleField) UnmarshalYAML(n *yaml.Node) error {
|
||||
return fmt.Errorf("name should be encoded as field definition key")
|
||||
|
||||
case "label":
|
||||
return decodeScalar(v, &wrap.res.Label)
|
||||
return decodeScalar(v, "module field label", &wrap.res.Label)
|
||||
|
||||
case "kind", "type":
|
||||
return decodeScalar(v, &wrap.res.Kind)
|
||||
return decodeScalar(v, "module field kind", &wrap.res.Kind)
|
||||
|
||||
case "options":
|
||||
return v.Decode(&wrap.res.Options)
|
||||
|
||||
case "private":
|
||||
return decodeScalar(v, &wrap.res.Private)
|
||||
return decodeScalar(v, "module field private", &wrap.res.Private)
|
||||
|
||||
case "required":
|
||||
return decodeScalar(v, &wrap.res.Required)
|
||||
return decodeScalar(v, "module field required", &wrap.res.Required)
|
||||
|
||||
case "visible":
|
||||
return decodeScalar(v, &wrap.res.Visible)
|
||||
return decodeScalar(v, "module field visible", &wrap.res.Visible)
|
||||
|
||||
case "multi":
|
||||
return decodeScalar(v, &wrap.res.Multi)
|
||||
return decodeScalar(v, "module field multi", &wrap.res.Multi)
|
||||
|
||||
case "default":
|
||||
return fmt.Errorf("field.default /// to be imple,emted")
|
||||
@@ -169,6 +210,10 @@ func (wrap *ComposeModuleField) UnmarshalYAML(n *yaml.Node) error {
|
||||
//
|
||||
// return
|
||||
//})
|
||||
|
||||
case "allow", "deny":
|
||||
return decodeAccessRoleOps(wrap.rbacRules, types.ModuleFieldRBACResource, k, v)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -2,16 +2,24 @@ package yaml
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/handle"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
ComposeNamespace struct {
|
||||
res *types.Namespace `yaml:",inline"`
|
||||
ref string
|
||||
Modules ComposeModuleSet
|
||||
Rbac `yaml:",inline"`
|
||||
// when namespace is at least partially defined
|
||||
res *types.Namespace `yaml:",inline"`
|
||||
|
||||
// when we only have reference (slug) to namespace
|
||||
ref string
|
||||
|
||||
// all known modules on a namespace
|
||||
modules ComposeModuleSet
|
||||
|
||||
// module's RBAC rules
|
||||
*rbacRules
|
||||
}
|
||||
ComposeNamespaceSet []*ComposeNamespace
|
||||
)
|
||||
@@ -53,7 +61,23 @@ func (wset *ComposeNamespaceSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (wset ComposeNamespaceSet) MarshalEnvoy() ([]envoy.Node, error) {
|
||||
// namespace usually have bunch of sub-resources defined
|
||||
nn := make([]envoy.Node, 0, len(wset)*10)
|
||||
|
||||
for _, res := range wset {
|
||||
if tmp, err := res.MarshalEnvoy(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
nn = append(nn, tmp...)
|
||||
}
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (wrap *ComposeNamespace) UnmarshalYAML(n *yaml.Node) error {
|
||||
|
||||
if isKind(n, yaml.ScalarNode) {
|
||||
wrap.ref = n.Value
|
||||
return nil
|
||||
@@ -73,16 +97,46 @@ func (wrap *ComposeNamespace) UnmarshalYAML(n *yaml.Node) error {
|
||||
return iterator(n, func(k, v *yaml.Node) (err error) {
|
||||
switch k.Value {
|
||||
case "slug":
|
||||
return decodeScalar(v, &wrap.res.Slug)
|
||||
return decodeScalar(v, "namespace slug", &wrap.res.Slug)
|
||||
|
||||
case "name":
|
||||
return decodeScalar(v, &wrap.res.Name)
|
||||
return decodeScalar(v, "namespace name", &wrap.res.Name)
|
||||
|
||||
case "enabled":
|
||||
return decodeScalar(v, &wrap.res.Enabled)
|
||||
return decodeScalar(v, "namespace enabled", &wrap.res.Enabled)
|
||||
|
||||
case "modules":
|
||||
return v.Decode(&wrap.modules)
|
||||
|
||||
case "allow", "deny":
|
||||
if wrap.rbacRules == nil {
|
||||
wrap.rbacRules = &rbacRules{}
|
||||
}
|
||||
return decodeAccessRoleOps(wrap.rbacRules, types.NamespaceRBACResource, k, v)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (wrap ComposeNamespace) MarshalEnvoy() ([]envoy.Node, error) {
|
||||
nn := make([]envoy.Node, 0, 1+len(wrap.modules))
|
||||
nn = append(nn, &envoy.ComposeNamespaceNode{Ns: wrap.res})
|
||||
|
||||
if tmp, err := wrap.modules.MarshalEnvoy(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
nn = append(nn, tmp...)
|
||||
}
|
||||
|
||||
// @todo rbac
|
||||
|
||||
//if tmp, err := wrap.rules.MarshalEnvoy(); err != nil {
|
||||
// return nil, err
|
||||
//} else {
|
||||
// nn = append(nn, tmp...)
|
||||
//}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ package yaml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
@@ -64,17 +65,17 @@ func TestComposeNamespace_UnmarshalYAML(t *testing.T) {
|
||||
req.Equal("crm", doc.compose.namespaces[0].res.Slug)
|
||||
})
|
||||
|
||||
t.Run("compose namespace file 2", func(t *testing.T) {
|
||||
doc, err := file(2)
|
||||
req.NoError(err)
|
||||
req.NotNil(doc)
|
||||
spew.Dump(doc.compose)
|
||||
req.NotNil(doc.compose)
|
||||
req.Len(doc.compose.namespaces, 1)
|
||||
req.NotNil(doc.compose.namespaces[0])
|
||||
}
|
||||
|
||||
req.Equal("CRM", doc.compose.namespaces[0].res.Name)
|
||||
req.Equal("crm", doc.compose.namespaces[0].res.Slug)
|
||||
})
|
||||
func TestComposeNamespace_MarshalEnvoy(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
|
||||
wrap = ComposeNamespace{res: &types.Namespace{ID: 42}}
|
||||
nn, err = wrap.MarshalEnvoy()
|
||||
)
|
||||
|
||||
req.NoError(err)
|
||||
req.NotEmpty(nn)
|
||||
req.Equal(uint64(42), nn[0].(*envoy.ComposeNamespaceNode).Ns.ID)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/handle"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -10,11 +12,12 @@ type (
|
||||
ComposeRecord struct {
|
||||
res *types.Record `yaml:",inline"`
|
||||
values ComposeRecordValues `yaml:"values"`
|
||||
moduleRef string
|
||||
createdByRef string
|
||||
updatedByRef string
|
||||
deletedByRef string
|
||||
ownedByRef string
|
||||
refModule string
|
||||
refNamespace string
|
||||
refCreatedBy string
|
||||
refUpdatedBy string
|
||||
refDeletedBy string
|
||||
refOwnedBy string
|
||||
}
|
||||
ComposeRecordSet []*ComposeRecord
|
||||
|
||||
@@ -51,7 +54,7 @@ func (wset *ComposeRecordSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
if isKind(v, yaml.SequenceNode) {
|
||||
// multiple records defined
|
||||
return iterator(v, func(_, r *yaml.Node) error {
|
||||
var wrap = &ComposeRecord{moduleRef: moduleRef}
|
||||
var wrap = &ComposeRecord{refModule: moduleRef}
|
||||
if err = r.Decode(&wrap); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -63,7 +66,7 @@ func (wset *ComposeRecordSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
|
||||
if isKind(v, yaml.MappingNode) {
|
||||
// one record defined
|
||||
var wrap = &ComposeRecord{moduleRef: moduleRef}
|
||||
var wrap = &ComposeRecord{refModule: moduleRef}
|
||||
if err = v.Decode(&wrap); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -75,6 +78,29 @@ func (wset *ComposeRecordSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (wset ComposeRecordSet) MarshalEnvoy() ([]envoy.Node, error) {
|
||||
// namespace usually have bunch of sub-resources defined
|
||||
var (
|
||||
nn = []envoy.Node{}
|
||||
)
|
||||
|
||||
// @todo
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (set ComposeRecordSet) setNamespaceRef(ref string) error {
|
||||
for _, res := range set {
|
||||
if res.refNamespace != "" && ref != res.refNamespace {
|
||||
return fmt.Errorf("cannot override namespace reference %s with %s", res.refNamespace, ref)
|
||||
}
|
||||
|
||||
res.refNamespace = ref
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wrap *ComposeRecord) UnmarshalYAML(n *yaml.Node) error {
|
||||
if !isKind(n, yaml.MappingNode) {
|
||||
return nodeErr(n, "expecting mapping node for record definition")
|
||||
@@ -87,7 +113,7 @@ func (wrap *ComposeRecord) UnmarshalYAML(n *yaml.Node) error {
|
||||
return iterator(n, func(k, v *yaml.Node) error {
|
||||
switch k.Value {
|
||||
case "module":
|
||||
return decodeRef(v, "module", &wrap.moduleRef)
|
||||
return decodeRef(v, "module", &wrap.refModule)
|
||||
|
||||
case "values":
|
||||
// Use aux structure to decode record values into RVS
|
||||
@@ -106,13 +132,13 @@ func (wrap *ComposeRecord) UnmarshalYAML(n *yaml.Node) error {
|
||||
case "deletedAt":
|
||||
return v.Decode(&wrap.res.DeletedAt)
|
||||
case "createdBy":
|
||||
return decodeRef(v, "user", &wrap.createdByRef)
|
||||
return decodeRef(v, "createdBy user", &wrap.refCreatedBy)
|
||||
case "updatedBy":
|
||||
return decodeRef(v, "user", &wrap.updatedByRef)
|
||||
return decodeRef(v, "updatedBy user", &wrap.refUpdatedBy)
|
||||
case "deletedBy":
|
||||
return decodeRef(v, "user", &wrap.deletedByRef)
|
||||
return decodeRef(v, "deletedBy user", &wrap.refDeletedBy)
|
||||
case "ownedBy":
|
||||
return decodeRef(v, "user", &wrap.ownedByRef)
|
||||
return decodeRef(v, "ownedBy user", &wrap.refOwnedBy)
|
||||
|
||||
default:
|
||||
return nodeErr(k, "unsupported key %s used for record definition", k.Value)
|
||||
|
||||
@@ -34,13 +34,10 @@ func TestCompose_UnmarshalYAML(t *testing.T) {
|
||||
req.Len(c.namespaces, 1)
|
||||
|
||||
c, err = parse(&compose{}, `namespace: { }`)
|
||||
req.NoError(err)
|
||||
req.NotNil(c)
|
||||
req.Len(c.namespaces, 1)
|
||||
req.Error(err)
|
||||
|
||||
c, err = parse(&compose{}, `namespace: foo`)
|
||||
req.NoError(err)
|
||||
req.NotNil(c)
|
||||
req.Len(c.namespaces, 1)
|
||||
req.Equal("foo", c.namespaces[0].ref)
|
||||
req.Empty(c.namespaces, "namespace ref should not result in namespace definition")
|
||||
}
|
||||
|
||||
+53
-54
@@ -3,7 +3,6 @@ package yaml
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io"
|
||||
@@ -94,7 +93,7 @@ func (y *decoder) Decode(ctx context.Context, r io.Reader, i os.FileInfo) ([]env
|
||||
// for modRef, rr := range c.Records {
|
||||
// // We can define a basic module representation as it will be updated later
|
||||
// // during validation/runtime
|
||||
// mod := &types.Module{}
|
||||
// mod := &types.module{}
|
||||
// mod.Handle = modRef
|
||||
// mod.Name = modRef
|
||||
//
|
||||
@@ -109,55 +108,55 @@ func (y *decoder) Decode(ctx context.Context, r io.Reader, i os.FileInfo) ([]env
|
||||
// return nn, nil
|
||||
//}
|
||||
|
||||
func (y *decoder) convertNamespaces(nss ComposeNamespaceSet) ([]envoy.Node, error) {
|
||||
nn := make([]envoy.Node, 0, 2)
|
||||
|
||||
for _, ns := range nss {
|
||||
nn = append(nn, &envoy.ComposeNamespaceNode{Ns: ns.res})
|
||||
|
||||
// Nested modules
|
||||
if ns.Modules != nil {
|
||||
mm, err := y.convertModules(ns.Modules, ns.res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nn = append(nn, mm...)
|
||||
}
|
||||
|
||||
// @todo nested RBAC
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (y *decoder) convertModules(mm ComposeModuleSet, ns *types.Namespace) ([]envoy.Node, error) {
|
||||
nn := make([]envoy.Node, 0)
|
||||
|
||||
for _, m := range mm {
|
||||
nn = append(nn, &envoy.ComposeModuleNode{
|
||||
Mod: m.res,
|
||||
Ns: ns,
|
||||
})
|
||||
|
||||
// @todo nested resources; should there be any?
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (y *decoder) convertRecords(rr ComposeRecordSet, m *types.Module) ([]envoy.Node, error) {
|
||||
// Iterator function for providing records to be imported.
|
||||
// This doesn't do any validation; that should be handled by other layers.
|
||||
f := func(f func(record *types.Record) error) error {
|
||||
//for _, r := range rr {
|
||||
//err := f(r.res)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return []envoy.Node{&envoy.ComposeRecordNode{Mod: m, Walk: f}}, nil
|
||||
}
|
||||
//func (y *decoder) convertNamespaces(nss ComposeNamespaceSet) ([]envoy.Node, error) {
|
||||
// nn := make([]envoy.Node, 0, 2)
|
||||
//
|
||||
// for _, ns := range nss {
|
||||
// nn = append(nn, &envoy.ComposeNamespaceNode{Ns: ns.res})
|
||||
//
|
||||
// // Nested modules
|
||||
// if ns.modules != nil {
|
||||
// mm, err := y.convertModules(ns.modules, ns.res)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// nn = append(nn, mm...)
|
||||
// }
|
||||
//
|
||||
// // @todo nested RBAC
|
||||
// }
|
||||
//
|
||||
// return nn, nil
|
||||
//}
|
||||
//
|
||||
//func (y *decoder) convertModules(mm ComposeModuleSet, ns *types.Namespace) ([]envoy.Node, error) {
|
||||
// nn := make([]envoy.Node, 0)
|
||||
//
|
||||
// for _, m := range mm {
|
||||
// nn = append(nn, &envoy.ComposeModuleNode{
|
||||
// module: m.res,
|
||||
// Ns: ns,
|
||||
// })
|
||||
//
|
||||
// // @todo nested resources; should there be any?
|
||||
// }
|
||||
//
|
||||
// return nn, nil
|
||||
//}
|
||||
//
|
||||
//func (y *decoder) convertRecords(rr ComposeRecordSet, m *types.Module) ([]envoy.Node, error) {
|
||||
// // Iterator function for providing records to be imported.
|
||||
// // This doesn't do any validation; that should be handled by other layers.
|
||||
// f := func(f func(record *types.Record) error) error {
|
||||
// //for _, r := range rr {
|
||||
// //err := f(r.res)
|
||||
// //if err != nil {
|
||||
// // return err
|
||||
// //}
|
||||
// //}
|
||||
//
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// return []envoy.Node{&envoy.ComposeRecordNode{Mod: m, Walk: f}}, nil
|
||||
//}
|
||||
|
||||
+11
-1
@@ -30,6 +30,16 @@ func (doc *Document) UnmarshalYAML(n *yaml.Node) error {
|
||||
func (doc *Document) Decode(ctx context.Context, l loader) ([]envoy.Node, error) {
|
||||
nn := make([]envoy.Node, 0, 100)
|
||||
|
||||
if doc.compose != nil {
|
||||
if tmp, err := doc.compose.MarshalEnvoy(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
nn = append(nn, tmp...)
|
||||
}
|
||||
}
|
||||
|
||||
return nn, nil
|
||||
|
||||
//// In case of namespaces...
|
||||
//if doc.Namespaces != nil {
|
||||
// nodes, err := doc.Namespaces.Decode(ctx, l)
|
||||
@@ -67,7 +77,7 @@ func (doc *Document) Decode(ctx context.Context, l loader) ([]envoy.Node, error)
|
||||
// for modRef, rr := range doc.Records {
|
||||
// // We can define a basic module representation as it will be updated later
|
||||
// // during validation/runtime
|
||||
// mod := &types.Module{}
|
||||
// mod := &types.module{}
|
||||
// mod.Handle = modRef
|
||||
// mod.Name = modRef
|
||||
//
|
||||
|
||||
+48
-4
@@ -1,9 +1,53 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
RbacRules map[string][]string
|
||||
Rbac struct {
|
||||
Allow RbacRules
|
||||
Deny RbacRules
|
||||
rbacRules struct {
|
||||
// mapping unresolved roles to defined RBAC rules
|
||||
rules map[string]rbac.RuleSet
|
||||
}
|
||||
)
|
||||
|
||||
// Decodes RBAC rules defined as access/role/ops...
|
||||
func decodeAccessRoleOps(wrap *rbacRules, res rbac.Resource, k, v *yaml.Node) error {
|
||||
var (
|
||||
access rbac.Access
|
||||
)
|
||||
|
||||
if wrap.rules == nil {
|
||||
wrap.rules = make(map[string]rbac.RuleSet)
|
||||
}
|
||||
|
||||
if !isKind(v, yaml.MappingNode) {
|
||||
return nodeErr(v, "rbac rule definition must be a map")
|
||||
}
|
||||
|
||||
if k.Value == "allow" {
|
||||
access = rbac.Allow
|
||||
}
|
||||
|
||||
return iterator(v, func(k, v *yaml.Node) error {
|
||||
|
||||
var (
|
||||
role = k.Value
|
||||
)
|
||||
|
||||
if _, set := wrap.rules[role]; !set {
|
||||
wrap.rules[role] = rbac.RuleSet{}
|
||||
}
|
||||
|
||||
if !isKind(v, yaml.SequenceNode) {
|
||||
return nodeErr(v, "rbac rule operations list must be a sequence")
|
||||
}
|
||||
|
||||
return iterator(v, func(_, v *yaml.Node) error {
|
||||
rule := &rbac.Rule{Resource: res, Operation: rbac.Operation(v.Value), Access: access}
|
||||
wrap.rules[role] = append(wrap.rules[role], rule)
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace:
|
||||
name: CRM
|
||||
slug: crm
|
||||
allow:
|
||||
everyone:
|
||||
- module.create
|
||||
- read
|
||||
deny:
|
||||
everyone:
|
||||
- delete
|
||||
@@ -84,9 +84,9 @@ func findKeyNode(n *yaml.Node, key string) *yaml.Node {
|
||||
}
|
||||
|
||||
// Checks validity of ref node and sets the value to given arg ptr
|
||||
func decodeScalar(n *yaml.Node, val interface{}) error {
|
||||
func decodeScalar(n *yaml.Node, name string, val interface{}) error {
|
||||
if !isKind(n, yaml.ScalarNode) {
|
||||
return nodeErr(n, "expecting scalar value")
|
||||
return nodeErr(n, "expecting scalar value for %s", name)
|
||||
}
|
||||
|
||||
return n.Decode(val)
|
||||
|
||||
Reference in New Issue
Block a user