mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 21:35:52 +00:00
99f7a1219e
This implements the Filter field on pbcatalog.WorkloadSelector to be a post-fetch in-memory filter using the https://github.com/hashicorp/go-bexpr expression language to filter resources based on their envelope metadata fields. All existing usages of WorkloadSelector should be able to make use of the filter.
106 lines
2.6 KiB
Go
106 lines
2.6 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package resource
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/go-bexpr"
|
|
|
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
|
)
|
|
|
|
// FilterResourcesByMetadata will use the provided go-bexpr based filter to
|
|
// retain matching items from the provided slice.
|
|
//
|
|
// The only variables usable in the expressions are the metadata keys prefixed
|
|
// by "metadata."
|
|
//
|
|
// If no filter is provided, then this does nothing and returns the input.
|
|
func FilterResourcesByMetadata(resources []*pbresource.Resource, filter string) ([]*pbresource.Resource, error) {
|
|
if filter == "" || len(resources) == 0 {
|
|
return resources, nil
|
|
}
|
|
|
|
eval, err := createMetadataFilterEvaluator(filter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
filtered := make([]*pbresource.Resource, 0, len(resources))
|
|
for _, res := range resources {
|
|
vars := &metadataFilterFieldDetails{
|
|
Meta: res.Metadata,
|
|
}
|
|
match, err := eval.Evaluate(vars)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if match {
|
|
filtered = append(filtered, res)
|
|
}
|
|
}
|
|
if len(filtered) == 0 {
|
|
return nil, nil
|
|
}
|
|
return filtered, nil
|
|
}
|
|
|
|
// FilterMatchesResourceMetadata will use the provided go-bexpr based filter to
|
|
// determine if the provided resource matches.
|
|
//
|
|
// The only variables usable in the expressions are the metadata keys prefixed
|
|
// by "metadata."
|
|
//
|
|
// If no filter is provided, then this returns true.
|
|
func FilterMatchesResourceMetadata(res *pbresource.Resource, filter string) (bool, error) {
|
|
if res == nil {
|
|
return false, nil
|
|
} else if filter == "" {
|
|
return true, nil
|
|
}
|
|
|
|
eval, err := createMetadataFilterEvaluator(filter)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
vars := &metadataFilterFieldDetails{
|
|
Meta: res.Metadata,
|
|
}
|
|
match, err := eval.Evaluate(vars)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return match, nil
|
|
}
|
|
|
|
// ValidateMetadataFilter will validate that the provided filter is going to be
|
|
// a valid input to the FilterResourcesByMetadata function.
|
|
//
|
|
// This is best called from a Validate hook.
|
|
func ValidateMetadataFilter(filter string) error {
|
|
if filter == "" {
|
|
return nil
|
|
}
|
|
|
|
_, err := createMetadataFilterEvaluator(filter)
|
|
return err
|
|
}
|
|
|
|
func createMetadataFilterEvaluator(filter string) (*bexpr.Evaluator, error) {
|
|
sampleVars := &metadataFilterFieldDetails{
|
|
Meta: make(map[string]string),
|
|
}
|
|
eval, err := bexpr.CreateEvaluatorForType(filter, nil, sampleVars)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("filter %q is invalid: %w", filter, err)
|
|
}
|
|
return eval, nil
|
|
}
|
|
|
|
type metadataFilterFieldDetails struct {
|
|
Meta map[string]string `bexpr:"metadata"`
|
|
}
|