@appthrust/kest 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +412 -0
- package/example/config-map.yaml +6 -0
- package/example/example-report.md +744 -0
- package/example/example.test.ts +225 -0
- package/example/hello-world-crd.yaml +145 -0
- package/package.json +62 -0
- package/ts/actions/apply-namespace.ts +29 -0
- package/ts/actions/apply-status.ts +23 -0
- package/ts/actions/apply.ts +25 -0
- package/ts/actions/assert-list.ts +63 -0
- package/ts/actions/assert.ts +34 -0
- package/ts/actions/exec.ts +21 -0
- package/ts/actions/get.ts +40 -0
- package/ts/actions/types.ts +48 -0
- package/ts/apis/index.ts +788 -0
- package/ts/bdd/index.ts +30 -0
- package/ts/duration/index.ts +171 -0
- package/ts/index.ts +3 -0
- package/ts/k8s-resource/index.ts +120 -0
- package/ts/kubectl/index.ts +351 -0
- package/ts/recording/index.ts +134 -0
- package/ts/reporter/index.ts +0 -0
- package/ts/reporter/interface.ts +5 -0
- package/ts/reporter/markdown.ts +962 -0
- package/ts/retry.ts +112 -0
- package/ts/reverting/index.ts +36 -0
- package/ts/scenario/index.ts +220 -0
- package/ts/test.ts +127 -0
- package/ts/workspace/find-up.ts +20 -0
- package/ts/workspace/index.ts +25 -0
- package/ts/yaml/index.ts +14 -0
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
# Example: applies ConfigMap using YAML, file import, and object literal
|
|
2
|
+
|
|
3
|
+
## Scenario Overview
|
|
4
|
+
|
|
5
|
+
| # | Action | Resource | Status |
|
|
6
|
+
|---|--------|----------|--------|
|
|
7
|
+
| 1 | Create namespace | kest-9hdhj | ✅ |
|
|
8
|
+
| 2 | Apply | ConfigMap/my-config-1 | ✅ |
|
|
9
|
+
| 3 | Apply | ConfigMap/my-config-2 | ✅ |
|
|
10
|
+
| 4 | Apply | ConfigMap/my-config-3 | ✅ |
|
|
11
|
+
| 5 | Assert | ConfigMap/my-config-1 | ✅ |
|
|
12
|
+
|
|
13
|
+
## Scenario Details
|
|
14
|
+
|
|
15
|
+
### Given: a new namespace exists
|
|
16
|
+
|
|
17
|
+
**✅ Create Namespace "kest-9hdhj"**
|
|
18
|
+
|
|
19
|
+
```shell
|
|
20
|
+
kubectl apply -f - <<EOF
|
|
21
|
+
apiVersion: v1
|
|
22
|
+
kind: Namespace
|
|
23
|
+
metadata:
|
|
24
|
+
name: kest-9hdhj
|
|
25
|
+
|
|
26
|
+
EOF
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```text title="stdout"
|
|
30
|
+
namespace/kest-9hdhj created
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### When: I apply ConfigMaps using different formats
|
|
34
|
+
|
|
35
|
+
**✅ Apply ConfigMap "my-config-1" in namespace "kest-9hdhj"**
|
|
36
|
+
|
|
37
|
+
```shell
|
|
38
|
+
kubectl apply -f - -n kest-9hdhj <<EOF
|
|
39
|
+
apiVersion: v1
|
|
40
|
+
kind: ConfigMap
|
|
41
|
+
metadata:
|
|
42
|
+
name: my-config-1
|
|
43
|
+
data:
|
|
44
|
+
mode: demo-1
|
|
45
|
+
|
|
46
|
+
EOF
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```text title="stdout"
|
|
50
|
+
configmap/my-config-1 created
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**✅ Apply ConfigMap "my-config-2" in namespace "kest-9hdhj"**
|
|
54
|
+
|
|
55
|
+
```shell
|
|
56
|
+
kubectl apply -f - -n kest-9hdhj <<EOF
|
|
57
|
+
apiVersion: v1
|
|
58
|
+
kind: ConfigMap
|
|
59
|
+
metadata:
|
|
60
|
+
name: my-config-2
|
|
61
|
+
data:
|
|
62
|
+
mode: demo-2
|
|
63
|
+
|
|
64
|
+
EOF
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```text title="stdout"
|
|
68
|
+
configmap/my-config-2 created
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**✅ Apply ConfigMap "my-config-3" in namespace "kest-9hdhj"**
|
|
72
|
+
|
|
73
|
+
```shell
|
|
74
|
+
kubectl apply -f - -n kest-9hdhj <<EOF
|
|
75
|
+
apiVersion: v1
|
|
76
|
+
kind: ConfigMap
|
|
77
|
+
metadata:
|
|
78
|
+
name: my-config-3
|
|
79
|
+
data:
|
|
80
|
+
mode: demo-3
|
|
81
|
+
|
|
82
|
+
EOF
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```text title="stdout"
|
|
86
|
+
configmap/my-config-3 created
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Then: the ConfigMap should have the expected data
|
|
90
|
+
|
|
91
|
+
**✅ Assert ConfigMap "my-config-1" in namespace "kest-9hdhj"**
|
|
92
|
+
|
|
93
|
+
```shell
|
|
94
|
+
kubectl get ConfigMap my-config-1 -o yaml -n kest-9hdhj
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```yaml title="stdout"
|
|
98
|
+
apiVersion: v1
|
|
99
|
+
data:
|
|
100
|
+
mode: demo-1
|
|
101
|
+
kind: ConfigMap
|
|
102
|
+
metadata:
|
|
103
|
+
annotations:
|
|
104
|
+
kubectl.kubernetes.io/last-applied-configuration: |
|
|
105
|
+
{"apiVersion":"v1","data":{"mode":"demo-1"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"my-config-1","namespace":"kest-9hdhj"}}
|
|
106
|
+
creationTimestamp: "2026-02-06T00:27:52Z"
|
|
107
|
+
name: my-config-1
|
|
108
|
+
namespace: kest-9hdhj
|
|
109
|
+
resourceVersion: "487392"
|
|
110
|
+
uid: c55d94fa-7096-4534-84ef-88a4e384da24
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Cleanup
|
|
114
|
+
|
|
115
|
+
| # | Action | Resource | Status |
|
|
116
|
+
|---|--------|----------|--------|
|
|
117
|
+
| 1 | Delete | ConfigMap/my-config-3 | ✅ |
|
|
118
|
+
| 2 | Delete | ConfigMap/my-config-2 | ✅ |
|
|
119
|
+
| 3 | Delete | ConfigMap/my-config-1 | ✅ |
|
|
120
|
+
| 4 | Delete namespace | kest-9hdhj | ✅ |
|
|
121
|
+
|
|
122
|
+
```shellsession
|
|
123
|
+
$ kubectl delete ConfigMap/my-config-3 -n kest-9hdhj
|
|
124
|
+
configmap "my-config-3" deleted from kest-9hdhj namespace
|
|
125
|
+
|
|
126
|
+
$ kubectl delete ConfigMap/my-config-2 -n kest-9hdhj
|
|
127
|
+
configmap "my-config-2" deleted from kest-9hdhj namespace
|
|
128
|
+
|
|
129
|
+
$ kubectl delete ConfigMap/my-config-1 -n kest-9hdhj
|
|
130
|
+
configmap "my-config-1" deleted from kest-9hdhj namespace
|
|
131
|
+
|
|
132
|
+
$ kubectl delete Namespace/kest-9hdhj
|
|
133
|
+
namespace "kest-9hdhj" deleted
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
# Example: asserts a non-existent ConfigMap (expected to fail)
|
|
137
|
+
|
|
138
|
+
## Scenario Overview
|
|
139
|
+
|
|
140
|
+
| # | Action | Resource | Status |
|
|
141
|
+
|---|--------|----------|--------|
|
|
142
|
+
| 1 | Create namespace | kest-k515q | ✅ |
|
|
143
|
+
| 2 | Assert | ConfigMap/non-existent-config | ❌ |
|
|
144
|
+
|
|
145
|
+
## Scenario Details
|
|
146
|
+
|
|
147
|
+
### Given: a new namespace exists
|
|
148
|
+
|
|
149
|
+
**✅ Create Namespace "kest-k515q"**
|
|
150
|
+
|
|
151
|
+
```shell
|
|
152
|
+
kubectl apply -f - <<EOF
|
|
153
|
+
apiVersion: v1
|
|
154
|
+
kind: Namespace
|
|
155
|
+
metadata:
|
|
156
|
+
name: kest-k515q
|
|
157
|
+
|
|
158
|
+
EOF
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```text title="stdout"
|
|
162
|
+
namespace/kest-k515q created
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Then: asserting a non-existent ConfigMap should fail
|
|
166
|
+
|
|
167
|
+
**❌ Assert ConfigMap "non-existent-config" in namespace "kest-k515q"** (Failed after 20 attempts)
|
|
168
|
+
|
|
169
|
+
```shell
|
|
170
|
+
kubectl get ConfigMap non-existent-config -o yaml -n kest-k515q
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```text title="stderr"
|
|
174
|
+
Error from server (NotFound): configmaps "non-existent-config" not found
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Error:
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
kubectl get failed (exit code 1): Error from server (NotFound): configmaps "non-existent-config" not found
|
|
181
|
+
|
|
182
|
+
Trace:
|
|
183
|
+
at runKubectl (/Users/suin/codes/github.com/appthrust/kest/ts/kubectl/index.ts:316:17)
|
|
184
|
+
at async get (/Users/suin/codes/github.com/appthrust/kest/ts/kubectl/index.ts:223:23)
|
|
185
|
+
at async <anonymous> (/Users/suin/codes/github.com/appthrust/kest/ts/actions/assert.ts:11:34)
|
|
186
|
+
at async retryUntil (/Users/suin/codes/github.com/appthrust/kest/ts/retry.ts:83:27)
|
|
187
|
+
at async <anonymous> (/Users/suin/codes/github.com/appthrust/kest/ts/scenario/index.ts:168:20)
|
|
188
|
+
at async <anonymous> (/Users/suin/codes/github.com/appthrust/kest/example/example.test.ts:69:12)
|
|
189
|
+
at async <anonymous> (/Users/suin/codes/github.com/appthrust/kest/ts/test.ts:65:15)
|
|
190
|
+
at processTicksAndRejections (unknown:7:39)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Cleanup
|
|
194
|
+
|
|
195
|
+
| # | Action | Resource | Status |
|
|
196
|
+
|---|--------|----------|--------|
|
|
197
|
+
| 1 | Delete namespace | kest-k515q | ✅ |
|
|
198
|
+
|
|
199
|
+
```shellsession
|
|
200
|
+
$ kubectl delete Namespace/kest-k515q
|
|
201
|
+
namespace "kest-k515q" deleted
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
# Example: manages resources across multiple clusters
|
|
205
|
+
|
|
206
|
+
## Scenario Overview
|
|
207
|
+
|
|
208
|
+
| # | Action | Resource | Status |
|
|
209
|
+
|---|--------|----------|--------|
|
|
210
|
+
| 1 | Apply | ConfigMap/my-config-1 | ✅ |
|
|
211
|
+
| 2 | Apply | ConfigMap/my-config-2 | ✅ |
|
|
212
|
+
| 3 | Assert | ConfigMap/my-config-1 | ✅ |
|
|
213
|
+
| 4 | Assert | ConfigMap/my-config-2 | ✅ |
|
|
214
|
+
|
|
215
|
+
## Scenario Details
|
|
216
|
+
|
|
217
|
+
### When: I apply ConfigMaps to each cluster
|
|
218
|
+
|
|
219
|
+
**✅ Apply ConfigMap "my-config-1"**
|
|
220
|
+
|
|
221
|
+
```shell
|
|
222
|
+
kubectl apply -f - --context kind-kest-test-cluster-1 --kubeconfig .kubeconfig.yaml <<EOF
|
|
223
|
+
apiVersion: v1
|
|
224
|
+
kind: ConfigMap
|
|
225
|
+
metadata:
|
|
226
|
+
name: my-config-1
|
|
227
|
+
data:
|
|
228
|
+
mode: demo-1
|
|
229
|
+
|
|
230
|
+
EOF
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```text title="stdout"
|
|
234
|
+
configmap/my-config-1 created
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**✅ Apply ConfigMap "my-config-2"**
|
|
238
|
+
|
|
239
|
+
```shell
|
|
240
|
+
kubectl apply -f - --context kind-kest-test-cluster-2 <<EOF
|
|
241
|
+
apiVersion: v1
|
|
242
|
+
kind: ConfigMap
|
|
243
|
+
metadata:
|
|
244
|
+
name: my-config-2
|
|
245
|
+
data:
|
|
246
|
+
mode: demo-2
|
|
247
|
+
|
|
248
|
+
EOF
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
```text title="stdout"
|
|
252
|
+
configmap/my-config-2 created
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Then: each cluster should have its ConfigMap
|
|
256
|
+
|
|
257
|
+
**✅ Assert ConfigMap "my-config-1"**
|
|
258
|
+
|
|
259
|
+
```shell
|
|
260
|
+
kubectl get ConfigMap my-config-1 -o yaml --context kind-kest-test-cluster-1 --kubeconfig .kubeconfig.yaml
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
```yaml title="stdout"
|
|
264
|
+
apiVersion: v1
|
|
265
|
+
data:
|
|
266
|
+
mode: demo-1
|
|
267
|
+
kind: ConfigMap
|
|
268
|
+
metadata:
|
|
269
|
+
annotations:
|
|
270
|
+
kubectl.kubernetes.io/last-applied-configuration: |
|
|
271
|
+
{"apiVersion":"v1","data":{"mode":"demo-1"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"my-config-1","namespace":"default"}}
|
|
272
|
+
creationTimestamp: "2026-02-06T00:27:58Z"
|
|
273
|
+
name: my-config-1
|
|
274
|
+
namespace: default
|
|
275
|
+
resourceVersion: "487408"
|
|
276
|
+
uid: 3747a790-9971-4d5a-95ad-a27a81a816f6
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**✅ Assert ConfigMap "my-config-2"**
|
|
280
|
+
|
|
281
|
+
```shell
|
|
282
|
+
kubectl get ConfigMap my-config-2 -o yaml --context kind-kest-test-cluster-2
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```yaml title="stdout"
|
|
286
|
+
apiVersion: v1
|
|
287
|
+
data:
|
|
288
|
+
mode: demo-2
|
|
289
|
+
kind: ConfigMap
|
|
290
|
+
metadata:
|
|
291
|
+
annotations:
|
|
292
|
+
kubectl.kubernetes.io/last-applied-configuration: |
|
|
293
|
+
{"apiVersion":"v1","data":{"mode":"demo-2"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"my-config-2","namespace":"default"}}
|
|
294
|
+
creationTimestamp: "2026-02-06T00:27:58Z"
|
|
295
|
+
name: my-config-2
|
|
296
|
+
namespace: default
|
|
297
|
+
resourceVersion: "485245"
|
|
298
|
+
uid: d79c985c-4c4c-4f89-9ec6-7f4a9d675e42
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Cleanup
|
|
302
|
+
|
|
303
|
+
| # | Action | Resource | Status |
|
|
304
|
+
|---|--------|----------|--------|
|
|
305
|
+
| 1 | Delete | ConfigMap/my-config-2 | ✅ |
|
|
306
|
+
| 2 | Delete | ConfigMap/my-config-1 | ✅ |
|
|
307
|
+
|
|
308
|
+
```shellsession
|
|
309
|
+
$ kubectl delete ConfigMap/my-config-2 --context kind-kest-test-cluster-2
|
|
310
|
+
configmap "my-config-2" deleted from default namespace
|
|
311
|
+
|
|
312
|
+
$ kubectl delete ConfigMap/my-config-1 --context kind-kest-test-cluster-1 --kubeconfig .kubeconfig.yaml
|
|
313
|
+
configmap "my-config-1" deleted from default namespace
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
# Example: executes shell commands with revert cleanup
|
|
317
|
+
|
|
318
|
+
## Scenario Overview
|
|
319
|
+
|
|
320
|
+
| # | Action | Resource | Status |
|
|
321
|
+
|---|--------|----------|--------|
|
|
322
|
+
| 1 | Exec | N/A | ✅ |
|
|
323
|
+
|
|
324
|
+
## Scenario Details
|
|
325
|
+
|
|
326
|
+
### Cleanup
|
|
327
|
+
|
|
328
|
+
| # | Action | Resource | Status |
|
|
329
|
+
|---|--------|----------|--------|
|
|
330
|
+
| 1 | Exec | N/A | ✅ |
|
|
331
|
+
|
|
332
|
+
# Example: asserts resource presence and absence in a list
|
|
333
|
+
|
|
334
|
+
## Scenario Overview
|
|
335
|
+
|
|
336
|
+
| # | Action | Resource | Status |
|
|
337
|
+
|---|--------|----------|--------|
|
|
338
|
+
| 1 | Create namespace | kest-cld1c | ✅ |
|
|
339
|
+
| 2 | Apply | ConfigMap/my-config-1 | ✅ |
|
|
340
|
+
| 3 | AssertList | ConfigMap | ✅ |
|
|
341
|
+
|
|
342
|
+
## Scenario Details
|
|
343
|
+
|
|
344
|
+
### Given: a new namespace exists
|
|
345
|
+
|
|
346
|
+
**✅ Create Namespace "kest-cld1c"**
|
|
347
|
+
|
|
348
|
+
```shell
|
|
349
|
+
kubectl apply -f - <<EOF
|
|
350
|
+
apiVersion: v1
|
|
351
|
+
kind: Namespace
|
|
352
|
+
metadata:
|
|
353
|
+
name: kest-cld1c
|
|
354
|
+
|
|
355
|
+
EOF
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
```text title="stdout"
|
|
359
|
+
namespace/kest-cld1c created
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### When: I apply a single ConfigMap
|
|
363
|
+
|
|
364
|
+
**✅ Apply ConfigMap "my-config-1" in namespace "kest-cld1c"**
|
|
365
|
+
|
|
366
|
+
```shell
|
|
367
|
+
kubectl apply -f - -n kest-cld1c <<EOF
|
|
368
|
+
apiVersion: v1
|
|
369
|
+
kind: ConfigMap
|
|
370
|
+
metadata:
|
|
371
|
+
name: my-config-1
|
|
372
|
+
data:
|
|
373
|
+
mode: demo-1
|
|
374
|
+
|
|
375
|
+
EOF
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
```text title="stdout"
|
|
379
|
+
configmap/my-config-1 created
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Then: the list should contain only the applied ConfigMap
|
|
383
|
+
|
|
384
|
+
**✅ AssertList ConfigMap in namespace "kest-cld1c"**
|
|
385
|
+
|
|
386
|
+
```shell
|
|
387
|
+
kubectl get ConfigMap -o yaml -n kest-cld1c
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
```yaml title="stdout"
|
|
391
|
+
apiVersion: v1
|
|
392
|
+
items:
|
|
393
|
+
- apiVersion: v1
|
|
394
|
+
data:
|
|
395
|
+
ca.crt: |
|
|
396
|
+
-----BEGIN CERTIFICATE-----
|
|
397
|
+
MIIDBTCCAe2gAwIBAgIIVDHmGXfVRt4wDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
|
|
398
|
+
AxMKa3ViZXJuZXRlczAeFw0yNjAxMzEwMTEwMzBaFw0zNjAxMjkwMTE1MzBaMBUx
|
|
399
|
+
EzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
|
400
|
+
AoIBAQC6b/cTN1kRXpFlzHMTO+KZQGkG0T49Jp/MkbSSDyieRJi3AK8KG1v2xMD5
|
|
401
|
+
CVMDba2oH+Cn4JdZ5ixhOJ4PRP4DUjVYvHWp6Em9n1VtB4QyX9QzJBBsu+0y+vNh
|
|
402
|
+
qGW2TbAcH7BfZi+Gxjrb98QbWbhg1d0drDqyTzA/yrbhRqEX1GwGb//VF06CCp5n
|
|
403
|
+
qktPSZsS265elmQoip5leaM+5hX3CbZvLVWpx5b964VJIuxNodgYVKNfg5K6Dogm
|
|
404
|
+
gjcSNrrJ6e0cszwuYVC0OQFnAThOXwXLyeLnSmgHuRb0tzP/SmWWLXm2SX05uPb9
|
|
405
|
+
vHqVoopdNURi82g1vplP/rb45TA9AgMBAAGjWTBXMA4GA1UdDwEB/wQEAwICpDAP
|
|
406
|
+
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRK0TknEEFFUSqlS2q4/K48/+GoSTAV
|
|
407
|
+
BgNVHREEDjAMggprdWJlcm5ldGVzMA0GCSqGSIb3DQEBCwUAA4IBAQAguduQSX4i
|
|
408
|
+
ONrSF15diokMuY1+3NY+xufuIFZ8rT5mhZF5cJjt9Av0s9fqDr7urI48a4BCO4Bv
|
|
409
|
+
mdB0hwsn/rKNYx7FgQyyAF1MXEywXjR66tNwCCAibRNs0k6rrWZ0hMvlNB0PkDpE
|
|
410
|
+
VtLYqjN9hzOzBvYKkNoTgAbt510iTPGyQaLlloJYWonsUZOHaYmLrksPAKa6l+WT
|
|
411
|
+
SyNvFcOTgYUXcwTHXDdXN8/nWJy9v9lWGPzyFbA5C0jUvfoWDIejRgxnOS7Tm6Qe
|
|
412
|
+
NU5rKBlVqheAqx+T6toLdlkgOs1wRJQlqu0XO4FcfoYDCUTMW41DeO45i54Ql8rQ
|
|
413
|
+
lAJH8vYIQyps
|
|
414
|
+
-----END CERTIFICATE-----
|
|
415
|
+
kind: ConfigMap
|
|
416
|
+
metadata:
|
|
417
|
+
annotations:
|
|
418
|
+
kubernetes.io/description: Contains a CA bundle that can be used to verify the
|
|
419
|
+
kube-apiserver when using internal endpoints such as the internal service
|
|
420
|
+
IP or kubernetes.default.svc. No other usage is guaranteed across distributions
|
|
421
|
+
of Kubernetes clusters.
|
|
422
|
+
creationTimestamp: "2026-02-06T00:27:58Z"
|
|
423
|
+
name: kube-root-ca.crt
|
|
424
|
+
namespace: kest-cld1c
|
|
425
|
+
resourceVersion: "487416"
|
|
426
|
+
uid: b60235f4-685c-4845-a143-bf89897c801c
|
|
427
|
+
- apiVersion: v1
|
|
428
|
+
data:
|
|
429
|
+
mode: demo-1
|
|
430
|
+
kind: ConfigMap
|
|
431
|
+
metadata:
|
|
432
|
+
annotations:
|
|
433
|
+
kubectl.kubernetes.io/last-applied-configuration: |
|
|
434
|
+
{"apiVersion":"v1","data":{"mode":"demo-1"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"my-config-1","namespace":"kest-cld1c"}}
|
|
435
|
+
creationTimestamp: "2026-02-06T00:27:58Z"
|
|
436
|
+
name: my-config-1
|
|
437
|
+
namespace: kest-cld1c
|
|
438
|
+
resourceVersion: "487417"
|
|
439
|
+
uid: 45e6c2a5-e745-408f-a4dd-b8ca671411c6
|
|
440
|
+
kind: List
|
|
441
|
+
metadata:
|
|
442
|
+
resourceVersion: ""
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Cleanup
|
|
446
|
+
|
|
447
|
+
| # | Action | Resource | Status |
|
|
448
|
+
|---|--------|----------|--------|
|
|
449
|
+
| 1 | Delete | ConfigMap/my-config-1 | ✅ |
|
|
450
|
+
| 2 | Delete namespace | kest-cld1c | ✅ |
|
|
451
|
+
|
|
452
|
+
```shellsession
|
|
453
|
+
$ kubectl delete ConfigMap/my-config-1 -n kest-cld1c
|
|
454
|
+
configmap "my-config-1" deleted from kest-cld1c namespace
|
|
455
|
+
|
|
456
|
+
$ kubectl delete Namespace/kest-cld1c
|
|
457
|
+
namespace "kest-cld1c" deleted
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
# Example: applies status subresource to custom resource
|
|
461
|
+
|
|
462
|
+
## Scenario Overview
|
|
463
|
+
|
|
464
|
+
| # | Action | Resource | Status |
|
|
465
|
+
|---|--------|----------|--------|
|
|
466
|
+
| 1 | Apply | CustomResourceDefinition/helloworlds.example.com | ✅ |
|
|
467
|
+
| 2 | Create namespace | kest-1j79x | ✅ |
|
|
468
|
+
| 3 | Apply | HelloWorld/my-hello-world | ✅ |
|
|
469
|
+
| 4 | ApplyStatus | HelloWorld/my-hello-world | ✅ |
|
|
470
|
+
| 5 | Assert | HelloWorld/my-hello-world | ✅ |
|
|
471
|
+
|
|
472
|
+
## Scenario Details
|
|
473
|
+
|
|
474
|
+
### Given: a HelloWorld custom resource definition exists
|
|
475
|
+
|
|
476
|
+
**✅ Apply CustomResourceDefinition "helloworlds.example.com"**
|
|
477
|
+
|
|
478
|
+
```shell
|
|
479
|
+
kubectl apply -f - <<EOF
|
|
480
|
+
apiVersion: apiextensions.k8s.io/v1
|
|
481
|
+
kind: CustomResourceDefinition
|
|
482
|
+
metadata:
|
|
483
|
+
name: helloworlds.example.com
|
|
484
|
+
spec:
|
|
485
|
+
group: example.com
|
|
486
|
+
names:
|
|
487
|
+
kind: HelloWorld
|
|
488
|
+
listKind: HelloWorldList
|
|
489
|
+
plural: helloworlds
|
|
490
|
+
singular: helloworld
|
|
491
|
+
scope: Namespaced
|
|
492
|
+
versions:
|
|
493
|
+
- name: v1
|
|
494
|
+
served: true
|
|
495
|
+
storage: true
|
|
496
|
+
subresources:
|
|
497
|
+
status:
|
|
498
|
+
{}
|
|
499
|
+
schema:
|
|
500
|
+
openAPIV3Schema:
|
|
501
|
+
type: object
|
|
502
|
+
properties:
|
|
503
|
+
apiVersion:
|
|
504
|
+
type: string
|
|
505
|
+
kind:
|
|
506
|
+
type: string
|
|
507
|
+
metadata:
|
|
508
|
+
type: object
|
|
509
|
+
status:
|
|
510
|
+
description: HelloWorldStatus defines the observed state of HelloWorld.
|
|
511
|
+
type: object
|
|
512
|
+
properties:
|
|
513
|
+
conditions:
|
|
514
|
+
description: "Conditions represent the latest available observations of an object's state."
|
|
515
|
+
type: array
|
|
516
|
+
x-kubernetes-list-type: map
|
|
517
|
+
x-kubernetes-list-map-keys:
|
|
518
|
+
- type
|
|
519
|
+
items:
|
|
520
|
+
description: Condition contains details for one aspect of the current state of this API Resource.
|
|
521
|
+
type: object
|
|
522
|
+
required:
|
|
523
|
+
- type
|
|
524
|
+
- status
|
|
525
|
+
- lastTransitionTime
|
|
526
|
+
- reason
|
|
527
|
+
- message
|
|
528
|
+
properties:
|
|
529
|
+
type:
|
|
530
|
+
description: Type of condition in CamelCase or in foo.example.com/CamelCase.
|
|
531
|
+
type: string
|
|
532
|
+
maxLength: 316
|
|
533
|
+
pattern: "^[A-Za-z0-9]([A-Za-z0-9_.-]*[A-Za-z0-9])?$"
|
|
534
|
+
status:
|
|
535
|
+
description: "Status of the condition, one of True, False, Unknown."
|
|
536
|
+
type: string
|
|
537
|
+
enum:
|
|
538
|
+
- "True"
|
|
539
|
+
- "False"
|
|
540
|
+
- Unknown
|
|
541
|
+
observedGeneration:
|
|
542
|
+
description: observedGeneration represents the .metadata.generation that the condition was set based upon.
|
|
543
|
+
type: integer
|
|
544
|
+
format: int64
|
|
545
|
+
minimum: 0
|
|
546
|
+
lastTransitionTime:
|
|
547
|
+
description: lastTransitionTime is the last time the condition transitioned from one status to another.
|
|
548
|
+
type: string
|
|
549
|
+
format: date-time
|
|
550
|
+
reason:
|
|
551
|
+
description: "reason contains a programmatic identifier indicating the reason for the condition's last transition."
|
|
552
|
+
type: string
|
|
553
|
+
minLength: 1
|
|
554
|
+
maxLength: 1024
|
|
555
|
+
pattern: "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$"
|
|
556
|
+
message:
|
|
557
|
+
description: message is a human readable message indicating details about the transition.
|
|
558
|
+
type: string
|
|
559
|
+
maxLength: 32768
|
|
560
|
+
- name: v2
|
|
561
|
+
served: true
|
|
562
|
+
storage: false
|
|
563
|
+
subresources:
|
|
564
|
+
status:
|
|
565
|
+
{}
|
|
566
|
+
schema:
|
|
567
|
+
openAPIV3Schema:
|
|
568
|
+
type: object
|
|
569
|
+
properties:
|
|
570
|
+
apiVersion:
|
|
571
|
+
type: string
|
|
572
|
+
kind:
|
|
573
|
+
type: string
|
|
574
|
+
metadata:
|
|
575
|
+
type: object
|
|
576
|
+
status:
|
|
577
|
+
description: HelloWorldStatus defines the observed state of HelloWorld.
|
|
578
|
+
type: object
|
|
579
|
+
properties:
|
|
580
|
+
conditions:
|
|
581
|
+
description: "Conditions represent the latest available observations of an object's state."
|
|
582
|
+
type: array
|
|
583
|
+
x-kubernetes-list-type: map
|
|
584
|
+
x-kubernetes-list-map-keys:
|
|
585
|
+
- type
|
|
586
|
+
items:
|
|
587
|
+
description: Condition contains details for one aspect of the current state of this API Resource.
|
|
588
|
+
type: object
|
|
589
|
+
required:
|
|
590
|
+
- type
|
|
591
|
+
- status
|
|
592
|
+
- lastTransitionTime
|
|
593
|
+
- reason
|
|
594
|
+
- message
|
|
595
|
+
properties:
|
|
596
|
+
type:
|
|
597
|
+
description: Type of condition in CamelCase or in foo.example.com/CamelCase.
|
|
598
|
+
type: string
|
|
599
|
+
maxLength: 316
|
|
600
|
+
pattern: "^[A-Za-z0-9]([A-Za-z0-9_.-]*[A-Za-z0-9])?$"
|
|
601
|
+
status:
|
|
602
|
+
description: "Status of the condition, one of True, False, Unknown."
|
|
603
|
+
type: string
|
|
604
|
+
enum:
|
|
605
|
+
- "True"
|
|
606
|
+
- "False"
|
|
607
|
+
- Unknown
|
|
608
|
+
observedGeneration:
|
|
609
|
+
description: observedGeneration represents the .metadata.generation that the condition was set based upon.
|
|
610
|
+
type: integer
|
|
611
|
+
format: int64
|
|
612
|
+
minimum: 0
|
|
613
|
+
lastTransitionTime:
|
|
614
|
+
description: lastTransitionTime is the last time the condition transitioned from one status to another.
|
|
615
|
+
type: string
|
|
616
|
+
format: date-time
|
|
617
|
+
reason:
|
|
618
|
+
description: "reason contains a programmatic identifier indicating the reason for the condition's last transition."
|
|
619
|
+
type: string
|
|
620
|
+
minLength: 1
|
|
621
|
+
maxLength: 1024
|
|
622
|
+
pattern: "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$"
|
|
623
|
+
message:
|
|
624
|
+
description: message is a human readable message indicating details about the transition.
|
|
625
|
+
type: string
|
|
626
|
+
maxLength: 32768
|
|
627
|
+
|
|
628
|
+
EOF
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
```text title="stdout"
|
|
632
|
+
customresourcedefinition.apiextensions.k8s.io/helloworlds.example.com created
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### Given: a new namespace exists
|
|
636
|
+
|
|
637
|
+
**✅ Create Namespace "kest-1j79x"**
|
|
638
|
+
|
|
639
|
+
```shell
|
|
640
|
+
kubectl apply -f - <<EOF
|
|
641
|
+
apiVersion: v1
|
|
642
|
+
kind: Namespace
|
|
643
|
+
metadata:
|
|
644
|
+
name: kest-1j79x
|
|
645
|
+
|
|
646
|
+
EOF
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
```text title="stdout"
|
|
650
|
+
namespace/kest-1j79x created
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### Given: a HelloWorld custom resource is created
|
|
654
|
+
|
|
655
|
+
**✅ Apply HelloWorld "my-hello-world" in namespace "kest-1j79x"**
|
|
656
|
+
|
|
657
|
+
```shell
|
|
658
|
+
kubectl apply -f - -n kest-1j79x <<EOF
|
|
659
|
+
apiVersion: example.com/v2
|
|
660
|
+
kind: HelloWorld
|
|
661
|
+
metadata:
|
|
662
|
+
name: my-hello-world
|
|
663
|
+
|
|
664
|
+
EOF
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
```text title="stdout"
|
|
668
|
+
helloworld.example.com/my-hello-world created
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
### When: I apply a status with Ready condition
|
|
672
|
+
|
|
673
|
+
**✅ ApplyStatus HelloWorld "my-hello-world" in namespace "kest-1j79x"**
|
|
674
|
+
|
|
675
|
+
```shell
|
|
676
|
+
kubectl apply --server-side --field-manager kest --subresource=status -f - -n kest-1j79x <<EOF
|
|
677
|
+
apiVersion: example.com/v2
|
|
678
|
+
kind: HelloWorld
|
|
679
|
+
metadata:
|
|
680
|
+
name: my-hello-world
|
|
681
|
+
status:
|
|
682
|
+
conditions:
|
|
683
|
+
- type: Ready
|
|
684
|
+
status: "True"
|
|
685
|
+
lastTransitionTime: 2026-02-05T00:00:00Z
|
|
686
|
+
reason: ManuallySet
|
|
687
|
+
message: Ready condition set to True via server-side apply.
|
|
688
|
+
|
|
689
|
+
EOF
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
```text title="stdout"
|
|
693
|
+
helloworld.example.com/my-hello-world serverside-applied
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
### Then: the HelloWorld should have the Ready status
|
|
697
|
+
|
|
698
|
+
**✅ Assert HelloWorld "my-hello-world" in namespace "kest-1j79x"**
|
|
699
|
+
|
|
700
|
+
```shell
|
|
701
|
+
kubectl get HelloWorld.v2.example.com my-hello-world -o yaml -n kest-1j79x
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
```yaml title="stdout"
|
|
705
|
+
apiVersion: example.com/v2
|
|
706
|
+
kind: HelloWorld
|
|
707
|
+
metadata:
|
|
708
|
+
annotations:
|
|
709
|
+
kubectl.kubernetes.io/last-applied-configuration: |
|
|
710
|
+
{"apiVersion":"example.com/v2","kind":"HelloWorld","metadata":{"annotations":{},"name":"my-hello-world","namespace":"kest-1j79x"}}
|
|
711
|
+
creationTimestamp: "2026-02-06T00:28:06Z"
|
|
712
|
+
generation: 1
|
|
713
|
+
name: my-hello-world
|
|
714
|
+
namespace: kest-1j79x
|
|
715
|
+
resourceVersion: "487441"
|
|
716
|
+
uid: 681105e4-0004-4bf4-a6b2-68fe5320e5d5
|
|
717
|
+
status:
|
|
718
|
+
conditions:
|
|
719
|
+
- lastTransitionTime: "2026-02-05T00:00:00Z"
|
|
720
|
+
message: Ready condition set to True via server-side apply.
|
|
721
|
+
reason: ManuallySet
|
|
722
|
+
status: "True"
|
|
723
|
+
type: Ready
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### Cleanup
|
|
727
|
+
|
|
728
|
+
| # | Action | Resource | Status |
|
|
729
|
+
|---|--------|----------|--------|
|
|
730
|
+
| 1 | Delete | HelloWorld/my-hello-world | ✅ |
|
|
731
|
+
| 2 | Delete namespace | kest-1j79x | ✅ |
|
|
732
|
+
| 3 | Delete | CustomResourceDefinition/helloworlds.example.com | ✅ |
|
|
733
|
+
|
|
734
|
+
```shellsession
|
|
735
|
+
$ kubectl delete HelloWorld/my-hello-world -n kest-1j79x
|
|
736
|
+
helloworld.example.com "my-hello-world" deleted from kest-1j79x namespace
|
|
737
|
+
|
|
738
|
+
$ kubectl delete Namespace/kest-1j79x
|
|
739
|
+
namespace "kest-1j79x" deleted
|
|
740
|
+
|
|
741
|
+
$ kubectl delete CustomResourceDefinition/helloworlds.example.com
|
|
742
|
+
customresourcedefinition.apiextensions.k8s.io "helloworlds.example.com" deleted
|
|
743
|
+
```
|
|
744
|
+
|