From d90c11153b39b7f552c6ba175aeb3bde41982886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Jerman?= Date: Mon, 23 Aug 2021 18:57:01 +0200 Subject: [PATCH] Additional envoy resource manipulation utilities --- pkg/envoy/builder_test.go | 10 ++ pkg/envoy/resource/application.go | 2 + pkg/envoy/resource/automation_workflow.go | 2 + pkg/envoy/resource/compose_chart.go | 20 +++ pkg/envoy/resource/compose_module.go | 24 +++ pkg/envoy/resource/compose_namespace.go | 2 + pkg/envoy/resource/compose_page.go | 191 +++++++++++++++++----- pkg/envoy/resource/resource.go | 33 +++- pkg/envoy/resource/role.go | 2 + pkg/envoy/resource/template.go | 2 + pkg/envoy/resource/types.go | 111 ++++++++++++- pkg/envoy/resource/user.go | 2 + 12 files changed, 349 insertions(+), 52 deletions(-) diff --git a/pkg/envoy/builder_test.go b/pkg/envoy/builder_test.go index 79737a6ce..578e2b358 100644 --- a/pkg/envoy/builder_test.go +++ b/pkg/envoy/builder_test.go @@ -37,6 +37,16 @@ func (t *testResource) Placeholder() bool { return t.ph } +func (t *testResource) ReID(ii resource.Identifiers) { + // no need for implementation + panic("not implemented") +} + +func (t *testResource) ReRef(old resource.RefSet, new resource.RefSet) { + // no need for implementation + panic("not implemented") +} + func TestGraphBuilder_Rel(t *testing.T) { req := require.New(t) ctx := context.Background() diff --git a/pkg/envoy/resource/application.go b/pkg/envoy/resource/application.go index 8913e41dd..46c4bd567 100644 --- a/pkg/envoy/resource/application.go +++ b/pkg/envoy/resource/application.go @@ -31,6 +31,8 @@ func NewApplication(res *types.Application) *Application { }) } + res.ID = 0 + return r } diff --git a/pkg/envoy/resource/automation_workflow.go b/pkg/envoy/resource/automation_workflow.go index 6a8243f31..f9cc3a868 100644 --- a/pkg/envoy/resource/automation_workflow.go +++ b/pkg/envoy/resource/automation_workflow.go @@ -51,6 +51,8 @@ func NewAutomationWorkflow(res *types.Workflow) *AutomationWorkflow { us.RunAs = MakeUserstampFromID(res.RunAs) r.SetUserstamps(us) + res.ID = 0 + return r } diff --git a/pkg/envoy/resource/compose_chart.go b/pkg/envoy/resource/compose_chart.go index 458298eab..f844b152f 100644 --- a/pkg/envoy/resource/compose_chart.go +++ b/pkg/envoy/resource/compose_chart.go @@ -37,9 +37,29 @@ func NewComposeChart(res *types.Chart, nsRef string, mmRef []string) *ComposeCha // Initial timestamps r.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil)) + res.ID = 0 + res.NamespaceID = 0 + return r } +func (r *ComposeChart) ReRef(old RefSet, new RefSet) { + r.base.ReRef(old, new) + + // Additional references + for _, n := range new { + if n.ResourceType == types.NamespaceResourceType { + r.RefNs = MakeRef(types.NamespaceResourceType, n.Identifiers) + } + } + + for i, o := range old { + if o.ResourceType == types.ModuleResourceType { + r.RefMods = r.RefMods.replaceRef(o, new[i]) + } + } +} + func (r *ComposeChart) SysID() uint64 { return r.Res.ID } diff --git a/pkg/envoy/resource/compose_module.go b/pkg/envoy/resource/compose_module.go index 39abd0550..f59353b0e 100644 --- a/pkg/envoy/resource/compose_module.go +++ b/pkg/envoy/resource/compose_module.go @@ -68,14 +68,38 @@ func NewComposeModule(res *types.Module, nsRef string) *ComposeModule { r.RefRoles = append(r.RefRoles, r.AddRef(systemTypes.RoleResourceType, refRole)) } } + + f.ID = 0 + f.NamespaceID = 0 + f.ModuleID = 0 } // Initial timestamps r.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil)) + res.ID = 0 + res.NamespaceID = 0 + return r } +func (r *ComposeModule) ReRef(old RefSet, new RefSet) { + r.base.ReRef(old, new) + + // Additional references + for _, n := range new { + if n.ResourceType == types.NamespaceResourceType { + r.RefNs = MakeRef(types.NamespaceResourceType, n.Identifiers) + } + } + + for i, o := range old { + if o.ResourceType == types.ModuleResourceType { + r.RefMods = r.RefMods.replaceRef(o, new[i]) + } + } +} + func (r *ComposeModule) SysID() uint64 { return r.Res.ID } diff --git a/pkg/envoy/resource/compose_namespace.go b/pkg/envoy/resource/compose_namespace.go index b1e79ccb7..ba698652a 100644 --- a/pkg/envoy/resource/compose_namespace.go +++ b/pkg/envoy/resource/compose_namespace.go @@ -24,6 +24,8 @@ func NewComposeNamespace(ns *types.Namespace) *ComposeNamespace { // Initial timestamps r.SetTimestamps(MakeTimestampsCUDA(&ns.CreatedAt, ns.UpdatedAt, ns.DeletedAt, nil)) + ns.ID = 0 + return r } diff --git a/pkg/envoy/resource/compose_page.go b/pkg/envoy/resource/compose_page.go index 021c3c0f5..4c9eb3c7d 100644 --- a/pkg/envoy/resource/compose_page.go +++ b/pkg/envoy/resource/compose_page.go @@ -48,32 +48,14 @@ func NewComposePage(pg *types.Page, nsRef, modRef, parentRef string) *ComposePag r.RefParent = r.AddRef(types.PageResourceType, parentRef).Constraint(r.RefNs) } - // Quick utility to extract references from options - ss := func(m map[string]interface{}, kk ...string) string { - for _, k := range kk { - if vr, has := m[k]; has { - v, _ := vr.(string) - return v - } - } - return "" - } - - add := func(rr RefSet, r *Ref) RefSet { - if rr == nil { - rr = make(RefSet, 0, 2) - } - - return append(rr, r) - } - + var ref *Ref for i, b := range pg.Blocks { switch b.Kind { case "RecordList": - id := ss(b.Options, "module", "moduleID") - if id != "" { - ref := r.AddRef(types.ModuleResourceType, id).Constraint(r.RefNs) - r.BlockRefs[i] = add(r.BlockRefs[i], ref) + ref = r.pbRecordList(b.Options) + if ref != nil { + r.addRef(ref) + r.BlockRefs[i] = append(r.BlockRefs[i], ref) r.ModRefs = append(r.ModRefs, ref) } @@ -81,27 +63,27 @@ func NewComposePage(pg *types.Page, nsRef, modRef, parentRef string) *ComposePag bb, _ := b.Options["buttons"].([]interface{}) for _, b := range bb { button, _ := b.(map[string]interface{}) - id := ss(button, "workflow", "workflowID") - if id != "" { - ref := r.AddRef(automationTypes.WorkflowResourceType, id) - r.BlockRefs[i] = add(r.BlockRefs[i], ref) + ref = r.pbAutomation(button) + if ref != nil { + r.addRef(ref) + r.BlockRefs[i] = append(r.BlockRefs[i], ref) r.WfRefs = append(r.WfRefs, ref) } } case "RecordOrganizer": - id := ss(b.Options, "module", "moduleID") - if id != "" { - ref := r.AddRef(types.ModuleResourceType, id).Constraint(r.RefNs) - r.BlockRefs[i] = add(r.BlockRefs[i], ref) + ref = r.pbRecordList(b.Options) + if ref != nil { + r.addRef(ref) + r.BlockRefs[i] = append(r.BlockRefs[i], ref) r.ModRefs = append(r.ModRefs, ref) } case "Chart": - id := ss(b.Options, "chart", "chartID") - if id != "" { - ref := r.AddRef(types.ChartResourceType, id).Constraint(r.RefNs) - r.BlockRefs[i] = add(r.BlockRefs[i], ref) + ref = r.pbChart(b.Options) + if ref != nil { + r.addRef(ref) + r.BlockRefs[i] = append(r.BlockRefs[i], ref) r.RefCharts = append(r.RefCharts, ref) } @@ -110,10 +92,11 @@ func NewComposePage(pg *types.Page, nsRef, modRef, parentRef string) *ComposePag for _, f := range ff { feed, _ := f.(map[string]interface{}) fOpts, _ := (feed["options"]).(map[string]interface{}) - id := ss(fOpts, "module", "moduleID") - if id != "" { - ref := r.AddRef(types.ModuleResourceType, id).Constraint(r.RefNs) - r.BlockRefs[i] = add(r.BlockRefs[i], ref) + + ref = r.pbCalendar(fOpts) + if ref != nil { + r.addRef(ref) + r.BlockRefs[i] = append(r.BlockRefs[i], ref) r.ModRefs = append(r.ModRefs, ref) } } @@ -122,10 +105,10 @@ func NewComposePage(pg *types.Page, nsRef, modRef, parentRef string) *ComposePag mm, _ := b.Options["metrics"].([]interface{}) for _, m := range mm { mops, _ := m.(map[string]interface{}) - id := ss(mops, "module", "moduleID") - if id != "" { - ref := r.AddRef(types.ModuleResourceType, id).Constraint(r.RefNs) - r.BlockRefs[i] = add(r.BlockRefs[i], ref) + ref = r.pbMetric(mops) + if ref != nil { + r.addRef(ref) + r.BlockRefs[i] = append(r.BlockRefs[i], ref) r.ModRefs = append(r.ModRefs, ref) } } @@ -143,9 +126,55 @@ func NewComposePage(pg *types.Page, nsRef, modRef, parentRef string) *ComposePag // Initial timestamps r.SetTimestamps(MakeTimestampsCUDA(&pg.CreatedAt, pg.UpdatedAt, pg.DeletedAt, nil)) + pg.ID = 0 + pg.SelfID = 0 + pg.NamespaceID = 0 + pg.ModuleID = 0 + return r } +func (r *ComposePage) ReRef(old RefSet, new RefSet) { + r.base.ReRef(old, new) + + for _, n := range new { + switch n.ResourceType { + case types.NamespaceResourceType: + r.RefNs = MakeRef(types.NamespaceResourceType, n.Identifiers) + case types.ModuleResourceType: + r.RefMod = MakeRef(types.ModuleResourceType, n.Identifiers) + case types.PageResourceType: + r.RefParent = MakeRef(types.PageResourceType, n.Identifiers) + } + } +} + +func (r *ComposePage) Prune(ref *Ref) { + var auxRef *Ref + +outer: + for i := len(r.Res.Blocks) - 1; i >= 0; i-- { + b := r.Res.Blocks[i] + + switch b.Kind { + // Implement the rest when support is needed + case "Automation": + bb, _ := b.Options["buttons"].([]interface{}) + for _, b := range bb { + button, _ := b.(map[string]interface{}) + auxRef = r.pbAutomation(button) + + if auxRef.equals(ref) { + r.ReplaceRef(ref, nil) + r.WfRefs = r.WfRefs.replaceRef(ref, nil) + r.removeBlock(i) + continue outer + } + } + } + } +} + func (r *ComposePage) SysID() uint64 { return r.Res.ID } @@ -196,3 +225,79 @@ func FindComposePage(rr InterfaceSet, ii Identifiers) (pg *types.Page) { func ComposePageErrUnresolved(ii Identifiers) error { return fmt.Errorf("compose page unresolved %v", ii.StringSlice()) } + +// Page block utilities +func (r *ComposePage) removeBlock(i int) { + // do the swap remove thing + r.Res.Blocks[i] = r.Res.Blocks[len(r.Res.Blocks)-1] + r.Res.Blocks = r.Res.Blocks[:len(r.Res.Blocks)-1] + + // correct block refs. + // +1 here because it's already cut + r.BlockRefs[i] = r.BlockRefs[len(r.Res.Blocks)] + delete(r.BlockRefs, len(r.Res.Blocks)) +} + +func (r *ComposePage) optString(opt map[string]interface{}, kk ...string) string { + for _, k := range kk { + if vr, has := opt[k]; has { + v, _ := vr.(string) + return v + } + } + return "" +} + +func (r *ComposePage) pbRecordList(opt map[string]interface{}) (out *Ref) { + id := r.optString(opt, "module", "moduleID") + if id == "" { + return + } + + return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs) +} + +func (r *ComposePage) pbAutomation(opt map[string]interface{}) (out *Ref) { + id := r.optString(opt, "workflow", "workflowID") + if id == "" { + return + } + + return MakeRef(automationTypes.WorkflowResourceType, MakeIdentifiers(id)) +} + +func (r *ComposePage) pbRecordOrganizer(opt map[string]interface{}) (out *Ref) { + id := r.optString(opt, "module", "moduleID") + if id == "" { + return + } + + return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs) +} + +func (r *ComposePage) pbChart(opt map[string]interface{}) (out *Ref) { + id := r.optString(opt, "chart", "chartID") + if id == "" { + return + } + + return MakeRef(types.ChartResourceType, MakeIdentifiers(id)).Constraint(r.RefNs) +} + +func (r *ComposePage) pbCalendar(opt map[string]interface{}) (out *Ref) { + id := r.optString(opt, "module", "moduleID") + if id == "" { + return + } + + return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs) +} + +func (r *ComposePage) pbMetric(opt map[string]interface{}) (out *Ref) { + id := r.optString(opt, "module", "moduleID") + if id == "" { + return + } + + return MakeRef(types.ModuleResourceType, MakeIdentifiers(id)).Constraint(r.RefNs) +} diff --git a/pkg/envoy/resource/resource.go b/pkg/envoy/resource/resource.go index 622804785..543f321b3 100644 --- a/pkg/envoy/resource/resource.go +++ b/pkg/envoy/resource/resource.go @@ -104,12 +104,13 @@ func (t *base) AddIdentifier(ss ...string) { t.ii.Add(ss...) } +// SetIdentifier sets the identifiers to whatever was provided +func (t *base) SetIdentifier(ii Identifiers) { + t.ii = ii +} + // AddRef adds a new reference to the current resource func (t *base) AddRef(rt string, ii ...string) *Ref { - if t.rr == nil { - t.rr = make(RefSet, 0, 10) - } - iiC := make([]string, 0, len(ii)) for _, i := range ii { if i != "" { @@ -117,10 +118,18 @@ func (t *base) AddRef(rt string, ii ...string) *Ref { } } - ref := &Ref{ResourceType: rt, Identifiers: Identifiers{}.Add(iiC...)} - t.rr = append(t.rr, ref) + return t.addRef(&Ref{ResourceType: rt, Identifiers: Identifiers{}.Add(iiC...)}) +} - return ref +func (t *base) addRef(r *Ref) *Ref { + t.rr = append(t.rr, r) + return r +} + +// ReplaceRef replaces the given ref with the new one. +// The new ref is added regardles if the old one exists or not. +func (t *base) ReplaceRef(old, new *Ref) { + t.rr = t.rr.replaceRef(old, new) } // SetResourceType sets the resource type of the current resource struct @@ -219,3 +228,13 @@ func MakeRef(rt string, ii Identifiers) *Ref { func IgnoreDepResolution(ref *Ref) bool { return ref.ResourceType == composeTypes.ModuleFieldResourceType } + +func (t *base) ReID(ii Identifiers) { + t.SetIdentifier(ii) +} + +func (t *base) ReRef(old RefSet, new RefSet) { + for i, o := range old { + t.ReplaceRef(o, new[i]) + } +} diff --git a/pkg/envoy/resource/role.go b/pkg/envoy/resource/role.go index 0497ee48d..b0e2ae430 100644 --- a/pkg/envoy/resource/role.go +++ b/pkg/envoy/resource/role.go @@ -25,6 +25,8 @@ func NewRole(rl *types.Role) *Role { // Initial timestamps r.SetTimestamps(MakeTimestampsCUDA(&rl.CreatedAt, rl.UpdatedAt, rl.DeletedAt, rl.ArchivedAt)) + rl.ID = 0 + return r } diff --git a/pkg/envoy/resource/template.go b/pkg/envoy/resource/template.go index 351b36774..599cff7a3 100644 --- a/pkg/envoy/resource/template.go +++ b/pkg/envoy/resource/template.go @@ -25,6 +25,8 @@ func NewTemplate(t *types.Template) *Template { // Initial timestamps r.SetTimestamps(MakeTimestampsCUDAS(&t.CreatedAt, t.UpdatedAt, t.DeletedAt, nil, nil)) + t.ID = 0 + return r } diff --git a/pkg/envoy/resource/types.go b/pkg/envoy/resource/types.go index 193fc2673..9cf991c0c 100644 --- a/pkg/envoy/resource/types.go +++ b/pkg/envoy/resource/types.go @@ -7,9 +7,10 @@ type ( Identifiers() Identifiers ResourceType() string Refs() RefSet - MarkPlaceholder() Placeholder() bool + ReID(Identifiers) + ReRef(old RefSet, new RefSet) } InterfaceSet []Interface @@ -39,6 +40,12 @@ type ( EncodeTranslations() ([]*ResourceTranslation, error) } + PrunableInterface interface { + Interface + + Prune(*Ref) + } + RefSet []*Ref Ref struct { // @todo check with Denis regarding strings here (the cdocs comment) @@ -58,6 +65,14 @@ var ( ResourceTranslationType = "resource-translation" ) +func MakeRef(rt string, ii Identifiers) *Ref { + return &Ref{ResourceType: rt, Identifiers: ii} +} + +func MakeWildRef(rt string) *Ref { + return &Ref{ResourceType: rt, Identifiers: MakeIdentifiers("*")} +} + func MakeIdentifiers(ss ...string) Identifiers { ii := make(Identifiers) ii.Add(ss...) @@ -128,6 +143,36 @@ func (rr InterfaceSet) Walk(f func(r Interface) error) (err error) { return nil } +// SearchForIdentifiers returns the resources where the provided identifiers exist +// +// The Resource is matching if at least one identifier matches. +func (rr InterfaceSet) SearchForIdentifiers(ii Identifiers) (out InterfaceSet) { + out = make(InterfaceSet, 0, len(rr)/2) + + for _, r := range rr { + if r.Identifiers().HasAny(ii) { + out = append(out, r) + } + } + + return +} + +// SearchForReferences returns the resources where the provided references exist +// +// The Resource is matching if at least one reference matches. +func (rr InterfaceSet) SearchForReferences(ref *Ref) (out InterfaceSet) { + out = make(InterfaceSet, 0, len(rr)/2) + + for _, r := range rr { + if r.Refs().HasRef(ref) { + out = append(out, r) + } + } + + return +} + // Constraint returns the current reference with added constraint func (r *Ref) Constraint(c *Ref) *Ref { if r.Constraints == nil { @@ -144,7 +189,69 @@ func (r *Ref) Constraint(c *Ref) *Ref { // IsWildcard checks if this Ref points to all resources of a specific resource type func (r *Ref) IsWildcard() bool { - return r.Identifiers["*"] + return r.Identifiers != nil && r.Identifiers["*"] +} + +func (a *Ref) equals(b *Ref) bool { + if a.ResourceType != b.ResourceType { + return false + } + + if !b.IsWildcard() && !a.Identifiers.HasAny(b.Identifiers) { + return false + } + + for _, c := range b.Constraints { + if !a.Constraints.HasRef(c) { + return false + } + } + + return true +} + +func (rr RefSet) findRef(ref *Ref) int { + for i, r := range rr { + if r.equals(ref) { + return i + } + } + + return -1 +} + +// replaceRef replaces the reference both on the ref level and on the +// constraint level. +func (rr RefSet) replaceRef(old, new *Ref) RefSet { + found := false + + for x := len(rr) - 1; x >= 0; x-- { + r := rr[x] + + if r.equals(old) { + found = true + if new == nil { + rr[x] = rr[len(rr)-1] + return rr[:len(rr)-1] + } else { + rr[x] = new + } + } else { + if len(r.Constraints) > 0 { + r.Constraints = r.Constraints.replaceRef(old, new) + } + } + } + + if !found { + return append(rr, new) + } + + return rr +} + +func (rr RefSet) HasRef(ref *Ref) bool { + return rr.findRef(ref) > -1 } // Unique returns only unique references diff --git a/pkg/envoy/resource/user.go b/pkg/envoy/resource/user.go index c0fc1dac8..901bbfcba 100644 --- a/pkg/envoy/resource/user.go +++ b/pkg/envoy/resource/user.go @@ -25,6 +25,8 @@ func NewUser(u *types.User) *User { // Initial timestamps r.SetTimestamps(MakeTimestampsCUDAS(&u.CreatedAt, u.UpdatedAt, u.DeletedAt, nil, u.SuspendedAt)) + u.ID = 0 + return r }