...

Source file src/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/arguments.go

Documentation: sigs.k8s.io/controller-runtime/pkg/internal/testing/process

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package process
    18  
    19  import (
    20  	"bytes"
    21  	"html/template"
    22  	"sort"
    23  	"strings"
    24  )
    25  
    26  // RenderTemplates returns an []string to render the templates
    27  //
    28  // Deprecated: will be removed in favor of Arguments.
    29  func RenderTemplates(argTemplates []string, data interface{}) (args []string, err error) {
    30  	var t *template.Template
    31  
    32  	for _, arg := range argTemplates {
    33  		t, err = template.New(arg).Parse(arg)
    34  		if err != nil {
    35  			args = nil
    36  			return
    37  		}
    38  
    39  		buf := &bytes.Buffer{}
    40  		err = t.Execute(buf, data)
    41  		if err != nil {
    42  			args = nil
    43  			return
    44  		}
    45  		args = append(args, buf.String())
    46  	}
    47  
    48  	return
    49  }
    50  
    51  // SliceToArguments converts a slice of arguments to structured arguments,
    52  // appending each argument that starts with `--` and contains an `=` to the
    53  // argument set (ignoring defaults), returning the rest.
    54  //
    55  // Deprecated: will be removed when RenderTemplates is removed.
    56  func SliceToArguments(sliceArgs []string, args *Arguments) []string {
    57  	var rest []string
    58  	for i, arg := range sliceArgs {
    59  		if arg == "--" {
    60  			rest = append(rest, sliceArgs[i:]...)
    61  			return rest
    62  		}
    63  		// skip non-flag arguments, skip arguments w/o equals because we
    64  		// can't tell if the next argument should take a value
    65  		if !strings.HasPrefix(arg, "--") || !strings.Contains(arg, "=") {
    66  			rest = append(rest, arg)
    67  			continue
    68  		}
    69  
    70  		parts := strings.SplitN(arg[2:], "=", 2)
    71  		name := parts[0]
    72  		val := parts[1]
    73  
    74  		args.AppendNoDefaults(name, val)
    75  	}
    76  
    77  	return rest
    78  }
    79  
    80  // TemplateDefaults specifies defaults to be used for joining structured arguments with templates.
    81  //
    82  // Deprecated: will be removed when RenderTemplates is removed.
    83  type TemplateDefaults struct {
    84  	// Data will be used to render the template.
    85  	Data interface{}
    86  	// Defaults will be used to default structured arguments if no template is passed.
    87  	Defaults map[string][]string
    88  	// MinimalDefaults will be used to default structured arguments if a template is passed.
    89  	// Use this for flags which *must* be present.
    90  	MinimalDefaults map[string][]string // for api server service-cluster-ip-range
    91  }
    92  
    93  // TemplateAndArguments joins structured arguments and non-structured arguments, preserving existing
    94  // behavior.  Namely:
    95  //
    96  //  1. if templ has len > 0, it will be rendered against data
    97  //  2. the rendered template values that look like `--foo=bar` will be split
    98  //     and appended to args, the rest will be kept around
    99  //  3. the given args will be rendered as string form.  If a template is given,
   100  //     no defaults will be used, otherwise defaults will be used
   101  //  4. a result of [args..., rest...] will be returned
   102  //
   103  // It returns the resulting rendered arguments, plus the arguments that were
   104  // not transferred to `args` during rendering.
   105  //
   106  // Deprecated: will be removed when RenderTemplates is removed.
   107  func TemplateAndArguments(templ []string, args *Arguments, data TemplateDefaults) (allArgs []string, nonFlagishArgs []string, err error) {
   108  	if len(templ) == 0 { // 3 & 4 (no template case)
   109  		return args.AsStrings(data.Defaults), nil, nil
   110  	}
   111  
   112  	// 1: render the template
   113  	rendered, err := RenderTemplates(templ, data.Data)
   114  	if err != nil {
   115  		return nil, nil, err
   116  	}
   117  
   118  	// 2: filter out structured args and add them to args
   119  	rest := SliceToArguments(rendered, args)
   120  
   121  	// 3 (template case): render structured args, no defaults (matching the
   122  	// legacy case where if Args was specified, no defaults were used)
   123  	res := args.AsStrings(data.MinimalDefaults)
   124  
   125  	// 4: return the rendered structured args + all non-structured args
   126  	return append(res, rest...), rest, nil
   127  }
   128  
   129  // EmptyArguments constructs an empty set of flags with no defaults.
   130  func EmptyArguments() *Arguments {
   131  	return &Arguments{
   132  		values: make(map[string]Arg),
   133  	}
   134  }
   135  
   136  // Arguments are structured, overridable arguments.
   137  // Each Arguments object contains some set of default arguments, which may
   138  // be appended to, or overridden.
   139  //
   140  // When ready, you can serialize them to pass to exec.Command and friends using
   141  // AsStrings.
   142  //
   143  // All flag-setting methods return the *same* instance of Arguments so that you
   144  // can chain calls.
   145  type Arguments struct {
   146  	// values contains the user-set values for the arguments.
   147  	// `values[key] = dontPass` means "don't pass this flag"
   148  	// `values[key] = passAsName` means "pass this flag without args like --key`
   149  	// `values[key] = []string{a, b, c}` means "--key=a --key=b --key=c`
   150  	// any values not explicitly set here will be copied from defaults on final rendering.
   151  	values map[string]Arg
   152  }
   153  
   154  // Arg is an argument that has one or more values,
   155  // and optionally falls back to default values.
   156  type Arg interface {
   157  	// Append adds new values to this argument, returning
   158  	// a new instance contain the new value.  The intermediate
   159  	// argument should generally be assumed to be consumed.
   160  	Append(vals ...string) Arg
   161  	// Get returns the full set of values, optionally including
   162  	// the passed in defaults.  If it returns nil, this will be
   163  	// skipped.  If it returns a non-nil empty slice, it'll be
   164  	// assumed that the argument should be passed as name-only.
   165  	Get(defaults []string) []string
   166  }
   167  
   168  type userArg []string
   169  
   170  func (a userArg) Append(vals ...string) Arg {
   171  	return userArg(append(a, vals...)) //nolint:unconvert
   172  }
   173  func (a userArg) Get(_ []string) []string {
   174  	return []string(a)
   175  }
   176  
   177  type defaultedArg []string
   178  
   179  func (a defaultedArg) Append(vals ...string) Arg {
   180  	return defaultedArg(append(a, vals...)) //nolint:unconvert
   181  }
   182  func (a defaultedArg) Get(defaults []string) []string {
   183  	res := append([]string(nil), defaults...)
   184  	return append(res, a...)
   185  }
   186  
   187  type dontPassArg struct{}
   188  
   189  func (a dontPassArg) Append(vals ...string) Arg {
   190  	return userArg(vals)
   191  }
   192  func (dontPassArg) Get(_ []string) []string {
   193  	return nil
   194  }
   195  
   196  type passAsNameArg struct{}
   197  
   198  func (a passAsNameArg) Append(_ ...string) Arg {
   199  	return passAsNameArg{}
   200  }
   201  func (passAsNameArg) Get(_ []string) []string {
   202  	return []string{}
   203  }
   204  
   205  var (
   206  	// DontPass indicates that the given argument will not actually be
   207  	// rendered.
   208  	DontPass Arg = dontPassArg{}
   209  	// PassAsName indicates that the given flag will be passed as `--key`
   210  	// without any value.
   211  	PassAsName Arg = passAsNameArg{}
   212  )
   213  
   214  // AsStrings serializes this set of arguments to a slice of strings appropriate
   215  // for passing to exec.Command and friends, making use of the given defaults
   216  // as indicated for each particular argument.
   217  //
   218  //   - Any flag in defaults that's not in Arguments will be present in the output
   219  //   - Any flag that's present in Arguments will be passed the corresponding
   220  //     defaults to do with as it will (ignore, append-to, suppress, etc).
   221  func (a *Arguments) AsStrings(defaults map[string][]string) []string {
   222  	// sort for deterministic ordering
   223  	keysInOrder := make([]string, 0, len(defaults)+len(a.values))
   224  	for key := range defaults {
   225  		if _, userSet := a.values[key]; userSet {
   226  			continue
   227  		}
   228  		keysInOrder = append(keysInOrder, key)
   229  	}
   230  	for key := range a.values {
   231  		keysInOrder = append(keysInOrder, key)
   232  	}
   233  	sort.Strings(keysInOrder)
   234  
   235  	var res []string
   236  	for _, key := range keysInOrder {
   237  		vals := a.Get(key).Get(defaults[key])
   238  		switch {
   239  		case vals == nil: // don't pass
   240  			continue
   241  		case len(vals) == 0: // pass as name
   242  			res = append(res, "--"+key)
   243  		default:
   244  			for _, val := range vals {
   245  				res = append(res, "--"+key+"="+val)
   246  			}
   247  		}
   248  	}
   249  
   250  	return res
   251  }
   252  
   253  // Get returns the value of the given flag.  If nil,
   254  // it will not be passed in AsString, otherwise:
   255  //
   256  // len == 0 --> `--key`, len > 0 --> `--key=val1 --key=val2 ...`.
   257  func (a *Arguments) Get(key string) Arg {
   258  	if vals, ok := a.values[key]; ok {
   259  		return vals
   260  	}
   261  	return defaultedArg(nil)
   262  }
   263  
   264  // Enable configures the given key to be passed as a "name-only" flag,
   265  // like, `--key`.
   266  func (a *Arguments) Enable(key string) *Arguments {
   267  	a.values[key] = PassAsName
   268  	return a
   269  }
   270  
   271  // Disable prevents this flag from be passed.
   272  func (a *Arguments) Disable(key string) *Arguments {
   273  	a.values[key] = DontPass
   274  	return a
   275  }
   276  
   277  // Append adds additional values to this flag.  If this flag has
   278  // yet to be set, initial values will include defaults.  If you want
   279  // to intentionally ignore defaults/start from scratch, call AppendNoDefaults.
   280  //
   281  // Multiple values will look like `--key=value1 --key=value2 ...`.
   282  func (a *Arguments) Append(key string, values ...string) *Arguments {
   283  	vals, present := a.values[key]
   284  	if !present {
   285  		vals = defaultedArg{}
   286  	}
   287  	a.values[key] = vals.Append(values...)
   288  	return a
   289  }
   290  
   291  // AppendNoDefaults adds additional values to this flag.  However,
   292  // unlike Append, it will *not* copy values from defaults.
   293  func (a *Arguments) AppendNoDefaults(key string, values ...string) *Arguments {
   294  	vals, present := a.values[key]
   295  	if !present {
   296  		vals = userArg{}
   297  	}
   298  	a.values[key] = vals.Append(values...)
   299  	return a
   300  }
   301  
   302  // Set resets the given flag to the specified values, ignoring any existing
   303  // values or defaults.
   304  func (a *Arguments) Set(key string, values ...string) *Arguments {
   305  	a.values[key] = userArg(values)
   306  	return a
   307  }
   308  
   309  // SetRaw sets the given flag to the given Arg value directly.  Use this if
   310  // you need to do some complicated deferred logic or something.
   311  //
   312  // Otherwise behaves like Set.
   313  func (a *Arguments) SetRaw(key string, val Arg) *Arguments {
   314  	a.values[key] = val
   315  	return a
   316  }
   317  
   318  // FuncArg is a basic implementation of Arg that can be used for custom argument logic,
   319  // like pulling values out of APIServer, or dynamically calculating values just before
   320  // launch.
   321  //
   322  // The given function will be mapped directly to Arg#Get, and will generally be
   323  // used in conjunction with SetRaw.  For example, to set `--some-flag` to the
   324  // API server's CertDir, you could do:
   325  //
   326  //	server.Configure().SetRaw("--some-flag", FuncArg(func(defaults []string) []string {
   327  //	    return []string{server.CertDir}
   328  //	}))
   329  //
   330  // FuncArg ignores Appends; if you need to support appending values too, consider implementing
   331  // Arg directly.
   332  type FuncArg func([]string) []string
   333  
   334  // Append is a no-op for FuncArg, and just returns itself.
   335  func (a FuncArg) Append(vals ...string) Arg { return a }
   336  
   337  // Get delegates functionality to the FuncArg function itself.
   338  func (a FuncArg) Get(defaults []string) []string {
   339  	return a(defaults)
   340  }
   341  

View as plain text