From 00c878f7b72c81bf805e70c000553bc592bfeece Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Fri, 2 Jul 2021 16:11:33 +0530 Subject: [PATCH] Fix #756: Service re-apply error happening during `k8s:watch` Append ClusterIP fetched from Service from Kubernetes API to the Service loaded from resource manifests in order to avoid patch error Signed-off-by: Rohan Kumar --- CHANGELOG.md | 1 + .../kit/config/service/ApplyService.java | 9 +- .../ApplyServiceWithoutMockServerTest.java | 89 +++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/ApplyServiceWithoutMockServerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 20b1f6367e..fc144f17b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Usage: * Fix #701: Update Fabric8 Kubernetes Client to 5.4.0 * Fix #425: Multi-layer support for Container Images * Fix #751: QuarkusGenerator: Multi-layer images for the different Quarkus packaging modes +* Fix #756: Service re-apply error happening during `k8s:watch` ### 1.3.0 (2021-05-18) * Fix #497: Assembly descriptor removed but still in documentation diff --git a/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ApplyService.java b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ApplyService.java index 8fdfc78726..d665eed503 100644 --- a/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ApplyService.java +++ b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ApplyService.java @@ -118,8 +118,12 @@ public class ApplyService { private static final Set projectsCreated = new HashSet<>(); public ApplyService(KubernetesClient kubernetesClient, KitLogger log) { + this(kubernetesClient, new PatchService(kubernetesClient, log), log); + } + + ApplyService(KubernetesClient kubernetesClient, PatchService patchService, KitLogger log) { this.kubernetesClient = kubernetesClient; - this.patchService = new PatchService(kubernetesClient, log); + this.patchService = patchService; this.log = log; } @@ -865,6 +869,9 @@ public void applyService(Service service, String sourceName) { kubernetesClient.services().inNamespace(currentNamespace).withName(id).delete(); doCreateService(service, currentNamespace, sourceName); } else { + if (service.getSpec().getClusterIP() == null) { + service.getSpec().setClusterIP(old.getSpec().getClusterIP()); + } doPatchEntity(old, service, currentNamespace, sourceName); } } diff --git a/jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/ApplyServiceWithoutMockServerTest.java b/jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/ApplyServiceWithoutMockServerTest.java new file mode 100644 index 0000000000..a690af6df3 --- /dev/null +++ b/jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/ApplyServiceWithoutMockServerTest.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.config.service; + +import io.fabric8.kubernetes.api.model.IntOrString; +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.ServiceBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import mockit.Expectations; +import mockit.Mocked; +import mockit.Verifications; +import org.eclipse.jkube.kit.common.KitLogger; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.Objects; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class ApplyServiceWithoutMockServerTest { + private ApplyService applyService; + + @Mocked + private KubernetesClient client; + + @Mocked + private KitLogger log; + + @Mocked + private PatchService patchService; + + @Before + public void setUp() { + patchService = new PatchService(client, log); + applyService = new ApplyService(client, patchService, log); + applyService.setNamespace("default"); + } + + @Test + public void testServiceRedeploy() { + Service serviceManifest = createServiceBuilder() + .editSpec().withType("ClusterIP").endSpec() + .build(); + Service serviceFromServer = createServiceBuilder() + .editSpec().withClusterIP("12.23.43.12").endSpec() + .build(); + new Expectations() {{ + client.services().inNamespace("default").withName("foo-svc").get(); + result = serviceFromServer; + }}; + + applyService.applyService(serviceManifest, "test"); + + new Verifications() {{ + Service finalMergedSvc = null; + patchService.compareAndPatchEntity("default", finalMergedSvc = withCapture(), serviceFromServer); + + assertThat(finalMergedSvc).isNotNull() + .hasFieldOrPropertyWithValue("metadata.name", "foo-svc") + .hasFieldOrPropertyWithValue("spec.clusterIP", "12.23.43.12") + .hasFieldOrPropertyWithValue("spec.type", "ClusterIP"); + }}; + } + + private ServiceBuilder createServiceBuilder() { + return new ServiceBuilder() + .withNewMetadata().withName("foo-svc").endMetadata() + .withNewSpec() + .addToSelector(Collections.singletonMap("foo", "bar")) + .addNewPort() + .withProtocol("TCP") + .withPort(80) + .withTargetPort(new IntOrString(9376)) + .endPort() + .endSpec(); + } +}