1# cli-utils
2
3`cli-utils` is a collection of Go libraries designed to facilitate bulk
4actuation of Kubernetes resource objects by wraping and enahancing
5`kubectl apply` with a more user friendly abstraction.
6
7While the name indicates a focus on CLI utilities, the project has evolved to
8encompass a broader scope, including CLI use and server-side use in GitOps
9controllers.
10
11## Features
12
131. **Pruning**
141. **Status Interpretation**
151. **Status Lookup**
161. **Diff & Preview**
171. **Waiting for Reconciliation**
181. **Resource Ordering**
191. **Explicit Dependency Ordering**
201. **Implicit Dependency Ordering**
211. **Apply Time Mutation**
221. **CLI Printers**
23
24### Pruning
25
26The Applier automatically deletes objects that were previously applied and then
27removed from the input set on a subsequent apply.
28
29The current implementation of `kubectl apply --prune` uses labels to identify the
30set of previously applied objects in the prune set calculation. But the use of labels
31has significant downsides. The current `kubectl apply --prune` implementation is alpha,
32and it is improbable that it will graduate to beta. `cli-utils` attempts to address
33the current `kubectl apply --prune` deficiencies by storing the set of previously
34applied objects in an **inventory** object which is applied to the cluster. The
35reference implementation uses a `ConfigMap` as an **inventory** object, and references
36to the applied objects are stored in the `data` section of the `ConfigMap`.
37
38The following example illustrates a `ConfigMap` resource used as an inventory object:
39
40```yaml
41apiVersion: v1
42kind: ConfigMap
43metadata:
44 # DANGER: Do not change the inventory object namespace.
45 # Changing the namespace will cause a loss of continuity
46 # with previously applied grouped objects. Set deletion
47 # and pruning functionality will be impaired.
48 namespace: test-namespace
49 # NOTE: The name of the inventory object does NOT have
50 # any impact on group-related functionality such as
51 # deletion or pruning.
52 name: inventory-26306433
53 labels:
54 # DANGER: Do not change the value of this label.
55 # Changing this value will cause a loss of continuity
56 # with previously applied grouped objects. Set deletion
57 # and pruning functionality will be impaired.
58 cli-utils.sigs.k8s.io/inventory-id: 46d8946c-c1fa-4e1d-9357-b37fb9bae25f
59```
60
61### Status Interpretation
62
63The `kstatus` library can be used to read an object's current status and interpret
64whether that object has be reconciled (aka Current) or not, including whether it
65is expected to never reconcile (aka Failed).
66
67### Status Lookup
68
69In addition to performing interpretation of status from an object in-memory,
70`cli-utils` can also be used to query status from the server, allowing you to
71retrieve the status of previously or concurrently applied objects.
72
73### Diff & Preview
74
75`cli-utils` can be used to compare local object manifests with remote objects
76from the server. These can be compared locally with diff or remotely with
77preview (aka dry-run). This can be useful for discovering drift or previewing
78which changes would be made, if the local manifests were applied.
79
80### Waiting for Reconciliation
81
82The Applier automatically watches applied and deleted objects and tracks their
83status, blocking until the objects have reconciled, failed, or been fully
84deleted.
85
86This functionality is similar to `kubectl delete <resource> <name> --wait`, in
87that it waits for all finalizers to complete, except it also works for creates
88and updates.
89
90While there is a `kubectl apply <resource> <name> --wait`, it only waits for
91deletes when combined with `--prune`. `cli-utils` provides an alternative that
92works for all spec changes, waiting for reconciliation, the convergence of
93status to the desired specification. After reconciliation, it is expected that
94the object has reached a steady state until the specification is changed again.
95
96### Resource Ordering
97
98The Applier and Destroyer use resource type to determine which order to apply
99and delete objects.
100
101In contrast, when using `kubectl apply`, the objects are applied in alphanumeric
102order of their file names, and top to bottom in each file. With `cli-utils`,
103this manual sorting is unnecessary for many common use cases.
104
105### Explicit Dependency Ordering
106
107While resource ordering provides a smart default user experience, sometimes
108resource type alone is not enough to determine desired ordering. In these cases,
109the user can use explicit dependency ordering by adding a
110`config.kubernetes.io/depends-on: <OBJECT_REFERENCE>` annotation to an object.
111
112The Applier and Destroyer use these explicit dependency directives to build a
113dependency tree and flatten it for determining apply ordering. When deleting,
114the order is reversed, ensuring that dependencies are not deleted before the
115objects that depend on them (aka dependents).
116
117In addition to ordering the applies and deletes, dependency ordering also waits
118for dependency reconciliation when applying and deletion finalization when
119deleting. This ensures that dependencies are not just applied first, but have
120reconciled before their dependents are applied. Likewise, dependents are not
121just deleted first, but have completed finalization before their dependencies
122are deleted.
123
124Also, because dependency ordering is enforced during actuation, a dependency
125cannot be pruned by the Applier unless all its dependents are also deleted. This
126prevents accidental premature deletion of objects that are still in active use.
127
128In the following example, the `config.kubernetes.io/depends-on` annotation
129identifies that `pod-c` must be successfully applied prior to `pod-a`
130actuation:
131
132```yaml
133apiVersion: v1
134kind: Pod
135metadata:
136 name: pod-a
137 annotations:
138 config.kubernetes.io/depends-on: /namespaces/default/Pod/pod-c
139spec:
140 containers:
141 - name: kubernetes-pause
142 image: k8s.gcr.io/pause:2.0
143```
144
145### Implicit Dependency Ordering
146
147In addition to being able to specify explicit dependencies, `cli-utils`
148automatically detects some implicit dependencies.
149
150Implicit dependencies include:
151
1521. Namespace-scoped resource objects depend on their Namespace.
1532. Custom resource objects depend on their Custom Resource Definition
154
155Like resource ordering, implicit dependency ordering improves the apply and
156delete experience to reduce the need to manually specify ordering for many
157common use cases. This allows more objects to be applied together all at once,
158with less manual orchestration.
159
160### Apply-Time Mutation
161
162The Applier can dynamically modify objects before applying them, performing
163field value substitution using input(s) from dependency fields.
164
165This allows for applying objects together in a set that you would otherwise need
166to seperate into multiple sets, with manual modifications between applies.
167
168Apply-Time Mutation is configured using the
169`config.kubernetes.io/apply-time-mutation` annotation on the target object to be
170modified. The annotation may specify one or more substitutions. Each
171substitution includes a source object, and source field path, and a target
172field path, with an optional token.
173
174If the token is specified, the token is
175replaced in the target field value string with the source field value. If the
176token is not specified, the whole target field value is replaced with the
177source field value. This alternatively allows either templated interpretation or
178type preservation.
179
180The source and target field paths are specified using JSONPath, allowing for
181robust navigation of complex resource field hierarchies using a familiar syntax.
182
183In the following example, `pod-a` will substitute the IP address and port from
184the spec and status of the source `pod-b` into the spec of the target `pod-a`:
185
186```yaml
187kind: Pod
188apiVersion: v1
189metadata:
190 name: pod-a
191 annotations:
192 config.kubernetes.io/apply-time-mutation: |
193 - sourceRef:
194 kind: Pod
195 name: pod-b
196 sourcePath: $.status.podIP
197 targetPath: $.spec.containers[?(@.name=="nginx")].env[?(@.name=="SERVICE_HOST")].value
198 token: ${pob-b-ip}
199 - sourceRef:
200 kind: Pod
201 name: pod-b
202 sourcePath: $.spec.containers[?(@.name=="nginx")].ports[?(@.name=="tcp")].containerPort
203 targetPath: $.spec.containers[?(@.name=="nginx")].env[?(@.name=="SERVICE_HOST")].value
204 token: ${pob-b-port}
205spec:
206 containers:
207 - name: nginx
208 image: nginx:1.21
209 ports:
210 - name: tcp
211 containerPort: 80
212 env:
213 - name: SERVICE_HOST
214 value: "${pob-b-ip}:${pob-b-port}"
215```
216
217The primary reason to do this with Apply-Time Mutation, instead of client-side
218manifest templating is that the pod IP is populated by a controller at runtime
219during reconciliation, and is not known before applying.
220
221That said, this is a toy example using built-in types. For pods, you probably
222actually want to use DNS for service discovery instead.
223
224Most use cases for Apply-Time Mutation are actually using custom resources, as a
225temporary alternative to building higher level abstractions, modifying
226interfaces, or creating dependencies between otherwise independent interfaces.
227
228### CLI Printers
229
230Since the original intent of `cli-utils` was to contain common code for CLIs,
231and end-to-end testing requires a reference implementation, a few printers are
232included to translate from the primary event stream into STDOUT text:
233
2341. **Event Printer**: The event printer just prints text to STDOT whenever an
235 event is recieved.
2361. **JSON Printer**: The JSON printer converts events into a JSON string per
237 line, intended for automated interpretation by machine.
2381. **Table Printer**: The table printer writes and updates in-place a table
239 with one object per line, intended for human consumption.
240
241## Packages
242
243├── **cmd**: the kapply CLI command
244├── **examples**: examples that serve as additional end-to-end tests using mdrip
245├── **hack**: hacky scripts used by make
246├── **pkg**
247│ ├── **apis**: API resources that satisfy the kubernetes Object interface
248│ ├── **apply**: bulk applier and destroyer
249│ ├── **common**: placeholder for common tools that should probably have their own package
250│ ├── **config**: inventory config bootstrapping
251│ ├── **errors**: error printing
252│ ├── **flowcontrol**: flow control enablement discovery
253│ ├── **inventory**: inventory resource reference implementation
254│ ├── **jsonpath**: utility for using jsonpath to read & write Unstructured object fields
255│ ├── **kstatus**: object status event watcher with ability to reduce status to a single enum
256│ ├── **manifestreader**: bolk resource object manifest reading and parsing
257│ ├── **multierror**: error composition
258│ ├── **object**: library for dealing with Unstructured objects
259│ ├── **ordering**: sort functionality for objects
260│ ├── **print**: CLI output
261│ ├── **printers**: CLI output
262│ └── **testutil**: utility for facilitating testing
263├── **release**: goreleaser config
264├── **scripts**: scripts used by make
265└── **test**: end-to-end and stress tests
266
267## kapply
268
269To facilitate testing, this repository includes a reference CLI called `kapply`.
270The `kapply` tool is not intended for direct consumer use, but may be useful
271when trying to determine how to best utilize the `cli-utils` library packages.
272
273## Community, discussion, contribution, and support
274
275Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/).
276
277You can reach the maintainers of this project at:
278
279- [Slack channel](https://kubernetes.slack.com/messages/sig-cli)
280- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-cli)
281
282### Code of conduct
283
284Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).
View as plain text