github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/site/guides/variant-constructor-pattern.md (about)

     1  # Variant construction pattern
     2  
     3  If you look at the config workflows, you will notice that creating a variant
     4  of a package is a very frequent operation, so reducing the steps
     5  required to create a variant can have significant benefits for the
     6  package consumers. In this guide, we will look at some techniques
     7  that a package author can use to enable automatic variant construction of a package.
     8  
     9  ## Types of packages
    10  
    11  kpt packages comes in two flavors:  `abstract package` and
    12  `deployable instance`. An `abstract` package is a reususable package that
    13  is used to create deployable instances that can be deployed to a
    14  kubernetes cluster. In programming language terms, you can think of an `abstract`
    15  packages as the class and `deployable instance` as the instances of the class.
    16  `deployable` instances of package are also referred to as `variant` of the package.
    17  
    18  Figure below shows a `package catalog` on the left that has `abstract` packages
    19  and `deployable instances` on the right. A good pattern is to keep the abstract
    20  packages and instance packages in separate repos and typically
    21  `deployable instances` repo will be setup to auto deploy to a kubernetes cluster
    22  using gitops tools such as `config-sync`, `fluxcd`, `argocd`.
    23  
    24  ![variant constructor pkg repo diagram](/static/images/variant-constructor-pkg-repo-diagram.png)
    25  
    26  Resources in an `abstract` package have placeholder values that need to be
    27  substituted with actual values to make them ready for deployment.
    28  For example, the name of the namespace resource below has `example` as a placeholder
    29  value. This is a part of the `abstract package`.
    30  
    31  ```yaml
    32  apiVersion:v1
    33  kind: Namespace
    34  metadata:
    35    name: example # <-- this is a placeholder value
    36  ```
    37  
    38  ## Customizing identity of resources
    39  
    40  A kpt package contains kubernetes resources. Whenever you are creating a
    41  variant of the package, first step is to ensure unique identity of the
    42  resources in that variant. For example, if the abstract package contains a
    43  `namespace` resource, then the variant package should contain a `namespace` resource
    44  corresponding to that variant.
    45  
    46  In a kubernetes cluster, resources are identified by their group, version, kind,
    47  namespace and name (also referred to as GVKNN). If resource is cluster scoped,
    48  then the `metadata.name` uniquely identifies the resource in a cluster. If the resource
    49  is namespace scoped, then (`metadata.namespace`, `metadata.name`) together identifies the
    50  resource uniquely.
    51  
    52  [kpt-function-catalog](https://catalog.kpt.dev) provides two function that helps
    53  with customizing the identify of the resources:
    54  
    55  1. [set-namespace](https://catalog.kpt.dev/set-namespace/v0.3/): sets the
    56     namespace for all resources in the package.
    57  2. [ensure-name-substring](https://catalog.kpt.dev/ensure-name-substring/v0.2/):
    58     sets the name of the resources in the package.
    59  
    60  You can use the appropriate functions from the catalog or implement a custom
    61  function to ensure unique identity of the resources.
    62  
    63  ## Customizing non-identifier fields of resources
    64  
    65  Packages can use other functions such as `set-labels`, `set-annotations`, `apply-replacements`
    66  or custom functions to transform other fields of resources.
    67  
    68  ## Core mechanism
    69  
    70  Enabling automatic variant construction involves two steps:
    71  
    72  1. Use functions to customize identity or other fields of the resources
    73  2. Generating inputs for the functions declarared in the package
    74  
    75  Here is an example of `Kptfile` of a package that uses `set-namespace` and `apply-transform`
    76  to enable customization.
    77  
    78  ```Kptfile
    79  # Kptfile
    80  ...
    81  pipeline:
    82    mutators:
    83      - image: set-namespace:v0.3.4
    84        configPath: ...
    85      - image: apply-transform:v0.1.0
    86        configPath: ...
    87  ...
    88  ```
    89  
    90  Now let's talk about the input to the functions. In most cases, variant's name
    91  (deployable instance name) itself can be used to derive unique identity
    92  of the resources in the variant. For example, if I create a variant of
    93  `microservice` package, I will name the deployable instance to
    94  `user-service` or `order-service`. So if the package's name is available to the
    95  functions, then they can use it to customize the name/namespace of the resources.
    96  So, starting with `kpt v1.0.0-beta.15+`, kpt makes `package name` available
    97  in a `ConfigMap` at a well-known path `package-context.yaml` in `data.name` field.
    98  The `package-context.yaml` is available to functions during `kpt fn render|eval`.
    99  
   100  Here are examples of `package-context.yaml` for abstract and deployable instance:
   101  
   102  ```yaml
   103  # package-context.yaml
   104  # package context for an abstract package.
   105  # This is automatically created on `kpt pkg init`.
   106  apiVersion: v1
   107  kind: ConfigMap
   108  data:
   109    name: example # <-- placeholder value
   110  ```
   111  
   112  ```yaml
   113  # package-context.yaml
   114  # package context for a deployable instance of a package.
   115  # This is automatically populated during `kpt pkg get`.
   116  apiVersion: v1
   117  kind: ConfigMap
   118  data:
   119    name: my-pkg-instance # <- deployable instance name
   120  ```
   121  
   122  kpt supports a way to create `deployable instance` such that `package-context.yaml`
   123  is automatically populated with the `deployable instance`'s name.
   124  
   125  ```shell
   126  $kpt pkg get <pkg-location> my-pkg-instance --for-deployment
   127  ```
   128  
   129  Now, let's look at how to provide the input to the functions.
   130  
   131  If you are using `set-namespace` function in your package, then
   132  `set-namespace` function supports reading input from `package-context.yaml`.
   133  Here is an example:
   134  
   135  ```Kptfile
   136  ...
   137  pipeline:
   138    mutators:
   139      - image: set-namespace:v0.3.4
   140        configPath: package-context.yaml
   141  ...
   142  ```
   143  
   144  By using `package-context.yaml` as input, `set-namespace` uses the value `example`
   145  for an `abstract` package and variant's name for a deployable instance. The
   146  same pattern can be applied to other functions also. For example, the
   147  [`namespace provisioning`](https://github.com/GoogleContainerTools/kpt-samples/tree/main/basens)
   148  package uses `apply-replacements` function to set the RoleBinding group
   149  using the name of the package.
   150  
   151  In some cases, the inputs needed to generate the variant will come from
   152  some external system or environment. Those can be generated imperatively or
   153  manually edited after the package is forked using `kpt pkg get`. Additional
   154  customizations could also be made at that point.
   155  
   156  So for a package consumer, creating a deployable instance involves the following:
   157  
   158  ```shell
   159  # pick name of the deployable instance say `my-pkg-instance`
   160  $ kpt pkg get <path-to-abstract-pkg> <my-pkg-instance> --for-deployement
   161  
   162  $ kpt fn render <my-pkg-instance>
   163  
   164  ```
   165  
   166  ## See it in action
   167  
   168  If you want to see `variant constructor pattern` in action for a real use-case,
   169  check out [`namespace provisioning using kpt CLI guide`](/guides/namespace-provisioning-cli.md).
   170  
   171  ## Summary
   172  
   173  With the above pattern and workflow, you can see - how a package publisher can
   174  enable automatic customization of deployable instance of a package with minimal
   175  input i.e. package instance name.
   176  
   177  ## Future plans
   178  
   179  Currently `--for-deployment` steps invokes built-in function to generate
   180  `package-context.yaml`. We would like to make it extensible for users to invoke their
   181  custom function for deploy workflow.