Define base Compose resources supported by envoy
* ComposeNamespace, * ComposeModule (+ fields), * ComposeRecord (+ values), * RBAC permissions.
This commit is contained in:
parent
e7d1dbb357
commit
d076dbd70f
@ -4,8 +4,9 @@ import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -91,6 +92,19 @@ func (opt ModuleFieldOptions) Strings(key string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns option value for key as single string
|
||||
//
|
||||
// Invalid, non-existing are returned as empty string ("")
|
||||
func (opt ModuleFieldOptions) String(key string) string {
|
||||
if _, has := opt[key]; has {
|
||||
if v, ok := opt[key].(string); ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsUnique - should value in this field be unique across records?
|
||||
func (opt ModuleFieldOptions) IsUnique() bool {
|
||||
return opt.Bool(moduleFieldOptionIsUnique)
|
||||
|
||||
86
pkg/envoy/types/compose_module.go
Normal file
86
pkg/envoy/types/compose_module.go
Normal file
@ -0,0 +1,86 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
compTypes "github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/util"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
ComposeModuleField struct {
|
||||
compTypes.ModuleField `yaml:",inline"`
|
||||
}
|
||||
ComposeModuleFieldSet []*ComposeModuleField
|
||||
|
||||
ComposeModule struct {
|
||||
compTypes.Module `yaml:",inline"`
|
||||
Fields ComposeModuleFieldSet `yaml:"fields"`
|
||||
Rbac `yaml:",inline"`
|
||||
}
|
||||
ComposeModuleSet []*ComposeModule
|
||||
)
|
||||
|
||||
func (mm *ComposeModuleSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
cms := ComposeModuleSet{}
|
||||
|
||||
err := util.YamlIterator(n, func(n, m *yaml.Node) error {
|
||||
handle := ""
|
||||
if n != nil {
|
||||
handle = n.Value
|
||||
}
|
||||
|
||||
mod := &ComposeModule{}
|
||||
err := m.Decode(mod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mod.Handle == "" {
|
||||
mod.Handle = handle
|
||||
}
|
||||
if mod.Name == "" {
|
||||
mod.Name = handle
|
||||
}
|
||||
cms = append(cms, mod)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*mm = cms
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ff *ComposeModuleFieldSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
ffs := ComposeModuleFieldSet{}
|
||||
|
||||
err := util.YamlIterator(n, func(n, m *yaml.Node) error {
|
||||
name := ""
|
||||
if n != nil {
|
||||
name = n.Value
|
||||
}
|
||||
f := &ComposeModuleField{}
|
||||
|
||||
err := m.Decode(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f.Name == "" {
|
||||
f.Name = name
|
||||
}
|
||||
if f.Label == "" {
|
||||
f.Label = name
|
||||
}
|
||||
ffs = append(ffs, f)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*ff = ffs
|
||||
return nil
|
||||
}
|
||||
110
pkg/envoy/types/compose_module_node.go
Normal file
110
pkg/envoy/types/compose_module_node.go
Normal file
@ -0,0 +1,110 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
)
|
||||
|
||||
type (
|
||||
// ComposeModuleNode represents a ComposeModule
|
||||
ComposeModuleNode struct {
|
||||
Mod *ComposeModule
|
||||
|
||||
// Related namespace
|
||||
Ns *ComposeNamespace
|
||||
}
|
||||
)
|
||||
|
||||
func (n *ComposeModuleNode) Identifiers() NodeIdentifiers {
|
||||
ii := make(NodeIdentifiers, 0)
|
||||
|
||||
if n.Mod.Handle != "" {
|
||||
ii = ii.Add(n.Mod.Handle)
|
||||
}
|
||||
|
||||
if n.Mod.Name != "" {
|
||||
ii = ii.Add(n.Mod.Name)
|
||||
}
|
||||
|
||||
if n.Mod.ID > 0 {
|
||||
ii = ii.Add(strconv.FormatUint(n.Mod.ID, 10))
|
||||
}
|
||||
|
||||
return ii
|
||||
}
|
||||
|
||||
func (n *ComposeModuleNode) Matches(resource string, identifiers ...string) bool {
|
||||
if resource != n.Resource() {
|
||||
return false
|
||||
}
|
||||
|
||||
return n.Identifiers().HasAny(identifiers...)
|
||||
}
|
||||
|
||||
func (n *ComposeModuleNode) Resource() string {
|
||||
return types.ModuleRBACResource.String()
|
||||
}
|
||||
|
||||
func (n *ComposeModuleNode) Relations() NodeRelationships {
|
||||
rel := make(NodeRelationships)
|
||||
|
||||
// Related namespace
|
||||
nsr := types.NamespaceRBACResource.String()
|
||||
if n.Ns.Slug != "" {
|
||||
rel.Add(nsr, n.Ns.Slug)
|
||||
}
|
||||
if n.Ns.Name != "" {
|
||||
rel.Add(nsr, n.Ns.Name)
|
||||
}
|
||||
if n.Ns.ID > 0 {
|
||||
rel.Add(nsr, strconv.FormatUint(n.Ns.ID, 10))
|
||||
}
|
||||
|
||||
// Related modules via Record module fields
|
||||
mdr := types.ModuleRBACResource.String()
|
||||
for _, f := range n.Mod.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"))
|
||||
}
|
||||
}
|
||||
|
||||
return rel
|
||||
}
|
||||
|
||||
func (n *ComposeModuleNode) Update(mm ...Node) {
|
||||
for _, m := range mm {
|
||||
n.updateNamespace(m)
|
||||
n.updateRecFields(m)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *ComposeModuleNode) updateNamespace(m Node) {
|
||||
if m.Resource() != types.NamespaceRBACResource.String() {
|
||||
return
|
||||
}
|
||||
|
||||
mn := m.(*ComposeNamespaceNode)
|
||||
n.Ns = mn.Ns
|
||||
}
|
||||
|
||||
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 {
|
||||
if f.Kind != "Record" {
|
||||
continue
|
||||
}
|
||||
|
||||
if mn.Identifiers().HasAny(f.Options.String("module")) {
|
||||
f.Options["module"] = strconv.FormatUint(mn.Mod.ID, 10)
|
||||
}
|
||||
}
|
||||
|
||||
n.Ns = mn.Ns
|
||||
}
|
||||
48
pkg/envoy/types/compose_namespace.go
Normal file
48
pkg/envoy/types/compose_namespace.go
Normal file
@ -0,0 +1,48 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
compTypes "github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/util"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
ComposeNamespace struct {
|
||||
compTypes.Namespace `yaml:",inline"`
|
||||
Modules ComposeModuleSet
|
||||
Rbac `yaml:",inline"`
|
||||
}
|
||||
ComposeNamespaceSet []*ComposeNamespace
|
||||
)
|
||||
|
||||
func (ss *ComposeNamespaceSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
nss := ComposeNamespaceSet{}
|
||||
|
||||
err := util.YamlIterator(n, func(n, m *yaml.Node) error {
|
||||
slug := ""
|
||||
if n != nil {
|
||||
slug = n.Value
|
||||
}
|
||||
|
||||
ns := &ComposeNamespace{}
|
||||
err := m.Decode(ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ns.Slug == "" {
|
||||
ns.Slug = slug
|
||||
}
|
||||
if ns.Name == "" {
|
||||
ns.Name = slug
|
||||
}
|
||||
nss = append(nss, ns)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*ss = nss
|
||||
return nil
|
||||
}
|
||||
45
pkg/envoy/types/compose_namespace_node.go
Normal file
45
pkg/envoy/types/compose_namespace_node.go
Normal file
@ -0,0 +1,45 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
)
|
||||
|
||||
type (
|
||||
ComposeNamespaceNode struct {
|
||||
Ns *ComposeNamespace
|
||||
}
|
||||
)
|
||||
|
||||
func (n *ComposeNamespaceNode) Identifiers() NodeIdentifiers {
|
||||
ii := make(NodeIdentifiers, 0)
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
return ii
|
||||
}
|
||||
|
||||
func (n *ComposeNamespaceNode) Matches(resource string, identifiers ...string) bool {
|
||||
if resource != n.Resource() {
|
||||
return false
|
||||
}
|
||||
|
||||
return n.Identifiers().HasAny(identifiers...)
|
||||
}
|
||||
|
||||
func (n *ComposeNamespaceNode) Resource() string {
|
||||
return types.NamespaceRBACResource.String()
|
||||
}
|
||||
|
||||
func (n *ComposeNamespaceNode) Relations() NodeRelationships {
|
||||
return nil
|
||||
}
|
||||
72
pkg/envoy/types/compose_record.go
Normal file
72
pkg/envoy/types/compose_record.go
Normal file
@ -0,0 +1,72 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
compTypes "github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/envoy/util"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
ComposeRecordValue struct {
|
||||
compTypes.RecordValue `yaml:",inline"`
|
||||
}
|
||||
ComposeRecordValueSet []*ComposeRecordValue
|
||||
|
||||
ComposeRecord struct {
|
||||
compTypes.Record `yaml:",inline"`
|
||||
Values ComposeRecordValueSet `yaml:"values"`
|
||||
}
|
||||
ComposeRecordSet []*ComposeRecord
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRecordDecoderMapNotSupported = errors.New("record decoder: maps not supported")
|
||||
)
|
||||
|
||||
func (rr *ComposeRecordSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
crs := ComposeRecordSet{}
|
||||
if n.Kind != yaml.SequenceNode {
|
||||
return ErrRecordDecoderMapNotSupported
|
||||
}
|
||||
|
||||
err := util.YamlIterator(n, func(n, m *yaml.Node) error {
|
||||
var r *ComposeRecord
|
||||
err := m.Decode(&r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
crs = append(crs, r)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*rr = crs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vv *ComposeRecordValueSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
vvs := ComposeRecordValueSet{}
|
||||
|
||||
err := util.YamlIterator(n, func(n, m *yaml.Node) error {
|
||||
v := &ComposeRecordValue{}
|
||||
|
||||
v.Name = n.Value
|
||||
v.Value = m.Value
|
||||
v.Updated = true
|
||||
vvs = append(vvs, v)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*vv = vvs
|
||||
return nil
|
||||
}
|
||||
76
pkg/envoy/types/compose_record_node.go
Normal file
76
pkg/envoy/types/compose_record_node.go
Normal file
@ -0,0 +1,76 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
)
|
||||
|
||||
type (
|
||||
RecordIterator func(func(*ComposeRecord) error) error
|
||||
|
||||
ComposeRecordNode struct {
|
||||
Walk RecordIterator
|
||||
|
||||
// Metafields for relationship management
|
||||
Mod *ComposeModule
|
||||
}
|
||||
)
|
||||
|
||||
func (n *ComposeRecordNode) Identifiers() NodeIdentifiers {
|
||||
return identifiersForModule(n.Mod)
|
||||
}
|
||||
|
||||
func (n *ComposeRecordNode) Resource() string {
|
||||
return "compose:record:"
|
||||
}
|
||||
|
||||
func (n *ComposeRecordNode) Matches(resource string, identifiers ...string) bool {
|
||||
if resource != n.Resource() {
|
||||
return false
|
||||
}
|
||||
|
||||
return n.Identifiers().HasAny(identifiers...)
|
||||
}
|
||||
|
||||
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:"
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
return rel
|
||||
}
|
||||
|
||||
func (n *ComposeRecordNode) Update(mm ...Node) {
|
||||
for _, m := range mm {
|
||||
switch m.Resource() {
|
||||
case types.ModuleRBACResource.String():
|
||||
mn, _ := m.(*ComposeModuleNode)
|
||||
// Direct module dependency
|
||||
if n.Matches(n.Resource(), identifiersForModule(mn.Mod)...) {
|
||||
n.Mod = mn.Mod
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
pkg/envoy/types/rbac.go
Normal file
9
pkg/envoy/types/rbac.go
Normal file
@ -0,0 +1,9 @@
|
||||
package types
|
||||
|
||||
type (
|
||||
RbacRules map[string][]string
|
||||
Rbac struct {
|
||||
Allow RbacRules
|
||||
Deny RbacRules
|
||||
}
|
||||
)
|
||||
24
pkg/envoy/types/util.go
Normal file
24
pkg/envoy/types/util.go
Normal file
@ -0,0 +1,24 @@
|
||||
package types
|
||||
|
||||
import "strconv"
|
||||
|
||||
// A little helper to extract identifiers for a given module
|
||||
func identifiersForModule(m *ComposeModule) NodeIdentifiers {
|
||||
ii := make(NodeIdentifiers, 0)
|
||||
|
||||
if m == nil {
|
||||
return ii
|
||||
}
|
||||
|
||||
if m.Handle != "" {
|
||||
ii = ii.Add(m.Handle)
|
||||
}
|
||||
if m.Name != "" {
|
||||
ii = ii.Add(m.Name)
|
||||
}
|
||||
if m.ID > 0 {
|
||||
ii = ii.Add(strconv.FormatUint(m.ID, 10))
|
||||
}
|
||||
|
||||
return ii
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user