From c1a47c8bb4061cb764eeef833d551d37a5325be9 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Thu, 18 Jul 2024 21:28:05 +0530 Subject: [PATCH] fix (kubernetes-client-api) : `Config`'s `autoConfigure` should disable auto configuration `Config`'s autoConfigure field should behave as per expectations. In order to make auto configuration configurable via builder, we need to move it after builder initialization is done. Do not autoConfigure in the default constructor `new Config()`. Auto configuration would be done in the final constructor annotated with the sundrio's `@Buildable` annotation. Then merge user provided fields with autoConfigured values. Signed-off-by: Rohan Kumar --- CHANGELOG.md | 1 + .../io/fabric8/kubernetes/client/Config.java | 249 ++++++++++++--- .../fabric8/kubernetes/client/ConfigTest.java | 295 +++++++++++++++--- .../DisabledAutoConfigurationIT.java | 62 ++++ .../openshift/client/OpenShiftConfig.java | 7 +- 5 files changed, 520 insertions(+), 94 deletions(-) create mode 100644 kubernetes-itests/src/test/java/io/fabric8/kubernetes/DisabledAutoConfigurationIT.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9cc76e670..1c6453453f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ #### Bugs * Fix #6038: Support for Gradle configuration cache * Fix #6110: VolumeSource (and other file mode fields) in Octal are correctly interpreted +* Fix #6137: `ConfigBuilder.withAutoConfigure` is not working #### Improvements * Fix #6008: removing the optional dependency on bouncy castle diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java index f7abf6151ee..fcb40b256c1 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java @@ -69,7 +69,7 @@ public class Config { private static final Logger LOGGER = LoggerFactory.getLogger(Config.class); /** - * Disables auto-configuration based on opinionated defaults in a {@link Config} object in the default constructor + * Disables auto-configuration based on opinionated defaults in a {@link Config} object in the Builder constructor */ public static final String KUBERNETES_DISABLE_AUTO_CONFIG_SYSTEM_PROPERTY = "kubernetes.disable.autoConfig"; public static final String KUBERNETES_MASTER_SYSTEM_PROPERTY = "kubernetes.master"; @@ -151,6 +151,9 @@ public class Config { private static final String ACCESS_TOKEN = "access-token"; private static final String ID_TOKEN = "id-token"; + private static final TlsVersion[] DEFAULT_TLS_VERSIONS_LIST = new TlsVersion[] { TlsVersion.TLS_1_3, TlsVersion.TLS_1_2 }; + private static final int DEFAULT_WATCH_RECONNECT_INTERVAL = 1000; + private static final int DEFAULT_CONNECTION_TIMEOUT = 10 * 1000; private boolean trustCerts; private boolean disableHostnameVerification; @@ -178,7 +181,7 @@ public class Config { private volatile String autoOAuthToken; private OAuthTokenProvider oauthTokenProvider; private long websocketPingInterval = DEFAULT_WEBSOCKET_PING_INTERVAL; - private int connectionTimeout = 10 * 1000; + private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; private int maxConcurrentRequests = DEFAULT_MAX_CONCURRENT_REQUESTS; private int maxConcurrentRequestsPerHost = DEFAULT_MAX_CONCURRENT_REQUESTS_PER_HOST; @@ -190,7 +193,7 @@ public class Config { /** * fields not used but needed for builder generation. */ - private int watchReconnectInterval = 1000; + private int watchReconnectInterval = DEFAULT_WATCH_RECONNECT_INTERVAL; private int watchReconnectLimit = -1; private int uploadRequestTimeout = DEFAULT_UPLOAD_REQUEST_TIMEOUT; private int requestRetryBackoffLimit; @@ -218,7 +221,7 @@ public class Config { private String proxyPassword; private String[] noProxy; private String userAgent = "fabric8-kubernetes-client/" + Version.clientVersion(); - private TlsVersion[] tlsVersions = new TlsVersion[] { TlsVersion.TLS_1_3, TlsVersion.TLS_1_2 }; + private TlsVersion[] tlsVersions = DEFAULT_TLS_VERSIONS_LIST; private boolean onlyHttpWatches; @@ -239,7 +242,7 @@ public class Config { */ @Deprecated public Config() { - this(!disableAutoConfig()); + this.autoConfigure = true; } private static boolean disableAutoConfig() { @@ -336,7 +339,7 @@ public Config(String masterUrl, String apiVersion, String namespace, boolean tru DEFAULT_UPLOAD_REQUEST_TIMEOUT, false, null, Collections.emptyList()); } - @Buildable(builderPackage = "io.fabric8.kubernetes.api.builder", editableEnabled = false) + @Deprecated public Config(String masterUrl, String apiVersion, String namespace, boolean trustCerts, boolean disableHostnameVerification, String caCertFile, String caCertData, String clientCertFile, String clientCertData, String clientKeyFile, String clientKeyData, String clientKeyAlgo, String clientKeyPassphrase, String username, String password, @@ -350,56 +353,198 @@ public Config(String masterUrl, String apiVersion, String namespace, boolean tru OAuthTokenProvider oauthTokenProvider, Map customHeaders, int requestRetryBackoffLimit, int requestRetryBackoffInterval, int uploadRequestTimeout, boolean onlyHttpWatches, NamedContext currentContext, List contexts) { - this.apiVersion = apiVersion; - this.namespace = namespace; - this.trustCerts = trustCerts; - this.disableHostnameVerification = disableHostnameVerification; - this.caCertFile = caCertFile; - this.caCertData = caCertData; - this.clientCertFile = clientCertFile; - this.clientCertData = clientCertData; - this.clientKeyFile = clientKeyFile; - this.clientKeyData = clientKeyData; - this.clientKeyAlgo = clientKeyAlgo; - this.clientKeyPassphrase = clientKeyPassphrase; - this.username = username; - this.password = password; - this.oauthToken = oauthToken; - this.websocketPingInterval = websocketPingInterval; - this.connectionTimeout = connectionTimeout; - - this.requestConfig = new RequestConfig(watchReconnectLimit, watchReconnectInterval, - requestTimeout, scaleTimeout, loggingInterval, - requestRetryBackoffLimit, requestRetryBackoffInterval, uploadRequestTimeout); - this.requestConfig.setImpersonateUsername(impersonateUsername); - this.requestConfig.setImpersonateGroups(impersonateGroups); - this.requestConfig.setImpersonateExtras(impersonateExtras); + this(masterUrl, apiVersion, namespace, trustCerts, disableHostnameVerification, caCertFile, caCertData, + clientCertFile, clientCertData, clientKeyFile, clientKeyData, clientKeyAlgo, clientKeyPassphrase, username, + password, oauthToken, autoOAuthToken, watchReconnectInterval, watchReconnectLimit, connectionTimeout, requestTimeout, + scaleTimeout, loggingInterval, maxConcurrentRequests, maxConcurrentRequestsPerHost, http2Disable, + httpProxy, httpsProxy, noProxy, userAgent, tlsVersions, websocketPingInterval, proxyUsername, proxyPassword, + trustStoreFile, trustStorePassphrase, keyStoreFile, keyStorePassphrase, impersonateUsername, impersonateGroups, + impersonateExtras, oauthTokenProvider, customHeaders, requestRetryBackoffLimit, requestRetryBackoffInterval, + uploadRequestTimeout, onlyHttpWatches, currentContext, contexts, false); + } - this.http2Disable = http2Disable; - this.httpProxy = httpProxy; - this.httpsProxy = httpsProxy; - this.noProxy = noProxy; - this.proxyUsername = proxyUsername; - this.proxyPassword = proxyPassword; - this.userAgent = userAgent; - this.tlsVersions = tlsVersions; - this.trustStoreFile = trustStoreFile; - this.trustStorePassphrase = trustStorePassphrase; - this.keyStoreFile = keyStoreFile; - this.keyStorePassphrase = keyStorePassphrase; - this.oauthTokenProvider = oauthTokenProvider; - this.customHeaders = customHeaders; + @Buildable(builderPackage = "io.fabric8.kubernetes.api.builder", editableEnabled = false) + public Config(String masterUrl, String apiVersion, String namespace, boolean trustCerts, boolean disableHostnameVerification, + String caCertFile, String caCertData, String clientCertFile, String clientCertData, String clientKeyFile, + String clientKeyData, String clientKeyAlgo, String clientKeyPassphrase, String username, String password, + String oauthToken, String autoOAuthToken, int watchReconnectInterval, int watchReconnectLimit, int connectionTimeout, + int requestTimeout, + long scaleTimeout, int loggingInterval, int maxConcurrentRequests, int maxConcurrentRequestsPerHost, + boolean http2Disable, String httpProxy, String httpsProxy, String[] noProxy, + String userAgent, TlsVersion[] tlsVersions, long websocketPingInterval, String proxyUsername, + String proxyPassword, String trustStoreFile, String trustStorePassphrase, String keyStoreFile, String keyStorePassphrase, + String impersonateUsername, String[] impersonateGroups, Map> impersonateExtras, + OAuthTokenProvider oauthTokenProvider, Map customHeaders, int requestRetryBackoffLimit, + int requestRetryBackoffInterval, int uploadRequestTimeout, boolean onlyHttpWatches, NamedContext currentContext, + List contexts, boolean autoConfigure) { + if (autoConfigure && !disableAutoConfig()) { + autoConfigure(this, null); + } + if (Utils.isNotNullOrEmpty(apiVersion)) { + this.apiVersion = apiVersion; + } + if (Utils.isNotNullOrEmpty(namespace)) { + this.namespace = namespace; + } + if (trustCerts) { + this.trustCerts = trustCerts; + } + if (disableHostnameVerification) { + this.disableHostnameVerification = disableHostnameVerification; + } + if (Utils.isNotNullOrEmpty(caCertData)) { + this.caCertData = caCertData; + } + if (Utils.isNotNullOrEmpty(caCertFile)) { + this.caCertFile = caCertFile; + } + if (Utils.isNotNullOrEmpty(clientCertFile)) { + this.clientCertFile = clientCertFile; + } + if (Utils.isNotNullOrEmpty(clientCertData)) { + this.clientCertData = clientCertData; + } + if (Utils.isNotNullOrEmpty(clientKeyFile)) { + this.clientKeyFile = clientKeyFile; + } + if (Utils.isNotNullOrEmpty(clientKeyData)) { + this.clientKeyData = clientKeyData; + } + if (Utils.isNotNullOrEmpty(clientKeyAlgo) && !clientKeyAlgo.equals("RSA")) { + this.clientKeyAlgo = clientKeyAlgo; + } + if (Utils.isNotNullOrEmpty(clientKeyPassphrase) && !clientKeyPassphrase.equals("changeit")) { + this.clientKeyPassphrase = clientKeyPassphrase; + } + if (Utils.isNotNullOrEmpty(username)) { + this.username = username; + } + if (Utils.isNotNullOrEmpty(password)) { + this.password = password; + } + if (Utils.isNotNullOrEmpty(oauthToken)) { + this.oauthToken = oauthToken; + } + if (websocketPingInterval != DEFAULT_WEBSOCKET_PING_INTERVAL) { + this.websocketPingInterval = websocketPingInterval; + } + if (connectionTimeout != DEFAULT_CONNECTION_TIMEOUT) { + this.connectionTimeout = connectionTimeout; + } + if (http2Disable) { + this.http2Disable = http2Disable; + } + if (Utils.isNotNullOrEmpty(httpProxy)) { + this.httpProxy = httpProxy; + } + if (Utils.isNotNullOrEmpty(httpsProxy)) { + this.httpsProxy = httpsProxy; + } + if (Utils.isNotNullOrEmpty(noProxy)) { + this.noProxy = noProxy; + } + if (Utils.isNotNullOrEmpty(proxyUsername)) { + this.proxyUsername = proxyUsername; + } + if (Utils.isNotNullOrEmpty(proxyPassword)) { + this.proxyPassword = proxyPassword; + } + if (Utils.isNotNullOrEmpty(userAgent)) { + this.userAgent = userAgent; + } + if (tlsVersions != null && tlsVersions.length > 0 && !Arrays.equals(tlsVersions, DEFAULT_TLS_VERSIONS_LIST)) { + this.tlsVersions = tlsVersions; + } + if (Utils.isNotNullOrEmpty(trustStoreFile)) { + this.trustStoreFile = trustStoreFile; + } + if (Utils.isNotNullOrEmpty(trustStorePassphrase)) { + this.trustStorePassphrase = trustStorePassphrase; + } + if (Utils.isNotNullOrEmpty(keyStoreFile)) { + this.keyStoreFile = keyStoreFile; + } + if (Utils.isNotNullOrEmpty(keyStorePassphrase)) { + this.keyStorePassphrase = keyStorePassphrase; + } + if (Utils.isNotNull(oauthTokenProvider)) { + this.oauthTokenProvider = oauthTokenProvider; + } + if (Utils.isNotNullOrEmpty(customHeaders)) { + this.customHeaders = customHeaders; + } //We need to keep this after ssl configuration & masterUrl //We set the masterUrl because it's needed by ensureHttps - this.masterUrl = masterUrl; - this.masterUrl = ensureEndsWithSlash(ensureHttps(masterUrl, this)); - this.maxConcurrentRequests = maxConcurrentRequests; - this.maxConcurrentRequestsPerHost = maxConcurrentRequestsPerHost; - this.autoOAuthToken = autoOAuthToken; - this.onlyHttpWatches = onlyHttpWatches; - this.contexts = contexts; - this.currentContext = currentContext; + if (Utils.isNotNullOrEmpty(masterUrl) && !masterUrl.equals(DEFAULT_MASTER_URL)) { + this.masterUrl = masterUrl; + this.masterUrl = ensureEndsWithSlash(ensureHttps(masterUrl, this)); + } + if (maxConcurrentRequests != DEFAULT_MAX_CONCURRENT_REQUESTS) { + this.maxConcurrentRequests = maxConcurrentRequests; + } + if (maxConcurrentRequestsPerHost != DEFAULT_MAX_CONCURRENT_REQUESTS_PER_HOST) { + this.maxConcurrentRequestsPerHost = maxConcurrentRequestsPerHost; + } + if (Utils.isNotNullOrEmpty(autoOAuthToken)) { + this.autoOAuthToken = autoOAuthToken; + } + if (onlyHttpWatches) { + this.onlyHttpWatches = onlyHttpWatches; + } + if (contexts != null && !contexts.isEmpty()) { + this.contexts = contexts; + } + if (currentContext != null) { + this.currentContext = currentContext; + } + + RequestConfig autoConfiguredRequestConfig = this.requestConfig; + this.requestConfig = mergeRequestConfig(new RequestConfig(watchReconnectLimit, watchReconnectInterval, + requestTimeout, scaleTimeout, loggingInterval, + requestRetryBackoffLimit, requestRetryBackoffInterval, uploadRequestTimeout), autoConfiguredRequestConfig); + this.requestConfig.setImpersonateUsername( + Optional.ofNullable(impersonateUsername).orElse(autoConfiguredRequestConfig.getImpersonateUsername())); + this.requestConfig.setImpersonateGroups( + Utils.isNotNullOrEmpty(impersonateGroups) ? impersonateGroups : autoConfiguredRequestConfig.getImpersonateGroups()); + this.requestConfig.setImpersonateExtras( + Utils.isNotNullOrEmpty(impersonateExtras) ? impersonateExtras : autoConfiguredRequestConfig.getImpersonateExtras()); + } + + private RequestConfig mergeRequestConfig(RequestConfig userProvidedRequestConfig, RequestConfig autoConfiguredRequestConfig) { + RequestConfigBuilder requestConfigBuilder = new RequestConfigBuilder(userProvidedRequestConfig); + if (userProvidedRequestConfig.getRequestTimeout() == DEFAULT_REQUEST_TIMEOUT + && autoConfiguredRequestConfig.getRequestTimeout() != DEFAULT_REQUEST_TIMEOUT) { + requestConfigBuilder.withRequestTimeout(autoConfiguredRequestConfig.getRequestTimeout()); + } + if (userProvidedRequestConfig.getWatchReconnectLimit() == -1 && autoConfiguredRequestConfig.getWatchReconnectLimit() > 0) { + requestConfigBuilder.withWatchReconnectLimit(autoConfiguredRequestConfig.getWatchReconnectLimit()); + } + if (userProvidedRequestConfig.getWatchReconnectInterval() == DEFAULT_WATCH_RECONNECT_INTERVAL + && autoConfiguredRequestConfig.getWatchReconnectInterval() != DEFAULT_WATCH_RECONNECT_INTERVAL) { + requestConfigBuilder.withWatchReconnectInterval(autoConfiguredRequestConfig.getWatchReconnectInterval()); + } + if (userProvidedRequestConfig.getRequestTimeout() == DEFAULT_REQUEST_TIMEOUT + && autoConfiguredRequestConfig.getRequestTimeout() != DEFAULT_REQUEST_TIMEOUT) { + requestConfigBuilder.withRequestTimeout(autoConfiguredRequestConfig.getRequestTimeout()); + } + if (userProvidedRequestConfig.getScaleTimeout() == DEFAULT_SCALE_TIMEOUT + && autoConfiguredRequestConfig.getScaleTimeout() != DEFAULT_SCALE_TIMEOUT) { + requestConfigBuilder.withScaleTimeout(autoConfiguredRequestConfig.getScaleTimeout()); + } + if (userProvidedRequestConfig.getRequestRetryBackoffLimit() == DEFAULT_REQUEST_RETRY_BACKOFFLIMIT + && autoConfiguredRequestConfig.getRequestRetryBackoffLimit() != DEFAULT_REQUEST_RETRY_BACKOFFLIMIT) { + requestConfigBuilder.withRequestRetryBackoffLimit(autoConfiguredRequestConfig.getRequestRetryBackoffLimit()); + } + if (userProvidedRequestConfig.getRequestRetryBackoffInterval() == DEFAULT_REQUEST_RETRY_BACKOFFINTERVAL + && autoConfiguredRequestConfig.getRequestRetryBackoffInterval() != DEFAULT_REQUEST_RETRY_BACKOFFINTERVAL) { + requestConfigBuilder.withRequestRetryBackoffInterval(autoConfiguredRequestConfig.getRequestRetryBackoffInterval()); + } + if (userProvidedRequestConfig.getUploadRequestTimeout() == DEFAULT_UPLOAD_REQUEST_TIMEOUT + && autoConfiguredRequestConfig.getRequestTimeout() != DEFAULT_UPLOAD_REQUEST_TIMEOUT) { + requestConfigBuilder.withUploadRequestTimeout(autoConfiguredRequestConfig.getUploadRequestTimeout()); + } + return requestConfigBuilder.build(); } public static void configFromSysPropsOrEnvVars(Config config) { diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java index 51ccf348c9d..163c7baf20d 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java @@ -17,11 +17,15 @@ import io.fabric8.kubernetes.api.model.ExecConfig; import io.fabric8.kubernetes.api.model.ExecConfigBuilder; +import io.fabric8.kubernetes.api.model.NamedContext; +import io.fabric8.kubernetes.api.model.NamedContextBuilder; import io.fabric8.kubernetes.client.http.TlsVersion; import io.fabric8.kubernetes.client.lib.FileSystem; import io.fabric8.kubernetes.client.utils.Utils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; @@ -136,48 +140,22 @@ public void tearDown() { } @Test + @DisplayName("Config loaded from system properties") void testWithSystemProperties() { - System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://somehost:80"); - System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "testns"); - - System.setProperty(Config.KUBERNETES_OAUTH_TOKEN_SYSTEM_PROPERTY, "token"); - System.setProperty(Config.KUBERNETES_AUTH_BASIC_USERNAME_SYSTEM_PROPERTY, "user"); - System.setProperty(Config.KUBERNETES_AUTH_BASIC_PASSWORD_SYSTEM_PROPERTY, "pass"); - System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); - System.setProperty(Config.KUBERNETES_DISABLE_HOSTNAME_VERIFICATION_SYSTEM_PROPERTY, "true"); - System.setProperty(Config.KUBERNETES_CA_CERTIFICATE_FILE_SYSTEM_PROPERTY, "/path/to/cert"); - System.setProperty(Config.KUBERNETES_CA_CERTIFICATE_DATA_SYSTEM_PROPERTY, "cacertdata"); - System.setProperty(Config.KUBERNETES_CLIENT_CERTIFICATE_FILE_SYSTEM_PROPERTY, "/path/to/clientcert"); - System.setProperty(Config.KUBERNETES_CLIENT_CERTIFICATE_DATA_SYSTEM_PROPERTY, "clientcertdata"); - System.setProperty(Config.KUBERNETES_CLIENT_KEY_FILE_SYSTEM_PROPERTY, "/path/to/clientkey"); - System.setProperty(Config.KUBERNETES_CLIENT_KEY_DATA_SYSTEM_PROPERTY, "clientkeydata"); - System.setProperty(Config.KUBERNETES_CLIENT_KEY_ALGO_SYSTEM_PROPERTY, "algo"); - System.setProperty(Config.KUBERNETES_CLIENT_KEY_PASSPHRASE_SYSTEM_PROPERTY, "passphrase"); - System.setProperty(Config.KUBERNETES_CLIENT_KEY_FILE_SYSTEM_PROPERTY, "/path/to/clientkey"); - System.setProperty(Config.KUBERNETES_MAX_CONCURRENT_REQUESTS, "120"); - System.setProperty(Config.KUBERNETES_MAX_CONCURRENT_REQUESTS_PER_HOST, "20"); - System.setProperty(Config.KUBERNETES_WATCH_RECONNECT_INTERVAL_SYSTEM_PROPERTY, "5000"); - System.setProperty(Config.KUBERNETES_WATCH_RECONNECT_LIMIT_SYSTEM_PROPERTY, "5"); - System.setProperty(Config.KUBERNETES_REQUEST_TIMEOUT_SYSTEM_PROPERTY, "5000"); - System.setProperty(Config.KUBERNETES_HTTP_PROXY, "httpProxy"); - - System.setProperty(Config.KUBERNETES_TLS_VERSIONS, "TLSv1.2,TLSv1.1"); - - System.setProperty(Config.KUBERNETES_TRUSTSTORE_FILE_PROPERTY, "/path/to/truststore"); - System.setProperty(Config.KUBERNETES_TRUSTSTORE_PASSPHRASE_PROPERTY, "truststorePassphrase"); - System.setProperty(Config.KUBERNETES_KEYSTORE_FILE_PROPERTY, "/path/to/keystore"); - System.setProperty(Config.KUBERNETES_KEYSTORE_PASSPHRASE_PROPERTY, "keystorePassphrase"); - - System.setProperty(Config.KUBERNETES_UPLOAD_REQUEST_TIMEOUT_SYSTEM_PROPERTY, "600000"); + // Given + setKubernetesClientConfigSystemProperties(); - Config config = new Config(); + // When + Config config = Config.autoConfigure(null); assertConfig(config, true); + // Then config = new ConfigBuilder().build(); assertConfig(config, true); } @Test + @DisplayName("Config created using ConfigBuilder") void testWithBuilder() { Config config = new ConfigBuilder() .withMasterUrl("http://somehost:80") @@ -208,12 +186,32 @@ void testWithBuilder() { .withTrustStorePassphrase("truststorePassphrase") .withKeyStoreFile("/path/to/keystore") .withKeyStorePassphrase("keystorePassphrase") + .withWebsocketPingInterval(1000L) + .withConnectionTimeout(1000) + .withScaleTimeout(1000L) + .withRequestRetryBackoffLimit(5) + .withRequestRetryBackoffInterval(10) + .withHttp2Disable(false) + .withHttpsProxy("httpsProxy") + .withNoProxy("no-proxy-url1.io", "no-proxy-url2.io") + .withProxyUsername("proxyUsername") + .withProxyPassword("proxyPassword") + .withAutoOAuthToken("sha256~auto-oauthtoken") + .withOnlyHttpWatches(true) + .withContexts(new NamedContextBuilder() + .withName("context1") + .build(), new NamedContextBuilder().withName("context2").build()) + .withCurrentContext(new NamedContextBuilder().withName("context1").build()) .build(); assertConfig(config, false); + assertEquals(Arrays.asList(new NamedContextBuilder().withName("context1").build(), + new NamedContextBuilder().withName("context2").build()), config.getContexts()); + assertEquals(new NamedContextBuilder().withName("context1").build(), config.getCurrentContext()); } @Test + @DisplayName("Config created using system properties and ConfigBuilder") void testWithBuilderAndSystemProperties() { System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://tobeoverriden:80"); System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "tobeoverriden"); @@ -247,6 +245,13 @@ void testWithBuilderAndSystemProperties() { System.setProperty(Config.KUBERNETES_KEYSTORE_PASSPHRASE_PROPERTY, "keystorePassphrase"); System.setProperty(Config.KUBERNETES_UPLOAD_REQUEST_TIMEOUT_SYSTEM_PROPERTY, "600000"); + System.setProperty(Config.KUBERNETES_WEBSOCKET_PING_INTERVAL_SYSTEM_PROPERTY, "1000"); + System.setProperty(Config.KUBERNETES_CONNECTION_TIMEOUT_SYSTEM_PROPERTY, "1000"); + System.setProperty(Config.KUBERNETES_SCALE_TIMEOUT_SYSTEM_PROPERTY, "1000"); + System.setProperty(Config.KUBERNETES_HTTPS_PROXY, "httpsProxy"); + System.setProperty(Config.KUBERNETES_NO_PROXY, "no-proxy-url1.io,no-proxy-url2.io"); + System.setProperty(Config.KUBERNETES_PROXY_USERNAME, "proxyUsername"); + System.setProperty(Config.KUBERNETES_PROXY_PASSWORD, "proxyPassword"); Config config = new ConfigBuilder() .withMasterUrl("http://somehost:80") @@ -296,7 +301,7 @@ void testMasterUrlWithServiceAccountIPv6() { @Test void testWithKubeConfig() { System.setProperty(Config.KUBERNETES_KUBECONFIG_FILE, TEST_KUBECONFIG_FILE); - Config config = new Config(); + Config config = Config.autoConfigure(null); assertNotNull(config); assertEquals("https://172.28.128.4:8443/", config.getMasterUrl()); @@ -339,7 +344,7 @@ void testWithKubeConfigAndSystemProperties() { System.setProperty(Config.KUBERNETES_KUBECONFIG_FILE, TEST_KUBECONFIG_FILE); System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://somehost:80"); - Config config = new Config(); + Config config = Config.autoConfigure(null); assertNotNull(config); assertEquals("http://somehost:80/", config.getMasterUrl()); assertEquals("testns", config.getNamespace()); @@ -388,7 +393,7 @@ void testWithNamespacePath() { System.setProperty(Config.KUBERNETES_NAMESPACE_FILE, TEST_NAMESPACE_FILE); System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://somehost:80"); - Config config = new Config(); + Config config = Config.autoConfigure(null); assertNotNull(config); assertEquals("http://somehost:80/", config.getMasterUrl()); assertEquals("testnsfrompath", config.getNamespace()); @@ -400,7 +405,7 @@ void testWithNonExistingNamespacePath() { System.setProperty(Config.KUBERNETES_NAMESPACE_FILE, "nonamespace"); System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://somehost:80"); - Config config = new Config(); + Config config = Config.autoConfigure(null); assertNotNull(config); assertEquals("http://somehost:80/", config.getMasterUrl()); assertNull(config.getNamespace()); @@ -412,7 +417,7 @@ void testWithNamespacePathAndSystemProperties() { System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://somehost:80"); System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "testns"); - Config config = new Config(); + Config config = Config.autoConfigure(null); assertNotNull(config); assertEquals("http://somehost:80/", config.getMasterUrl()); assertEquals("testns", config.getNamespace()); @@ -421,7 +426,7 @@ void testWithNamespacePathAndSystemProperties() { @Test void testWithKubeConfigAndNoContext() { System.setProperty(Config.KUBERNETES_KUBECONFIG_FILE, TEST_KUBECONFIG_NO_CURRENT_CONTEXT_FILE); - Config config = new Config(); + Config config = Config.autoConfigure(null); assertNotNull(config); assertNull(config.getCurrentContext()); @@ -653,6 +658,173 @@ void testEmptyConfig() { .satisfies(e -> assertThat(e.getUserAgent()).isNotNull()); } + @Nested + @DisplayName("autoConfigure disabled") + class NoAutoConfiguration { + @Nested + @DisplayName("system properties present should be ignored with auto configuration disabled") + class SystemPropertiesConfigured extends AutoConfiguredDisabledScenarios { + @BeforeEach + void setUp() { + setKubernetesClientConfigSystemProperties(); + } + } + + @Nested + @DisplayName("kubeconfig present should be ignored with auto configuration disabled") + class KubeConfigPresent extends AutoConfiguredDisabledScenarios { + @BeforeEach + void setUp() throws IOException { + if (FileSystem.getCurrent() == FileSystem.WINDOWS) { + System.setProperty(Config.KUBERNETES_KUBECONFIG_FILE, TEST_KUBECONFIG_EXEC_FILE_WIN_NULL_ARGS); + } else { + Files.setPosixFilePermissions(Paths.get(TEST_TOKEN_GENERATOR_FILE), PosixFilePermissions.fromString("rwxrwxr-x")); + System.setProperty(Config.KUBERNETES_KUBECONFIG_FILE, TEST_KUBECONFIG_EXEC_FILE_NULL_ARGS); + } + } + + @AfterEach + void tearDown() { + System.clearProperty(Config.KUBERNETES_KUBECONFIG_FILE); + } + } + } + + private abstract static class AutoConfiguredDisabledScenarios { + private final NamedContext userConfiguredNamedContext = new NamedContextBuilder() + .withName("context1") + .withNewContext() + .withCluster("api-test-openshiftapps-com:6443") + .withUser("testuser/api-test-openshiftapps-com:6443") + .endContext() + .build(); + + @Nested + @DisplayName("kubernetes.disable.autoConfig=true") + class AutoConfigDisabledViaProperty { + @BeforeEach + void setUp() { + System.setProperty(Config.KUBERNETES_DISABLE_AUTO_CONFIG_SYSTEM_PROPERTY, "true"); + } + + @Test + @DisplayName("no user configuration, the use default values") + void whenConfigDisabledViaPropertyAndUserProvidesNoConfiguration_thenUseConfigDefaultValues() { + assertConfigUsesDefaultValues(new ConfigBuilder().build()); + } + + @Test + @DisplayName("user configuration via builder, the user configuration used") + void whenConfigDisabledViaPropertyAndUserProvidesConfigurationViaConfigBuilder_thenUseConfigDefaultValues() { + assertConfigUsesUserProvidedValues(createConfigBuilderWithUserConfiguration().build()); + } + + @AfterEach + void tearDown() { + System.clearProperty(Config.KUBERNETES_DISABLE_AUTO_CONFIG_SYSTEM_PROPERTY); + } + } + + @Test + @DisplayName("no value configured via builder, should use default values") + void noneConfiguredViaBuilder_shouldUseConfigDefaultValues() { + assertConfigUsesDefaultValues(new ConfigBuilder().withAutoConfigure(false).build()); + } + + @Test + @DisplayName("fields configured via ConfigBuilder, should get most precedence") + void userProvidedFieldsInConfigBuilderShouldGetMostPrecedence() { + // Given + When + Config configWithoutAutoConfigure = createConfigBuilderWithUserConfiguration() + .withAutoConfigure(false) + .build(); + + // Then + assertConfigUsesUserProvidedValues(configWithoutAutoConfigure); + } + + private io.fabric8.kubernetes.client.ConfigBuilder createConfigBuilderWithUserConfiguration() { + return new ConfigBuilder() + .withMasterUrl("https://api-test.openshiftapps.com:6443") + .withContexts(userConfiguredNamedContext) + .withCurrentContext(userConfiguredNamedContext) + .withMaxConcurrentRequests(30) + .withMaxConcurrentRequestsPerHost(10) + .withTrustCerts() + .withDisableHostnameVerification() + .withClientKeyAlgo("EC") + .withWatchReconnectInterval(500) + .withWatchReconnectLimit(10) + .withConnectionTimeout(1000) + .withRequestTimeout(1000) + .withScaleTimeout(1000) + .withLoggingInterval(1000) + .withWebsocketPingInterval(10000L) + .withUploadRequestTimeout(1000) + .withImpersonateExtras(Collections.singletonMap("acme%2Fproject", Collections.singletonList("some-project"))) + .withHttp2Disable(true) + .withTlsVersions(new TlsVersion[] { TlsVersion.TLS_1_3 }) + .withCurrentContext(userConfiguredNamedContext) + .withImpersonateGroups("developer", "admin") + .withUserAgent("custom-user-agent"); + } + + void assertConfigUsesDefaultValues(Config configWithoutAutoConfigure) { + assertThat(configWithoutAutoConfigure) + .hasFieldOrPropertyWithValue("autoConfigure", false) + .hasFieldOrPropertyWithValue("masterUrl", "https://kubernetes.default.svc") + .hasFieldOrPropertyWithValue("contexts", Collections.emptyList()) + .hasFieldOrPropertyWithValue("maxConcurrentRequests", 64) + .hasFieldOrPropertyWithValue("maxConcurrentRequestsPerHost", 5) + .hasFieldOrPropertyWithValue("trustCerts", false) + .hasFieldOrPropertyWithValue("disableHostnameVerification", false) + .hasFieldOrPropertyWithValue("clientKeyAlgo", "RSA") + .hasFieldOrPropertyWithValue("clientKeyPassphrase", "changeit") + .hasFieldOrPropertyWithValue("watchReconnectInterval", 1000) + .hasFieldOrPropertyWithValue("watchReconnectLimit", -1) + .hasFieldOrPropertyWithValue("connectionTimeout", 10000) + .hasFieldOrPropertyWithValue("requestTimeout", 10000) + .hasFieldOrPropertyWithValue("scaleTimeout", 600000L) + .hasFieldOrPropertyWithValue("loggingInterval", 20000) + .hasFieldOrPropertyWithValue("websocketPingInterval", 30000L) + .hasFieldOrPropertyWithValue("uploadRequestTimeout", 120000) + .hasFieldOrPropertyWithValue("impersonateExtras", Collections.emptyMap()) + .hasFieldOrPropertyWithValue("http2Disable", false) + .hasFieldOrPropertyWithValue("tlsVersions", new TlsVersion[] { TlsVersion.TLS_1_3, TlsVersion.TLS_1_2 }) + .satisfies(e -> assertThat(e.getCurrentContext()).isNull()) + .satisfies(e -> assertThat(e.getImpersonateGroups()).isEmpty()) + .satisfies(e -> assertThat(e.getUserAgent()).isNotNull()); + } + + void assertConfigUsesUserProvidedValues(Config config) { + assertThat(config) + .hasFieldOrPropertyWithValue("autoConfigure", false) + .hasFieldOrPropertyWithValue("masterUrl", "https://api-test.openshiftapps.com:6443/") + .hasFieldOrPropertyWithValue("contexts", Collections.singletonList(userConfiguredNamedContext)) + .hasFieldOrPropertyWithValue("maxConcurrentRequests", 30) + .hasFieldOrPropertyWithValue("maxConcurrentRequestsPerHost", 10) + .hasFieldOrPropertyWithValue("trustCerts", true) + .hasFieldOrPropertyWithValue("disableHostnameVerification", true) + .hasFieldOrPropertyWithValue("clientKeyAlgo", "EC") + .hasFieldOrPropertyWithValue("clientKeyPassphrase", "changeit") + .hasFieldOrPropertyWithValue("watchReconnectInterval", 500) + .hasFieldOrPropertyWithValue("watchReconnectLimit", 10) + .hasFieldOrPropertyWithValue("connectionTimeout", 1000) + .hasFieldOrPropertyWithValue("requestTimeout", 1000) + .hasFieldOrPropertyWithValue("scaleTimeout", 1000L) + .hasFieldOrPropertyWithValue("loggingInterval", 1000) + .hasFieldOrPropertyWithValue("websocketPingInterval", 10000L) + .hasFieldOrPropertyWithValue("uploadRequestTimeout", 1000) + .hasFieldOrPropertyWithValue("impersonateExtras", + Collections.singletonMap("acme%2Fproject", Collections.singletonList("some-project"))) + .hasFieldOrPropertyWithValue("http2Disable", true) + .hasFieldOrPropertyWithValue("tlsVersions", new TlsVersion[] { TlsVersion.TLS_1_3 }) + .satisfies(e -> assertThat(e.getCurrentContext()).isEqualTo(userConfiguredNamedContext)) + .satisfies(e -> assertThat(e.getImpersonateGroups()).containsExactly("developer", "admin")) + .satisfies(e -> assertThat(e.getUserAgent()).isEqualTo("custom-user-agent")); + } + } + private void assertConfig(Config config, boolean autoToken) { assertNotNull(config); assertTrue(config.isTrustCerts()); @@ -677,6 +849,7 @@ private void assertConfig(Config config, boolean autoToken) { assertEquals("passphrase", config.getClientKeyPassphrase()); assertEquals("httpProxy", config.getHttpProxy()); + assertEquals("httpsProxy", config.getHttpsProxy()); assertEquals(5000, config.getWatchReconnectInterval()); assertEquals(5, config.getWatchReconnectLimit()); @@ -692,6 +865,15 @@ private void assertConfig(Config config, boolean autoToken) { assertEquals(120, config.getMaxConcurrentRequests()); assertEquals(20, config.getMaxConcurrentRequestsPerHost()); + assertEquals(1000, config.getWebsocketPingInterval()); + assertEquals(1000L, config.getConnectionTimeout()); + assertEquals(1000, config.getScaleTimeout()); + assertEquals(5000, config.getWatchReconnectInterval()); + assertFalse(config.isHttp2Disable()); + assertEquals("httpsProxy", config.getHttpsProxy()); + assertEquals("proxyUsername", config.getProxyUsername()); + assertEquals("proxyPassword", config.getProxyPassword()); + assertArrayEquals(new String[] { "no-proxy-url1.io", "no-proxy-url2.io" }, config.getNoProxy()); } @Test @@ -883,4 +1065,39 @@ void givenEmptyKubeConfig_whenConfigCreated_thenShouldNotProduceNPE() throws URI // Then assertThat(config).isNotNull(); } + + private static void setKubernetesClientConfigSystemProperties() { + System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://somehost:80"); + System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "testns"); + + System.setProperty(Config.KUBERNETES_OAUTH_TOKEN_SYSTEM_PROPERTY, "token"); + System.setProperty(Config.KUBERNETES_AUTH_BASIC_USERNAME_SYSTEM_PROPERTY, "user"); + System.setProperty(Config.KUBERNETES_AUTH_BASIC_PASSWORD_SYSTEM_PROPERTY, "pass"); + System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); + System.setProperty(Config.KUBERNETES_DISABLE_HOSTNAME_VERIFICATION_SYSTEM_PROPERTY, "true"); + System.setProperty(Config.KUBERNETES_CA_CERTIFICATE_FILE_SYSTEM_PROPERTY, "/path/to/cert"); + System.setProperty(Config.KUBERNETES_CA_CERTIFICATE_DATA_SYSTEM_PROPERTY, "cacertdata"); + System.setProperty(Config.KUBERNETES_CLIENT_CERTIFICATE_FILE_SYSTEM_PROPERTY, "/path/to/clientcert"); + System.setProperty(Config.KUBERNETES_CLIENT_CERTIFICATE_DATA_SYSTEM_PROPERTY, "clientcertdata"); + System.setProperty(Config.KUBERNETES_CLIENT_KEY_FILE_SYSTEM_PROPERTY, "/path/to/clientkey"); + System.setProperty(Config.KUBERNETES_CLIENT_KEY_DATA_SYSTEM_PROPERTY, "clientkeydata"); + System.setProperty(Config.KUBERNETES_CLIENT_KEY_ALGO_SYSTEM_PROPERTY, "algo"); + System.setProperty(Config.KUBERNETES_CLIENT_KEY_PASSPHRASE_SYSTEM_PROPERTY, "passphrase"); + System.setProperty(Config.KUBERNETES_CLIENT_KEY_FILE_SYSTEM_PROPERTY, "/path/to/clientkey"); + System.setProperty(Config.KUBERNETES_MAX_CONCURRENT_REQUESTS, "120"); + System.setProperty(Config.KUBERNETES_MAX_CONCURRENT_REQUESTS_PER_HOST, "20"); + System.setProperty(Config.KUBERNETES_WATCH_RECONNECT_INTERVAL_SYSTEM_PROPERTY, "5000"); + System.setProperty(Config.KUBERNETES_WATCH_RECONNECT_LIMIT_SYSTEM_PROPERTY, "5"); + System.setProperty(Config.KUBERNETES_REQUEST_TIMEOUT_SYSTEM_PROPERTY, "5000"); + System.setProperty(Config.KUBERNETES_HTTP_PROXY, "httpProxy"); + + System.setProperty(Config.KUBERNETES_TLS_VERSIONS, "TLSv1.2,TLSv1.1"); + + System.setProperty(Config.KUBERNETES_TRUSTSTORE_FILE_PROPERTY, "/path/to/truststore"); + System.setProperty(Config.KUBERNETES_TRUSTSTORE_PASSPHRASE_PROPERTY, "truststorePassphrase"); + System.setProperty(Config.KUBERNETES_KEYSTORE_FILE_PROPERTY, "/path/to/keystore"); + System.setProperty(Config.KUBERNETES_KEYSTORE_PASSPHRASE_PROPERTY, "keystorePassphrase"); + + System.setProperty(Config.KUBERNETES_UPLOAD_REQUEST_TIMEOUT_SYSTEM_PROPERTY, "600000"); + } } diff --git a/kubernetes-itests/src/test/java/io/fabric8/kubernetes/DisabledAutoConfigurationIT.java b/kubernetes-itests/src/test/java/io/fabric8/kubernetes/DisabledAutoConfigurationIT.java new file mode 100644 index 00000000000..6f748b13299 --- /dev/null +++ b/kubernetes-itests/src/test/java/io/fabric8/kubernetes/DisabledAutoConfigurationIT.java @@ -0,0 +1,62 @@ +/* + * 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. + */ +package io.fabric8.kubernetes; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.ConfigBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.fabric8.kubernetes.client.KubernetesClientException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; + +class DisabledAutoConfigurationIT { + KubernetesClient client; + + @Test + @DisplayName("with autoConfigure=false, then client should not load kubeconfig contents") + void givenConfigWithAutoConfigureDisabled_shouldNotLoadLocalKubeConfig() { + client = new KubernetesClientBuilder().withConfig(new ConfigBuilder() + .withAutoConfigure(false) + .withRequestRetryBackoffLimit(0) + .build()).build(); + + assertThatExceptionOfType(KubernetesClientException.class) + .isThrownBy(() -> client.pods().list()) + .withMessageContaining("Operation: [list] for kind: [Pod] with name: [null] in namespace: [null] failed."); + } + + @Test + @DisplayName("kubernetes.disable.autoConfig=true, then client should not load kubeconfig contents") + void givenDisableAutoConfigPropertyTrue_shouldNotLoadLocalKubeConfig() { + try { + // Given + System.setProperty(Config.KUBERNETES_DISABLE_AUTO_CONFIG_SYSTEM_PROPERTY, "true"); + client = new KubernetesClientBuilder().withConfig(new ConfigBuilder() + .withRequestRetryBackoffLimit(0) + .build()).build(); + + // When + Then + assertThatExceptionOfType(KubernetesClientException.class) + .isThrownBy(() -> client.pods().list()) + .withMessageContaining("Operation: [list] for kind: [Pod] with name: [null] in namespace: [null] failed."); + } finally { + System.clearProperty(Config.KUBERNETES_DISABLE_AUTO_CONFIG_SYSTEM_PROPERTY); + } + } +} diff --git a/openshift-client-api/src/main/java/io/fabric8/openshift/client/OpenShiftConfig.java b/openshift-client-api/src/main/java/io/fabric8/openshift/client/OpenShiftConfig.java index a7562ad5683..6e447d1a378 100644 --- a/openshift-client-api/src/main/java/io/fabric8/openshift/client/OpenShiftConfig.java +++ b/openshift-client-api/src/main/java/io/fabric8/openshift/client/OpenShiftConfig.java @@ -88,7 +88,7 @@ public OpenShiftConfig(String openShiftUrl, String oapiVersion, String masterUrl String[] impersonateGroups, Map> impersonateExtras, OAuthTokenProvider oauthTokenProvider, Map customHeaders, int requestRetryBackoffLimit, int requestRetryBackoffInterval, int uploadRequestTimeout, boolean onlyHttpWatches, long buildTimeout, - boolean disableApiGroupCheck, NamedContext currentContext, List contexts) { + boolean disableApiGroupCheck, NamedContext currentContext, List contexts, boolean autoConfigure) { super(masterUrl, apiVersion, namespace, trustCerts, disableHostnameVerification, caCertFile, caCertData, clientCertFile, clientCertData, clientKeyFile, clientKeyData, clientKeyAlgo, clientKeyPassphrase, username, password, @@ -100,7 +100,7 @@ public OpenShiftConfig(String openShiftUrl, String oapiVersion, String masterUrl impersonateExtras, oauthTokenProvider, customHeaders, requestRetryBackoffLimit, requestRetryBackoffInterval, - uploadRequestTimeout, onlyHttpWatches, currentContext, contexts); + uploadRequestTimeout, onlyHttpWatches, currentContext, contexts, autoConfigure); this.setOapiVersion(oapiVersion); this.setBuildTimeout(buildTimeout); this.setDisableApiGroupCheck(disableApiGroupCheck); @@ -144,7 +144,8 @@ public OpenShiftConfig(Config kubernetesConfig, String openShiftUrl, String oapi buildTimeout, false, kubernetesConfig.getCurrentContext(), - kubernetesConfig.getContexts()); + kubernetesConfig.getContexts(), + kubernetesConfig.getAutoConfigure()); } public static OpenShiftConfig wrap(Config config) {