From 5d351ed8b844cd486ffe4cedf229e3f24c1dad2a Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Sun, 4 Jan 2026 16:50:33 +0100 Subject: [PATCH 1/2] test against recent kubectl --- .github/latest-version.sh | 2 +- Jenkinsfile | 8 ++++---- README.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/latest-version.sh b/.github/latest-version.sh index 43f8bc8..c8d74ae 100755 --- a/.github/latest-version.sh +++ b/.github/latest-version.sh @@ -1,7 +1,7 @@ #!/bin/bash major=1 -for minor in `seq 28 50`; do +for minor in `seq 32 50`; do for patch in `seq 0 50`; do code=`curl -s -I https://dl.k8s.io/release/v$major.$minor.$patch/bin/linux/amd64/kubectl -o /dev/null -L -w "%{http_code}"` if [ "$code" != "200" ]; then diff --git a/Jenkinsfile b/Jenkinsfile index 64899b6..9bf4d66 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -10,10 +10,10 @@ String[] platforms = [ // Kubectl versions to test against String[] kubectlVersions = [ - '1.29.14', - '1.30.10', - '1.31.6', - '1.32.2', + '1.32.11', + '1.33.7', + '1.34.3', + '1.35.0', ] // Not sure what this does yet diff --git a/README.md b/README.md index 3afa775..eee26d8 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ node { ## Prerequisites - A jenkins installation running version 2.401.1 or higher (with jdk17 or jdk21). -- An executor with `kubectl` installed (tested against [v1.22 to v1.32][Jenkinsfile] included). +- An executor with `kubectl` installed (tested against [v1.32 to v1.35][Jenkinsfile] included). - A Kubernetes cluster. ## How it works From e135eb2a9b419176b9673c83e5b1e85b0eb4ae98 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Mon, 5 Jan 2026 09:49:02 +0100 Subject: [PATCH 2/2] improve assertions and adapt for kubectl >= 1.34 --- .../cli/KubectlIntegrationTest.java | 320 +++++++++++------- 1 file changed, 206 insertions(+), 114 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/kubernetes/cli/KubectlIntegrationTest.java b/src/test/java/org/jenkinsci/plugins/kubernetes/cli/KubectlIntegrationTest.java index 122fb38..cdddd47 100644 --- a/src/test/java/org/jenkinsci/plugins/kubernetes/cli/KubectlIntegrationTest.java +++ b/src/test/java/org/jenkinsci/plugins/kubernetes/cli/KubectlIntegrationTest.java @@ -1,6 +1,5 @@ package org.jenkinsci.plugins.kubernetes.cli; -import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -36,6 +35,11 @@ import hudson.model.Fingerprint; import hudson.FilePath; import io.jenkins.cli.shaded.org.apache.commons.lang.SystemUtils; +import io.fabric8.kubernetes.client.utils.Serialization; +import io.fabric8.kubernetes.api.model.Config; +import io.fabric8.kubernetes.api.model.NamedCluster; +import io.fabric8.kubernetes.api.model.NamedContext; +import io.fabric8.kubernetes.api.model.NamedAuthInfo; /** * @author Max Laverse @@ -116,25 +120,35 @@ public void testSingleKubeConfig() throws Exception { assertTrue(configDump.exists()); String configDumpContent = configDump.readToString().trim(); - assertThat(configDumpContent, containsString("apiVersion: v1\n" + - "clusters:\n" + - "- cluster:\n" + - " insecure-skip-tls-verify: true\n" + - " server: https://localhost:6443\n" + - " name: k8s\n" + - "contexts:\n" + - "- context:\n" + - " cluster: k8s\n" + - " user: test-credentials\n" + - " name: k8s\n" + - "current-context: k8s\n" + - "kind: Config\n" + - "preferences: {}\n" + - "users:\n" + - "- name: test-credentials\n" + - " user:\n" + - " password: s3cr3t\n" + - " username: bob")); + Config config = Serialization.unmarshal(configDumpContent, Config.class); + assertNotNull(config); + assertEquals("v1", config.getApiVersion()); + assertEquals("Config", config.getKind()); + assertEquals("k8s", config.getCurrentContext()); + + NamedCluster cluster = config.getClusters().stream() + .filter(c -> "k8s".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(cluster); + assertEquals("https://localhost:6443", cluster.getCluster().getServer()); + assertTrue(cluster.getCluster().getInsecureSkipTlsVerify()); + + NamedContext context = config.getContexts().stream() + .filter(c -> "k8s".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(context); + assertEquals("k8s", context.getContext().getCluster()); + assertEquals("test-credentials", context.getContext().getUser()); + + NamedAuthInfo user = config.getUsers().stream() + .filter(u -> "test-credentials".equals(u.getName())) + .findFirst() + .orElse(null); + assertNotNull(user); + assertEquals("bob", user.getUser().getUsername()); + assertEquals("s3cr3t", user.getUser().getPassword()); } @Test @@ -156,33 +170,55 @@ public void testMultiKubeConfig() throws Exception { assertTrue(configDump.exists()); String configDumpContent = configDump.readToString().trim(); - assertThat(configDumpContent, containsString("apiVersion: v1\n" + - "clusters:\n" + - "- cluster:\n" + - " insecure-skip-tls-verify: true\n" + - " server: https://test-cluster\n" + - " name: test-cluster\n" + - "- cluster:\n" + - " insecure-skip-tls-verify: true\n" + - " server: https://test-cluster2\n" + - " name: test-cluster2\n" + - "contexts:\n" + - "- context:\n" + - " cluster: test-cluster\n" + - " user: test-user\n" + - " name: test-cluster\n" + - "- context:\n" + - " cluster: test-cluster2\n" + - " user: test-user2\n" + - " name: test-cluster2\n" + - "current-context: test-cluster\n" + - "kind: Config\n" + - "preferences: {}\n" + - "users:\n" + - "- name: test-user\n" + - " user: {}\n" + - "- name: test-user2\n" + - " user: {}")); + Config config = Serialization.unmarshal(configDumpContent, Config.class); + assertNotNull(config); + assertEquals("v1", config.getApiVersion()); + assertEquals("Config", config.getKind()); + assertEquals("test-cluster", config.getCurrentContext()); + + NamedCluster cluster1 = config.getClusters().stream() + .filter(c -> "test-cluster".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(cluster1); + assertEquals("https://test-cluster", cluster1.getCluster().getServer()); + assertTrue(cluster1.getCluster().getInsecureSkipTlsVerify()); + + NamedCluster cluster2 = config.getClusters().stream() + .filter(c -> "test-cluster2".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(cluster2); + assertEquals("https://test-cluster2", cluster2.getCluster().getServer()); + assertTrue(cluster2.getCluster().getInsecureSkipTlsVerify()); + + NamedContext context1 = config.getContexts().stream() + .filter(c -> "test-cluster".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(context1); + assertEquals("test-cluster", context1.getContext().getCluster()); + assertEquals("test-user", context1.getContext().getUser()); + + NamedContext context2 = config.getContexts().stream() + .filter(c -> "test-cluster2".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(context2); + assertEquals("test-cluster2", context2.getContext().getCluster()); + assertEquals("test-user2", context2.getContext().getUser()); + + NamedAuthInfo user1 = config.getUsers().stream() + .filter(u -> "test-user".equals(u.getName())) + .findFirst() + .orElse(null); + assertNotNull(user1); + + NamedAuthInfo user2 = config.getUsers().stream() + .filter(u -> "test-user2".equals(u.getName())) + .findFirst() + .orElse(null); + assertNotNull(user2); } @Test @@ -205,46 +241,76 @@ public void testMultiKubeConfigUsernames() throws Exception { assertTrue(configDump.exists()); String configDumpContent = configDump.readToString().trim(); + Config config = Serialization.unmarshal(configDumpContent, Config.class); + assertNotNull(config); + assertEquals("v1", config.getApiVersion()); + assertEquals("Config", config.getKind()); + assertEquals("cont1234", config.getCurrentContext()); + + NamedCluster cluster1 = config.getClusters().stream() + .filter(c -> "clus1234".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(cluster1); + assertEquals("https://localhost:1234", cluster1.getCluster().getServer()); + assertTrue(cluster1.getCluster().getInsecureSkipTlsVerify()); + + NamedCluster cluster2 = config.getClusters().stream() + .filter(c -> "clus9999".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(cluster2); + assertEquals("https://localhost:9999", cluster2.getCluster().getServer()); + assertTrue(cluster2.getCluster().getInsecureSkipTlsVerify()); + + NamedCluster cluster3 = config.getClusters().stream() + .filter(c -> "test-cluster2".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(cluster3); + assertEquals("https://test-cluster2", cluster3.getCluster().getServer()); + assertTrue(cluster3.getCluster().getInsecureSkipTlsVerify()); + + NamedContext context1 = config.getContexts().stream() + .filter(c -> "cont1234".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(context1); + assertEquals("clus1234", context1.getContext().getCluster()); + assertEquals("test-credentials", context1.getContext().getUser()); + + NamedContext context2 = config.getContexts().stream() + .filter(c -> "cont9999".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(context2); + assertEquals("clus9999", context2.getContext().getCluster()); + assertEquals("", context2.getContext().getUser()); + + NamedContext context3 = config.getContexts().stream() + .filter(c -> "test-cluster2".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(context3); + assertEquals("test-cluster2", context3.getContext().getCluster()); + assertEquals("test-user2", context3.getContext().getUser()); + + NamedAuthInfo user1 = config.getUsers().stream() + .filter(u -> "test-credentials".equals(u.getName())) + .findFirst() + .orElse(null); + assertNotNull(user1); String expectedToken = "REDACTED"; if ((new Version("1.19.0")).compareTo(KubectlVersion()) >= 0) { expectedToken = "s3cr3t"; } - assertEquals("apiVersion: v1\n" + - "clusters:\n" + - "- cluster:\n" + - " insecure-skip-tls-verify: true\n" + - " server: https://localhost:1234\n" + - " name: clus1234\n" + - "- cluster:\n" + - " insecure-skip-tls-verify: true\n" + - " server: https://localhost:9999\n" + - " name: clus9999\n" + - "- cluster:\n" + - " insecure-skip-tls-verify: true\n" + - " server: https://test-cluster2\n" + - " name: test-cluster2\n" + - "contexts:\n" + - "- context:\n" + - " cluster: clus1234\n" + - " user: test-credentials\n" + - " name: cont1234\n" + - "- context:\n" + - " cluster: clus9999\n" + - " user: \"\"\n" + - " name: cont9999\n" + - "- context:\n" + - " cluster: test-cluster2\n" + - " user: test-user2\n" + - " name: test-cluster2\n" + - "current-context: cont1234\n" + - "kind: Config\n" + - "preferences: {}\n" + - "users:\n" + - "- name: test-credentials\n" + - " user:\n" + - " token: " + expectedToken + "\n" + - "- name: test-user2\n" + - " user: {}", configDumpContent); + assertEquals(expectedToken, user1.getUser().getToken()); + + NamedAuthInfo user2 = config.getUsers().stream() + .filter(u -> "test-user2".equals(u.getName())) + .findFirst() + .orElse(null); + assertNotNull(user2); } @Test @@ -266,37 +332,63 @@ public void testMultiKubeConfigWithServer() throws Exception { assertTrue(configDump.exists()); String configDumpContent = configDump.readToString().trim(); - assertThat(configDumpContent, containsString("apiVersion: v1\n" + - "clusters:\n" + - "- cluster:\n" + - " insecure-skip-tls-verify: true\n" + - " server: https://localhost:9999\n" + - " name: cred9999\n" + - "- cluster:\n" + - " insecure-skip-tls-verify: true\n" + - " server: https://test-cluster\n" + - " name: test-cluster\n" + - "- cluster:\n" + - " insecure-skip-tls-verify: true\n" + - " server: https://test-cluster2\n" + - " name: test-cluster2\n" + - "contexts:\n" + - "- context:\n" + - " cluster: test-cluster\n" + - " user: test-user\n" + - " name: test-cluster\n" + - "- context:\n" + - " cluster: cred9999\n" + - " user: test-user2\n" + - " name: test-cluster2\n" + - "current-context: test-cluster\n" + - "kind: Config\n" + - "preferences: {}\n" + - "users:\n" + - "- name: test-user\n" + - " user: {}\n" + - "- name: test-user2\n" + - " user: {}")); + Config config = Serialization.unmarshal(configDumpContent, Config.class); + assertNotNull(config); + assertEquals("v1", config.getApiVersion()); + assertEquals("Config", config.getKind()); + assertEquals("test-cluster", config.getCurrentContext()); + + NamedCluster cluster1 = config.getClusters().stream() + .filter(c -> "cred9999".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(cluster1); + assertEquals("https://localhost:9999", cluster1.getCluster().getServer()); + assertTrue(cluster1.getCluster().getInsecureSkipTlsVerify()); + + NamedCluster cluster2 = config.getClusters().stream() + .filter(c -> "test-cluster".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(cluster2); + assertEquals("https://test-cluster", cluster2.getCluster().getServer()); + assertTrue(cluster2.getCluster().getInsecureSkipTlsVerify()); + + NamedCluster cluster3 = config.getClusters().stream() + .filter(c -> "test-cluster2".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(cluster3); + assertEquals("https://test-cluster2", cluster3.getCluster().getServer()); + assertTrue(cluster3.getCluster().getInsecureSkipTlsVerify()); + + NamedContext context1 = config.getContexts().stream() + .filter(c -> "test-cluster".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(context1); + assertEquals("test-cluster", context1.getContext().getCluster()); + assertEquals("test-user", context1.getContext().getUser()); + + NamedContext context2 = config.getContexts().stream() + .filter(c -> "test-cluster2".equals(c.getName())) + .findFirst() + .orElse(null); + assertNotNull(context2); + assertEquals("cred9999", context2.getContext().getCluster()); + assertEquals("test-user2", context2.getContext().getUser()); + + NamedAuthInfo user1 = config.getUsers().stream() + .filter(u -> "test-user".equals(u.getName())) + .findFirst() + .orElse(null); + assertNotNull(user1); + + NamedAuthInfo user2 = config.getUsers().stream() + .filter(u -> "test-user2".equals(u.getName())) + .findFirst() + .orElse(null); + assertNotNull(user2); } @Test