@appthrust/kest 0.2.0 → 0.3.1
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/README.md +93 -13
- package/package.json +2 -1
- package/ts/actions/apply-namespace.ts +35 -3
- package/ts/actions/apply-status.ts +8 -1
- package/ts/actions/apply.ts +8 -1
- package/ts/actions/assert-absence.ts +38 -0
- package/ts/actions/assert-list.ts +3 -0
- package/ts/actions/assert.ts +3 -0
- package/ts/actions/create.ts +34 -0
- package/ts/actions/delete.ts +4 -12
- package/ts/actions/exec.ts +3 -0
- package/ts/actions/get.ts +3 -0
- package/ts/actions/kubectl-type.ts +19 -0
- package/ts/actions/label.ts +23 -0
- package/ts/actions/types.ts +3 -0
- package/ts/apis/index.ts +331 -4
- package/ts/k8s-resource/index.ts +22 -0
- package/ts/kubectl/index.ts +54 -0
- package/ts/recording/index.ts +81 -115
- package/ts/reporter/markdown/index.ts +23 -0
- package/ts/reporter/markdown/model.ts +63 -0
- package/ts/reporter/markdown/parser/index.ts +361 -0
- package/ts/reporter/markdown/renderer/index.ts +296 -0
- package/ts/reporter/shiki.ts +58 -0
- package/ts/retry.ts +0 -6
- package/ts/scenario/index.ts +43 -31
- package/ts/test.ts +2 -1
- package/ts/reporter/index.ts +0 -0
- package/ts/reporter/markdown.ts +0 -962
package/ts/apis/index.ts
CHANGED
|
@@ -54,6 +54,39 @@ export interface Scenario {
|
|
|
54
54
|
options?: undefined | ActionOptions
|
|
55
55
|
): Promise<void>;
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Creates a Kubernetes resource with `kubectl create`.
|
|
59
|
+
*
|
|
60
|
+
* The manifest is validated and then created. When the action succeeds, Kest
|
|
61
|
+
* registers a cleanup handler that deletes the resource using
|
|
62
|
+
* `kubectl delete <kind>/<metadata.name>` during scenario cleanup.
|
|
63
|
+
*
|
|
64
|
+
* Unlike {@link Scenario.apply}, this action uses `kubectl create` which
|
|
65
|
+
* fails if the resource already exists. Use this when you need to ensure the
|
|
66
|
+
* resource is freshly created (e.g. for resources that use `generateName` or
|
|
67
|
+
* when you want to guarantee no prior state).
|
|
68
|
+
*
|
|
69
|
+
* This action is retried when it throws.
|
|
70
|
+
*
|
|
71
|
+
* @template T - The expected Kubernetes resource shape.
|
|
72
|
+
* @param manifest - YAML string, resource object, or imported YAML module.
|
|
73
|
+
* @param options - Retry options such as timeout and polling interval.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* await s.create({
|
|
78
|
+
* apiVersion: "v1",
|
|
79
|
+
* kind: "ConfigMap",
|
|
80
|
+
* metadata: { name: "my-config" },
|
|
81
|
+
* data: { mode: "demo" },
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
create<T extends K8sResource>(
|
|
86
|
+
manifest: ApplyingManifest<T>,
|
|
87
|
+
options?: undefined | ActionOptions
|
|
88
|
+
): Promise<void>;
|
|
89
|
+
|
|
57
90
|
/**
|
|
58
91
|
* Applies the `status` subresource using server-side apply.
|
|
59
92
|
*
|
|
@@ -114,6 +147,38 @@ export interface Scenario {
|
|
|
114
147
|
options?: undefined | ActionOptions
|
|
115
148
|
): Promise<void>;
|
|
116
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Adds, updates, or removes labels on a Kubernetes resource using
|
|
152
|
+
* `kubectl label`.
|
|
153
|
+
*
|
|
154
|
+
* Set a label value to a string to add/update it, or to `null` to remove it.
|
|
155
|
+
*
|
|
156
|
+
* This action is retried when it throws.
|
|
157
|
+
*
|
|
158
|
+
* Note: this is a one-way mutation and does not register a cleanup handler.
|
|
159
|
+
*
|
|
160
|
+
* @template T - The expected Kubernetes resource shape.
|
|
161
|
+
* @param input - Resource reference and label changes.
|
|
162
|
+
* @param options - Retry options such as timeout and polling interval.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* await s.label({
|
|
167
|
+
* apiVersion: "v1",
|
|
168
|
+
* kind: "ConfigMap",
|
|
169
|
+
* name: "my-config",
|
|
170
|
+
* labels: {
|
|
171
|
+
* env: "production", // add or update
|
|
172
|
+
* deprecated: null, // remove
|
|
173
|
+
* },
|
|
174
|
+
* });
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
label<T extends K8sResource>(
|
|
178
|
+
input: LabelInput<T>,
|
|
179
|
+
options?: undefined | ActionOptions
|
|
180
|
+
): Promise<void>;
|
|
181
|
+
|
|
117
182
|
/**
|
|
118
183
|
* Fetches a Kubernetes resource and returns it as a typed object.
|
|
119
184
|
*
|
|
@@ -176,6 +241,32 @@ export interface Scenario {
|
|
|
176
241
|
options?: undefined | ActionOptions
|
|
177
242
|
): Promise<T>;
|
|
178
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Asserts that a Kubernetes resource does not exist.
|
|
246
|
+
*
|
|
247
|
+
* Internally, this uses `kubectl get` and expects it to fail with a
|
|
248
|
+
* "not found" error. If the resource exists, the assertion fails.
|
|
249
|
+
*
|
|
250
|
+
* This action is retried until the resource is absent or a timeout expires.
|
|
251
|
+
*
|
|
252
|
+
* @template T - The expected Kubernetes resource shape.
|
|
253
|
+
* @param resource - Group/version/kind and name of the resource.
|
|
254
|
+
* @param options - Retry options such as timeout and polling interval.
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```ts
|
|
258
|
+
* await s.assertAbsence({
|
|
259
|
+
* apiVersion: "v1",
|
|
260
|
+
* kind: "ConfigMap",
|
|
261
|
+
* name: "deleted-config",
|
|
262
|
+
* });
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
assertAbsence<T extends K8sResource>(
|
|
266
|
+
resource: K8sResourceReference<T>,
|
|
267
|
+
options?: undefined | ActionOptions
|
|
268
|
+
): Promise<void>;
|
|
269
|
+
|
|
179
270
|
/**
|
|
180
271
|
* Lists Kubernetes resources of a given type and runs a test function.
|
|
181
272
|
*
|
|
@@ -212,7 +303,11 @@ export interface Scenario {
|
|
|
212
303
|
* `kest-abc12`). The namespace creation is a mutating action that registers a
|
|
213
304
|
* cleanup handler; the namespace is deleted during scenario cleanup.
|
|
214
305
|
*
|
|
215
|
-
*
|
|
306
|
+
* You can also pass `{ generateName: "prefix-" }` to generate a name with a
|
|
307
|
+
* custom prefix (e.g. `"prefix-d7kpn"`).
|
|
308
|
+
*
|
|
309
|
+
* @param name - Optional namespace name, or `{ generateName }` for prefixed
|
|
310
|
+
* generation.
|
|
216
311
|
* @param options - Retry options such as timeout and polling interval.
|
|
217
312
|
*
|
|
218
313
|
* @example
|
|
@@ -225,9 +320,15 @@ export interface Scenario {
|
|
|
225
320
|
* data: { mode: "namespaced" },
|
|
226
321
|
* });
|
|
227
322
|
* ```
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```ts
|
|
326
|
+
* // Generate a namespace with a custom prefix (e.g. "foo-d7kpn")
|
|
327
|
+
* const ns = await s.newNamespace({ generateName: "foo-" });
|
|
328
|
+
* ```
|
|
228
329
|
*/
|
|
229
330
|
newNamespace(
|
|
230
|
-
name?: undefined | string,
|
|
331
|
+
name?: undefined | string | { readonly generateName: string },
|
|
231
332
|
options?: undefined | ActionOptions
|
|
232
333
|
): Promise<Namespace>;
|
|
233
334
|
|
|
@@ -374,6 +475,30 @@ export interface Cluster {
|
|
|
374
475
|
options?: undefined | ActionOptions
|
|
375
476
|
): Promise<void>;
|
|
376
477
|
|
|
478
|
+
/**
|
|
479
|
+
* Creates a Kubernetes resource with `kubectl create` and registers cleanup.
|
|
480
|
+
*
|
|
481
|
+
* Unlike {@link Cluster.apply}, this uses `kubectl create` which fails if the
|
|
482
|
+
* resource already exists.
|
|
483
|
+
*
|
|
484
|
+
* @template T - The expected Kubernetes resource shape.
|
|
485
|
+
* @param manifest - YAML string, resource object, or imported YAML module.
|
|
486
|
+
* @param options - Retry options such as timeout and polling interval.
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* ```ts
|
|
490
|
+
* await cluster.create({
|
|
491
|
+
* apiVersion: "v1",
|
|
492
|
+
* kind: "Namespace",
|
|
493
|
+
* metadata: { name: "my-team" },
|
|
494
|
+
* });
|
|
495
|
+
* ```
|
|
496
|
+
*/
|
|
497
|
+
create<T extends K8sResource>(
|
|
498
|
+
manifest: ApplyingManifest<T>,
|
|
499
|
+
options?: undefined | ActionOptions
|
|
500
|
+
): Promise<void>;
|
|
501
|
+
|
|
377
502
|
/**
|
|
378
503
|
* Applies the `status` subresource using server-side apply.
|
|
379
504
|
*
|
|
@@ -412,6 +537,38 @@ export interface Cluster {
|
|
|
412
537
|
options?: undefined | ActionOptions
|
|
413
538
|
): Promise<void>;
|
|
414
539
|
|
|
540
|
+
/**
|
|
541
|
+
* Adds, updates, or removes labels on a Kubernetes resource in this cluster
|
|
542
|
+
* using `kubectl label`.
|
|
543
|
+
*
|
|
544
|
+
* Set a label value to a string to add/update it, or to `null` to remove it.
|
|
545
|
+
*
|
|
546
|
+
* This action is retried when it throws.
|
|
547
|
+
*
|
|
548
|
+
* Note: this is a one-way mutation and does not register a cleanup handler.
|
|
549
|
+
*
|
|
550
|
+
* @template T - The expected Kubernetes resource shape.
|
|
551
|
+
* @param input - Resource reference and label changes.
|
|
552
|
+
* @param options - Retry options such as timeout and polling interval.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```ts
|
|
556
|
+
* await cluster.label({
|
|
557
|
+
* apiVersion: "v1",
|
|
558
|
+
* kind: "Namespace",
|
|
559
|
+
* name: "my-team",
|
|
560
|
+
* labels: {
|
|
561
|
+
* team: "backend",
|
|
562
|
+
* deprecated: null,
|
|
563
|
+
* },
|
|
564
|
+
* });
|
|
565
|
+
* ```
|
|
566
|
+
*/
|
|
567
|
+
label<T extends K8sResource>(
|
|
568
|
+
input: LabelInput<T>,
|
|
569
|
+
options?: undefined | ActionOptions
|
|
570
|
+
): Promise<void>;
|
|
571
|
+
|
|
415
572
|
/**
|
|
416
573
|
* Fetches a Kubernetes resource by GVK and name.
|
|
417
574
|
*
|
|
@@ -457,6 +614,27 @@ export interface Cluster {
|
|
|
457
614
|
options?: undefined | ActionOptions
|
|
458
615
|
): Promise<T>;
|
|
459
616
|
|
|
617
|
+
/**
|
|
618
|
+
* Asserts that a Kubernetes resource does not exist in this cluster.
|
|
619
|
+
*
|
|
620
|
+
* @template T - The expected Kubernetes resource shape.
|
|
621
|
+
* @param resource - Group/version/kind and name of the resource.
|
|
622
|
+
* @param options - Retry options such as timeout and polling interval.
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```ts
|
|
626
|
+
* await cluster.assertAbsence({
|
|
627
|
+
* apiVersion: "v1",
|
|
628
|
+
* kind: "Namespace",
|
|
629
|
+
* name: "deleted-ns",
|
|
630
|
+
* });
|
|
631
|
+
* ```
|
|
632
|
+
*/
|
|
633
|
+
assertAbsence<T extends K8sResource>(
|
|
634
|
+
resource: K8sResourceReference<T>,
|
|
635
|
+
options?: undefined | ActionOptions
|
|
636
|
+
): Promise<void>;
|
|
637
|
+
|
|
460
638
|
/**
|
|
461
639
|
* Lists Kubernetes resources of a given type and runs a test function.
|
|
462
640
|
*
|
|
@@ -483,7 +661,8 @@ export interface Cluster {
|
|
|
483
661
|
/**
|
|
484
662
|
* Creates a new namespace in this cluster and returns a namespaced API.
|
|
485
663
|
*
|
|
486
|
-
* @param name - Optional namespace name
|
|
664
|
+
* @param name - Optional namespace name, or `{ generateName }` for prefixed
|
|
665
|
+
* generation.
|
|
487
666
|
* @param options - Retry options such as timeout and polling interval.
|
|
488
667
|
*
|
|
489
668
|
* @example
|
|
@@ -496,9 +675,15 @@ export interface Cluster {
|
|
|
496
675
|
* data: { mode: "from-cluster" },
|
|
497
676
|
* });
|
|
498
677
|
* ```
|
|
678
|
+
*
|
|
679
|
+
* @example
|
|
680
|
+
* ```ts
|
|
681
|
+
* // Generate a namespace with a custom prefix
|
|
682
|
+
* const ns = await cluster.newNamespace({ generateName: "foo-" });
|
|
683
|
+
* ```
|
|
499
684
|
*/
|
|
500
685
|
newNamespace(
|
|
501
|
-
name?: undefined | string,
|
|
686
|
+
name?: undefined | string | { readonly generateName: string },
|
|
502
687
|
options?: undefined | ActionOptions
|
|
503
688
|
): Promise<Namespace>;
|
|
504
689
|
}
|
|
@@ -522,6 +707,11 @@ export interface Cluster {
|
|
|
522
707
|
* causes `kubectl` to fail.
|
|
523
708
|
*/
|
|
524
709
|
export interface Namespace {
|
|
710
|
+
/**
|
|
711
|
+
* The name of this namespace (e.g. `"kest-abc12"`).
|
|
712
|
+
*/
|
|
713
|
+
readonly name: string;
|
|
714
|
+
|
|
525
715
|
/**
|
|
526
716
|
* Applies a Kubernetes manifest in this namespace and registers cleanup.
|
|
527
717
|
*
|
|
@@ -550,6 +740,37 @@ export interface Namespace {
|
|
|
550
740
|
options?: undefined | ActionOptions
|
|
551
741
|
): Promise<void>;
|
|
552
742
|
|
|
743
|
+
/**
|
|
744
|
+
* Creates a Kubernetes resource in this namespace with `kubectl create` and
|
|
745
|
+
* registers cleanup.
|
|
746
|
+
*
|
|
747
|
+
* The target namespace is controlled by this {@link Namespace} instance.
|
|
748
|
+
* Prefer omitting `manifest.metadata.namespace`; if it is set, it must match
|
|
749
|
+
* this namespace (otherwise `kubectl` fails).
|
|
750
|
+
*
|
|
751
|
+
* Unlike {@link Namespace.apply}, this uses `kubectl create` which fails if
|
|
752
|
+
* the resource already exists.
|
|
753
|
+
*
|
|
754
|
+
* @template T - The expected Kubernetes resource shape.
|
|
755
|
+
* @param manifest - YAML string, resource object, or imported YAML module.
|
|
756
|
+
* @param options - Retry options such as timeout and polling interval.
|
|
757
|
+
*
|
|
758
|
+
* @example
|
|
759
|
+
* ```ts
|
|
760
|
+
* const ns = await s.newNamespace("my-ns");
|
|
761
|
+
* await ns.create({
|
|
762
|
+
* apiVersion: "v1",
|
|
763
|
+
* kind: "ConfigMap",
|
|
764
|
+
* metadata: { name: "my-config" },
|
|
765
|
+
* data: { mode: "demo" },
|
|
766
|
+
* });
|
|
767
|
+
* ```
|
|
768
|
+
*/
|
|
769
|
+
create<T extends K8sResource>(
|
|
770
|
+
manifest: ApplyingManifest<T>,
|
|
771
|
+
options?: undefined | ActionOptions
|
|
772
|
+
): Promise<void>;
|
|
773
|
+
|
|
553
774
|
/**
|
|
554
775
|
* Applies the `status` subresource in this namespace using server-side apply.
|
|
555
776
|
*
|
|
@@ -592,6 +813,40 @@ export interface Namespace {
|
|
|
592
813
|
options?: undefined | ActionOptions
|
|
593
814
|
): Promise<void>;
|
|
594
815
|
|
|
816
|
+
/**
|
|
817
|
+
* Adds, updates, or removes labels on a namespaced Kubernetes resource using
|
|
818
|
+
* `kubectl label`.
|
|
819
|
+
*
|
|
820
|
+
* The target namespace is controlled by this {@link Namespace} instance.
|
|
821
|
+
*
|
|
822
|
+
* Set a label value to a string to add/update it, or to `null` to remove it.
|
|
823
|
+
*
|
|
824
|
+
* This action is retried when it throws.
|
|
825
|
+
*
|
|
826
|
+
* Note: this is a one-way mutation and does not register a cleanup handler.
|
|
827
|
+
*
|
|
828
|
+
* @template T - The expected Kubernetes resource shape.
|
|
829
|
+
* @param input - Resource reference and label changes.
|
|
830
|
+
* @param options - Retry options such as timeout and polling interval.
|
|
831
|
+
*
|
|
832
|
+
* @example
|
|
833
|
+
* ```ts
|
|
834
|
+
* await ns.label({
|
|
835
|
+
* apiVersion: "v1",
|
|
836
|
+
* kind: "ConfigMap",
|
|
837
|
+
* name: "my-config",
|
|
838
|
+
* labels: {
|
|
839
|
+
* env: "production",
|
|
840
|
+
* deprecated: null,
|
|
841
|
+
* },
|
|
842
|
+
* });
|
|
843
|
+
* ```
|
|
844
|
+
*/
|
|
845
|
+
label<T extends K8sResource>(
|
|
846
|
+
input: LabelInput<T>,
|
|
847
|
+
options?: undefined | ActionOptions
|
|
848
|
+
): Promise<void>;
|
|
849
|
+
|
|
595
850
|
/**
|
|
596
851
|
* Fetches a namespaced Kubernetes resource by GVK and name.
|
|
597
852
|
*
|
|
@@ -637,6 +892,27 @@ export interface Namespace {
|
|
|
637
892
|
options?: undefined | ActionOptions
|
|
638
893
|
): Promise<T>;
|
|
639
894
|
|
|
895
|
+
/**
|
|
896
|
+
* Asserts that a namespaced Kubernetes resource does not exist.
|
|
897
|
+
*
|
|
898
|
+
* @template T - The expected Kubernetes resource shape.
|
|
899
|
+
* @param resource - Group/version/kind and name of the resource.
|
|
900
|
+
* @param options - Retry options such as timeout and polling interval.
|
|
901
|
+
*
|
|
902
|
+
* @example
|
|
903
|
+
* ```ts
|
|
904
|
+
* await ns.assertAbsence({
|
|
905
|
+
* apiVersion: "v1",
|
|
906
|
+
* kind: "ConfigMap",
|
|
907
|
+
* name: "deleted-config",
|
|
908
|
+
* });
|
|
909
|
+
* ```
|
|
910
|
+
*/
|
|
911
|
+
assertAbsence<T extends K8sResource>(
|
|
912
|
+
resource: K8sResourceReference<T>,
|
|
913
|
+
options?: undefined | ActionOptions
|
|
914
|
+
): Promise<void>;
|
|
915
|
+
|
|
640
916
|
/**
|
|
641
917
|
* Lists namespaced Kubernetes resources of a given type and runs a test.
|
|
642
918
|
*
|
|
@@ -737,6 +1013,57 @@ export interface K8sResourceReference<T extends K8sResource = K8sResource> {
|
|
|
737
1013
|
readonly name: string;
|
|
738
1014
|
}
|
|
739
1015
|
|
|
1016
|
+
/**
|
|
1017
|
+
* Input for {@link Scenario.label}, {@link Cluster.label}, and
|
|
1018
|
+
* {@link Namespace.label}.
|
|
1019
|
+
*
|
|
1020
|
+
* Identifies a Kubernetes resource and the label changes to apply.
|
|
1021
|
+
*
|
|
1022
|
+
* - A label value of `string` adds or updates the label.
|
|
1023
|
+
* - A label value of `null` removes the label.
|
|
1024
|
+
*/
|
|
1025
|
+
export interface LabelInput<T extends K8sResource = K8sResource> {
|
|
1026
|
+
/**
|
|
1027
|
+
* Kubernetes API version (e.g. `"v1"`, `"apps/v1"`).
|
|
1028
|
+
*/
|
|
1029
|
+
readonly apiVersion: T["apiVersion"];
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Kubernetes kind (e.g. `"ConfigMap"`, `"Deployment"`).
|
|
1033
|
+
*/
|
|
1034
|
+
readonly kind: T["kind"];
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* `metadata.name` of the target resource.
|
|
1038
|
+
*/
|
|
1039
|
+
readonly name: string;
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Optional namespace override.
|
|
1043
|
+
*
|
|
1044
|
+
* When used on a {@link Namespace}-scoped API surface the namespace is
|
|
1045
|
+
* already set; this field is mainly useful at the {@link Scenario} or
|
|
1046
|
+
* {@link Cluster} level for namespaced resources.
|
|
1047
|
+
*/
|
|
1048
|
+
readonly namespace?: undefined | string;
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Label mutations to apply.
|
|
1052
|
+
*
|
|
1053
|
+
* - `"value"` -- add or update the label to the given value.
|
|
1054
|
+
* - `null` -- remove the label.
|
|
1055
|
+
*/
|
|
1056
|
+
readonly labels: Readonly<Record<string, string | null>>;
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* When `true`, passes `--overwrite` to allow updating labels that already
|
|
1060
|
+
* exist on the resource.
|
|
1061
|
+
*
|
|
1062
|
+
* @default false
|
|
1063
|
+
*/
|
|
1064
|
+
readonly overwrite?: undefined | boolean;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
740
1067
|
/**
|
|
741
1068
|
* A test definition for {@link Scenario.assert}.
|
|
742
1069
|
*/
|
package/ts/k8s-resource/index.ts
CHANGED
|
@@ -118,3 +118,25 @@ export interface ESM {
|
|
|
118
118
|
function isESM(value: unknown): value is ESM {
|
|
119
119
|
return typeof value === "object" && value !== null && "default" in value;
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
export function getResourceMeta(
|
|
123
|
+
value: unknown
|
|
124
|
+
): undefined | { kind: string; name: string } {
|
|
125
|
+
if (typeof value === "string") {
|
|
126
|
+
const result = parseK8sResourceYaml(value);
|
|
127
|
+
if (result.ok) {
|
|
128
|
+
return { kind: result.value.kind, name: result.value.metadata.name };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (isESM(value)) {
|
|
132
|
+
const result = parseK8sResourceFromESM(value);
|
|
133
|
+
if (result.ok) {
|
|
134
|
+
return { kind: result.value.kind, name: result.value.metadata.name };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const result = parseK8sResource(value);
|
|
138
|
+
if (result.ok) {
|
|
139
|
+
return { kind: result.value.kind, name: result.value.metadata.name };
|
|
140
|
+
}
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
package/ts/kubectl/index.ts
CHANGED
|
@@ -135,6 +135,26 @@ export interface Kubectl {
|
|
|
135
135
|
name: string,
|
|
136
136
|
options?: KubectlDeleteOptions
|
|
137
137
|
): Promise<string>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Adds, updates, or removes labels on a Kubernetes resource using
|
|
141
|
+
* `kubectl label <resource>/<name> key=value ... [--overwrite]`.
|
|
142
|
+
*
|
|
143
|
+
* Labels with a `null` value are removed (emitted as `key-`).
|
|
144
|
+
*
|
|
145
|
+
* @param resource - The resource type (e.g., "configmap", "deployment.v1.apps")
|
|
146
|
+
* @param name - The name of the resource to label
|
|
147
|
+
* @param labels - Label mutations (string to set, null to remove)
|
|
148
|
+
* @param options - Optional label options (overwrite, context)
|
|
149
|
+
* @returns The trimmed stdout from kubectl
|
|
150
|
+
* @throws Error if kubectl exits with non-zero code
|
|
151
|
+
*/
|
|
152
|
+
label(
|
|
153
|
+
resource: string,
|
|
154
|
+
name: string,
|
|
155
|
+
labels: Readonly<Record<string, string | null>>,
|
|
156
|
+
options?: KubectlLabelOptions
|
|
157
|
+
): Promise<string>;
|
|
138
158
|
}
|
|
139
159
|
|
|
140
160
|
export interface KubectlDeleteOptions {
|
|
@@ -146,6 +166,15 @@ export interface KubectlDeleteOptions {
|
|
|
146
166
|
readonly context?: undefined | KubectlContext;
|
|
147
167
|
}
|
|
148
168
|
|
|
169
|
+
export interface KubectlLabelOptions {
|
|
170
|
+
/**
|
|
171
|
+
* If true, adds `--overwrite` to allow updating labels that already
|
|
172
|
+
* exist on the resource.
|
|
173
|
+
*/
|
|
174
|
+
readonly overwrite?: undefined | boolean;
|
|
175
|
+
readonly context?: undefined | KubectlContext;
|
|
176
|
+
}
|
|
177
|
+
|
|
149
178
|
export class RealKubectl implements Kubectl {
|
|
150
179
|
private readonly recorder: Recorder;
|
|
151
180
|
private readonly cwd: undefined | string;
|
|
@@ -283,6 +312,31 @@ export class RealKubectl implements Kubectl {
|
|
|
283
312
|
});
|
|
284
313
|
}
|
|
285
314
|
|
|
315
|
+
async label(
|
|
316
|
+
resource: string,
|
|
317
|
+
name: string,
|
|
318
|
+
labels: Readonly<Record<string, string | null>>,
|
|
319
|
+
options?: KubectlLabelOptions
|
|
320
|
+
): Promise<string> {
|
|
321
|
+
const args: [string, ...Array<string>] = ["label", `${resource}/${name}`];
|
|
322
|
+
for (const [key, value] of Object.entries(labels)) {
|
|
323
|
+
if (value === null) {
|
|
324
|
+
args.push(`${key}-`);
|
|
325
|
+
} else {
|
|
326
|
+
args.push(`${key}=${value}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (options?.overwrite) {
|
|
330
|
+
args.push("--overwrite");
|
|
331
|
+
}
|
|
332
|
+
return await this.runKubectl({
|
|
333
|
+
args,
|
|
334
|
+
stdoutLanguage: "text",
|
|
335
|
+
stderrLanguage: "text",
|
|
336
|
+
overrideContext: options?.context,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
286
340
|
private async runKubectl(params: ExecParams): Promise<string> {
|
|
287
341
|
const cmd = "kubectl";
|
|
288
342
|
const ctx = { ...this.defaultContext, ...params.overrideContext };
|