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  ![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 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  ![img](/static/images/func-target.svg)
   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