Skip to content

Commit

Permalink
feat(resource selector): add scope
Browse files Browse the repository at this point in the history
  • Loading branch information
adityathebe authored and moshloop committed Jul 11, 2024
1 parent f2e96a4 commit 1556f6f
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 11 deletions.
13 changes: 13 additions & 0 deletions query/resource_selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down
44 changes: 35 additions & 9 deletions relationship_selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -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.
Expand All @@ -121,52 +139,60 @@ 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
}
}

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
}
}

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
}
}

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
}
}

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
}
}

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
}

Expand Down
10 changes: 9 additions & 1 deletion types/resource_selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 &&
Expand Down Expand Up @@ -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)),
Expand Down
2 changes: 1 addition & 1 deletion types/resource_selector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
}

Expand Down
1 change: 1 addition & 0 deletions zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1556f6f

Please sign in to comment.