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.