From 1556f6feb37a5277c9622d0e032ea84e290806d8 Mon Sep 17 00:00:00 2001 From: Aditya Thebe Date: Thu, 11 Jul 2024 18:31:19 +0545 Subject: [PATCH] feat(resource selector): add scope --- query/resource_selector.go | 13 ++++++++++ relationship_selector.go | 44 ++++++++++++++++++++++++++------- types/resource_selector.go | 10 +++++++- types/resource_selector_test.go | 2 +- zz_generated.deepcopy.go | 1 + 5 files changed, 59 insertions(+), 11 deletions(-) diff --git a/query/resource_selector.go b/query/resource_selector.go index c4eadcde..fa6beafe 100644 --- a/query/resource_selector.go +++ b/query/resource_selector.go @@ -140,6 +140,19 @@ func SetResourceSelectorClause(ctx context.Context, resourceSelector types.Resou query = query.Where("status IN ?", resourceSelector.Statuses) } + if resourceSelector.Scope != "" { + switch table { + case "checks": + query = query.Where("canary_id = ?", resourceSelector.Scope) + case "config_items": + query = query.Where("scraper_id = ?", resourceSelector.Scope) + case "components": + query = query.Where("topology_id = ?", resourceSelector.Scope) + default: + return api.Errorf(api.EINVALID, "scope is not supported for %s", table) + } + } + if resourceSelector.Agent == "" { query = query.Where("agent_id = ?", uuid.Nil) } else if resourceSelector.Agent == "all" { diff --git a/relationship_selector.go b/relationship_selector.go index 089c766b..cc39fa9d 100644 --- a/relationship_selector.go +++ b/relationship_selector.go @@ -57,10 +57,16 @@ type RelationshipSelector struct { Type string `json:"type,omitempty"` Agent string `json:"agent,omitempty"` Labels map[string]string `json:"labels,omitempty"` + + // Scope is the id parent of the resource to select. + // Example: For config items, the scope is the scraper id + // - for checks, it's canaries and + // - for components, it's topology. + Scope string `json:"scope,omitempty"` } func (t *RelationshipSelector) IsEmpty() bool { - return t.ID == "" && t.ExternalID == "" && t.Name == "" && t.Namespace == "" && t.Type == "" && t.Agent == "" && len(t.Labels) == 0 + return t.ID == "" && t.ExternalID == "" && t.Name == "" && t.Namespace == "" && t.Type == "" && t.Agent == "" && len(t.Labels) == 0 && t.Scope == "" } func (t *RelationshipSelector) ToResourceSelector() types.ResourceSelector { @@ -72,6 +78,7 @@ func (t *RelationshipSelector) ToResourceSelector() types.ResourceSelector { rs := types.ResourceSelector{ ID: t.ID, + Scope: t.Scope, Name: t.Name, Agent: t.Agent, LabelSelector: labelSelector, @@ -94,16 +101,27 @@ type RelationshipSelectorTemplate struct { Name Lookup `json:"name,omitempty"` Namespace Lookup `json:"namespace,omitempty"` Type Lookup `json:"type,omitempty"` + // Agent can be one of // - agent id // - agent name // - 'self' (no agent) - Agent Lookup `json:"agent,omitempty"` + Agent Lookup `json:"agent,omitempty"` + + // Scope is the id of the parent of the resource to select. + // Example: For config items, the scope is the scraper id + // - for checks, it's canaries and + // - for components, it's topology. + // If left empty, the scope is the requester's scope. + // Use `all` to disregard scope. + Scope Lookup `json:"scope,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } func (t *RelationshipSelectorTemplate) IsEmpty() bool { - return t.ID.IsEmpty() && t.ExternalID.IsEmpty() && t.Name.IsEmpty() && t.Namespace.IsEmpty() && t.Type.IsEmpty() && t.Agent.IsEmpty() && len(t.Labels) == 0 + return t.ID.IsEmpty() && t.ExternalID.IsEmpty() && t.Name.IsEmpty() && t.Namespace.IsEmpty() && + t.Scope.IsEmpty() && t.Type.IsEmpty() && t.Agent.IsEmpty() && len(t.Labels) == 0 } // Eval evaluates the template and returns a RelationshipSelector. @@ -121,7 +139,7 @@ func (t *RelationshipSelectorTemplate) Eval(labels map[string]string, env map[st if !t.ID.IsEmpty() { if output.ID, err = t.ID.Eval(labels, env); err != nil { - return nil, fmt.Errorf("failed to evaluate id: %v for config relationship: %w", t.ID, err) + return nil, fmt.Errorf("failed to evaluate id: %v for relationship: %w", t.ID, err) } else if output.ID == "" { return nil, nil } @@ -129,7 +147,7 @@ func (t *RelationshipSelectorTemplate) Eval(labels map[string]string, env map[st if !t.ExternalID.IsEmpty() { if output.ExternalID, err = t.ExternalID.Eval(labels, env); err != nil { - return nil, fmt.Errorf("failed to evaluate external id: %v for config relationship: %w", t.ExternalID, err) + return nil, fmt.Errorf("failed to evaluate external id: %v for relationship: %w", t.ExternalID, err) } else if output.ExternalID == "" { return nil, nil } @@ -137,7 +155,7 @@ func (t *RelationshipSelectorTemplate) Eval(labels map[string]string, env map[st if !t.Name.IsEmpty() { if output.Name, err = t.Name.Eval(labels, env); err != nil { - return nil, fmt.Errorf("failed to evaluate name: %v for config relationship: %w", t.Name, err) + return nil, fmt.Errorf("failed to evaluate name: %v for relationship: %w", t.Name, err) } else if output.Name == "" { return nil, nil } @@ -145,7 +163,7 @@ func (t *RelationshipSelectorTemplate) Eval(labels map[string]string, env map[st if !t.Namespace.IsEmpty() { if output.Namespace, err = t.Namespace.Eval(labels, env); err != nil { - return nil, fmt.Errorf("failed to evaluate namespace: %v for config relationship: %w", t.Namespace, err) + return nil, fmt.Errorf("failed to evaluate namespace: %v for relationship: %w", t.Namespace, err) } else if output.Namespace == "" { return nil, nil } @@ -153,7 +171,7 @@ func (t *RelationshipSelectorTemplate) Eval(labels map[string]string, env map[st if !t.Type.IsEmpty() { if output.Type, err = t.Type.Eval(labels, env); err != nil { - return nil, fmt.Errorf("failed to evaluate type: %v for config relationship: %w", t.Type, err) + return nil, fmt.Errorf("failed to evaluate type: %v for relationship: %w", t.Type, err) } else if output.Type == "" { return nil, nil } @@ -161,12 +179,20 @@ func (t *RelationshipSelectorTemplate) Eval(labels map[string]string, env map[st if !t.Agent.IsEmpty() { if output.Agent, err = t.Agent.Eval(labels, env); err != nil { - return nil, fmt.Errorf("failed to evaluate agent_id: %v for config relationship: %w", t.Agent, err) + return nil, fmt.Errorf("failed to evaluate agent_id: %v for relationship: %w", t.Agent, err) } else if output.Agent == "" { return nil, nil } } + if !t.Scope.IsEmpty() { + if output.Scope, err = t.Scope.Eval(labels, env); err != nil { + return nil, fmt.Errorf("failed to evaluate scope: %v for relationship: %w", t.Scope, err) + } else if output.Scope == "" { + return nil, nil + } + } + return &output, nil } diff --git a/types/resource_selector.go b/types/resource_selector.go index 2237e446..6a136244 100644 --- a/types/resource_selector.go +++ b/types/resource_selector.go @@ -22,6 +22,12 @@ type ResourceSelector struct { // Additionally, the special "self" value can be used to select resources without an agent. Agent string `yaml:"agent,omitempty" json:"agent,omitempty"` + // Scope is the id parent of the resource to select. + // Example: For config items, the scope is the scraper id + // - for checks, it's canaries and + // - for components, it's topology. + Scope string + // Cache directives // 'no-cache' (should not fetch from cache but can be cached) // 'no-store' (should not cache) @@ -41,7 +47,8 @@ type ResourceSelector struct { } func (c ResourceSelector) IsEmpty() bool { - return c.ID == "" && c.Name == "" && c.Namespace == "" && c.Agent == "" && len(c.Types) == 0 && + return c.ID == "" && c.Name == "" && c.Namespace == "" && c.Agent == "" && c.Scope == "" && + len(c.Types) == 0 && len(c.Statuses) == 0 && len(c.TagSelector) == 0 && len(c.LabelSelector) == 0 && @@ -77,6 +84,7 @@ func (c ResourceSelector) Hash() string { c.Name, c.Namespace, c.Agent, + c.Scope, strings.Join(c.Types.Sort(), ","), strings.Join(c.Statuses.Sort(), ","), collections.SortedMap(collections.SelectorToMap(c.TagSelector)), diff --git a/types/resource_selector_test.go b/types/resource_selector_test.go index 83a5bea8..93c9e76f 100644 --- a/types/resource_selector_test.go +++ b/types/resource_selector_test.go @@ -30,7 +30,7 @@ func TestResourceSelector_Hash_Consistency(t *testing.T) { LabelSelector: "app=example,env=production", FieldSelector: "owner=admin,path=/,icon=example.png", }, - expectedHash: "870f326c398aa91734ab5ff6959e488500a844788eb0f31e76744d697ceae400", + expectedHash: "b2eadc3ba5ed46dc207470a96b1cc88ab7129c5cbb906ecee666e8fdd93d8fed", }, } diff --git a/zz_generated.deepcopy.go b/zz_generated.deepcopy.go index f438c94d..5428fc39 100644 --- a/zz_generated.deepcopy.go +++ b/zz_generated.deepcopy.go @@ -52,6 +52,7 @@ func (in *RelationshipSelectorTemplate) DeepCopyInto(out *RelationshipSelectorTe out.Namespace = in.Namespace out.Type = in.Type out.Agent = in.Agent + out.Scope = in.Scope if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in))