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