diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper.java index fec3e43c7e..b55442c7a4 100644 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper.java @@ -68,13 +68,15 @@ public class KubectlBuildWrapper extends SimpleBuildWrapper { private final String serverUrl; private final String credentialsId; private final String caCertificate; + private final String httpsProxy; @DataBoundConstructor public KubectlBuildWrapper(@Nonnull String serverUrl, @Nonnull String credentialsId, - @Nonnull String caCertificate) { + @Nonnull String caCertificate, String httpsProxy) { this.serverUrl = serverUrl; this.credentialsId = credentialsId; this.caCertificate = caCertificate; + this.httpsProxy = httpsProxy; } public String getServerUrl() { @@ -89,6 +91,10 @@ public String getCaCertificate() { return caCertificate; } + public String getHttpsProxy() { + return httpsProxy; + } + @Override public void setUp(Context context, Run build, FilePath workspace, Launcher launcher, TaskListener listener, EnvVars initialEnvironment) throws IOException, InterruptedException { @@ -99,6 +105,7 @@ public void setUp(Context context, Run build, FilePath workspace, Launcher context.setDisposer(new CleanupDisposer(tempFiles)); String tlsConfig; + String kubectlBegin = ""; if (caCertificate != null && !caCertificate.isEmpty()) { FilePath caCrtFile = workspace.createTempFile("cert-auth", "crt"); String ca = caCertificate; @@ -113,8 +120,10 @@ public void setUp(Context context, Run build, FilePath workspace, Launcher tlsConfig = " --insecure-skip-tls-verify=true"; } - int status = launcher.launch() - .cmdAsSingleString("kubectl config --kubeconfig=\"" + configFile.getRemote() + kubectlBegin += "kubectl config --kubeconfig=\""; + + int status = launcher.launch().envs("HTTPS_PROXY="+this.httpsProxy) + .cmdAsSingleString(kubectlBegin + configFile.getRemote() + "\" set-cluster k8s --server=" + serverUrl + tlsConfig) .join(); if (status != 0) throw new IOException("Failed to run kubectl config "+status); @@ -173,19 +182,19 @@ public void setUp(Context context, Run build, FilePath workspace, Launcher throw new AbortException("Unsupported Credentials type " + c.getClass().getName()); } - status = launcher.launch() - .cmdAsSingleString("kubectl config --kubeconfig=\"" + configFile.getRemote() + "\" set-credentials cluster-admin " + login) + status = launcher.launch().envs("HTTPS_PROXY="+this.httpsProxy) + .cmdAsSingleString(kubectlBegin + configFile.getRemote() + "\" set-credentials cluster-admin " + login) .masks(false, false, false, false, false, false, true) .join(); if (status != 0) throw new IOException("Failed to run kubectl config "+status); - status = launcher.launch() - .cmdAsSingleString("kubectl config --kubeconfig=\"" + configFile.getRemote() + "\" set-context k8s --cluster=k8s --user=cluster-admin") + status = launcher.launch().envs("HTTPS_PROXY="+this.httpsProxy) + .cmdAsSingleString(kubectlBegin + configFile.getRemote() + "\" set-context k8s --cluster=k8s --user=cluster-admin") .join(); if (status != 0) throw new IOException("Failed to run kubectl config "+status); - status = launcher.launch() - .cmdAsSingleString("kubectl config --kubeconfig=\"" + configFile.getRemote() + "\" use-context k8s") + status = launcher.launch().envs("HTTPS_PROXY="+this.httpsProxy) + .cmdAsSingleString(kubectlBegin + configFile.getRemote() + "\" use-context k8s") .join(); if (status != 0) throw new IOException("Failed to run kubectl config "+status); } diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesClientProvider.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesClientProvider.java index a05ba0fac5..7b12c847fd 100644 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesClientProvider.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesClientProvider.java @@ -92,7 +92,7 @@ static KubernetesClient createClient(KubernetesCloud cloud) throws NoSuchAlgorit String displayName = cloud.getDisplayName(); final Client c = clients.getIfPresent(displayName); if (c == null) { - KubernetesClient client = new KubernetesFactoryAdapter(cloud.getServerUrl(), cloud.getNamespace(), + KubernetesClient client = new KubernetesFactoryAdapter(cloud.getServerUrl(), cloud.getHttpsProxy(), cloud.getNamespace(), cloud.getServerCertificate(), cloud.getCredentialsId(), cloud.isSkipTlsVerify(), cloud.getConnectTimeout(), cloud.getReadTimeout(), cloud.getMaxRequestsPerHost()).createClient(); clients.put(displayName, new Client(getValidity(cloud), client)); diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud.java index e29d837753..623ad9c205 100644 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud.java @@ -98,6 +98,7 @@ public class KubernetesCloud extends Cloud { @Nonnull private List templates = new ArrayList<>(); private String serverUrl; + private String httpsProxy; @CheckForNull private String serverCertificate; @@ -148,6 +149,7 @@ public KubernetesCloud(@NonNull String name, @NonNull KubernetesCloud source) { this.defaultsProviderTemplate = source.defaultsProviderTemplate; this.templates.addAll(source.templates); this.serverUrl = source.serverUrl; + this.httpsProxy = source.httpsProxy; this.skipTlsVerify = source.skipTlsVerify; this.addMasterProxyEnvVars = source.addMasterProxyEnvVars; this.namespace = source.namespace; @@ -248,6 +250,15 @@ public void setServerUrl(@Nonnull String serverUrl) { this.serverUrl = serverUrl; } + public String getHttpsProxy() { + return httpsProxy; + } + + @DataBoundSetter + public void setHttpsProxy(@Nonnull String httpsProxy) { + this.httpsProxy = httpsProxy; + } + public String getServerCertificate() { return serverCertificate; } @@ -731,6 +742,7 @@ public FormValidation doTestConnection(@QueryParameter String name, @QueryParame @QueryParameter String serverCertificate, @QueryParameter boolean skipTlsVerify, @QueryParameter String namespace, + @QueryParameter String httpsProxy, @QueryParameter int connectionTimeout, @QueryParameter int readTimeout) throws Exception { Jenkins.get().checkPermission(Jenkins.ADMINISTER); @@ -738,7 +750,7 @@ public FormValidation doTestConnection(@QueryParameter String name, @QueryParame if (StringUtils.isBlank(name)) return FormValidation.error("name is required"); - try (KubernetesClient client = new KubernetesFactoryAdapter(serverUrl, namespace, + try (KubernetesClient client = new KubernetesFactoryAdapter(serverUrl, httpsProxy, namespace, Util.fixEmpty(serverCertificate), Util.fixEmpty(credentialsId), skipTlsVerify, connectionTimeout, readTimeout).createClient()) { // test listing pods diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesFactoryAdapter.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesFactoryAdapter.java index 150c7ba796..2c5845d75f 100644 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesFactoryAdapter.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesFactoryAdapter.java @@ -71,23 +71,24 @@ public class KubernetesFactoryAdapter { private final int connectTimeout; private final int readTimeout; private final int maxRequestsPerHost; + private final String httpsProxy; public KubernetesFactoryAdapter(String serviceAddress, @CheckForNull String caCertData, @CheckForNull String credentials, boolean skipTlsVerify) { - this(serviceAddress, null, caCertData, credentials, skipTlsVerify); + this(serviceAddress, null, null, caCertData, credentials, skipTlsVerify); } - public KubernetesFactoryAdapter(String serviceAddress, String namespace, @CheckForNull String caCertData, + public KubernetesFactoryAdapter(String serviceAddress, @CheckForNull String httpsProxy, String namespace, @CheckForNull String caCertData, @CheckForNull String credentials, boolean skipTlsVerify) { - this(serviceAddress, namespace, caCertData, credentials, skipTlsVerify, DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT); + this(serviceAddress, httpsProxy, namespace, caCertData, credentials, skipTlsVerify, DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT); } - public KubernetesFactoryAdapter(String serviceAddress, String namespace, @CheckForNull String caCertData, + public KubernetesFactoryAdapter(String serviceAddress, @CheckForNull String httpsProxy, String namespace, @CheckForNull String caCertData, @CheckForNull String credentials, boolean skipTlsVerify, int connectTimeout, int readTimeout) { - this(serviceAddress, namespace, caCertData, credentials, skipTlsVerify, connectTimeout, readTimeout, KubernetesCloud.DEFAULT_MAX_REQUESTS_PER_HOST); + this(serviceAddress, httpsProxy, namespace, caCertData, credentials, skipTlsVerify, connectTimeout, readTimeout, KubernetesCloud.DEFAULT_MAX_REQUESTS_PER_HOST); } - public KubernetesFactoryAdapter(String serviceAddress, String namespace, @CheckForNull String caCertData, + public KubernetesFactoryAdapter(String serviceAddress, @CheckForNull String httpsProxy, String namespace, @CheckForNull String caCertData, @CheckForNull String credentials, boolean skipTlsVerify, int connectTimeout, int readTimeout, int maxRequestsPerHost) { this.serviceAddress = serviceAddress; this.namespace = namespace; @@ -97,6 +98,7 @@ public KubernetesFactoryAdapter(String serviceAddress, String namespace, @CheckF this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; this.maxRequestsPerHost = maxRequestsPerHost; + this.httpsProxy = httpsProxy != null && !httpsProxy.isEmpty() ? httpsProxy : null; } private StandardCredentials getCredentials(String credentials) { @@ -171,6 +173,11 @@ public KubernetesClient createClient() throws NoSuchAlgorithmException, Unrecove builder = builder.withRequestTimeout(readTimeout * 1000).withConnectionTimeout(connectTimeout * 1000); builder.withMaxConcurrentRequestsPerHost(maxRequestsPerHost); + if(httpsProxy != null && !httpsProxy.isEmpty()) { + LOGGER.info("Https Proxy used is " + httpsProxy); + builder.withHttpsProxy(httpsProxy); + } + if (!StringUtils.isBlank(namespace)) { builder.withNamespace(namespace); } else if (StringUtils.isBlank(builder.getNamespace())) { diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper/config.jelly b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper/config.jelly index ea2d5fb3d1..ea5f2a46e1 100644 --- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper/config.jelly +++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper/config.jelly @@ -5,6 +5,10 @@ + + + + diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper/help-httpsProxy.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper/help-httpsProxy.html new file mode 100644 index 0000000000..f97b17e805 --- /dev/null +++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubectlBuildWrapper/help-httpsProxy.html @@ -0,0 +1,3 @@ +
+ Use proxy when kubernetes api server is behind the firewall. +
\ No newline at end of file diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud/config.jelly b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud/config.jelly index e5e5ced341..433d5411b8 100644 --- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud/config.jelly +++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud/config.jelly @@ -9,6 +9,10 @@ + + + + @@ -25,7 +29,7 @@ - + diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud/help-httpsProxy.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud/help-httpsProxy.html new file mode 100644 index 0000000000..f97b17e805 --- /dev/null +++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud/help-httpsProxy.html @@ -0,0 +1,3 @@ +
+ Use proxy when kubernetes api server is behind the firewall. +
\ No newline at end of file diff --git a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesClientProviderTest.java b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesClientProviderTest.java new file mode 100644 index 0000000000..989fcb8aa4 --- /dev/null +++ b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesClientProviderTest.java @@ -0,0 +1,40 @@ +package org.csanchez.jenkins.plugins.kubernetes; + +import io.fabric8.kubernetes.client.KubernetesClient; +import org.junit.Before; +import org.junit.Test; +import static io.fabric8.kubernetes.client.Config.*; +import static org.junit.Assert.assertEquals; + +/** + * Test the creation of client with httpsProxy + */ +public class KubernetesClientProviderTest { + private static final String HTTPS_PROXY = "https://example.com:123"; + private static final String DEFAULT_HTTPS_PROXY = "https://example.com:1234"; + + @Before + public void injectEnvironmentVariables() { + System.setProperty(KUBERNETES_HTTPS_PROXY, DEFAULT_HTTPS_PROXY); + } + @Test + public void testHttpsProxyInjection() throws Exception { + KubernetesFactoryAdapter factory = new KubernetesFactoryAdapter("http://example.com", HTTPS_PROXY, null, null, null, false, 1, 1, 1); + KubernetesClient client = factory.createClient(); + assertEquals(HTTPS_PROXY, client.getConfiguration().getHttpsProxy()); + } + + @Test + public void testDefaultHttpsProxyInjection() throws Exception { + KubernetesFactoryAdapter factory = new KubernetesFactoryAdapter("http://example.com", null, null, null, null, false, 1, 1, 1); + KubernetesClient client = factory.createClient(); + assertEquals(DEFAULT_HTTPS_PROXY, client.getConfiguration().getHttpsProxy()); + } + + @Test + public void testEmptyHttpsProxyInjection() throws Exception { + KubernetesFactoryAdapter factory = new KubernetesFactoryAdapter("http://example.com", "", null, null, null, false, 1, 1, 1); + KubernetesClient client = factory.createClient(); + assertEquals(DEFAULT_HTTPS_PROXY, client.getConfiguration().getHttpsProxy()); + } +}