From 629cc757162df4a82c0bc30970e0596dab0e5eeb Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 3 Aug 2021 10:23:34 +0300 Subject: [PATCH 1/4] fix: If internal links are not specified, the external one should be used (#45) * Don't set public url value to internal url settings. Signed-off-by: Oleksandr Andriienko --- .../webapp/WEB-INF/classes/che/che.properties | 11 ++++--- .../WEB-INF/classes/che/multiuser.properties | 2 +- .../helm/che/templates/configmap.yaml | 16 +++------- ...ubernetesCheApiExternalEnvVarProvider.java | 2 +- ...ubernetesCheApiInternalEnvVarProvider.java | 8 ++++- .../BrokerEnvironmentFactory.java | 12 ++++++-- .../KubernetesBrokerEnvironmentFactory.java | 6 ++-- .../BrokerEnvironmentFactoryTest.java | 1 + .../OpenshiftBrokerEnvironmentFactory.java | 2 ++ .../keycloak/server/KeycloakJwkProvider.java | 7 ++++- .../server/KeycloakProfileRetriever.java | 7 ++++- .../multiuser/keycloak/server/OIDCInfo.java | 30 ++++++++----------- .../keycloak/server/OIDCInfoProvider.java | 10 +++---- .../keycloak/server/OIDCInfoProviderTest.java | 21 ++++++------- .../provision/env/CheApiEnvVarProvider.java | 13 ++++++-- 15 files changed, 84 insertions(+), 64 deletions(-) diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties index c539aa30547..e602f03989b 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties @@ -22,12 +22,15 @@ che.database=${che.home}/storage che.api=http://${CHE_HOST}:${CHE_PORT}/api # API service internal network URL. Back-end services should initiate REST communications to {prod-short} server with this URL -che.api.internal=http://${CHE_HOST}:${CHE_PORT}/api +che.api.internal=NULL # {prod-short} WebSocket major endpoint. Provides basic communication endpoint # for major WebSocket interactions and messaging. che.websocket.endpoint=ws://${CHE_HOST}:${CHE_PORT}/api/websocket +# {prod-short} WebSocket major internal endpoint. Provides basic communication endpoint +# for major WebSocket interactions and messaging. +che.websocket.internal.endpoint=NULL # Your projects are synchronized from the {prod-short} server into the machine running each # workspace. This is the directory in the machine where your projects are placed. @@ -742,9 +745,9 @@ che.infra.kubernetes.async.storage.image=quay.io/eclipse/che-workspace-data-sync # key=value pairs, for example: `disktype=ssd,cpu=xlarge,foo=bar` che.workspace.pod.node_selector=NULL -# Optionally configures tolerations for workspace Pod. Format is a string representing a JSON Array of taint tolerations, -# or `NULL` to disable it. The objects contained in the array have to follow the -# link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core[toleration v1 core specifications]. +# Optionally configures tolerations for workspace Pod. Format is a string representing a JSON Array of taint tolerations, +# or `NULL` to disable it. The objects contained in the array have to follow the +# link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core[toleration v1 core specifications]. # Example: `[{"effect":"NoExecute","key":"aNodeTaint","operator":"Equal","value":"aValue"}]` che.workspace.pod.tolerations_json=NULL diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties index d3cf3598e49..b88cff9ffb8 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties @@ -110,7 +110,7 @@ che.keycloak.realm=che # Keycloak client identifier in `che.keycloak.realm` to authenticate users in the dashboard, the IDE, and the CLI. che.keycloak.client_id=che-public - + # URL to access OSO OAuth tokens che.keycloak.oso.endpoint=NULL diff --git a/deploy/kubernetes/helm/che/templates/configmap.yaml b/deploy/kubernetes/helm/che/templates/configmap.yaml index 5ac63297adb..7f278d8817b 100644 --- a/deploy/kubernetes/helm/che/templates/configmap.yaml +++ b/deploy/kubernetes/helm/che/templates/configmap.yaml @@ -21,8 +21,6 @@ data: CHE_API: https://{{ template "cheHost" . }}/api {{- if .Values.global.useInternalClusterSVCNames }} CHE_API_INTERNAL: http://che-host.{{ .Release.Namespace }}.svc:8080/api - {{- else }} - CHE_API_INTERNAL: https://{{ template "cheHost" . }}/api {{- end }} CHE_WEBSOCKET_ENDPOINT: wss://{{ template "cheHost" . }}/api/websocket CHE_INFRA_KUBERNETES_BOOTSTRAPPER_BINARY__URL: https://{{ template "cheHost" . }}/agent-binaries/linux_amd64/bootstrapper/bootstrapper @@ -30,12 +28,14 @@ data: CHE_API: http://{{ template "cheHost" . }}/api {{- if .Values.global.useInternalClusterSVCNames }} CHE_API_INTERNAL: http://che-host.{{ .Release.Namespace }}.svc:8080/api - {{- else }} - CHE_API_INTERNAL: http://{{ template "cheHost" . }}/api {{- end }} CHE_WEBSOCKET_ENDPOINT: ws://{{ template "cheHost" . }}/api/websocket CHE_WEBSOCKET_ENDPOINT__MINOR: ws://{{ template "cheHost" . }}/api/websocket-minor CHE_INFRA_KUBERNETES_BOOTSTRAPPER_BINARY__URL: http://{{ template "cheHost" . }}/agent-binaries/linux_amd64/bootstrapper/bootstrapper +{{- end }} +{{- if .Values.global.useInternalClusterSVCNames }} + CHE_WEBSOCKET_INTERNAL_ENDPOINT: ws://che-host.{{ .Release.Namespace }}.svc:8080/api/websocket + CHE_WEBSOCKET_INTERNAL_ENDPOINT__MINOR: ws://che-host.{{ .Release.Namespace }}.svc:8080/api/websocket-minor {{- end }} CHE_DEBUG_SERVER: "true" CHE_INFRASTRUCTURE_ACTIVE: "kubernetes" @@ -54,8 +54,6 @@ data: CHE_KEYCLOAK_AUTH__SERVER__URL: {{ template "keycloakAuthUrl" . }} {{- if .Values.global.useInternalClusterSVCNames }} CHE_KEYCLOAK_AUTH__INTERNAL__SERVER__URL: http://keycloak.{{ .Release.Namespace }}.svc:5050/auth - {{- else }} - CHE_KEYCLOAK_AUTH__INTERNAL__SERVER__URL: {{ template "keycloakAuthUrl" . }} {{- end }} CHE_KEYCLOAK_REALM: {{ .Values.cheKeycloakRealm }} {{- end }} @@ -121,24 +119,18 @@ data: {{- end }} {{- if .Values.che.workspace.devfileRegistryUrl }} CHE_WORKSPACE_DEVFILE__REGISTRY__URL: {{ .Values.che.workspace.devfileRegistryUrl | quote }} - CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL: {{ .Values.che.workspace.devfileRegistryUrl | quote }} {{- else if .Values.cheDevfileRegistry.deploy }} CHE_WORKSPACE_DEVFILE__REGISTRY__URL: {{ template "devfileRegistryUrl" . }} {{- if .Values.global.useInternalClusterSVCNames }} CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL: http://devfile-registry.{{ .Release.Namespace }}.svc:8080 - {{- else }} - CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL: {{ template "devfileRegistryUrl" . }} {{- end }} {{- end }} {{- if .Values.che.workspace.pluginRegistryUrl }} CHE_WORKSPACE_PLUGIN__REGISTRY__URL: {{ .Values.che.workspace.pluginRegistryUrl | quote }} - CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL: {{ .Values.che.workspace.pluginRegistryUrl | quote }} {{- else if .Values.chePluginRegistry.deploy }} CHE_WORKSPACE_PLUGIN__REGISTRY__URL: {{ template "pluginRegistryUrl" . }} {{- if .Values.global.useInternalClusterSVCNames }} CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL: http://plugin-registry.{{ .Release.Namespace }}.svc:8080/v3 - {{- else }} - CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL: {{ template "pluginRegistryUrl" . }} {{- end }} {{- end }} {{- if .Values.che.workspace.pluginBroker }} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/KubernetesCheApiExternalEnvVarProvider.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/KubernetesCheApiExternalEnvVarProvider.java index b414ae5b29a..7e9edaf5133 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/KubernetesCheApiExternalEnvVarProvider.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/KubernetesCheApiExternalEnvVarProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018 Red Hat, Inc. + * Copyright (c) 2012-2021 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/ diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/KubernetesCheApiInternalEnvVarProvider.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/KubernetesCheApiInternalEnvVarProvider.java index 63ee78858d2..db2e6ab3d31 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/KubernetesCheApiInternalEnvVarProvider.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/KubernetesCheApiInternalEnvVarProvider.java @@ -11,11 +11,14 @@ */ package org.eclipse.che.workspace.infrastructure.kubernetes.provision; +import static com.google.common.base.Strings.isNullOrEmpty; + import javax.inject.Inject; import javax.inject.Named; import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.api.workspace.server.spi.provision.env.CheApiInternalEnvVarProvider; +import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.lang.Pair; /** @@ -29,12 +32,15 @@ public class KubernetesCheApiInternalEnvVarProvider implements CheApiInternalEnv @Inject public KubernetesCheApiInternalEnvVarProvider( - @Named("che.api.internal") String cheServerEndpoint) { + @Nullable @Named("che.api.internal") String cheServerEndpoint) { this.cheServerEndpoint = cheServerEndpoint; } @Override public Pair get(RuntimeIdentity runtimeIdentity) throws InfrastructureException { + if (isNullOrEmpty(this.cheServerEndpoint)) { + return null; + } return Pair.of(CHE_API_INTERNAL_VARIABLE, cheServerEndpoint); } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/BrokerEnvironmentFactory.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/BrokerEnvironmentFactory.java index e04a1b57cfc..83720f5bd33 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/BrokerEnvironmentFactory.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/BrokerEnvironmentFactory.java @@ -85,24 +85,30 @@ public abstract class BrokerEnvironmentFactory public BrokerEnvironmentFactory( String cheWebsocketEndpoint, + String cheWebsocketInternalEndpoint, String brokerPullPolicy, AgentAuthEnableEnvVarProvider authEnableEnvVarProvider, MachineTokenEnvVarProvider machineTokenEnvVarProvider, String artifactsBrokerImage, String metadataBrokerImage, - String pluginRegistryUrl, + String pluginRegistryExternalUrl, String pluginRegistryInternalUrl, TrustedCAProvisioner trustedCAProvisioner, String certificateMountPath, CertificateProvisioner certProvisioner) { - this.cheWebsocketEndpoint = cheWebsocketEndpoint; + this.cheWebsocketEndpoint = + isNullOrEmpty(cheWebsocketInternalEndpoint) + ? cheWebsocketEndpoint + : cheWebsocketInternalEndpoint; this.brokerPullPolicy = brokerPullPolicy; this.authEnableEnvVarProvider = authEnableEnvVarProvider; this.machineTokenEnvVarProvider = machineTokenEnvVarProvider; this.artifactsBrokerImage = artifactsBrokerImage; this.metadataBrokerImage = metadataBrokerImage; this.pluginRegistryUrl = - isNullOrEmpty(pluginRegistryInternalUrl) ? pluginRegistryUrl : pluginRegistryInternalUrl; + isNullOrEmpty(pluginRegistryInternalUrl) + ? pluginRegistryExternalUrl + : pluginRegistryInternalUrl; this.trustedCAProvisioner = trustedCAProvisioner; this.certificateMountPath = certificateMountPath; this.certProvisioner = certProvisioner; diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/KubernetesBrokerEnvironmentFactory.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/KubernetesBrokerEnvironmentFactory.java index fd983315c5b..53852f8e9e9 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/KubernetesBrokerEnvironmentFactory.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/KubernetesBrokerEnvironmentFactory.java @@ -35,25 +35,27 @@ public class KubernetesBrokerEnvironmentFactory @Inject public KubernetesBrokerEnvironmentFactory( @Named("che.websocket.endpoint") String cheWebsocketEndpoint, + @Nullable @Named("che.websocket.internal.endpoint") String cheWebsocketInternalEndpoint, @Named("che.workspace.plugin_broker.pull_policy") String brokerPullPolicy, @Named("che.infra.kubernetes.trusted_ca.mount_path") String certificateMountPath, AgentAuthEnableEnvVarProvider authEnableEnvVarProvider, MachineTokenEnvVarProvider machineTokenEnvVarProvider, @Named("che.workspace.plugin_broker.artifacts.image") String artifactsBrokerImage, @Named("che.workspace.plugin_broker.metadata.image") String metadataBrokerImage, - @Nullable @Named("che.workspace.plugin_registry_url") String pluginRegistryUrl, + @Nullable @Named("che.workspace.plugin_registry_url") String pluginRegistryExternalUrl, @Nullable @Named("che.workspace.plugin_registry_internal_url") String pluginRegistryInternalUrl, KubernetesTrustedCAProvisioner trustedCAProvisioner, CertificateProvisioner certProvisioner) { super( cheWebsocketEndpoint, + cheWebsocketInternalEndpoint, brokerPullPolicy, authEnableEnvVarProvider, machineTokenEnvVarProvider, artifactsBrokerImage, metadataBrokerImage, - pluginRegistryUrl, + pluginRegistryExternalUrl, pluginRegistryInternalUrl, trustedCAProvisioner, certificateMountPath, diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/BrokerEnvironmentFactoryTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/BrokerEnvironmentFactoryTest.java index 4816d440802..7daae2d7337 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/BrokerEnvironmentFactoryTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/BrokerEnvironmentFactoryTest.java @@ -72,6 +72,7 @@ public void setUp() throws Exception { spy( new BrokerEnvironmentFactory( PUSH_ENDPOINT, + null, IMAGE_PULL_POLICY, authEnableEnvVarProvider, machineTokenEnvVarProvider, diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/wsplugins/brokerphases/OpenshiftBrokerEnvironmentFactory.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/wsplugins/brokerphases/OpenshiftBrokerEnvironmentFactory.java index a288cba138e..db871511143 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/wsplugins/brokerphases/OpenshiftBrokerEnvironmentFactory.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/wsplugins/brokerphases/OpenshiftBrokerEnvironmentFactory.java @@ -33,6 +33,7 @@ public class OpenshiftBrokerEnvironmentFactory @Inject public OpenshiftBrokerEnvironmentFactory( @Named("che.websocket.endpoint") String cheWebsocketEndpoint, + @Nullable @Named("che.websocket.internal.endpoint") String cheWebsocketInternalEndpoint, @Named("che.workspace.plugin_broker.pull_policy") String brokerPullPolicy, AgentAuthEnableEnvVarProvider authEnableEnvVarProvider, MachineTokenEnvVarProvider machineTokenEnvVarProvider, @@ -46,6 +47,7 @@ public OpenshiftBrokerEnvironmentFactory( OpenshiftTrustedCAProvisioner trustedCAProvisioner) { super( cheWebsocketEndpoint, + cheWebsocketInternalEndpoint, brokerPullPolicy, authEnableEnvVarProvider, machineTokenEnvVarProvider, diff --git a/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/KeycloakJwkProvider.java b/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/KeycloakJwkProvider.java index 3db151e42ca..bcc00917288 100644 --- a/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/KeycloakJwkProvider.java +++ b/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/KeycloakJwkProvider.java @@ -11,6 +11,8 @@ */ package org.eclipse.che.multiuser.keycloak.server; +import static com.google.common.base.Strings.isNullOrEmpty; + import com.auth0.jwk.GuavaCachedJwkProvider; import com.auth0.jwk.JwkProvider; import com.auth0.jwk.UrlJwkProvider; @@ -27,7 +29,10 @@ public class KeycloakJwkProvider implements Provider { @Inject public KeycloakJwkProvider(OIDCInfo oidcInfo) throws MalformedURLException { - final String jwksUrl = oidcInfo.getJwksUri(); + final String jwksUrl = + isNullOrEmpty(oidcInfo.getJwksInternalUri()) + ? oidcInfo.getJwksPublicUri() + : oidcInfo.getJwksInternalUri(); if (jwksUrl == null) { throw new ConfigurationException("Jwks endpoint url not found in keycloak settings"); diff --git a/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/KeycloakProfileRetriever.java b/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/KeycloakProfileRetriever.java index 314a0c4508c..e0ab9ff74af 100644 --- a/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/KeycloakProfileRetriever.java +++ b/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/KeycloakProfileRetriever.java @@ -11,6 +11,8 @@ */ package org.eclipse.che.multiuser.keycloak.server; +import static com.google.common.base.Strings.isNullOrEmpty; + import java.io.IOException; import java.util.Map; import javax.inject.Inject; @@ -37,7 +39,10 @@ public class KeycloakProfileRetriever { @Inject public KeycloakProfileRetriever(OIDCInfo oidcInfo, HttpJsonRequestFactory requestFactory) { this.requestFactory = requestFactory; - this.keyclockCurrentUserInfoUrl = oidcInfo.getUserInfoEndpoint(); + this.keyclockCurrentUserInfoUrl = + isNullOrEmpty(oidcInfo.getUserInfoInternalEndpoint()) + ? oidcInfo.getUserInfoPublicEndpoint() + : oidcInfo.getUserInfoInternalEndpoint(); } /** diff --git a/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfo.java b/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfo.java index 593a39df444..eb76cd805a1 100644 --- a/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfo.java +++ b/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfo.java @@ -17,9 +17,9 @@ public class OIDCInfo { private final String tokenPublicEndpoint; private final String endSessionPublicEndpoint; private final String userInfoPublicEndpoint; - private final String userInfoEndpoint; + private final String userInfoInternalEndpoint; private final String jwksPublicUri; - private final String jwksUri; + private final String jwksInternalUri; private final String authServerURL; private final String authServerPublicURL; @@ -27,17 +27,17 @@ public OIDCInfo( String tokenPublicEndpoint, String endSessionPublicEndpoint, String userInfoPublicEndpoint, - String userInfoEndpoint, + String userInfoInternalEndpoint, String jwksPublicUri, - String jwksUri, + String jwksInternalUri, String authServerURL, String authServerPublicURL) { this.tokenPublicEndpoint = tokenPublicEndpoint; this.endSessionPublicEndpoint = endSessionPublicEndpoint; this.userInfoPublicEndpoint = userInfoPublicEndpoint; - this.userInfoEndpoint = userInfoEndpoint; + this.userInfoInternalEndpoint = userInfoInternalEndpoint; this.jwksPublicUri = jwksPublicUri; - this.jwksUri = jwksUri; + this.jwksInternalUri = jwksInternalUri; this.authServerURL = authServerURL; this.authServerPublicURL = authServerPublicURL; @@ -58,12 +58,9 @@ public String getUserInfoPublicEndpoint() { return userInfoPublicEndpoint; } - /** - * @return url to get user profile information. Url will be internal if internal network enabled, - * otherwise url will be public. - */ - public String getUserInfoEndpoint() { - return userInfoEndpoint; + /** @return internal network url to get user profile information. */ + public String getUserInfoInternalEndpoint() { + return userInfoInternalEndpoint; } /** @return public url to retrieve JWK public key for token validation. */ @@ -71,12 +68,9 @@ public String getJwksPublicUri() { return jwksPublicUri; } - /** - * @return url to retrieve JWK public key for token validation. Url will be internal if internal - * network enabled, otherwise url will be public. - */ - public String getJwksUri() { - return jwksUri; + /** @return internal network url to retrieve JWK public key for token validation. */ + public String getJwksInternalUri() { + return jwksInternalUri; } /** diff --git a/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfoProvider.java b/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfoProvider.java index a15537345a7..ad3ab74253f 100644 --- a/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfoProvider.java +++ b/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfoProvider.java @@ -86,16 +86,16 @@ public OIDCInfo get() { String endSessionPublicEndpoint = setPublicUrl((String) openIdConfiguration.get("end_session_endpoint")); String jwksPublicUri = setPublicUrl((String) openIdConfiguration.get("jwks_uri")); - String jwksUri = setInternalUrl(jwksPublicUri); - String userInfoEndpoint = setInternalUrl(userInfoPublicEndpoint); + String jwksInternalUri = setInternalUrl(jwksPublicUri); + String userInfoInternalEndpoint = setInternalUrl(userInfoPublicEndpoint); return new OIDCInfo( tokenPublicEndPoint, endSessionPublicEndpoint, userInfoPublicEndpoint, - userInfoEndpoint, + userInfoInternalEndpoint, jwksPublicUri, - jwksUri, + jwksInternalUri, serverAuthUrl, serverURL); } catch (IOException e) { @@ -136,7 +136,7 @@ private String setInternalUrl(String endpointUrl) { if (serverURL != null && serverInternalURL != null) { return endpointUrl.replace(serverURL, serverInternalURL); } - return endpointUrl; + return null; } private String setPublicUrl(String endpointUrl) { diff --git a/multiuser/keycloak/che-multiuser-keycloak-server/src/test/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfoProviderTest.java b/multiuser/keycloak/che-multiuser-keycloak-server/src/test/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfoProviderTest.java index 3ffa44da927..815c7a42664 100644 --- a/multiuser/keycloak/che-multiuser-keycloak-server/src/test/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfoProviderTest.java +++ b/multiuser/keycloak/che-multiuser-keycloak-server/src/test/java/org/eclipse/che/multiuser/keycloak/server/OIDCInfoProviderTest.java @@ -17,6 +17,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; @@ -110,12 +111,8 @@ public void shouldParseOIDCConfigurationForServerUrl() { assertEquals( serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/logout", oidcInfo.getEndSessionPublicEndpoint()); - assertEquals( - serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/userinfo", - oidcInfo.getUserInfoEndpoint()); - assertEquals( - serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/certs", - oidcInfo.getJwksUri()); + assertNull(oidcInfo.getUserInfoInternalEndpoint()); + assertNull(oidcInfo.getJwksInternalUri()); } @Test @@ -174,10 +171,10 @@ public void shouldParseOIDCConfigurationForInternalServerUrl() { assertEquals( serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/certs", - oidcInfo.getJwksUri()); + oidcInfo.getJwksInternalUri()); assertEquals( serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/userinfo", - oidcInfo.getUserInfoEndpoint()); + oidcInfo.getUserInfoInternalEndpoint()); assertEquals(serverUrl, oidcInfo.getAuthServerURL()); } @@ -239,10 +236,10 @@ public void shouldParseOIDCConfigurationWithPublicUrlsForInternalServerUrl() { assertEquals( serverInternalUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/certs", - oidcInfo.getJwksUri()); + oidcInfo.getJwksInternalUri()); assertEquals( serverInternalUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/userinfo", - oidcInfo.getUserInfoEndpoint()); + oidcInfo.getUserInfoInternalEndpoint()); assertEquals(serverInternalUrl, oidcInfo.getAuthServerURL()); assertEquals(serverPublicUrl, oidcInfo.getAuthServerPublicURL()); @@ -271,10 +268,10 @@ public void shouldParseOIDCConfigurationForOIDCProviderUrl() { oidcInfo.getEndSessionPublicEndpoint()); assertEquals( serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/userinfo", - oidcInfo.getUserInfoEndpoint()); + oidcInfo.getUserInfoInternalEndpoint()); assertEquals( serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/certs", - oidcInfo.getJwksUri()); + oidcInfo.getJwksInternalUri()); } @Test( diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/provision/env/CheApiEnvVarProvider.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/provision/env/CheApiEnvVarProvider.java index b6c4a2a68aa..1558f0200ec 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/provision/env/CheApiEnvVarProvider.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/provision/env/CheApiEnvVarProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018 Red Hat, Inc. + * Copyright (c) 2012-2021 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/ @@ -32,10 +32,14 @@ public class CheApiEnvVarProvider implements EnvVarProvider { public static final String CHE_API_VARIABLE = "CHE_API"; private final CheApiInternalEnvVarProvider cheApiInternalEnvVarProvider; + private final CheApiExternalEnvVarProvider cheApiExternalEnvVarProvider; @Inject - public CheApiEnvVarProvider(CheApiInternalEnvVarProvider cheApiInternalEnvVarProvider) { + public CheApiEnvVarProvider( + CheApiInternalEnvVarProvider cheApiInternalEnvVarProvider, + CheApiExternalEnvVarProvider cheApiExternalEnvVarProvider) { this.cheApiInternalEnvVarProvider = cheApiInternalEnvVarProvider; + this.cheApiExternalEnvVarProvider = cheApiExternalEnvVarProvider; } /** @@ -45,6 +49,9 @@ public CheApiEnvVarProvider(CheApiInternalEnvVarProvider cheApiInternalEnvVarPro */ @Override public Pair get(RuntimeIdentity runtimeIdentity) throws InfrastructureException { - return Pair.of(CHE_API_VARIABLE, cheApiInternalEnvVarProvider.get(runtimeIdentity).second); + if (cheApiInternalEnvVarProvider.get(runtimeIdentity) != null) { + return Pair.of(CHE_API_VARIABLE, cheApiInternalEnvVarProvider.get(runtimeIdentity).second); + } + return Pair.of(CHE_API_VARIABLE, cheApiExternalEnvVarProvider.get(runtimeIdentity).second); } } From 7bb2641d151a759e1797c4fe706af808b1d751a8 Mon Sep 17 00:00:00 2001 From: disaster37 Date: Tue, 3 Aug 2021 11:31:36 +0200 Subject: [PATCH 2/4] feat: Allow to add annotations when che server create namspace like labels (#57) * Allow to add annotations when che server create namspace like labels Signed-off-by: disaster37 * Rename che.infra.kubernetes.namespace.annotate to che.infra.kubernetes.namespace.annotation Signed-off-by: disaster37 * Remove quick fix on devfile (need dedicated PR) Signed-off-by: disaster37 * Replace placeHolders on namespace annotations Signed-off-by: disaster37 * Add tests to valide placeholders on annotation. Rename properties to che.infra.kubernetes.namespace.annotate Signed-off-by: disaster37 * Fix annotations placeholder on openshift project Signed-off-by: disaster37 * Try to fix test with add cleanup context on KeycloakProviderConfigFactoryTest Signed-off-by: disaster37 * Add lenient method on test to avoid unnecessary stubbing error Signed-off-by: disaster37 * clean test by remove unused stubs Signed-off-by: disaster37 --- .../webapp/WEB-INF/classes/che/che.properties | 6 ++ .../namespace/KubernetesNamespace.java | 61 ++++++++++- .../namespace/KubernetesNamespaceFactory.java | 13 ++- .../KubernetesNamespaceFactoryTest.java | 66 +++++++++++- .../namespace/KubernetesNamespaceTest.java | 100 +++++++++++++++--- .../openshift/project/OpenShiftProject.java | 7 +- .../project/OpenShiftProjectFactory.java | 11 +- .../KeycloakProviderConfigFactoryTest.java | 6 ++ .../project/OpenShiftProjectFactoryTest.java | 56 +++++++++- .../project/OpenShiftProjectTest.java | 86 +++++++++++++-- 10 files changed, 383 insertions(+), 29 deletions(-) diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties index e602f03989b..b4cb69a43e2 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties @@ -312,6 +312,9 @@ che.infra.kubernetes.namespace.default=-che # Defines whether che-server should try to label the workspace namespaces. che.infra.kubernetes.namespace.label=true +# Defines whether che-server should try to annotate the workspace namespaces. +che.infra.kubernetes.namespace.annotate=true + # List of labels to find {orch-namespace} that are used for {prod-short} Workspaces. # They are used to: # - find prepared {orch-namespace} for users in combination with `che.infra.kubernetes.namespace.annotations`. @@ -323,6 +326,9 @@ che.infra.kubernetes.namespace.labels=app.kubernetes.io/part-of=che.eclipse.org, # {orch-namespace} that matches both `che.infra.kubernetes.namespace.labels` and `che.infra.kubernetes.namespace.annotations` # will be preferentially used for User's workspaces. # It's possible to use `` placeholder to specify the {orch-namespace} to concrete user. +# They are used to: +# - find prepared {orch-namespace} for users in combination with `che.infra.kubernetes.namespace.labels`. +# - actively annotate {orch-namespace} with any workspace. che.infra.kubernetes.namespace.annotations=che.eclipse.org/username= # Defines Kubernetes Service Account name which should be specified to be bound to all workspaces Pods. diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespace.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespace.java index 2d5cff710e2..295ca576739 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespace.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespace.java @@ -132,13 +132,19 @@ public KubernetesNamespace( * namespace already exists or we create new one. If update labels operation fail due to lack of * permission, we do not fail completely. * + *

The method will try to annotate the namespace with provided `annotations`. It does not + * matter if the namespace already exists or we create new one. If update annotations operation + * fail due to lack of permission, we do not fail completely. + * * @param canCreate defines what to do when the namespace is not found. The namespace is created * when {@code true}, otherwise an exception is thrown. * @param labels labels that should be set to the namespace + * @param annotations annotations that should be set to the namespace * @throws InfrastructureException if any exception occurs during namespace preparation or if the * namespace doesn't exist and {@code canCreate} is {@code false}. */ - void prepare(boolean canCreate, Map labels) throws InfrastructureException { + void prepare(boolean canCreate, Map labels, Map annotations) + throws InfrastructureException { KubernetesClient client = clientFactory.create(workspaceId); Namespace namespace = get(name, client); @@ -150,6 +156,7 @@ void prepare(boolean canCreate, Map labels) throws Infrastructur namespace = create(name, client); } label(namespace, labels); + annotate(namespace, annotations); } /** @@ -204,6 +211,58 @@ protected void label(Namespace namespace, Map ensureLabels) } } + /** + * Applies given `ensureAnnotations` into given `namespace` and update the `namespace` in the + * Kubernetes. + * + *

If we do not have permissions to do so (code=403), this method does not throw any exception. + * + * @param namespace namespace to annotate + * @param ensureAnnotations these annotations should be applied on given `namespace` + * @throws InfrastructureException if something goes wrong with update, except lack of permissions + */ + protected void annotate(Namespace namespace, Map ensureAnnotations) + throws InfrastructureException { + if (ensureAnnotations.isEmpty()) { + return; + } + Map currentAnnotations = namespace.getMetadata().getAnnotations(); + Map newAnnotations = + currentAnnotations != null ? new HashMap<>(currentAnnotations) : new HashMap<>(); + + if (newAnnotations.entrySet().containsAll(ensureAnnotations.entrySet())) { + LOG.debug( + "Nothing to do, namespace [{}] already has all required annotations.", + namespace.getMetadata().getName()); + return; + } + + try { + // update the namespace with new annotations + cheSAClientFactory + .create() + .namespaces() + .createOrReplace( + new NamespaceBuilder(namespace) + .editMetadata() + .addToAnnotations(ensureAnnotations) + .endMetadata() + .build()); + } catch (KubernetesClientException kce) { + if (kce.getCode() == 403) { + LOG.warn( + "Can't annotate the namespace due to lack of permissions. Grant cluster-wide permissions " + + "to `get` and `update` the `namespaces` to the `che` service account " + + "(Che operator might have already prepared a cluster role called " + + "`che-namespace-editor` for this, depending on its configuration). " + + "Alternatively, consider disabling the feature by setting " + + "`che.infra.kubernetes.namepsace.annotate` to `false`."); + return; + } + throw new InfrastructureException(kce); + } + } + /** * Deletes the namespace. Deleting a non-existent namespace is not an error as is not an attempt * to delete a namespace that is already being deleted. diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java index 5ac78d01d9f..f787b5c0827 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java @@ -93,6 +93,7 @@ public class KubernetesNamespaceFactory { private final String defaultNamespaceName; protected final boolean labelNamespaces; + protected final boolean annotateNamespaces; protected final Map namespaceLabels; protected final Map namespaceAnnotations; @@ -112,6 +113,7 @@ public KubernetesNamespaceFactory( @Nullable @Named("che.infra.kubernetes.namespace.default") String defaultNamespaceName, @Named("che.infra.kubernetes.namespace.creation_allowed") boolean namespaceCreationAllowed, @Named("che.infra.kubernetes.namespace.label") boolean labelNamespaces, + @Named("che.infra.kubernetes.namespace.annotate") boolean annotateNamespaces, @Named("che.infra.kubernetes.namespace.labels") String namespaceLabels, @Named("che.infra.kubernetes.namespace.annotations") String namespaceAnnotations, KubernetesClientFactory clientFactory, @@ -129,6 +131,7 @@ public KubernetesNamespaceFactory( this.preferenceManager = preferenceManager; this.sharedPool = sharedPool; this.labelNamespaces = labelNamespaces; + this.annotateNamespaces = annotateNamespaces; //noinspection UnstableApiUsage Splitter.MapSplitter csvMapSplitter = Splitter.on(",").withKeyValueSeparator("="); @@ -327,7 +330,15 @@ protected boolean isWorkspaceNamespaceManaged(String namespaceName, Workspace wo public KubernetesNamespace getOrCreate(RuntimeIdentity identity) throws InfrastructureException { KubernetesNamespace namespace = get(identity); - namespace.prepare(canCreateNamespace(identity), labelNamespaces ? namespaceLabels : emptyMap()); + NamespaceResolutionContext resolutionCtx = + new NamespaceResolutionContext(EnvironmentContext.getCurrent().getSubject()); + Map namespaceAnnotationsEvaluated = + evaluateAnnotationPlaceholders(resolutionCtx); + + namespace.prepare( + canCreateNamespace(identity), + labelNamespaces ? namespaceLabels : emptyMap(), + annotateNamespaces ? namespaceAnnotationsEvaluated : emptyMap()); if (!isNullOrEmpty(serviceAccountName)) { KubernetesWorkspaceServiceAccount workspaceServiceAccount = diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java index 0cd0e49cef8..384a70417bc 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java @@ -165,6 +165,7 @@ public void shouldNotThrowExceptionIfDefaultNamespaceIsSpecifiedOnCheckingIfName "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -192,6 +193,7 @@ public void shouldLookAtStoredNamespacesOnCheckingIfNamespaceIsAllowed() throws "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -217,6 +219,7 @@ public void shouldLookAtStoredNamespacesOnCheckingIfNamespaceIsAllowed() throws "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -239,6 +242,7 @@ public void shouldThrowExceptionIfNoDefaultNamespaceIsConfigured() { null, true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -289,6 +293,7 @@ public void shouldReturnPreparedNamespacesWhenFound() throws InfrastructureExcep "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -332,6 +337,7 @@ public void shouldNotThrowAnExceptionWhenNotAllowedToListNamespaces() throws Exc "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -362,6 +368,7 @@ public void throwAnExceptionWhenErrorListingNamespaces() throws Exception { "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -395,6 +402,7 @@ public void shouldReturnDefaultNamespaceWhenItExists() throws Exception { "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -423,6 +431,7 @@ public void shouldReturnDefaultNamespaceWhenItDoesNotExistAndUserDefinedIsNotAll "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -454,6 +463,7 @@ public void shouldThrowExceptionWhenFailedToGetInfoAboutDefaultNamespace() throw "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -479,6 +489,7 @@ public void shouldThrowExceptionWhenFailedToGetNamespaces() throws Exception { "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -509,6 +520,7 @@ public void shouldRequireNamespacePriorExistenceIfDifferentFromDefaultAndUserDef "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -526,7 +538,7 @@ public void shouldRequireNamespacePriorExistenceIfDifferentFromDefaultAndUserDef // then assertEquals(toReturnNamespace, namespace); verify(namespaceFactory, never()).doCreateServiceAccount(any(), any()); - verify(toReturnNamespace).prepare(eq(false), any()); + verify(toReturnNamespace).prepare(eq(false), any(), any()); } @Test @@ -540,6 +552,7 @@ public void shouldReturnDefaultNamespaceWhenCreatingIsNotIsNotAllowed() throws E "-che", false, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -558,7 +571,7 @@ public void shouldReturnDefaultNamespaceWhenCreatingIsNotIsNotAllowed() throws E // then assertEquals(toReturnNamespace, namespace); verify(namespaceFactory, never()).doCreateServiceAccount(any(), any()); - verify(toReturnNamespace).prepare(eq(false), any()); + verify(toReturnNamespace).prepare(eq(false), any(), any()); } @Test @@ -573,6 +586,7 @@ public void shouldPrepareWorkspaceServiceAccountIfItIsConfiguredAndNamespaceIsNo "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -610,6 +624,7 @@ public void shouldBindToAllConfiguredClusterRoles() throws Exception { "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -679,6 +694,7 @@ public void shouldCreateExecAndViewRolesAndBindings() throws Exception { "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -735,6 +751,7 @@ public void testNullClusterRolesResultsInEmptySet() { "che-", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -754,6 +771,7 @@ public void testClusterRolesProperlyParsed() { "che-", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -777,6 +795,7 @@ public void testClusterRolesProperlyParsed() { "che-", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -802,6 +821,7 @@ public void testEvalNamespaceUsesNamespaceFromUserPreferencesIfExist() throws Ex "che-", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -832,6 +852,7 @@ public void testEvalNamespaceSkipsNamespaceFromUserPreferencesIfTemplateChanged( "che--", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -863,6 +884,7 @@ public void testEvalNamespaceSkipsNamespaceFromUserPreferencesIfUserAllowedPrope "che--", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -894,6 +916,7 @@ public void testEvalNamespaceUsesWorkspaceRecordedNamespaceIfWorkspaceRecordsIt( "che-", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -923,6 +946,7 @@ public void testEvalNamespaceTreatsWorkspaceRecordedNamespaceLiterally() throws "che-", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -973,6 +997,7 @@ public void testEvalNamespaceNameWhenPreparedNamespacesFound() throws Infrastruc "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -1010,6 +1035,7 @@ public void testUsernamePlaceholderInLabelsIsNotEvaluated() throws Infrastructur "-che", true, true, + true, "try_placeholder_here=", NAMESPACE_ANNOTATIONS, clientFactory, @@ -1023,6 +1049,40 @@ public void testUsernamePlaceholderInLabelsIsNotEvaluated() throws Infrastructur verify(namespaceOperation).withLabels(Map.of("try_placeholder_here", "")); } + @Test + public void testUsernamePlaceholderInAnnotationsIsEvaluated() throws InfrastructureException { + + // given + namespaceFactory = + spy( + new KubernetesNamespaceFactory( + "", + "", + "-che", + true, + true, + true, + NAMESPACE_LABELS, + "try_placeholder_here=", + clientFactory, + cheClientFactory, + userManager, + preferenceManager, + pool)); + EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); + KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); + doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); + + // when + RuntimeIdentity identity = new RuntimeIdentityImpl("workspace123", null, USER_ID, "old-che"); + KubernetesNamespace namespace = namespaceFactory.getOrCreate(identity); + + // then + assertEquals(toReturnNamespace, namespace); + verify(toReturnNamespace) + .prepare(eq(false), any(), eq(Map.of("try_placeholder_here", "jondoe"))); + } + @Test(dataProvider = "invalidUsernames") public void normalizeTest(String raw, String expected) { namespaceFactory = @@ -1032,6 +1092,7 @@ public void normalizeTest(String raw, String expected) { "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -1051,6 +1112,7 @@ public void normalizeLengthTest() { "che-", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceTest.java index fab2d6c85ef..964bf2ce3b8 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceTest.java @@ -12,6 +12,7 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.namespace; import static io.fabric8.kubernetes.api.model.DeletionPropagation.BACKGROUND; +import static java.util.Collections.emptyMap; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -40,7 +41,6 @@ import io.fabric8.kubernetes.client.dsl.MixedOperation; import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; import io.fabric8.kubernetes.client.dsl.Resource; -import java.util.Collections; import java.util.Map; import java.util.concurrent.Executor; import org.eclipse.che.api.workspace.server.spi.InfrastructureException; @@ -116,7 +116,7 @@ public void testKubernetesNamespacePreparingWhenNamespaceExists() throws Excepti new KubernetesNamespace(clientFactory, cheClientFactory, executor, NAMESPACE, WORKSPACE_ID); // when - namespace.prepare(true, Map.of()); + namespace.prepare(true, Map.of(), Map.of()); // then verify(namespaceOperation, never()).create(any(Namespace.class)); @@ -132,7 +132,7 @@ public void testKubernetesNamespacePreparingCreationWhenNamespaceDoesNotExist() new KubernetesNamespace(clientFactory, cheClientFactory, executor, NAMESPACE, WORKSPACE_ID); // when - namespace.prepare(true, Map.of()); + namespace.prepare(true, Map.of(), Map.of()); // then ArgumentCaptor captor = ArgumentCaptor.forClass(Namespace.class); @@ -149,7 +149,7 @@ public void throwsExceptionIfNamespaceDoesntExistAndNotAllowedToCreateIt() throw new KubernetesNamespace(clientFactory, cheClientFactory, executor, NAMESPACE, WORKSPACE_ID); // when - namespace.prepare(false, Map.of()); + namespace.prepare(false, Map.of(), Map.of()); // then // exception is thrown @@ -196,7 +196,7 @@ public void testThrowsInfrastructureExceptionWhenFailedToGetNamespaceServiceAcco doThrow(KubernetesClientException.class).when(kubernetesClient).serviceAccounts(); new KubernetesNamespace(clientFactory, cheClientFactory, executor, NAMESPACE, WORKSPACE_ID) - .prepare(false, Map.of()); + .prepare(false, Map.of(), Map.of()); } @Test(expectedExceptions = InfrastructureException.class) @@ -207,7 +207,7 @@ public void testThrowsInfrastructureExceptionWhenServiceAccountEventNotPublished when(serviceAccountResource.get()).thenReturn(null); new KubernetesNamespace(clientFactory, cheClientFactory, executor, NAMESPACE, WORKSPACE_ID) - .prepare(false, Map.of()); + .prepare(false, Map.of(), Map.of()); } @Test(expectedExceptions = InfrastructureException.class) @@ -226,7 +226,7 @@ public void testThrowsInfrastructureExceptionWhenWatcherClosed() throws Exceptio .watch(any()); new KubernetesNamespace(clientFactory, cheClientFactory, executor, NAMESPACE, WORKSPACE_ID) - .prepare(false, Map.of()); + .prepare(false, Map.of(), Map.of()); } @Test @@ -245,7 +245,7 @@ public void testStopsWaitingServiceAccountEventJustAfterEventReceived() throws E .watch(any()); new KubernetesNamespace(clientFactory, cheClientFactory, executor, NAMESPACE, WORKSPACE_ID) - .prepare(true, Map.of()); + .prepare(true, Map.of(), Map.of()); verify(serviceAccountResource).get(); verify(serviceAccountResource).watch(any()); @@ -318,7 +318,7 @@ public void testLabelNamespace() throws InfrastructureException { Map labels = Map.of("label.with.this", "yes", "and.this", "of courese"); // when - kubernetesNamespace.prepare(true, labels); + kubernetesNamespace.prepare(true, labels, emptyMap()); // then Namespace updatedNamespace = namespaceArgumentCaptor.getValue(); @@ -349,7 +349,7 @@ public void testDontTryToLabelNamespaceIfAlreadyLabeled() throws InfrastructureE .createOrReplace(any(Namespace.class)); // when - kubernetesNamespace.prepare(true, labels); + kubernetesNamespace.prepare(true, labels, emptyMap()); // then assertTrue(namespace.getMetadata().getLabels().entrySet().containsAll(labels.entrySet())); @@ -377,7 +377,7 @@ public void testDontTryToLabelNamespaceIfNoNamespacesProvided() throws Infrastru .createOrReplace(any(Namespace.class)); // when - kubernetesNamespace.prepare(true, Collections.emptyMap()); + kubernetesNamespace.prepare(true, emptyMap(), emptyMap()); // then assertTrue( @@ -385,6 +385,80 @@ public void testDontTryToLabelNamespaceIfNoNamespacesProvided() throws Infrastru verify(nonNamespaceOperation, never()).createOrReplace(any()); } + public void testAnnotateNamespace() throws InfrastructureException { + // given + prepareNamespace(NAMESPACE); + KubernetesNamespace kubernetesNamespace = + new KubernetesNamespace(clientFactory, cheClientFactory, executor, NAMESPACE, WORKSPACE_ID); + + KubernetesClient cheKubeClient = mock(KubernetesClient.class); + doReturn(cheKubeClient).when(cheClientFactory).create(); + + NonNamespaceOperation nonNamespaceOperation = mock(NonNamespaceOperation.class); + doReturn(nonNamespaceOperation).when(cheKubeClient).namespaces(); + + ArgumentCaptor namespaceArgumentCaptor = ArgumentCaptor.forClass(Namespace.class); + doAnswer(a -> a.getArgument(0)) + .when(nonNamespaceOperation) + .createOrReplace(namespaceArgumentCaptor.capture()); + + Map annotations = Map.of("annotate.with.this", "yes", "and.this", "of courese"); + + // when + kubernetesNamespace.prepare(true, emptyMap(), annotations); + + // then + Namespace updatedNamespace = namespaceArgumentCaptor.getValue(); + assertTrue( + updatedNamespace + .getMetadata() + .getAnnotations() + .entrySet() + .containsAll(annotations.entrySet())); + assertEquals(updatedNamespace.getMetadata().getName(), NAMESPACE); + } + + @Test + public void testDontTryToAnnotateNamespaceIfAlreadyAnnoted() throws InfrastructureException { + // given + Map annotations = + Map.of("annotation.with.this", "yes", "and.this", "of courese"); + + Namespace namespace = prepareNamespace(NAMESPACE); + namespace.getMetadata().setAnnotations(annotations); + KubernetesNamespace kubernetesNamespace = + new KubernetesNamespace(clientFactory, cheClientFactory, executor, NAMESPACE, WORKSPACE_ID); + + // when + kubernetesNamespace.prepare(true, emptyMap(), annotations); + + // then + assertTrue( + namespace.getMetadata().getAnnotations().entrySet().containsAll(annotations.entrySet())); + } + + @Test + public void testDontTryToAnnotateNamespaceIfNoNamespacesProvided() + throws InfrastructureException { + // given + Map existingAnnotations = Map.of("some", "annotations"); + Namespace namespace = prepareNamespace(NAMESPACE); + namespace.getMetadata().setAnnotations(existingAnnotations); + KubernetesNamespace kubernetesNamespace = + new KubernetesNamespace(clientFactory, cheClientFactory, executor, NAMESPACE, WORKSPACE_ID); + + // when + kubernetesNamespace.prepare(true, emptyMap(), emptyMap()); + + // then + assertTrue( + namespace + .getMetadata() + .getAnnotations() + .entrySet() + .containsAll(existingAnnotations.entrySet())); + } + @Test public void testDoNotFailWhenNoPermissionsToUpdateNamespace() throws InfrastructureException { // given @@ -407,7 +481,7 @@ public void testDoNotFailWhenNoPermissionsToUpdateNamespace() throws Infrastruct .createOrReplace(namespaceArgumentCaptor.capture()); // when - kubernetesNamespace.prepare(true, labels); + kubernetesNamespace.prepare(true, labels, emptyMap()); // then Namespace updatedNamespace = namespaceArgumentCaptor.getValue(); @@ -437,7 +511,7 @@ public void testFailWhenFailToUpdateNamespace() throws InfrastructureException { .createOrReplace(any(Namespace.class)); // when - kubernetesNamespace.prepare(true, labels); + kubernetesNamespace.prepare(true, labels, emptyMap()); // then verify(nonNamespaceOperation).createOrReplace(namespace); diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProject.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProject.java index 1859ab0a1f6..9d202ade4d0 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProject.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProject.java @@ -110,7 +110,11 @@ public OpenShiftProject( * @throws InfrastructureException if any exception occurs during project preparation or if the * project doesn't exist and {@code canCreate} is {@code false}. */ - void prepare(boolean canCreate, boolean initWithCheServerSa, Map labels) + void prepare( + boolean canCreate, + boolean initWithCheServerSa, + Map labels, + Map annotations) throws InfrastructureException { String workspaceId = getWorkspaceId(); String projectName = getName(); @@ -153,6 +157,7 @@ void prepare(boolean canCreate, boolean initWithCheServerSa, Map } } label(osClient.namespaces().withName(projectName).get(), labels); + annotate(osClient.namespaces().withName(projectName).get(), annotations); } /** diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java index d5b980bd2e6..b193ddec631 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java @@ -36,6 +36,7 @@ import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext; import org.eclipse.che.commons.annotation.Nullable; +import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; @@ -71,6 +72,7 @@ public OpenShiftProjectFactory( @Nullable @Named("che.infra.kubernetes.namespace.default") String defaultNamespaceName, @Named("che.infra.kubernetes.namespace.creation_allowed") boolean namespaceCreationAllowed, @Named("che.infra.kubernetes.namespace.label") boolean labelProjects, + @Named("che.infra.kubernetes.namespace.annotate") boolean annotateProjects, @Named("che.infra.kubernetes.namespace.labels") String projectLabels, @Named("che.infra.kubernetes.namespace.annotations") String projectAnnotations, @Named("che.infra.openshift.project.init_with_server_sa") boolean initWithCheServerSa, @@ -89,6 +91,7 @@ public OpenShiftProjectFactory( defaultNamespaceName, namespaceCreationAllowed, labelProjects, + annotateProjects, projectLabels, projectAnnotations, clientFactory, @@ -106,10 +109,16 @@ public OpenShiftProjectFactory( public OpenShiftProject getOrCreate(RuntimeIdentity identity) throws InfrastructureException { OpenShiftProject osProject = get(identity); + NamespaceResolutionContext resolutionCtx = + new NamespaceResolutionContext(EnvironmentContext.getCurrent().getSubject()); + Map namespaceAnnotationsEvaluated = + evaluateAnnotationPlaceholders(resolutionCtx); + osProject.prepare( canCreateNamespace(identity), initWithCheServerSa && !isNullOrEmpty(oAuthIdentityProvider), - labelNamespaces ? namespaceLabels : emptyMap()); + labelNamespaces ? namespaceLabels : emptyMap(), + annotateNamespaces ? namespaceAnnotationsEvaluated : emptyMap()); if (!isNullOrEmpty(getServiceAccountName())) { OpenShiftWorkspaceServiceAccount osWorkspaceServiceAccount = diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/multiuser/oauth/KeycloakProviderConfigFactoryTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/multiuser/oauth/KeycloakProviderConfigFactoryTest.java index 6eea1e6d240..501c6bcf2cf 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/multiuser/oauth/KeycloakProviderConfigFactoryTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/multiuser/oauth/KeycloakProviderConfigFactoryTest.java @@ -44,6 +44,7 @@ import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakTokenResponse; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Listeners; import org.testng.annotations.Test; @@ -134,6 +135,11 @@ public void setUp() throws Exception { defaultConfig = new io.fabric8.kubernetes.client.ConfigBuilder().build(); } + @AfterMethod + public void cleanup() { + EnvironmentContext.reset(); + } + @Test public void testFallbackToDefaultConfigWhenProvideIsNull() throws Exception { configBuilder = diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java index 3a781b1c753..3d5e47dc06a 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java @@ -152,6 +152,7 @@ public void shouldNotThrowExceptionIfDefaultNamespaceIsSpecifiedOnCheckingIfName "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -184,6 +185,7 @@ public void shouldNotThrowExceptionIfDefaultNamespaceIsSpecifiedOnCheckingIfName "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -216,6 +218,7 @@ public void shouldNotThrowExceptionIfDefaultNamespaceIsSpecifiedOnCheckingIfName null, true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -253,6 +256,7 @@ public void shouldReturnPreparedNamespacesWhenFound() throws InfrastructureExcep "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -291,6 +295,7 @@ public void shouldNotThrowAnExceptionWhenNotAllowedToListNamespaces() throws Exc "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -325,6 +330,7 @@ public void throwAnExceptionWhenErrorListingNamespaces() throws Exception { "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -369,6 +375,7 @@ public void shouldReturnDefaultProjectWhenItExistsAndUserDefinedIsNotAllowed() t "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -407,6 +414,7 @@ public void shouldReturnDefaultProjectWhenItDoesNotExistAndUserDefinedIsNotAllow "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -445,6 +453,7 @@ public void shouldThrowExceptionWhenFailedToGetInfoAboutDefaultNamespace() throw "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -473,6 +482,7 @@ public void shouldThrowExceptionWhenFailedToGetNamespaces() throws Exception { "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -506,6 +516,7 @@ public void shouldRequireNamespacePriorExistenceIfDifferentFromDefaultAndUserDef "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -528,7 +539,7 @@ public void shouldRequireNamespacePriorExistenceIfDifferentFromDefaultAndUserDef // then assertEquals(toReturnProject, project); verify(projectFactory, never()).doCreateServiceAccount(any(), any()); - verify(toReturnProject).prepare(eq(false), eq(false), any()); + verify(toReturnProject).prepare(eq(false), eq(false), any(), any()); } @Test @@ -543,6 +554,7 @@ public void shouldPrepareWorkspaceServiceAccountIfItIsConfiguredAndProjectIsNotP "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -582,6 +594,7 @@ public void shouldCallStopWorkspaceRoleProvisionWhenIdentityProviderIsDefined() "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -623,6 +636,7 @@ public void shouldNotCallStopWorkspaceRoleProvisionWhenIdentityProviderIsDefined "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -676,6 +690,7 @@ public void testEvalNamespaceNameWhenPreparedNamespacesFound() throws Infrastruc "-che", true, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, true, @@ -710,6 +725,7 @@ public void testUsernamePlaceholderInLabelsIsNotEvaluated() throws Infrastructur "-che", true, true, + true, "try_placeholder_here=", NAMESPACE_ANNOTATIONS, true, @@ -727,6 +743,44 @@ public void testUsernamePlaceholderInLabelsIsNotEvaluated() throws Infrastructur verify(projectOperation).withLabels(Map.of("try_placeholder_here", "")); } + @Test + public void testUsernamePlaceholderInAnnotationsIsEvaluated() throws InfrastructureException { + + // given + projectFactory = + spy( + new OpenShiftProjectFactory( + "", + null, + "-che", + true, + true, + true, + NAMESPACE_LABELS, + "try_placeholder_here=", + true, + clientFactory, + cheClientFactory, + cheServerOpenshiftClientFactory, + stopWorkspaceRoleProvisioner, + userManager, + preferenceManager, + pool, + NO_OAUTH_IDENTITY_PROVIDER)); + EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); + OpenShiftProject toReturnProject = mock(OpenShiftProject.class); + doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any()); + + // when + RuntimeIdentity identity = new RuntimeIdentityImpl("workspace123", null, USER_ID, "old-che"); + OpenShiftProject project = projectFactory.getOrCreate(identity); + + // then + assertEquals(toReturnProject, project); + verify(toReturnProject) + .prepare(eq(false), eq(false), any(), eq(Map.of("try_placeholder_here", "jondoe"))); + } + private void prepareNamespaceToBeFoundByName(String name, Project project) throws Exception { @SuppressWarnings("unchecked") Resource getProjectByNameOperation = mock(Resource.class); diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectTest.java index 09772c49867..1e8ad7820ba 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectTest.java @@ -156,7 +156,7 @@ public void testOpenShiftProjectPreparingWhenProjectExists() throws Exception { WORKSPACE_ID); // when - project.prepare(true, true, Map.of()); + project.prepare(true, true, Map.of(), Map.of()); // then verify(metadataNested, never()).withName(PROJECT_NAME); @@ -179,7 +179,7 @@ public void testOpenShiftProjectPreparingWhenProjectDoesNotExist() throws Except WORKSPACE_ID); // when - openShiftProject.prepare(true, false, Map.of()); + openShiftProject.prepare(true, false, Map.of(), Map.of()); // then ArgumentCaptor captor = ArgumentCaptor.forClass(ProjectRequest.class); @@ -210,7 +210,7 @@ public void testOpenShiftProjectPreparingWhenProjectDoesNotExistWithCheServerSA( when(openShiftClient.currentUser()) .thenReturn(new UserBuilder().withNewMetadata().withName("user").endMetadata().build()); // when - openShiftProject.prepare(true, true, Map.of()); + openShiftProject.prepare(true, true, Map.of(), Map.of()); // then ArgumentCaptor captor = ArgumentCaptor.forClass(ProjectRequest.class); @@ -247,7 +247,7 @@ public void testOpenShiftProjectPreparingRoleBindingWhenProjectDoesNotExistWithC when(openShiftClient.currentUser()) .thenReturn(new UserBuilder().withNewMetadata().withName("jdoe").endMetadata().build()); // when - openShiftProject.prepare(true, true, Map.of()); + openShiftProject.prepare(true, true, Map.of(), Map.of()); // then ArgumentCaptor roleBindingArgumentCaptor = @@ -275,7 +275,7 @@ public void throwsExceptionIfNamespaceDoesntExistAndNotAllowedToCreateIt() throw WORKSPACE_ID); // when - project.prepare(false, true, Map.of()); + project.prepare(false, true, Map.of(), Map.of()); // then // exception is thrown @@ -406,7 +406,7 @@ public void testLabelNamespace() throws InfrastructureException { Map labels = Map.of("label.with.this", "yes", "and.this", "of courese"); // when - openShiftProject.prepare(true, true, labels); + openShiftProject.prepare(true, true, labels, Map.of()); // then Namespace updatedNamespace = namespaceArgumentCaptor.getValue(); @@ -444,13 +444,81 @@ public void testDontTryToLabelNamespaceIfAlreadyLabeled() throws InfrastructureE .createOrReplace(any(Namespace.class)); // when - openShiftProject.prepare(true, true, labels); + openShiftProject.prepare(true, true, labels, Map.of()); // then assertTrue(namespace.getMetadata().getLabels().entrySet().containsAll(labels.entrySet())); verify(nonNamespaceOperation, never()).createOrReplace(any()); } + @Test + public void testAnnotateNamespace() throws InfrastructureException { + // given + prepareProject(PROJECT_NAME); + prepareNamespaceGet(PROJECT_NAME); + OpenShiftProject openShiftProject = + new OpenShiftProject( + clientFactory, + cheClientFactory, + cheServerOpenshiftClientFactory, + executor, + PROJECT_NAME, + WORKSPACE_ID); + + KubernetesClient cheKubeClient = mock(KubernetesClient.class); + doReturn(cheKubeClient).when(cheClientFactory).create(); + + NonNamespaceOperation nonNamespaceOperation = mock(NonNamespaceOperation.class); + doReturn(nonNamespaceOperation).when(cheKubeClient).namespaces(); + + ArgumentCaptor namespaceArgumentCaptor = ArgumentCaptor.forClass(Namespace.class); + doAnswer(a -> a.getArgument(0)) + .when(nonNamespaceOperation) + .createOrReplace(namespaceArgumentCaptor.capture()); + + Map annotations = + Map.of("annotation.with.this", "yes", "and.this", "of courese"); + + // when + openShiftProject.prepare(true, true, Map.of(), annotations); + + // then + Namespace updatedNamespace = namespaceArgumentCaptor.getValue(); + assertTrue( + updatedNamespace + .getMetadata() + .getAnnotations() + .entrySet() + .containsAll(annotations.entrySet())); + assertEquals(updatedNamespace.getMetadata().getName(), PROJECT_NAME); + } + + @Test + public void testDontTryToAnnotateNamespaceIfAlreadyAnnotated() throws InfrastructureException { + // given + Map annotations = + Map.of("annotation.with.this", "yes", "and.this", "of courese"); + + prepareProject(PROJECT_NAME); + Namespace namespace = prepareNamespaceGet(PROJECT_NAME); + namespace.getMetadata().setAnnotations(annotations); + OpenShiftProject openShiftProject = + new OpenShiftProject( + clientFactory, + cheClientFactory, + cheServerOpenshiftClientFactory, + executor, + PROJECT_NAME, + WORKSPACE_ID); + + // when + openShiftProject.prepare(true, true, Map.of(), annotations); + + // then + assertTrue( + namespace.getMetadata().getAnnotations().entrySet().containsAll(annotations.entrySet())); + } + @Test public void testDoNotFailWhenNoPermissionsToUpdateNamespace() throws InfrastructureException { // given @@ -480,7 +548,7 @@ public void testDoNotFailWhenNoPermissionsToUpdateNamespace() throws Infrastruct .createOrReplace(namespaceArgumentCaptor.capture()); // when - openShiftProject.prepare(true, true, labels); + openShiftProject.prepare(true, true, labels, Map.of()); // then Namespace updatedNamespace = namespaceArgumentCaptor.getValue(); @@ -517,7 +585,7 @@ public void testFailWhenFailToUpdateNamespace() throws InfrastructureException { .createOrReplace(any(Namespace.class)); // when - openShiftProject.prepare(true, true, labels); + openShiftProject.prepare(true, true, labels, Map.of()); // then verify(nonNamespaceOperation).createOrReplace(namespace); From 74b47fa68b1c3b7c1cff233fae938aa0d22b72b2 Mon Sep 17 00:00:00 2001 From: Sergii Kabashniuk Date: Tue, 3 Aug 2021 12:32:22 +0300 Subject: [PATCH 3/4] feat: REST Service on the Che server-side that will initiate k8s namespace provisioning (#61) * feat: REST Service on the Che server-side that will initiate k8s namespace provisioning Signed-off-by: Sergii Kabashniuk --- .../server/KubernetesNamespaceService.java | 24 ++++ .../namespace/KubernetesNamespaceFactory.java | 16 +++ .../KubernetesNamespaceServiceTest.java | 81 ++++++++++++ .../KubernetesNamespaceFactoryTest.java | 115 ++++++++++++++++++ 4 files changed, 236 insertions(+) diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceService.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceService.java index 1940a353134..fd19ca736cf 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceService.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceService.java @@ -22,10 +22,13 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import org.eclipse.che.api.core.rest.Service; import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext; +import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.dto.server.DtoFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.dto.KubernetesNamespaceMetaDto; @@ -63,6 +66,27 @@ public List getNamespaces() throws InfrastructureExc return namespaceFactory.list().stream().map(this::asDto).collect(Collectors.toList()); } + @POST + @Path("provision") + @Produces(APPLICATION_JSON) + @ApiOperation( + value = "Provision k8s namespace where user is able to create workspaces", + notes = + "This operation can be performed only by an authorized user." + + " This is a beta feature that may be significantly changed.", + response = KubernetesNamespaceMetaDto.class) + @ApiResponses({ + @ApiResponse(code = 200, message = "The namespace successfully provisioned"), + @ApiResponse( + code = 500, + message = "Internal server error occurred during namespace provisioning") + }) + public KubernetesNamespaceMetaDto provision() throws InfrastructureException { + return asDto( + namespaceFactory.provision( + new NamespaceResolutionContext(EnvironmentContext.getCurrent().getSubject()))); + } + private KubernetesNamespaceMetaDto asDto(KubernetesNamespaceMeta kubernetesNamespaceMeta) { return DtoFactory.newDto(KubernetesNamespaceMetaDto.class) .withName(kubernetesNamespaceMeta.getName()) diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java index f787b5c0827..75a0d33328a 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java @@ -50,6 +50,7 @@ import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; import org.eclipse.che.api.user.server.PreferenceManager; import org.eclipse.che.api.user.server.UserManager; +import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl; import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext; import org.eclipse.che.commons.annotation.Nullable; @@ -349,6 +350,21 @@ public KubernetesNamespace getOrCreate(RuntimeIdentity identity) throws Infrastr return namespace; } + public KubernetesNamespaceMeta provision(NamespaceResolutionContext namespaceResolutionContext) + throws InfrastructureException { + KubernetesNamespace namespace = + getOrCreate( + new RuntimeIdentityImpl( + null, + null, + namespaceResolutionContext.getUserId(), + evaluateNamespaceName(namespaceResolutionContext))); + + return fetchNamespace(namespace.getName()) + .orElseThrow( + () -> new InfrastructureException("Not able to find namespace " + namespace.getName())); + } + public KubernetesNamespace get(RuntimeIdentity identity) throws InfrastructureException { String workspaceId = identity.getWorkspaceId(); String namespaceName = identity.getInfrastructureNamespace(); diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceServiceTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceServiceTest.java index faf373df2d4..9fa5ac97eff 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceServiceTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceServiceTest.java @@ -16,6 +16,7 @@ import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME; import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD; import static org.everrest.assured.JettyHttpServer.SECURE_PATH; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; @@ -24,15 +25,25 @@ import com.jayway.restassured.response.Response; import java.util.Collections; import java.util.List; +import org.eclipse.che.api.core.rest.ApiExceptionMapper; import org.eclipse.che.api.core.rest.CheJsonProvider; +import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext; +import org.eclipse.che.commons.env.EnvironmentContext; +import org.eclipse.che.commons.subject.Subject; +import org.eclipse.che.commons.subject.SubjectImpl; import org.eclipse.che.dto.server.DtoFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.dto.KubernetesNamespaceMetaDto; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory; import org.everrest.assured.EverrestJetty; +import org.everrest.core.Filter; +import org.everrest.core.GenericContainerRequest; +import org.everrest.core.RequestFilter; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; +import org.testng.Assert; import org.testng.annotations.Listeners; import org.testng.annotations.Test; @@ -44,6 +55,14 @@ @Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class}) public class KubernetesNamespaceServiceTest { + @SuppressWarnings("unused") + private static final ApiExceptionMapper MAPPER = new ApiExceptionMapper(); + + @SuppressWarnings("unused") + private static final EnvironmentFilter FILTER = new EnvironmentFilter(); + + private static final Subject SUBJECT = new SubjectImpl("john", "id-123", "token", false); + @SuppressWarnings("unused") // is declared for deploying by everrest-assured private CheJsonProvider jsonProvider = new CheJsonProvider(Collections.emptySet()); @@ -73,7 +92,69 @@ public void shouldReturnNamespaces() throws Exception { verify(namespaceFactory).list(); } + @Test + public void shouldProvisionNamespace() throws Exception { + // given + KubernetesNamespaceMetaImpl namespaceMeta = + new KubernetesNamespaceMetaImpl( + "ws-namespace", ImmutableMap.of("phase", "active", "default", "true")); + when(namespaceFactory.provision(any(NamespaceResolutionContext.class))) + .thenReturn(namespaceMeta); + // when + final Response response = + given() + .auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .post(SECURE_PATH + "/kubernetes/namespace/provision"); + // then + + assertEquals(response.getStatusCode(), 200); + KubernetesNamespaceMetaDto actual = unwrapDto(response, KubernetesNamespaceMetaDto.class); + assertEquals(actual.getName(), namespaceMeta.getName()); + assertEquals(actual.getAttributes(), namespaceMeta.getAttributes()); + verify(namespaceFactory).provision(any(NamespaceResolutionContext.class)); + } + + @Test + public void shouldProvisionNamespaceWithCorrectContext() throws Exception { + // given + KubernetesNamespaceMetaImpl namespaceMeta = + new KubernetesNamespaceMetaImpl( + "ws-namespace", ImmutableMap.of("phase", "active", "default", "true")); + when(namespaceFactory.provision(any(NamespaceResolutionContext.class))) + .thenReturn(namespaceMeta); + // when + final Response response = + given() + .auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .post(SECURE_PATH + "/kubernetes/namespace/provision"); + // then + + assertEquals(response.getStatusCode(), 200); + ArgumentCaptor captor = + ArgumentCaptor.forClass(NamespaceResolutionContext.class); + verify(namespaceFactory).provision(captor.capture()); + NamespaceResolutionContext actualContext = captor.getValue(); + assertEquals(actualContext.getUserId(), SUBJECT.getUserId()); + assertEquals(actualContext.getUserName(), SUBJECT.getUserName()); + Assert.assertNull(actualContext.getWorkspaceId()); + } + private static List unwrapDtoList(Response response, Class dtoClass) { return DtoFactory.getInstance().createListDtoFromJson(response.body().print(), dtoClass); } + + private static T unwrapDto(Response response, Class dtoClass) { + return DtoFactory.getInstance().createDtoFromJson(response.body().print(), dtoClass); + } + + @Filter + public static class EnvironmentFilter implements RequestFilter { + public void doFilter(GenericContainerRequest request) { + EnvironmentContext.getCurrent().setSubject(SUBJECT); + } + } } diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java index 384a70417bc..053bb7c1c8f 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java @@ -33,6 +33,7 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.Appender; @@ -57,6 +58,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -76,6 +78,7 @@ import org.eclipse.che.inject.ConfigurationException; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory; +import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool; import org.mockito.ArgumentCaptor; @@ -1013,6 +1016,118 @@ public void testEvalNamespaceNameWhenPreparedNamespacesFound() throws Infrastruc assertEquals(namespace, "ns1"); } + @Test + public void shouldHandleProvision() throws InfrastructureException { + // given + namespaceFactory = + spy( + new KubernetesNamespaceFactory( + "", + "", + "-che", + false, + true, + NAMESPACE_LABELS, + NAMESPACE_ANNOTATIONS, + clientFactory, + cheClientFactory, + userManager, + preferenceManager, + pool)); + KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); + when(toReturnNamespace.getName()).thenReturn("jondoe-che"); + doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); + KubernetesNamespaceMetaImpl namespaceMeta = + new KubernetesNamespaceMetaImpl( + "jondoe-che", ImmutableMap.of("phase", "active", "default", "true")); + doReturn(Optional.of(namespaceMeta)).when(namespaceFactory).fetchNamespace(eq("jondoe-che")); + + // when + NamespaceResolutionContext context = + new NamespaceResolutionContext("workspace123", "user123", "jondoe"); + KubernetesNamespaceMeta actual = namespaceFactory.provision(context); + + // then + assertEquals(actual.getName(), "jondoe-che"); + assertEquals(actual.getAttributes(), ImmutableMap.of("phase", "active", "default", "true")); + } + + @Test( + expectedExceptions = InfrastructureException.class, + expectedExceptionsMessageRegExp = "Not able to find namespace jondoe-cha-cha-cha") + public void shouldFailToProvisionIfNotAbleToFindNamespace() throws InfrastructureException { + // given + namespaceFactory = + spy( + new KubernetesNamespaceFactory( + "", + "", + "-cha-cha-cha", + false, + true, + NAMESPACE_LABELS, + NAMESPACE_ANNOTATIONS, + clientFactory, + cheClientFactory, + userManager, + preferenceManager, + pool)); + KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); + when(toReturnNamespace.getName()).thenReturn("jondoe-cha-cha-cha"); + doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); + KubernetesNamespaceMetaImpl namespaceMeta = + new KubernetesNamespaceMetaImpl( + "jondoe-cha-cha-cha", ImmutableMap.of("phase", "active", "default", "true")); + doReturn(Optional.empty()).when(namespaceFactory).fetchNamespace(eq("jondoe-cha-cha-cha")); + + // when + NamespaceResolutionContext context = + new NamespaceResolutionContext("workspace123", "user123", "jondoe"); + namespaceFactory.provision(context); + + // then + fail("should not reach this point since exception has to be thrown"); + } + + @Test( + expectedExceptions = InfrastructureException.class, + expectedExceptionsMessageRegExp = "Error occurred when tried to fetch default namespace") + public void shouldFail2ProvisionIfNotAbleToFindNamespace() throws InfrastructureException { + // given + namespaceFactory = + spy( + new KubernetesNamespaceFactory( + "", + "", + "-cha-cha-cha", + false, + true, + NAMESPACE_LABELS, + NAMESPACE_ANNOTATIONS, + clientFactory, + cheClientFactory, + userManager, + preferenceManager, + pool)); + KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); + when(toReturnNamespace.getName()).thenReturn("jondoe-cha-cha-cha"); + doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); + KubernetesNamespaceMetaImpl namespaceMeta = + new KubernetesNamespaceMetaImpl( + "jondoe-cha-cha-cha", ImmutableMap.of("phase", "active", "default", "true")); + doThrow(new InfrastructureException("Error occurred when tried to fetch default namespace")) + .when(namespaceFactory) + .fetchNamespace(eq("jondoe-cha-cha-cha")); + + // when + NamespaceResolutionContext context = + new NamespaceResolutionContext("workspace123", "user123", "jondoe"); + namespaceFactory.provision(context); + + // then + fail("should not reach this point since exception has to be thrown"); + } + @Test public void testUsernamePlaceholderInLabelsIsNotEvaluated() throws InfrastructureException { List namespaces = From 159194c1be3134890b001c814bac9573979ffaa5 Mon Sep 17 00:00:00 2001 From: Sergii Kabashniuk Date: Tue, 3 Aug 2021 20:44:09 +0300 Subject: [PATCH 4/4] chore(build): Added extra argument to match the constructor in KubernetesNamespaceFactoryTest (#72) --- .../kubernetes/namespace/KubernetesNamespaceFactoryTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java index 053bb7c1c8f..53893dbb407 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java @@ -1027,6 +1027,7 @@ public void shouldHandleProvision() throws InfrastructureException { "-che", false, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -1065,6 +1066,7 @@ public void shouldFailToProvisionIfNotAbleToFindNamespace() throws Infrastructur "-cha-cha-cha", false, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory, @@ -1102,6 +1104,7 @@ public void shouldFail2ProvisionIfNotAbleToFindNamespace() throws Infrastructure "-cha-cha-cha", false, true, + true, NAMESPACE_LABELS, NAMESPACE_ANNOTATIONS, clientFactory,