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