package flags import ( "fmt" "strings" "github.com/spf13/pflag" "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/validation/field" ) // Apply stores the provided arguments onto a flag set, reporting any errors // encountered during the process. func Apply(args map[string][]string, flags *pflag.FlagSet) []error { var errs []error for key, value := range args { flag := flags.Lookup(key) if flag == nil { errs = append(errs, field.Invalid(field.NewPath("flag"), key, "is not a valid flag")) continue } for _, s := range value { if err := flag.Value.Set(s); err != nil { errs = append(errs, field.Invalid(field.NewPath(key), s, fmt.Sprintf("could not be set: %v", err))) break } } } return errs } func Resolve(args map[string][]string, fn func(*pflag.FlagSet)) []error { fs := pflag.NewFlagSet("extended", pflag.ContinueOnError) fn(fs) return Apply(args, fs) } // ComponentFlag represents a set of enabled components used in a command line. type ComponentFlag struct { enabled string disabled string enabledSet func() bool calculated sets.String allowed sets.String mappings map[string][]string } // NewComponentFlag returns a flag that represents the allowed components and can be bound to command line flags. func NewComponentFlag(mappings map[string][]string, allowed ...string) *ComponentFlag { set := sets.NewString(allowed...) return &ComponentFlag{ allowed: set, mappings: mappings, enabled: strings.Join(set.List(), ","), enabledSet: func() bool { return false }, } } // DefaultEnable resets the enabled components to only those provided that are also in the allowed // list. func (f *ComponentFlag) DefaultEnable(components ...string) *ComponentFlag { f.enabled = strings.Join(f.allowed.Union(sets.NewString(components...)).List(), ",") return f } // DefaultDisable resets the default enabled set to all allowed components except the provided. func (f *ComponentFlag) DefaultDisable(components ...string) *ComponentFlag { f.enabled = strings.Join(f.allowed.Difference(sets.NewString(components...)).List(), ",") return f } // Disable marks the provided components as disabled. func (f *ComponentFlag) Disable(components ...string) { f.Calculated().Delete(components...) } // Enabled returns true if the component is enabled. func (f *ComponentFlag) Enabled(name string) bool { return f.Calculated().Has(name) } // Calculated returns the effective enabled list. func (f *ComponentFlag) Calculated() sets.String { if f.calculated == nil { f.calculated = f.Expand(f.enabled).Difference(f.Expand(f.disabled)).Intersection(f.allowed) } return f.calculated } // Validate returns a copy of the set of enabled components, or an error if there are conflicts. func (f *ComponentFlag) Validate() (sets.String, error) { enabled := f.Expand(f.enabled) disabled := f.Expand(f.disabled) if diff := enabled.Difference(f.allowed); enabled.Len() > 0 && diff.Len() > 0 { return nil, fmt.Errorf("the following components are not recognized: %s", strings.Join(diff.List(), ", ")) } if diff := disabled.Difference(f.allowed); disabled.Len() > 0 && diff.Len() > 0 { return nil, fmt.Errorf("the following components are not recognized: %s", strings.Join(diff.List(), ", ")) } if inter := enabled.Intersection(disabled); f.enabledSet() && inter.Len() > 0 { return nil, fmt.Errorf("the following components can't be both disabled and enabled: %s", strings.Join(inter.List(), ", ")) } return enabled.Difference(disabled), nil } // Expand turns a string into a fully expanded set of components, resolving any mappings. func (f *ComponentFlag) Expand(value string) sets.String { if len(value) == 0 { return sets.NewString() } items := strings.Split(value, ",") set := sets.NewString() for _, s := range items { if mapped, ok := f.mappings[s]; ok { set.Insert(mapped...) } else { set.Insert(s) } } return set } // Allowed returns a copy of the allowed list of components. func (f *ComponentFlag) Allowed() sets.String { return sets.NewString(f.allowed.List()...) } // Mappings returns a copy of the mapping list for short names. func (f *ComponentFlag) Mappings() map[string][]string { copied := make(map[string][]string) for k, v := range f.mappings { copiedV := make([]string, len(v)) copy(copiedV, v) copied[k] = copiedV } return copied } // Bind registers the necessary flags with a flag set. func (f *ComponentFlag) Bind(flags *pflag.FlagSet, flagFormat, messagePrefix string) { flags.StringVar(&f.enabled, fmt.Sprintf(flagFormat, "enable"), f.enabled, messagePrefix+" enable") flags.StringVar(&f.disabled, fmt.Sprintf(flagFormat, "disable"), f.disabled, messagePrefix+" disable") f.enabledSet = func() bool { return flags.Lookup(fmt.Sprintf(flagFormat, "enable")).Changed } }