Skip to content

Commit

Permalink
render: fix selectors (#394)
Browse files Browse the repository at this point in the history
Without this patch selectors don't work as expected.  This patch
fixes selectors such that each --selector flag value configures one
selector containing multiple positive or negative label matchers.

Result:

Render build plans for cluster dev or cluster test.  Note the use of two
flags indicating logical OR.

    holos render platform --selector cluster=test --selector cluster=dev
    rendered external-secrets for cluster test in 299.897542ms
    rendered external-secrets for cluster dev in 299.9225ms
    rendered external-secrets-crds for cluster test in 667.6075ms
    rendered external-secrets-crds for cluster dev in 708.126541ms
    rendered platform in 708.795625ms

Render build plans for prod clusters that are not customer facing.  Note
the use of one selector with comma separated labels.

    holos render platform --selector "tier=prod,scope!=customer"
  • Loading branch information
jeffmccune committed Jan 9, 2025
1 parent 5862725 commit 6a60b61
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 19 deletions.
4 changes: 2 additions & 2 deletions internal/builder/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
// platform.
type PlatformOpts struct {
Fn func(context.Context, int, holos.Component) error
Selector holos.Selector
Selectors holos.Selectors
Concurrency int
InfoEnabled bool
}
Expand All @@ -31,7 +31,7 @@ type Platform struct {
func (p *Platform) Build(ctx context.Context, opts PlatformOpts) error {
limit := max(opts.Concurrency, 1)
parentStart := time.Now()
components := p.Select(opts.Selector)
components := p.Select(opts.Selectors...)
total := len(components)

g, ctx := errgroup.WithContext(ctx)
Expand Down
6 changes: 3 additions & 3 deletions internal/cli/render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ func newPlatform(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
cmd.Flags().StringVar(&platform, "platform", "./platform", "platform directory path")
var extractYAMLs holos.StringSlice
cmd.Flags().Var(&extractYAMLs, "extract-yaml", "data file paths to extract and unify with the platform config")
var selector holos.Selector
cmd.Flags().VarP(&selector, "selector", "l", "label selector (e.g. label==string,label!=string)")
var selectors holos.Selectors
cmd.Flags().VarP(&selectors, "selector", "l", "label selector (e.g. label==string,label!=string)")
tagMap := make(holos.TagMap)
cmd.Flags().VarP(&tagMap, "inject", "t", tagHelp)

Expand Down Expand Up @@ -75,7 +75,7 @@ func newPlatform(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
}
opts := builder.PlatformOpts{
Fn: makeComponentRenderFunc(cmd.ErrOrStderr(), prefixArgs, tagMap.Tags()),
Selector: selector,
Selectors: selectors,
Concurrency: concurrency,
InfoEnabled: true,
}
Expand Down
6 changes: 3 additions & 3 deletions internal/cli/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ func newShowBuildPlanCmd() (cmd *cobra.Command) {
cmd.Flags().Var(&extractYAMLs, "extract-yaml", "data file paths to extract and unify with the platform config")
var format string
cmd.Flags().StringVar(&format, "format", "yaml", "yaml or json format")
var selector holos.Selector
cmd.Flags().VarP(&selector, "selector", "l", "label selector (e.g. label==string,label!=string)")
var selectors holos.Selectors
cmd.Flags().VarP(&selectors, "selector", "l", "label selector (e.g. label==string,label!=string)")
tagMap := make(holos.TagMap)
cmd.Flags().VarP(&tagMap, "inject", "t", "set the value of a cue @tag field from a key=value pair")
var concurrency int
Expand Down Expand Up @@ -102,7 +102,7 @@ func newShowBuildPlanCmd() (cmd *cobra.Command) {

platformOpts := builder.PlatformOpts{
Fn: makeBuildFunc(encoder, buildPlanOpts),
Selector: selector,
Selectors: selectors,
Concurrency: concurrency,
}

Expand Down
40 changes: 30 additions & 10 deletions internal/holos/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,28 @@ func (e *EnvFlagger) Flag(name feature) bool {

type Labels map[string]string

type Selectors []Selector

// String implements the flag.Value interface.
func (s *Selectors) String() string {
return fmt.Sprint(*s)
}

// Type implements the pflag.Value interface and describes the type.
func (s *Selectors) Type() string {
return "selectors"
}

// Set implements the flag.Value interface.
func (s *Selectors) Set(value string) error {
selector := Selector{}
if err := selector.Set(value); err != nil {
return err
}
*s = append(*s, selector)
return nil
}

type Selector struct {
Positive map[string]string
Negative map[string]string
Expand All @@ -118,14 +140,9 @@ func (s *Selector) IsSelected(labels Labels) bool {
return true // Nil selector selects everything
}

if len(s.Positive) == 0 && len(s.Negative) == 0 {
return true // Empty selector selects everything
}

// Check positive matches
for k, v := range s.Positive {
val, ok := labels[k]
if !ok || v != val {
if val, ok := labels[k]; !ok || v != val {
return false
}
}
Expand Down Expand Up @@ -251,15 +268,18 @@ func (y *yamlEncoder) Close() error {
return errors.Wrap(y.enc.Close())
}

// IsSelected returns true if all selectors select the given labels or no
// IsSelected returns true if any one selector selects the given labels or no
// selectors are given.
func IsSelected(labels Labels, selectors ...Selector) bool {
if len(selectors) == 0 {
return true
}
for _, selector := range selectors {
if !selector.IsSelected(labels) {
return false
if selector.IsSelected(labels) {
return true
}
}
return true
return false
}

type orderedEncoder struct {
Expand Down
2 changes: 1 addition & 1 deletion version/embedded/patch
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3
4

0 comments on commit 6a60b61

Please sign in to comment.