github.com/SamarSidharth/kpt@v0.0.0-20231122062228-c7d747ae3ace/site/book/04-using-functions/01-declarative-function-execution.md (about) 1 In many real-world scenarios, it's not sufficient to only have packages of 2 static, fully-rendered resource configuration. You want the package to declare 3 both static data as well as operations that should be performed on current 4 resources and any resource that may be added in the future as you edit the 5 package. Example use cases: 6 7 - Set the namespace on all namespace-scoped resources 8 - Always perform schema validation on resources in the package 9 - Always enforce a constraint policy on resources in the package 10 - Generate resources using a human-authored custom resource 11 12 In kpt, this is achieved by declaring a pipeline of functions in the `Kptfile` 13 and executing all the pipelines in the package hierarchy in a depth-first order 14 using the `fn render` command. 15 16 In our wordpress example, the top-level `wordpress` package declares this 17 pipeline: 18 19 ```yaml 20 # wordpress/Kptfile (Excerpt) 21 apiVersion: kpt.dev/v1 22 kind: Kptfile 23 metadata: 24 name: wordpress 25 pipeline: 26 mutators: 27 - image: gcr.io/kpt-fn/set-labels:v0.1 28 configMap: 29 app: wordpress 30 validators: 31 - image: gcr.io/kpt-fn/kubeval:v0.1 32 ``` 33 34 This declares two functions: 35 36 - `set-label` is a mutator function which adds a set of labels to resources. 37 - `kubeval` is a validator function which validates the resources against their 38 OpenAPI schema. 39 40 ?> Refer to the [Functions Catalog](https://catalog.kpt.dev/ ":target=_self") 41 for details on how to use a particular function. 42 43 There are two differences between mutators and validators: 44 45 1. Validators are not allowed to modify resources. 46 2. Validators are always executed after mutators. 47 48 The `mysql` subpackage declares only a mutator function: 49 50 ```yaml 51 # wordpress/mysql/Kptfile 52 apiVersion: kpt.dev/v1 53 kind: Kptfile 54 metadata: 55 name: mysql 56 pipeline: 57 mutators: 58 - image: gcr.io/kpt-fn/set-labels:v0.1 59 configMap: 60 tier: mysql 61 ``` 62 63 Now, let's render the package hierarchy: 64 65 ```shell 66 $ kpt fn render wordpress 67 Package "wordpress/mysql": 68 69 [PASS] "gcr.io/kpt-fn/set-labels:v0.1" 70 71 Package "wordpress": 72 73 [PASS] "gcr.io/kpt-fn/set-labels:v0.1" 74 [PASS] "gcr.io/kpt-fn/kubeval:v0.1" 75 76 Successfully executed 3 function(s) in 2 package(s). 77 ``` 78 79 ?> Refer to the [render command reference][render-doc] for usage. 80 81 When you invoke the `render` command, kpt performs the following steps: 82 83 1. Sequentially executes the list of mutators declared in the `mysql` package. 84 The input to the first function is the set of resources read from the 85 configuration files in the `mysql` package. The output of the first function 86 is the input of the second function and so on. 87 2. Similarly, executes all the validators declared in the `mysql` package. The 88 input to the first validator is the output of the last mutator. The output of 89 the last validator is the output of the pipeline in the `mysql` package. 90 3. Sequentially executes the list of mutators declared in the `wordpress` 91 package. The input to the first function is the union of: 92 93 - Resources read from configuration files in the `wordpress` package AND 94 - Output of the pipeline from the `mysql` package (Step 2). 95 96 4. Similarly, execute all the validators declared in the `wordpress` package. 97 The output of the last validator is the output of the pipeline in the 98 `wordpress` package. 99 5. Write the output of step 4 by modifying the local filesystem in-place. This 100 can change both `wordpress` and `mysql` packages. 101 102 The end result is that: 103 104 1. Resources in the `mysql` package are labelled with `tier: mysql`. 105 2. Resources in `mysql` and `wordpress` packages are labelled with 106 `app: wordpress`. 107 3. Resources in `mysql` and `wordpress` packages are validated against their 108 OpenAPI spec. 109 110 If any of the functions in the pipeline fails for whatever reason, then the 111 entire pipeline is aborted and the local filesystem is left intact. 112 113 ## Specifying `function` 114 115 ### `image` 116 117 The `image` field specifies the container image for the function. You can specify 118 an image from any container registry. If the registry is omitted, the default 119 container registry for functions catalog (`gcr.io/kpt-fn`) is prepended automatically. 120 For example, `set-labels:v0.1` is automatically expanded to `gcr.io/kpt-fn/set-labels:v0.1`. 121 122 ### `exec` 123 124 The `exec` field specifies the executable command for the function. You can specify 125 an executable with arguments. 126 127 Example below uses `sed` executable to replace all occurances of `foo` with `bar` 128 in the package resources. 129 130 ```yaml 131 # PKG_DIR/Kptfile (Excerpt) 132 apiVersion: kpt.dev/v1 133 kind: Kptfile 134 metadata: 135 name: app 136 pipeline: 137 mutators: 138 - exec: "sed -e 's/foo/bar/'" 139 ``` 140 141 Note that you must render the package by allowing executables by specifying `--allow-exec` 142 command line flag as shown below. 143 144 ```shell 145 $ kpt fn render [PKG_DIR] --allow-exec 146 ``` 147 148 Using `exec` is not recommended for two reasons: 149 150 - It makes the package non-portable since rendering the package requires the 151 executables to be present on the system. 152 - Executing binaries is not very secure since they can perform privileged operations 153 on the system. 154 155 ## Specifying `functionConfig` 156 157 In [Chapter 2], we saw this conceptual representation of a function invocation: 158 159  160 161 `functionConfig` is an optional meta resource containing the arguments to a 162 particular invocation of the function. There are two different ways to declare 163 the `functionConfig`. 164 165 ### `configPath` 166 167 The general way to provide a `functionConfig` of arbitrary kind (core or custom 168 resources), is to declare the resource in a separate file in the same directory 169 as the `Kptfile` and refer to it using the `configPath` field. 170 171 For example: 172 173 ```yaml 174 # wordpress/mysql/Kptfile 175 apiVersion: kpt.dev/v1 176 kind: Kptfile 177 metadata: 178 name: mysql 179 pipeline: 180 mutators: 181 - image: set-labels:v0.1 182 configPath: labels.yaml 183 ``` 184 185 ```yaml 186 # wordpress/mysql/labels.yaml 187 apiVersion: v1 188 kind: ConfigMap 189 metadata: 190 name: labels 191 data: 192 tier: mysql 193 ``` 194 195 ### `configMap` 196 197 Many functions take a `functionConfig` of kind `ConfigMap` since they only need 198 simple key/value pairs as argument. For convenience, there is a way to inline 199 the key/value pairs in the `Kptfile`. 200 201 The following is equivalent to what we showed before: 202 203 ```yaml 204 # wordpress/mysql/Kptfile 205 apiVersion: kpt.dev/v1 206 kind: Kptfile 207 metadata: 208 name: mysql 209 pipeline: 210 mutators: 211 - image: set-labels:v0.1 212 configMap: 213 tier: mysql 214 ``` 215 216 ## Specifying function `name` 217 218 Functions can optionally be named using the `pipeline.mutators.name` 219 or `pipeline.validators.name` field to identify a function. 220 221 For example: 222 223 ```yaml 224 # wordpress/mysql/Kptfile 225 apiVersion: kpt.dev/v1 226 kind: Kptfile 227 metadata: 228 name: mysql 229 pipeline: 230 mutators: 231 - name: set tier label 232 image: set-labels:v0.1 233 configMap: 234 tier: mysql 235 ``` 236 237 Unique function names for all functions in the Kptfile function 238 pipeline is recommended. If `name` is specified, `kpt pkg update` 239 will merge each function pipeline list as an associative list, using 240 `name` as the merge key. An unspecified `name` or duplicated names may 241 result in unexpected merges. 242 243 ## Specifying `selectors` 244 245 In some cases, you want to invoke the function only on a subset of resources based on a 246 selection criteria. This can be accomplished using selectors. At a high level, selectors 247 work as follows: 248 249  250 251 Resources that are selected are passed as input to the function. 252 Resources that are not selected are passed through unchanged. 253 254 For example, let's add a function to the pipeline that adds an annotation to 255 resources with name `wordpress-mysql` only: 256 257 ```yaml 258 # wordpress/Kptfile (Excerpt) 259 apiVersion: kpt.dev/v1 260 kind: Kptfile 261 metadata: 262 name: wordpress 263 pipeline: 264 mutators: 265 - image: gcr.io/kpt-fn/set-annotations:v0.1 266 configMap: 267 tier: mysql 268 selectors: 269 - name: wordpress-mysql 270 - image: gcr.io/kpt-fn/set-labels:v0.1 271 configMap: 272 app: wordpress 273 validators: 274 - image: gcr.io/kpt-fn/kubeval:v0.1 275 ``` 276 277 When you invoke the render command, the `mysql` package is rendered first, and `set-annotations` 278 function is invoked only on the resources with name `wordpress-mysql`. Then, `set-label` 279 function is invoked on all the resources in the package hierarchy of `wordpress` package. 280 281 ```shell 282 $ kpt fn render wordpress 283 Package "wordpress/mysql": 284 [RUNNING] "gcr.io/kpt-fn/set-label:v0.1" 285 [PASS] "gcr.io/kpt-fn/set-label:v0.1" 286 287 Package "wordpress": 288 [RUNNING] "gcr.io/kpt-fn/set-annotations:v0.1" on 3 resource(s) 289 [PASS] "gcr.io/kpt-fn/set-annotations:v0.1" 290 [RUNNING] "gcr.io/kpt-fn/set-label:v0.1" 291 [PASS] "gcr.io/kpt-fn/set-label:v0.1" 292 [RUNNING] "gcr.io/kpt-fn/kubeval:v0.1" 293 [PASS] "gcr.io/kpt-fn/kubeval:v0.1" 294 295 Successfully executed 4 function(s) in 2 package(s). 296 ``` 297 298 As another example, let's add another function to the pipeline that adds a prefix to the name of a resource if: 299 - it has kind `Deployment` AND name `wordpress` 300 **OR** 301 - it has kind `Service` AND name `wordpress` 302 303 ```yaml 304 # wordpress/Kptfile (Excerpt) 305 apiVersion: kpt.dev/v1 306 kind: Kptfile 307 metadata: 308 name: wordpress 309 pipeline: 310 mutators: 311 - image: gcr.io/kpt-fn/set-annotations:v0.1 312 configMap: 313 tier: mysql 314 selectors: 315 - name: wordpress-mysql 316 - image: gcr.io/kpt-fn/set-labels:v0.1 317 configMap: 318 app: wordpress 319 - image: gcr.io/kpt-fn/ensure-name-substring:v0.1 320 configMap: 321 prepend: dev- 322 selectors: 323 - kind: Deployment 324 name: wordpress 325 - kind: Service 326 name: wordpress 327 validators: 328 - image: gcr.io/kpt-fn/kubeval:v0.1 329 ``` 330 331 Now, let's render the package: 332 333 ```shell 334 kpt fn render wordpress 335 Package "wordpress/mysql": 336 [RUNNING] "gcr.io/kpt-fn/set-label:v0.1" 337 [PASS] "gcr.io/kpt-fn/set-label:v0.1" 338 339 Package "wordpress": 340 [RUNNING] "gcr.io/kpt-fn/set-annotations:v0.1" on 3 resource(s) 341 [PASS] "gcr.io/kpt-fn/set-annotations:v0.1" 342 [RUNNING] "gcr.io/kpt-fn/set-label:v0.1" 343 [PASS] "gcr.io/kpt-fn/set-label:v0.1" 344 [RUNNING] "gcr.io/kpt-fn/ensure-name-substring:v0.1" on 2 resource(s) 345 [PASS] "gcr.io/kpt-fn/ensure-name-substring:v0.1" 346 [RUNNING] "gcr.io/kpt-fn/kubeval:v0.1" 347 [PASS] "gcr.io/kpt-fn/kubeval:v0.1" 348 349 Successfully executed 5 function(s) in 2 package(s). 350 ``` 351 352 Note that the `ensure-name-substring` function is applied only to the 353 resources matching the selection criteria. 354 355 If you have resources with particular labels or annotations that you want to use to 356 select your resources, you can do so. For example, here is a function that will only 357 be applied to resources matching the label `foo: bar`: 358 359 ```yaml 360 apiVersion: kpt.dev/v1 361 kind: Kptfile 362 metadata: 363 name: wordpress 364 pipeline: 365 mutators: 366 - image: gcr.io/kpt-fn/set-annotations:v0.1 367 configMap: 368 tier: mysql 369 selectors: 370 - labels: 371 foo: bar 372 validators: 373 - image: gcr.io/kpt-fn/kubeval:v0.1 374 ``` 375 376 The following are the matchers you can specify in a selector: 377 378 1. `apiVersion`: `apiVersion` field value of resources to be selected. 379 2. `kind`: `kind` field value of resources to be selected. 380 3. `name`: `metadata.name` field value of resources to be selected. 381 4. `namespace`: `metadata.namespace` field of resources to be selected. 382 5. `annotations`: resources with matching annotations will be selected. 383 6. `labels`: resources with matching labels will be selected. 384 385 ### Specifying exclusions 386 387 Similar to `selectors`, you can also specify resources that should be excluded from functions. 388 389 For example, you can exclude a resource if it has both kind "Deployment" and name "nginx": 390 391 ```yaml 392 apiVersion: kpt.dev/v1 393 kind: Kptfile 394 metadata: 395 name: wordpress 396 pipeline: 397 mutators: 398 - image: gcr.io/kpt-fn/set-annotations:v0.1 399 configMap: 400 tier: mysql 401 exclude: 402 - kind: Deployment 403 name: nginx 404 validators: 405 - image: gcr.io/kpt-fn/kubeval:v0.1 406 ``` 407 408 This is distinct from the following, which excludes a resource if it has either kind "Deployment" or name "nginx": 409 410 ```yaml 411 apiVersion: kpt.dev/v1 412 kind: Kptfile 413 metadata: 414 name: wordpress 415 pipeline: 416 mutators: 417 - image: gcr.io/kpt-fn/set-annotations:v0.1 418 configMap: 419 tier: mysql 420 exclude: 421 - kind: Deployment 422 - name: nginx 423 validators: 424 - image: gcr.io/kpt-fn/kubeval:v0.1 425 ``` 426 427 The following are the matchers you can specify in an exclusion: 428 429 1. `apiVersion`: `apiVersion` field value of resources to be excluded. 430 2. `kind`: `kind` field value of resources to be excluded. 431 3. `name`: `metadata.name` field value of resources to be excluded. 432 4. `namespace`: `metadata.namespace` field of resources to be excluded. 433 5. `annotations`: resources with matching annotations will be excluded. 434 6. `labels`: resources with matching labels will be excluded. 435 436 [chapter 2]: /book/02-concepts/03-functions 437 [render-doc]: /reference/cli/fn/render/ 438 [Package identifier]: book/03-packages/01-getting-a-package?id=package-name-and-identifier