From 2bfc938ff8046c033f3b8ea3ecf539b6e5c1a8cb Mon Sep 17 00:00:00 2001 From: Steve Hawkins Date: Tue, 5 Apr 2022 08:42:29 -0400 Subject: [PATCH] fix #3993: further restricting / refining type resolution --- doc/MIGRATION-v6.md | 8 +++- .../resources/replicationcontroller-it.yml | 4 ++ .../src/test/resources/secret-it.yml | 8 ++-- .../internal/KubernetesDeserializer.java | 4 +- .../client/mock/DeploymentTest.java | 15 ------- .../client/mock/NetworkingV1IngressTest.java | 13 ------- .../mock/NetworkingV1beta1IngressTest.java | 13 ------- .../mock/V1CustomResourceDefinitionTest.java | 12 ------ .../mock/V1HorizontalPodAutoscalerTest.java | 13 ------- .../V1MutatingWebhookConfigurationTest.java | 15 ------- .../V1ValidatingWebhookConfigurationTest.java | 14 ------- .../test/resources/test-crd-no-apiversion.yml | 31 --------------- .../src/test/resources/test-cronjob.yml | 2 +- .../test/resources/test-hpa-no-apiversion.yml | 34 ---------------- .../resources/test-ingress-no-apiversion.yml | 30 -------------- .../test/resources/test-mwc-no-apiversion.yml | 30 -------------- .../src/test/resources/test-statefulset.yml | 2 +- .../src/test/resources/test-template.yml | 8 ++-- .../test/resources/test-vwc-no-apiversion.yml | 35 ----------------- .../valid-deployment-without-apiversion.json | 39 ------------------- 20 files changed, 24 insertions(+), 306 deletions(-) delete mode 100644 kubernetes-tests/src/test/resources/test-crd-no-apiversion.yml delete mode 100644 kubernetes-tests/src/test/resources/test-hpa-no-apiversion.yml delete mode 100644 kubernetes-tests/src/test/resources/test-ingress-no-apiversion.yml delete mode 100644 kubernetes-tests/src/test/resources/test-mwc-no-apiversion.yml delete mode 100644 kubernetes-tests/src/test/resources/test-vwc-no-apiversion.yml delete mode 100644 kubernetes-tests/src/test/resources/valid-deployment-without-apiversion.json diff --git a/doc/MIGRATION-v6.md b/doc/MIGRATION-v6.md index aa3a5269a73..18db6905c85 100644 --- a/doc/MIGRATION-v6.md +++ b/doc/MIGRATION-v6.md @@ -89,7 +89,13 @@ To use it, exclude the kubernetes-httpclient-okhttp dependency and add the kuber ## Deserialization Resolution -The group on the object being deserialized is not required to match the prospective class - even for built-in types. This prevents the unintentional parsing of custom types without a registered class as a built-in type of the same name. This also means that you must ensure the apiVersion values are correct on the objects you are deserializing as they will no longer resolve to built-in type of the same name when there is a mistake. +The apiVersion on an resource being deserialized is required. + +If a version only is specified as the apiVersion, it may match kubernetes built-in types with an empty group (for example Pod), or OpenShift built in types (for example kind: Template apiVersion: v1 is allowed to match apiVersion: template.openshift.io/v1). + +Otherwise with group and version specified, the resource match uniquely with a class. This prevents the unintentional parsing of custom types without a registered class as a built-in type of the same name. + +This means that you must ensure the apiVersion values are correct on the objects you are deserializing as they will no longer resolve to built-in type of the same name when there is a mistake. ## Deprecation Removals diff --git a/kubernetes-itests/src/test/resources/replicationcontroller-it.yml b/kubernetes-itests/src/test/resources/replicationcontroller-it.yml index c0b3f6c48ba..8483006150c 100644 --- a/kubernetes-itests/src/test/resources/replicationcontroller-it.yml +++ b/kubernetes-itests/src/test/resources/replicationcontroller-it.yml @@ -16,6 +16,7 @@ --- kind: ReplicationController +apiVersion: v1 metadata: name: rc-get spec: @@ -35,6 +36,7 @@ spec: - containerPort: 80 --- kind: ReplicationController +apiVersion: v1 metadata: name: rc-list spec: @@ -54,6 +56,7 @@ spec: - containerPort: 80 --- kind: ReplicationController +apiVersion: v1 metadata: name: rc-update spec: @@ -73,6 +76,7 @@ spec: - containerPort: 80 --- kind: ReplicationController +apiVersion: v1 metadata: name: rc-delete spec: diff --git a/kubernetes-itests/src/test/resources/secret-it.yml b/kubernetes-itests/src/test/resources/secret-it.yml index f5756f12e16..99f8d50d2b2 100644 --- a/kubernetes-itests/src/test/resources/secret-it.yml +++ b/kubernetes-itests/src/test/resources/secret-it.yml @@ -15,7 +15,7 @@ # --- -apiversion: v1 +apiVersion: v1 kind: Secret metadata: name: secret-get @@ -24,7 +24,7 @@ data: username: "guccifer" password: "shadowgovernment" --- -apiversion: v1 +apiVersion: v1 kind: Secret metadata: name: secret-list @@ -33,7 +33,7 @@ data: username: "guccifer" password: "shadowgovernment" --- -apiversion: v1 +apiVersion: v1 kind: Secret metadata: name: secret-update @@ -42,7 +42,7 @@ data: username: "guccifer" password: "shadowgovernment" --- -apiversion: v1 +apiVersion: v1 kind: Secret metadata: name: secret-delete diff --git a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java index 2f6d1f4a2d6..5e08fbab282 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java +++ b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java @@ -282,8 +282,10 @@ public Class getForKey(TypeKey key) { } bestMatch = typeKey; break; + } else if (typeKey.apiGroup != null && !typeKey.apiGroup.endsWith(".openshift.io")) { + continue; } - if (key.version != null && key.version.equals(typeKey.apiGroup)) { + if (key.version != null && key.version.equals(typeKey.version)) { bestMatch = typeKey; break; } diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/DeploymentTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/DeploymentTest.java index c66386555ad..43a3cdc2756 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/DeploymentTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/DeploymentTest.java @@ -708,21 +708,6 @@ void testDeploymentGetLogMultiContainer() { assertEquals("hello", log); } - @Test - void testDeploymentLoadWithoutApiVersion() { - // Given - - // When - List list = client.load(getClass().getResourceAsStream("/valid-deployment-without-apiversion.json")).get(); - Deployment deployment = (Deployment) list.get(0); - - // Then - assertNotNull(deployment); - assertEquals("test", deployment.getMetadata().getName()); - assertEquals(1, deployment.getSpec().getReplicas()); - assertEquals(1, deployment.getSpec().getTemplate().getSpec().getContainers().size()); - } - private DeploymentBuilder createDeploymentBuilder() { return new DeploymentBuilder() .withNewMetadata() diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/NetworkingV1IngressTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/NetworkingV1IngressTest.java index 9a904662064..146eb1f8db4 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/NetworkingV1IngressTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/NetworkingV1IngressTest.java @@ -181,19 +181,6 @@ void testCreateWithNameMismatch() { Assertions.assertThrows(KubernetesClientException.class, () -> ingressOp.create(ingress1)); } - @Test - void testIngressLoadWithoutApiVersion() { - // Given - - // When - List items = client.load(getClass().getResourceAsStream("/test-ingress-no-apiversion.yml")).get(); - - // Then - assertNotNull(items); - assertEquals(1, items.size()); - assertTrue(items.get(0) instanceof io.fabric8.kubernetes.api.model.networking.v1.Ingress); - } - @Test void testCreateOrReplaceWhenAnnotationUpdated() { // Given diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/NetworkingV1beta1IngressTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/NetworkingV1beta1IngressTest.java index da6844b7d9a..fbd96a87e3e 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/NetworkingV1beta1IngressTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/NetworkingV1beta1IngressTest.java @@ -178,19 +178,6 @@ void testCreateWithNameMismatch() { }); } - @Test - void testIngressLoadWithoutApiVersion() { - // Given - - // When - List items = client.load(getClass().getResourceAsStream("/test-ingress-no-apiversion.yml")).get(); - - // Then - assertNotNull(items); - assertEquals(1, items.size()); - assertTrue(items.get(0) instanceof io.fabric8.kubernetes.api.model.networking.v1.Ingress); - } - @Test void testCreateOrReplaceWhenAnnotationUpdated() { // Given diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1CustomResourceDefinitionTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1CustomResourceDefinitionTest.java index 800d05aeece..d4a4b2deb3f 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1CustomResourceDefinitionTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1CustomResourceDefinitionTest.java @@ -25,7 +25,6 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -120,17 +119,6 @@ void testDelete() { assertTrue(deleted); } - @Test - void testCustomResourceDefinitionTest() { - // When - List items = client.load(getClass().getResourceAsStream("/test-crd-no-apiversion.yml")).get(); - - // Then - Assertions.assertNotNull(items); - Assertions.assertEquals(1, items.size()); - Assertions.assertTrue(items.get(0) instanceof CustomResourceDefinition); - } - JSONSchemaProps readSchema() throws IOException { ObjectMapper mapper = new ObjectMapper(); final URL resource = getClass().getResource("/test-crd-validation-schema.json"); diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1HorizontalPodAutoscalerTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1HorizontalPodAutoscalerTest.java index 529a5005c8e..d1bab3fb29a 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1HorizontalPodAutoscalerTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1HorizontalPodAutoscalerTest.java @@ -189,17 +189,4 @@ void testBuild() { assertNotNull(horizontalPodAutoscaler); } - @Test - void testHorizontalPodAutoscalerLoadWithNoApiVersion() { - // Given - - // When - List items = client.load(getClass().getResourceAsStream("/test-hpa-no-apiversion.yml")).get(); - - // Then - assertNotNull(items); - assertEquals(1, items.size()); - assertTrue(items.get(0) instanceof HorizontalPodAutoscaler); - } - } diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1MutatingWebhookConfigurationTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1MutatingWebhookConfigurationTest.java index 5cdaead2c4e..34b695c6f84 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1MutatingWebhookConfigurationTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1MutatingWebhookConfigurationTest.java @@ -26,8 +26,6 @@ import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; import org.junit.jupiter.api.Test; -import java.util.List; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -101,19 +99,6 @@ public void delete() { assertTrue(isDeleted); } - @Test - void testMutatingWebhookConfigurationLoadWithNoApiVersion() { - // Given - - // When - List items = client.load(getClass().getResourceAsStream("/test-mwc-no-apiversion.yml")).get(); - - // Then - assertNotNull(items); - assertEquals(1, items.size()); - assertTrue(items.get(0) instanceof MutatingWebhookConfiguration); - } - public MutatingWebhookConfiguration getMutatingWebhookConfigurationSample() { return new MutatingWebhookConfigurationBuilder() .withNewMetadata().withName("mutatingWebhookConfiguration1").endMetadata() diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1ValidatingWebhookConfigurationTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1ValidatingWebhookConfigurationTest.java index ad04639be48..c689737eda4 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1ValidatingWebhookConfigurationTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/V1ValidatingWebhookConfigurationTest.java @@ -26,8 +26,6 @@ import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; import org.junit.jupiter.api.Test; -import java.util.List; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -110,18 +108,6 @@ public void delete() { assertTrue(isDeleted); } - @Test - void testValidatingWebhookConfigurationLoadWithNoApiVersion() { - - // When - List items = client.load(getClass().getResourceAsStream("/test-vwc-no-apiversion.yml")).get(); - - // Then - assertNotNull(items); - assertEquals(1, items.size()); - assertTrue(items.get(0) instanceof ValidatingWebhookConfiguration); - } - public ValidatingWebhookConfiguration getValidatingWebhookConfigurationSample() { return new ValidatingWebhookConfigurationBuilder() .withNewMetadata().withName("validatingWebhookConfiguration1").endMetadata() diff --git a/kubernetes-tests/src/test/resources/test-crd-no-apiversion.yml b/kubernetes-tests/src/test/resources/test-crd-no-apiversion.yml deleted file mode 100644 index c6049f34c06..00000000000 --- a/kubernetes-tests/src/test/resources/test-crd-no-apiversion.yml +++ /dev/null @@ -1,31 +0,0 @@ -# -# Copyright (C) 2015 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -kind: CustomResourceDefinition -metadata: - name: dummies.demo.fabric8.io -spec: - group: demo.fabric8.io - scope: Namespaced - versions: - - name: v1 - served: true - storage: true - names: - kind: Dummy - plural: dummies - shortNames: - - dummy diff --git a/kubernetes-tests/src/test/resources/test-cronjob.yml b/kubernetes-tests/src/test/resources/test-cronjob.yml index 5358ca27ebc..c540a5c4002 100644 --- a/kubernetes-tests/src/test/resources/test-cronjob.yml +++ b/kubernetes-tests/src/test/resources/test-cronjob.yml @@ -14,7 +14,7 @@ # limitations under the License. # -apiVersion: batch/v2alpha1 +apiVersion: batch/v1beta1 kind: CronJob metadata: name: pi diff --git a/kubernetes-tests/src/test/resources/test-hpa-no-apiversion.yml b/kubernetes-tests/src/test/resources/test-hpa-no-apiversion.yml deleted file mode 100644 index 08ba23c0778..00000000000 --- a/kubernetes-tests/src/test/resources/test-hpa-no-apiversion.yml +++ /dev/null @@ -1,34 +0,0 @@ -# -# Copyright (C) 2015 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -kind: HorizontalPodAutoscaler -metadata: - name: php-apache - namespace: default -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: php-apache - minReplicas: 1 - maxReplicas: 10 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 50 diff --git a/kubernetes-tests/src/test/resources/test-ingress-no-apiversion.yml b/kubernetes-tests/src/test/resources/test-ingress-no-apiversion.yml deleted file mode 100644 index 0dc09ad47de..00000000000 --- a/kubernetes-tests/src/test/resources/test-ingress-no-apiversion.yml +++ /dev/null @@ -1,30 +0,0 @@ -# -# Copyright (C) 2015 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -kind: Ingress -metadata: - name: test-ingress - annotations: - nginx.ingress.kubernetes.io/rewrite-target: / -spec: - rules: - - http: - paths: - - path: /testpath - pathType: Prefix - backend: - serviceName: test - servicePort: 80 diff --git a/kubernetes-tests/src/test/resources/test-mwc-no-apiversion.yml b/kubernetes-tests/src/test/resources/test-mwc-no-apiversion.yml deleted file mode 100644 index 9781ce4c3d8..00000000000 --- a/kubernetes-tests/src/test/resources/test-mwc-no-apiversion.yml +++ /dev/null @@ -1,30 +0,0 @@ -# -# Copyright (C) 2015 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -kind: MutatingWebhookConfiguration -metadata: - name: "my-webhook.example.com" -webhooks: - - name: my-webhook.example.com - objectSelector: - matchLabels: - foo: bar - rules: - - operations: ["CREATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*"] - scope: "*" diff --git a/kubernetes-tests/src/test/resources/test-statefulset.yml b/kubernetes-tests/src/test/resources/test-statefulset.yml index fd7efd151d9..17325df664c 100644 --- a/kubernetes-tests/src/test/resources/test-statefulset.yml +++ b/kubernetes-tests/src/test/resources/test-statefulset.yml @@ -15,7 +15,7 @@ # kind: StatefulSet -apiVersion: apps/v1beta1 +apiVersion: apps/v1 metadata: name: example spec: diff --git a/kubernetes-tests/src/test/resources/test-template.yml b/kubernetes-tests/src/test/resources/test-template.yml index 44658b16354..fb7e380ccba 100644 --- a/kubernetes-tests/src/test/resources/test-template.yml +++ b/kubernetes-tests/src/test/resources/test-template.yml @@ -107,7 +107,7 @@ description: "The web server's http port." - kind: "Route" - apiVersion: "v1" + apiVersion: "route.openshift.io/v1" id: "${APPLICATION_NAME}-http-route" metadata: name: "${APPLICATION_NAME}-http-route" @@ -121,14 +121,14 @@ name: "${APPLICATION_NAME}" - kind: "ImageStream" - apiVersion: "v1" + apiVersion: "image.openshift.io/v1" metadata: name: "${APPLICATION_NAME}" labels: application: "${APPLICATION_NAME}" - kind: "BuildConfig" - apiVersion: "v1" + apiVersion: "build.openshift.io/v1" metadata: name: "${APPLICATION_NAME}" labels: @@ -165,7 +165,7 @@ imageChange: {} - kind: "DeploymentConfig" - apiVersion: "v1" + apiVersion: "apps.openshift.io/v1" metadata: name: "${APPLICATION_NAME}" labels: diff --git a/kubernetes-tests/src/test/resources/test-vwc-no-apiversion.yml b/kubernetes-tests/src/test/resources/test-vwc-no-apiversion.yml deleted file mode 100644 index 01018ae8852..00000000000 --- a/kubernetes-tests/src/test/resources/test-vwc-no-apiversion.yml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (C) 2015 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -kind: ValidatingWebhookConfiguration -metadata: - name: "pod-policy.example.com" -webhooks: - - name: "pod-policy.example.com" - rules: - - apiGroups: [""] - apiVersions: ["v1"] - operations: ["CREATE"] - resources: ["pods"] - scope: "Namespaced" - clientConfig: - service: - namespace: "example-namespace" - name: "example-service" - caBundle: "Ci0tLS0tQk...<`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.>...tLS0K" - admissionReviewVersions: ["v1", "v1beta1"] - sideEffects: None - timeoutSeconds: 5 diff --git a/kubernetes-tests/src/test/resources/valid-deployment-without-apiversion.json b/kubernetes-tests/src/test/resources/valid-deployment-without-apiversion.json deleted file mode 100644 index 3bdb6e14b67..00000000000 --- a/kubernetes-tests/src/test/resources/valid-deployment-without-apiversion.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "kind": "Deployment", - "metadata": { - "name": "test", - "labels": { - "app": "test" - } - }, - "spec": { - "selector": { - "matchLabels": { - "app": "test" - } - }, - "replicas": 1, - "template": { - "metadata": { - "labels": { - "app": "test" - } - }, - "spec": { - "containers": [ - { - "name": "test", - "image": "busybox:latest", - "command": [ - "/bin/sh", - "-c" - ], - "args": [ - "sleep 60" - ] - } - ] - } - } - } -}