From ef64498533e37b1c7a55a07d1a88fe4feb59bd25 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Thu, 20 Jun 2024 15:15:28 +0530 Subject: [PATCH] fix (jkube-kit/common) : Update `KubernetesHelper.exportKubernetesClientConfigToFile` to add opinionated Cluster Context When using KubernetesClient with Kubernetes Mock Server, `kubernetesClient.getConfiguration()` returns a Config object with no context set. Handle this case in KubernetesMockServerUtil to create opinionated Context until this gets fixed in KubernetesMockServer Signed-off-by: Rohan Kumar --- .../kit/common/util/KubernetesHelper.java | 5 +- .../kit/common/util/KubernetesHelperTest.java | 39 +++++++++++++++ .../common/util/KubernetesMockServerUtil.java | 47 +++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/KubernetesMockServerUtil.java diff --git a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/KubernetesHelper.java b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/KubernetesHelper.java index 308812edee..977459fab2 100644 --- a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/KubernetesHelper.java +++ b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/KubernetesHelper.java @@ -13,12 +13,10 @@ */ package org.eclipse.jkube.kit.common.util; - import java.io.File; import java.io.IOException; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; import java.time.Instant; import java.time.format.DateTimeFormatter; @@ -860,6 +858,9 @@ private static NamedCluster createKubeConfigClusterFromClient(io.fabric8.kuberne if (StringUtils.isNotBlank(kubernetesClientConfig.getCaCertData())) { clusterBuilder.withCertificateAuthorityData(kubernetesClientConfig.getCaCertData()); } + if (kubernetesClientConfig.isTrustCerts()) { + clusterBuilder.withInsecureSkipTlsVerify(true); + } return new NamedClusterBuilder().withName(Optional.ofNullable(kubernetesClientConfig.getCurrentContext()) .map(NamedContext::getContext) .map(Context::getCluster) diff --git a/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/KubernetesHelperTest.java b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/KubernetesHelperTest.java index fa63ca1cc4..9094e0959f 100644 --- a/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/KubernetesHelperTest.java +++ b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/KubernetesHelperTest.java @@ -40,6 +40,7 @@ import io.fabric8.kubernetes.api.model.runtime.RawExtension; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.http.TlsVersion; import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; import org.assertj.core.api.InstanceOfAssertFactories; @@ -72,6 +73,7 @@ import io.fabric8.openshift.api.model.Template; import org.eclipse.jkube.kit.common.TestHttpStaticServer; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; @@ -454,6 +456,7 @@ void extractPodLabelSelector_withJobWithNoSelector_shouldReturnTemplateLabels() @Test + @DisplayName("when invalid target kubeconfig file provided, then thrown exception") void exportKubernetesClientConfigToFile_whenInvalidFileProvided_thenThrowException(@TempDir Path temporaryFolder) { // Given io.fabric8.kubernetes.client.Config kubernetesClientConfig = createKubernetesClientConfig(); @@ -465,6 +468,42 @@ void exportKubernetesClientConfigToFile_whenInvalidFileProvided_thenThrowExcepti } @Test + @DisplayName("should work with KubernetesClient config provided by KubernetesMockServer") + void exportKubernetesClientConfigToFile_whenTrustCertsEnabled_thenWriteKubeConfigWithInsecureSkipTlsVerify(@TempDir Path temporaryFolder) throws IOException { + // Given + io.fabric8.kubernetes.client.Config kubernetesClientConfig = new io.fabric8.kubernetes.client.ConfigBuilder(io.fabric8.kubernetes.client.Config.empty()) + .withMasterUrl("https://localhost:32354") + .withTrustCerts() + .withTlsVersions(TlsVersion.TLS_1_2) + .withNamespace("test") + .withHttp2Disable() + .build(); + // When + kubernetesClientConfig.setCurrentContext(KubernetesMockServerUtil.createOpinionatedKubernetesContextForMockKubernetesClientConfiguration(kubernetesClientConfig)); + Path exportedKubeConfig = KubernetesHelper.exportKubernetesClientConfigToFile(kubernetesClientConfig, temporaryFolder.resolve("config")); + + // Then + assertThat(exportedKubeConfig).isNotNull(); + assertThat(Serialization.unmarshal(exportedKubeConfig.toFile(), io.fabric8.kubernetes.api.model.Config.class)) + .hasFieldOrPropertyWithValue("currentContext", "jkube-context") + .satisfies(c -> assertThat(c.getContexts()) + .singleElement(InstanceOfAssertFactories.type(NamedContext.class)) + .hasFieldOrPropertyWithValue("name", "jkube-context") + .hasFieldOrPropertyWithValue("context.cluster", "localhost:32354") + .hasFieldOrPropertyWithValue("context.namespace", "test") + .hasFieldOrPropertyWithValue("context.user", "jkube")) + .satisfies(c -> assertThat(c.getClusters()) + .singleElement(InstanceOfAssertFactories.type(NamedCluster.class)) + .hasFieldOrPropertyWithValue("name", "localhost:32354") + .hasFieldOrPropertyWithValue("cluster.server", "https://localhost:32354/") + .hasFieldOrPropertyWithValue("cluster.insecureSkipTlsVerify", true)) + .satisfies(c -> assertThat(c.getUsers()) + .singleElement(InstanceOfAssertFactories.type(NamedAuthInfo.class)) + .hasFieldOrPropertyWithValue("name", "jkube")); + } + + @Test + @DisplayName("should work with valid kube config") void exportKubernetesClientConfigToFile_whenValidTargetFile_thenWriteKubeConfigToFile(@TempDir Path temporaryFolder) throws IOException { // Given io.fabric8.kubernetes.client.Config kubernetesClientConfig = createKubernetesClientConfig(); diff --git a/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/KubernetesMockServerUtil.java b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/KubernetesMockServerUtil.java new file mode 100644 index 0000000000..4f47f91334 --- /dev/null +++ b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/KubernetesMockServerUtil.java @@ -0,0 +1,47 @@ +/* + * 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.common.util; + +import io.fabric8.kubernetes.api.model.NamedContext; +import io.fabric8.kubernetes.api.model.NamedContextBuilder; +import io.fabric8.kubernetes.client.Config; +import org.eclipse.jkube.kit.common.JKubeException; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Optional; + +public class KubernetesMockServerUtil { + private KubernetesMockServerUtil() { } + + public static NamedContext createOpinionatedKubernetesContextForMockKubernetesClientConfiguration(Config kubernetesClientConfig) { + try { + if (kubernetesClientConfig != null && kubernetesClientConfig.getCurrentContext() == null) { + URI uri = new URI(kubernetesClientConfig.getMasterUrl()); + return new NamedContextBuilder() + .withName("jkube-context") + .withNewContext() + .withNamespace(kubernetesClientConfig.getNamespace()) + .withCluster(String.format("%s:%d", uri.getHost(), uri.getPort())) + .withUser(Optional.ofNullable(kubernetesClientConfig.getUsername()) + .orElse("jkube")) + .endContext() + .build(); + } + return null; + } catch (URISyntaxException uriSyntaxException) { + throw new JKubeException("Invalid Kubernetes cluster url ", uriSyntaxException); + } + } +}