github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/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 ![img](/static/images/func.svg) 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 `selectors` 217 218 In some cases, you want to invoke the function only on a subset of resources based on a 219 selection criteria. This can be accomplished using selectors. At a high level, selectors 220 work as follows: 221 222 ![img](/static/images/func-target.svg) 223 224 Resources that are selected are passed as input to the function. 225 Resources that are not selected are passed through unchanged. 226 227 For example, let's add a function to the pipeline that adds an annotation to 228 resources with name `wordpress-mysql` only: 229 230 ```yaml 231 # wordpress/Kptfile (Excerpt) 232 apiVersion: kpt.dev/v1 233 kind: Kptfile 234 metadata: 235 name: wordpress 236 pipeline: 237 mutators: 238 - image: gcr.io/kpt-fn/set-annotations:v0.1 239 configMap: 240 tier: mysql 241 selectors: 242 - name: wordpress-mysql 243 - image: gcr.io/kpt-fn/set-labels:v0.1 244 configMap: 245 app: wordpress 246 validators: 247 - image: gcr.io/kpt-fn/kubeval:v0.1 248 ``` 249 250 When you invoke the render command, the `mysql` package is rendered first, and `set-annotations` 251 function is invoked only on the resources with name `wordpress-mysql`. Then, `set-label` 252 function is invoked on all the resources in the package hierarchy of `wordpress` package. 253 254 ```shell 255 $ kpt fn render wordpress 256 Package "wordpress/mysql": 257 [RUNNING] "gcr.io/kpt-fn/set-label:v0.1" 258 [PASS] "gcr.io/kpt-fn/set-label:v0.1" 259 260 Package "wordpress": 261 [RUNNING] "gcr.io/kpt-fn/set-annotations:v0.1" on 3 resource(s) 262 [PASS] "gcr.io/kpt-fn/set-annotations:v0.1" 263 [RUNNING] "gcr.io/kpt-fn/set-label:v0.1" 264 [PASS] "gcr.io/kpt-fn/set-label:v0.1" 265 [RUNNING] "gcr.io/kpt-fn/kubeval:v0.1" 266 [PASS] "gcr.io/kpt-fn/kubeval:v0.1" 267 268 Successfully executed 4 function(s) in 2 package(s). 269 ``` 270 271 As another example, let's add another function to the pipeline that adds a prefix to the name of a resource if: 272 - it has kind `Deployment` AND name `wordpress` 273 **OR** 274 - it has kind `Service` AND name `wordpress` 275 276 ```yaml 277 # wordpress/Kptfile (Excerpt) 278 apiVersion: kpt.dev/v1 279 kind: Kptfile 280 metadata: 281 name: wordpress 282 pipeline: 283 mutators: 284 - image: gcr.io/kpt-fn/set-annotations:v0.1 285 configMap: 286 tier: mysql 287 selectors: 288 - name: wordpress-mysql 289 - image: gcr.io/kpt-fn/set-labels:v0.1 290 configMap: 291 app: wordpress 292 - image: gcr.io/kpt-fn/ensure-name-substring:v0.1 293 configMap: 294 prepend: dev- 295 selectors: 296 - kind: Deployment 297 name: wordpress 298 - kind: Service 299 name: wordpress 300 validators: 301 - image: gcr.io/kpt-fn/kubeval:v0.1 302 ``` 303 304 Now, let's render the package: 305 306 ```shell 307 kpt fn render wordpress 308 Package "wordpress/mysql": 309 [RUNNING] "gcr.io/kpt-fn/set-label:v0.1" 310 [PASS] "gcr.io/kpt-fn/set-label:v0.1" 311 312 Package "wordpress": 313 [RUNNING] "gcr.io/kpt-fn/set-annotations:v0.1" on 3 resource(s) 314 [PASS] "gcr.io/kpt-fn/set-annotations:v0.1" 315 [RUNNING] "gcr.io/kpt-fn/set-label:v0.1" 316 [PASS] "gcr.io/kpt-fn/set-label:v0.1" 317 [RUNNING] "gcr.io/kpt-fn/ensure-name-substring:v0.1" on 2 resource(s) 318 [PASS] "gcr.io/kpt-fn/ensure-name-substring:v0.1" 319 [RUNNING] "gcr.io/kpt-fn/kubeval:v0.1" 320 [PASS] "gcr.io/kpt-fn/kubeval:v0.1" 321 322 Successfully executed 5 function(s) in 2 package(s). 323 ``` 324 325 Note that the `ensure-name-substring` function is applied only to the 326 resources matching the selection criteria. 327 328 If you have resources with particular labels or annotations that you want to use to 329 select your resources, you can do so. For example, here is a function that will only 330 be applied to resources matching the label `foo: bar`: 331 332 ```yaml 333 apiVersion: kpt.dev/v1 334 kind: Kptfile 335 metadata: 336 name: wordpress 337 pipeline: 338 mutators: 339 - image: gcr.io/kpt-fn/set-annotations:v0.1 340 configMap: 341 tier: mysql 342 selectors: 343 - labels: 344 foo: bar 345 validators: 346 - image: gcr.io/kpt-fn/kubeval:v0.1 347 ``` 348 349 The following are the matchers you can specify in a selector: 350 351 1. `apiVersion`: `apiVersion` field value of resources to be selected. 352 2. `kind`: `kind` field value of resources to be selected. 353 3. `name`: `metadata.name` field value of resources to be selected. 354 4. `namespace`: `metadata.namespace` field of resources to be selected. 355 5. `annotations`: resources with matching annotations will be selected. 356 6. `labels`: resources with matching labels will be selected. 357 358 ### Specifying exclusions 359 360 Similar to `selectors`, you can also specify resources that should be excluded from functions. 361 362 For example, you can exclude a resource if it has both kind "Deployment" and name "nginx": 363 364 ```yaml 365 apiVersion: kpt.dev/v1 366 kind: Kptfile 367 metadata: 368 name: wordpress 369 pipeline: 370 mutators: 371 - image: gcr.io/kpt-fn/set-annotations:v0.1 372 configMap: 373 tier: mysql 374 exclude: 375 - kind: Deployment 376 name: nginx 377 validators: 378 - image: gcr.io/kpt-fn/kubeval:v0.1 379 ``` 380 381 This is distinct from the following, which excludes a resource if it has either kind "Deployment" or name "nginx": 382 383 ```yaml 384 apiVersion: kpt.dev/v1 385 kind: Kptfile 386 metadata: 387 name: wordpress 388 pipeline: 389 mutators: 390 - image: gcr.io/kpt-fn/set-annotations:v0.1 391 configMap: 392 tier: mysql 393 exclude: 394 - kind: Deployment 395 - name: nginx 396 validators: 397 - image: gcr.io/kpt-fn/kubeval:v0.1 398 ``` 399 400 The following are the matchers you can specify in an exclusion: 401 402 1. `apiVersion`: `apiVersion` field value of resources to be excluded. 403 2. `kind`: `kind` field value of resources to be excluded. 404 3. `name`: `metadata.name` field value of resources to be excluded. 405 4. `namespace`: `metadata.namespace` field of resources to be excluded. 406 5. `annotations`: resources with matching annotations will be excluded. 407 6. `labels`: resources with matching labels will be excluded. 408 409 [chapter 2]: /book/02-concepts/03-functions 410 [render-doc]: /reference/cli/fn/render/ 411 [Package identifier]: book/03-packages/01-getting-a-package?id=package-name-and-identifier