diff --git a/deploy/kubernetes/helm/che/templates/deployment.yaml b/deploy/kubernetes/helm/che/templates/deployment.yaml index 339fb24e5db..4b084b83b9d 100644 --- a/deploy/kubernetes/helm/che/templates/deployment.yaml +++ b/deploy/kubernetes/helm/che/templates/deployment.yaml @@ -72,6 +72,25 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + + + # If git-self-signed-cert is used then configure Che Server with certificate content + # to propagate it to the specified location and provide particular configuration for Git service + {{- if .Values.global.useGitSelfSignedCerts }} + - name: CHE_GIT_SELF__SIGNED__CERT + valueFrom: + configMapKeyRef: + name: {{ .Values.global.cheGitSelfSignedCertConfigMapName }} + key: ca.crt + optional: false + - name: CHE_GIT_SELF__SIGNED__CERT__HOST + valueFrom: + configMapKeyRef: + name: {{ .Values.global.cheGitSelfSignedCertConfigMapName }} + key: githost + optional: false + {{- end }} + {{- if .Values.global.tls.enabled }} # If self-signed-cert is used then configure Che Server with certificate content diff --git a/deploy/kubernetes/helm/che/values.yaml b/deploy/kubernetes/helm/che/values.yaml index 24ab39322c6..da647e2eead 100644 --- a/deploy/kubernetes/helm/che/values.yaml +++ b/deploy/kubernetes/helm/che/values.yaml @@ -50,6 +50,13 @@ global: useSelfSignedCerts: false selfSignedCertSecretName: self-signed-cert + + ## If using git self-signed certificate is enabled + ## then certificate from `cheGitSelfSignedCertConfigMapName` will be propagated to Che components' + ## and provide particular configuration for Git + useGitSelfSignedCerts: false + cheGitSelfSignedCertConfigMapName: che-git-self-signed-cert + gitHubClientID: "" gitHubClientSecret: "" pvcClaim: "1Gi" diff --git a/deploy/openshift/templates/che-server-template.yaml b/deploy/openshift/templates/che-server-template.yaml index c6e1b3514fd..48b633f395a 100644 --- a/deploy/openshift/templates/che-server-template.yaml +++ b/deploy/openshift/templates/che-server-template.yaml @@ -159,6 +159,18 @@ objects: key: ca.crt name: self-signed-certificate optional: true + - name: CHE_GIT_SELF__SIGNED__CERT + valueFrom: + configMapKeyRef: + name: che-git-self-signed-cert + key: ca.crt + optional: true + - name: CHE_GIT_SELF__SIGNED__CERT__HOST + valueFrom: + configMapKeyRef: + name: che-git-self-signed-cert + key: githost + optional: true - name: CHE_WORKSPACE_PLUGIN__REGISTRY__URL value: "${CHE_WORKSPACE_PLUGIN__REGISTRY__URL}" - name: CHE_WORKSPACE_DEVFILE__REGISTRY__URL diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisioner.java index 2c8725d610c..7c1b3e66bcd 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisioner.java @@ -21,7 +21,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.WorkspaceVolumesStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.CertificateProvisioner; -import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GitUserProfileProvisioner; +import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GitConfigProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ImagePullSecretProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.IngressTlsProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.LogsVolumeMachineProvisioner; @@ -31,6 +31,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ServiceAccountProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.UniqueNamesProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSshKeysProvisioner; +import org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSslCertificateProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.env.EnvVarsConverter; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.limits.ram.RamLimitRequestProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.restartpolicy.RestartPolicyRewriter; @@ -73,8 +74,9 @@ class KubernetesEnvironmentProvisionerImpl private final ServiceAccountProvisioner serviceAccountProvisioner; private final CertificateProvisioner certificateProvisioner; private final VcsSshKeysProvisioner vcsSshKeysProvisioner; - private final GitUserProfileProvisioner gitUserProfileProvisioner; + private final GitConfigProvisioner gitConfigProvisioner; private final PreviewUrlExposer previewUrlExposer; + private final VcsSslCertificateProvisioner vcsSslCertificateProvisioner; @Inject public KubernetesEnvironmentProvisionerImpl( @@ -94,8 +96,9 @@ public KubernetesEnvironmentProvisionerImpl( ServiceAccountProvisioner serviceAccountProvisioner, CertificateProvisioner certificateProvisioner, VcsSshKeysProvisioner vcsSshKeysProvisioner, - GitUserProfileProvisioner gitUserProfileProvisioner, - PreviewUrlExposer previewUrlExposer) { + GitConfigProvisioner gitConfigProvisioner, + PreviewUrlExposer previewUrlExposer, + VcsSslCertificateProvisioner vcsSslCertificateProvisioner) { this.pvcEnabled = pvcEnabled; this.volumesStrategy = volumesStrategy; this.uniqueNamesProvisioner = uniqueNamesProvisioner; @@ -112,7 +115,8 @@ public KubernetesEnvironmentProvisionerImpl( this.serviceAccountProvisioner = serviceAccountProvisioner; this.certificateProvisioner = certificateProvisioner; this.vcsSshKeysProvisioner = vcsSshKeysProvisioner; - this.gitUserProfileProvisioner = gitUserProfileProvisioner; + this.vcsSslCertificateProvisioner = vcsSslCertificateProvisioner; + this.gitConfigProvisioner = gitConfigProvisioner; this.previewUrlExposer = previewUrlExposer; } @@ -152,7 +156,8 @@ public void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity) serviceAccountProvisioner.provision(k8sEnv, identity); certificateProvisioner.provision(k8sEnv, identity); vcsSshKeysProvisioner.provision(k8sEnv, identity); - gitUserProfileProvisioner.provision(k8sEnv, identity); + vcsSslCertificateProvisioner.provision(k8sEnv, identity); + gitConfigProvisioner.provision(k8sEnv, identity); LOG.debug("Provisioning Kubernetes environment done for workspace '{}'", workspaceId); } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitUserProfileProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitConfigProvisioner.java similarity index 85% rename from infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitUserProfileProvisioner.java rename to infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitConfigProvisioner.java index 843e97fc8e1..d2152fc4d81 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitUserProfileProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitConfigProvisioner.java @@ -49,7 +49,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; @Singleton -public class GitUserProfileProvisioner implements ConfigurationProvisioner { +public class GitConfigProvisioner implements ConfigurationProvisioner { private final String GIT_CONFIG_MAP_NAME_SUFFIX = "-gitconfig"; @@ -60,14 +60,20 @@ public class GitUserProfileProvisioner implements ConfigurationProvisioner prepareGitConfigurationContent(String userName, String config.append('\t').append("email = ").append(userEmail).append('\n'); } + if (vcsSslCertificateProvisioner.isConfigured()) { + String host = vcsSslCertificateProvisioner.getGitServerHost(); + + // Will add leading scheme (https://) if it not provide in configuration. + // If host not configured wil return empty string, it will means that + // provided certificate will used for all https connections. + + StringBuilder gitServerHosts = new StringBuilder(); + if (!isNullOrEmpty(host)) { + gitServerHosts.append(" \""); + if (!host.startsWith(HTTPS)) { + gitServerHosts.append(HTTPS); + } + gitServerHosts.append(host); + gitServerHosts.append("\""); + } + + config + .append("[http") + .append(gitServerHosts.toString()) + .append("]") + .append('\n') + .append('\t') + .append("sslCAInfo = ") + .append(vcsSslCertificateProvisioner.getCertPath()); + } + return of(config.toString()); } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/VcsSslCertificateProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/VcsSslCertificateProvisioner.java new file mode 100644 index 00000000000..71581253ed2 --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/VcsSslCertificateProvisioner.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.provision; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.util.Collections.singletonMap; + +import com.google.common.annotations.VisibleForTesting; +import com.google.inject.Inject; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ConfigMapVolumeSourceBuilder; +import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.Volume; +import io.fabric8.kubernetes.api.model.VolumeBuilder; +import io.fabric8.kubernetes.api.model.VolumeMount; +import io.fabric8.kubernetes.api.model.VolumeMountBuilder; +import java.util.Optional; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; + +/** + * Mount configured self-signed certificate for git provider as file in each workspace machines if + * configured. + * + * @author Vitalii Parfonov + */ +@Singleton +public class VcsSslCertificateProvisioner + implements ConfigurationProvisioner { + static final String CHE_GIT_SELF_SIGNED_CERT_CONFIG_MAP_SUFFIX = "-che-git-self-signed-cert"; + static final String CHE_GIT_SELF_SIGNED_VOLUME = "che-git-self-signed-cert"; + static final String CERT_MOUNT_PATH = "/etc/che/git/cert/"; + static final String CA_CERT_FILE = "ca.crt"; + + @Inject(optional = true) + @Named("che.git.self_signed_cert") + private String certificate; + + @Inject(optional = true) + @Named("che.git.self_signed_cert_host") + private String host; + + public VcsSslCertificateProvisioner() {} + + @VisibleForTesting + VcsSslCertificateProvisioner(String certificate, String host) { + this.certificate = certificate; + this.host = host; + } + + /** + * @return true only if system configured for using self-signed certificate fot https git + * operation + */ + public boolean isConfigured() { + return !isNullOrEmpty(certificate); + } + + /** @return path to the certificate file */ + public String getCertPath() { + return CERT_MOUNT_PATH + CA_CERT_FILE; + } + + /** + * Return given in configuration git server host (e.g. 110.23.0.1:3000). + * + * @return git server host for git config if it configured in che.git.self_signed_cert_host + */ + public String getGitServerHost() { + return host; + } + + @Override + public void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity) + throws InfrastructureException { + if (!isConfigured()) { + return; + } + String selfSignedCertConfigMapName = + identity.getWorkspaceId() + CHE_GIT_SELF_SIGNED_CERT_CONFIG_MAP_SUFFIX; + k8sEnv + .getConfigMaps() + .put( + selfSignedCertConfigMapName, + new ConfigMapBuilder() + .withNewMetadata() + .withName(selfSignedCertConfigMapName) + .endMetadata() + .withData(singletonMap(CA_CERT_FILE, certificate)) + .build()); + + for (PodData pod : k8sEnv.getPodsData().values()) { + Optional certVolume = + pod.getSpec() + .getVolumes() + .stream() + .filter(v -> v.getName().equals(CHE_GIT_SELF_SIGNED_VOLUME)) + .findAny(); + + if (!certVolume.isPresent()) { + pod.getSpec().getVolumes().add(buildCertVolume(selfSignedCertConfigMapName)); + } + + for (Container container : pod.getSpec().getInitContainers()) { + provisionCertVolumeMountIfNeeded(container); + } + for (Container container : pod.getSpec().getContainers()) { + provisionCertVolumeMountIfNeeded(container); + } + } + } + + private void provisionCertVolumeMountIfNeeded(Container container) { + Optional certVolumeMount = + container + .getVolumeMounts() + .stream() + .filter(vm -> vm.getName().equals(CHE_GIT_SELF_SIGNED_VOLUME)) + .findAny(); + if (!certVolumeMount.isPresent()) { + container.getVolumeMounts().add(buildCertVolumeMount()); + } + } + + private VolumeMount buildCertVolumeMount() { + return new VolumeMountBuilder() + .withName(CHE_GIT_SELF_SIGNED_VOLUME) + .withNewReadOnly(true) + .withMountPath(CERT_MOUNT_PATH) + .build(); + } + + private Volume buildCertVolume(String configMapName) { + return new VolumeBuilder() + .withName(CHE_GIT_SELF_SIGNED_VOLUME) + .withConfigMap(new ConfigMapVolumeSourceBuilder().withName(configMapName).build()) + .build(); + } +} diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisionerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisionerTest.java index 970ce43c3a2..ed60bdd2daa 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisionerTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisionerTest.java @@ -19,7 +19,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.WorkspaceVolumesStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.CertificateProvisioner; -import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GitUserProfileProvisioner; +import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GitConfigProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ImagePullSecretProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.IngressTlsProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.LogsVolumeMachineProvisioner; @@ -29,6 +29,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ServiceAccountProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.UniqueNamesProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSshKeysProvisioner; +import org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSslCertificateProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.env.EnvVarsConverter; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.limits.ram.RamLimitRequestProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.restartpolicy.RestartPolicyRewriter; @@ -66,8 +67,9 @@ public class KubernetesEnvironmentProvisionerTest { @Mock private ServiceAccountProvisioner serviceAccountProvisioner; @Mock private CertificateProvisioner certificateProvisioner; @Mock private VcsSshKeysProvisioner vcsSshKeysProvisioner; - @Mock private GitUserProfileProvisioner gitUserProfileProvisioner; + @Mock private GitConfigProvisioner gitConfigProvisioner; @Mock private PreviewUrlExposer previewUrlExposer; + @Mock private VcsSslCertificateProvisioner vcsSslCertificateProvisioner; private KubernetesEnvironmentProvisioner k8sInfraProvisioner; @@ -93,8 +95,9 @@ public void setUp() { serviceAccountProvisioner, certificateProvisioner, vcsSshKeysProvisioner, - gitUserProfileProvisioner, - previewUrlExposer); + gitConfigProvisioner, + previewUrlExposer, + vcsSslCertificateProvisioner); provisionOrder = inOrder( logsVolumeMachineProvisioner, @@ -111,7 +114,7 @@ public void setUp() { proxySettingsProvisioner, serviceAccountProvisioner, certificateProvisioner, - gitUserProfileProvisioner, + gitConfigProvisioner, previewUrlExposer); } @@ -137,7 +140,7 @@ public void performsOrderedProvisioning() throws Exception { provisionOrder.verify(proxySettingsProvisioner).provision(eq(k8sEnv), eq(runtimeIdentity)); provisionOrder.verify(serviceAccountProvisioner).provision(eq(k8sEnv), eq(runtimeIdentity)); provisionOrder.verify(certificateProvisioner).provision(eq(k8sEnv), eq(runtimeIdentity)); - provisionOrder.verify(gitUserProfileProvisioner).provision(eq(k8sEnv), eq(runtimeIdentity)); + provisionOrder.verify(gitConfigProvisioner).provision(eq(k8sEnv), eq(runtimeIdentity)); provisionOrder.verifyNoMoreInteractions(); } } diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitUserProfileProvisionerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitConfigProvisionerTest.java similarity index 84% rename from infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitUserProfileProvisionerTest.java rename to infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitConfigProvisionerTest.java index 55e8c18b1cc..77e570782d5 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitUserProfileProvisionerTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/GitConfigProvisionerTest.java @@ -52,7 +52,7 @@ import org.testng.annotations.Test; @Listeners(MockitoTestNGListener.class) -public class GitUserProfileProvisionerTest { +public class GitConfigProvisionerTest { private KubernetesEnvironment k8sEnv; @@ -68,7 +68,9 @@ public class GitUserProfileProvisionerTest { @Mock private UserManager userManager; - private GitUserProfileProvisioner gitUserProfileProvisioner; + @Mock private VcsSslCertificateProvisioner vcsSslCertificateProvisioner; + + private GitConfigProvisioner gitConfigProvisioner; @BeforeMethod public void setup() { @@ -77,7 +79,8 @@ public void setup() { when(pod.getMetadata()).thenReturn(podMeta); when(pod.getSpec()).thenReturn(podSpec); k8sEnv.addPod(pod); - gitUserProfileProvisioner = new GitUserProfileProvisioner(preferenceManager, userManager); + gitConfigProvisioner = + new GitConfigProvisioner(preferenceManager, userManager, vcsSslCertificateProvisioner); Subject subject = new SubjectImpl(null, "id", null, false); EnvironmentContext environmentContext = new EnvironmentContext(); @@ -95,7 +98,7 @@ public void testShouldDoNothingWhenGitPreferencesAndUserManagerAreEmpty() throws when(user.getName()).thenReturn(null); when(user.getEmail()).thenReturn(null); - gitUserProfileProvisioner.provision(k8sEnv, runtimeIdentity); + gitConfigProvisioner.provision(k8sEnv, runtimeIdentity); verifyZeroInteractions(runtimeIdentity); } @@ -105,7 +108,7 @@ public void testShouldExpectWarningWhenPreferenceManagerThrowsServerException() when(preferenceManager.find(eq("id"), eq("theia-user-preferences"))) .thenThrow(new ServerException("message")); - gitUserProfileProvisioner.provision(k8sEnv, runtimeIdentity); + gitConfigProvisioner.provision(k8sEnv, runtimeIdentity); verifyZeroInteractions(runtimeIdentity); @@ -128,7 +131,7 @@ public void testShouldExpectWarningWhenJsonObjectInPreferencesIsNotValid() throw Map preferences = singletonMap("theia-user-preferences", "{#$%}"); when(preferenceManager.find(eq("id"), eq("theia-user-preferences"))).thenReturn(preferences); - gitUserProfileProvisioner.provision(k8sEnv, runtimeIdentity); + gitConfigProvisioner.provision(k8sEnv, runtimeIdentity); verifyZeroInteractions(runtimeIdentity); @@ -153,7 +156,7 @@ public void testShouldExpectWarningWhenJsonObjectInPreferencesIsNotValid() throw public void testShouldExpectWarningWhenUserManagerThrowsServerException() throws Exception { when(userManager.getById(eq("id"))).thenThrow(new ServerException("message")); - gitUserProfileProvisioner.provision(k8sEnv, runtimeIdentity); + gitConfigProvisioner.provision(k8sEnv, runtimeIdentity); verifyZeroInteractions(runtimeIdentity); @@ -188,7 +191,7 @@ public void testShouldCheckIfPodHasMountAndK8HasConfigMapForGitConfig() throws E when(container.getVolumeMounts()).thenReturn(volumeMounts); k8sEnv.addPod(pod); - gitUserProfileProvisioner.provision(k8sEnv, runtimeIdentity); + gitConfigProvisioner.provision(k8sEnv, runtimeIdentity); assertEquals(volumeMounts.size(), 1); @@ -240,7 +243,7 @@ public void testShouldParseOnlyNameWhenEmailIsNotAString(String json) throws Exc when(container.getVolumeMounts()).thenReturn(volumeMounts); k8sEnv.addPod(pod); - gitUserProfileProvisioner.provision(k8sEnv, runtimeIdentity); + gitConfigProvisioner.provision(k8sEnv, runtimeIdentity); assertEquals(volumeMounts.size(), 1); @@ -292,7 +295,7 @@ public void testShouldParseOnlyEmailWhenNameIsNotAString(String json) throws Exc when(container.getVolumeMounts()).thenReturn(volumeMounts); k8sEnv.addPod(pod); - gitUserProfileProvisioner.provision(k8sEnv, runtimeIdentity); + gitConfigProvisioner.provision(k8sEnv, runtimeIdentity); assertEquals(volumeMounts.size(), 1); @@ -337,7 +340,7 @@ public void testShouldParseOnlyEmailWhenNameIsNotAString(String json) throws Exc when(container.getVolumeMounts()).thenReturn(volumeMounts); k8sEnv.addPod(pod); - gitUserProfileProvisioner.provision(k8sEnv, runtimeIdentity); + gitConfigProvisioner.provision(k8sEnv, runtimeIdentity); assertEquals(volumeMounts.size(), 1); @@ -385,7 +388,7 @@ public void testShouldProvisionNameAndEmailFromUserManagerWhenUserPreferencesEmp when(container.getVolumeMounts()).thenReturn(volumeMounts); k8sEnv.addPod(pod); - gitUserProfileProvisioner.provision(k8sEnv, runtimeIdentity); + gitConfigProvisioner.provision(k8sEnv, runtimeIdentity); assertEquals(volumeMounts.size(), 1); @@ -409,4 +412,53 @@ public void testShouldProvisionNameAndEmailFromUserManagerWhenUserPreferencesEmp assertEquals(gitconfig, expectedGitconfig); } + + @Test + public void testShouldProvisionConfigForHttpsServer() throws Exception { + when(vcsSslCertificateProvisioner.isConfigured()).thenReturn(true); + when(vcsSslCertificateProvisioner.getGitServerHost()).thenReturn("https://localhost"); + when(vcsSslCertificateProvisioner.getCertPath()).thenReturn("/some/path"); + + when(runtimeIdentity.getWorkspaceId()).thenReturn("wksp"); + + ObjectMeta podMeta = new ObjectMetaBuilder().withName("wksp").build(); + when(pod.getMetadata()).thenReturn(podMeta); + when(pod.getSpec()).thenReturn(podSpec); + when(podSpec.getContainers()).thenReturn(singletonList(container)); + + User userMock = mock(User.class); + when(userMock.getName()).thenReturn("userMockName"); + when(userMock.getEmail()).thenReturn("userMockEmail"); + when(userManager.getById(eq("id"))).thenReturn(userMock); + + List volumeMounts = new ArrayList<>(); + + when(container.getVolumeMounts()).thenReturn(volumeMounts); + k8sEnv.addPod(pod); + + gitConfigProvisioner.provision(k8sEnv, runtimeIdentity); + + assertEquals(volumeMounts.size(), 1); + + VolumeMount mount = volumeMounts.get(0); + + assertEquals(mount.getMountPath(), "/etc/gitconfig"); + assertEquals(mount.getName(), "gitconfigvolume"); + assertFalse(mount.getReadOnly()); + assertEquals(mount.getSubPath(), "gitconfig"); + + assertEquals(k8sEnv.getConfigMaps().size(), 1); + assertTrue(k8sEnv.getConfigMaps().containsKey("wksp-gitconfig")); + + ConfigMap configMap = k8sEnv.getConfigMaps().get("wksp-gitconfig"); + + assertEquals(configMap.getData().size(), 1); + assertTrue(configMap.getData().containsKey("gitconfig")); + + String gitconfig = configMap.getData().get("gitconfig"); + String expectedGitconfig = + "[user]\n\tname = userMockName\n\temail = userMockEmail\n[http \"https://localhost\"]\n\tsslCAInfo = /some/path"; + + assertEquals(gitconfig, expectedGitconfig); + } } diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/VcsSslCertificateProvisionerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/VcsSslCertificateProvisionerTest.java new file mode 100644 index 00000000000..129d776f997 --- /dev/null +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/VcsSslCertificateProvisionerTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.provision; + +import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSslCertificateProvisioner.CA_CERT_FILE; +import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSslCertificateProvisioner.CERT_MOUNT_PATH; +import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSslCertificateProvisioner.CHE_GIT_SELF_SIGNED_CERT_CONFIG_MAP_SUFFIX; +import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSslCertificateProvisioner.CHE_GIT_SELF_SIGNED_VOLUME; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapVolumeSource; +import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.ContainerBuilder; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodBuilder; +import io.fabric8.kubernetes.api.model.Volume; +import io.fabric8.kubernetes.api.model.VolumeMount; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +/** + * Tests {@link VcsSslCertificateProvisioner}. + * + * @author Vitalii Parfonov + */ +@Listeners(MockitoTestNGListener.class) +public class VcsSslCertificateProvisionerTest { + + private static final String WORKSPACE_ID = "workspace123"; + private static final String EXPECTED_CERT_NAME = + WORKSPACE_ID + CHE_GIT_SELF_SIGNED_CERT_CONFIG_MAP_SUFFIX; + + private static final String CERT_CONTENT = + "-----BEGIN CERTIFICATE-----\n" + + UUID.randomUUID().toString() + + "\n-----END CERTIFICATE-----"; + @Mock private RuntimeIdentity runtimeId; + + private VcsSslCertificateProvisioner provisioner; + private KubernetesEnvironment k8sEnv; + + @BeforeMethod + public void setUp() { + when(runtimeId.getWorkspaceId()).thenReturn(WORKSPACE_ID); + + provisioner = new VcsSslCertificateProvisioner(CERT_CONTENT, ""); + k8sEnv = KubernetesEnvironment.builder().build(); + } + + @Test + public void shouldReturnFalseIfCertificateIsNotConfigured() { + provisioner = new VcsSslCertificateProvisioner(); + assertFalse(provisioner.isConfigured()); + } + + @Test + public void shouldReturnTrueIfCertificateIsConfigured() { + provisioner = new VcsSslCertificateProvisioner(CERT_CONTENT, "localhost"); + assertTrue(provisioner.isConfigured()); + } + + @Test + public void shouldReturnCertPathFile() { + String certPath = provisioner.getCertPath(); + assertEquals(certPath, "/etc/che/git/cert/ca.crt"); + } + + @Test + public void shouldAddConfigMapWithCertificateIntoEnvironment() throws Exception { + provisioner.provision(k8sEnv, runtimeId); + + Map configMaps = k8sEnv.getConfigMaps(); + assertEquals(configMaps.size(), 1); + + ConfigMap configMap = configMaps.get(EXPECTED_CERT_NAME); + assertNotNull(configMap); + assertEquals(configMap.getMetadata().getName(), EXPECTED_CERT_NAME); + assertEquals(configMap.getData().get(CA_CERT_FILE), CERT_CONTENT); + } + + @Test + public void shouldAddVolumeAndVolumeMountsToPodsAndContainersInEnvironment() throws Exception { + k8sEnv.addPod(createPod("pod")); + k8sEnv.addPod(createPod("pod2")); + + provisioner.provision(k8sEnv, runtimeId); + + for (Pod pod : k8sEnv.getPodsCopy().values()) { + verifyVolumeIsPresent(pod); + + for (Container container : pod.getSpec().getInitContainers()) { + verifyVolumeMountIsPresent(container); + } + + for (Container container : pod.getSpec().getContainers()) { + verifyVolumeMountIsPresent(container); + } + } + } + + @Test + public void + shouldNotAddVolumeAndVolumeMountsToPodsAndContainersInEnvironmentIfCertIsNotConfigured() + throws Exception { + provisioner = new VcsSslCertificateProvisioner("", ""); + k8sEnv.addPod(createPod("pod")); + k8sEnv.addPod(createPod("pod2")); + + provisioner.provision(k8sEnv, runtimeId); + + for (Pod pod : k8sEnv.getPodsCopy().values()) { + assertTrue(pod.getSpec().getVolumes().isEmpty()); + for (Container container : pod.getSpec().getContainers()) { + assertTrue(container.getVolumeMounts().isEmpty()); + } + } + } + + private void verifyVolumeIsPresent(Pod pod) { + List podVolumes = pod.getSpec().getVolumes(); + assertEquals(podVolumes.size(), 1); + Volume certVolume = podVolumes.get(0); + assertEquals(certVolume.getName(), CHE_GIT_SELF_SIGNED_VOLUME); + ConfigMapVolumeSource volumeConfigMap = certVolume.getConfigMap(); + assertNotNull(volumeConfigMap); + assertEquals(volumeConfigMap.getName(), EXPECTED_CERT_NAME); + } + + private void verifyVolumeMountIsPresent(Container container) { + List volumeMounts = container.getVolumeMounts(); + assertEquals(volumeMounts.size(), 1); + VolumeMount volumeMount = volumeMounts.get(0); + assertEquals(volumeMount.getName(), CHE_GIT_SELF_SIGNED_VOLUME); + assertTrue(volumeMount.getReadOnly()); + assertEquals(volumeMount.getMountPath(), CERT_MOUNT_PATH); + } + + private Pod createPod(String podName) { + return new PodBuilder() + .withNewMetadata() + .withName(podName) + .endMetadata() + .withNewSpec() + .withInitContainers(new ContainerBuilder().build()) + .withContainers(new ContainerBuilder().build(), new ContainerBuilder().build()) + .endSpec() + .build(); + } +} diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisioner.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisioner.java index 505eef32329..e02e68838e3 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisioner.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisioner.java @@ -21,7 +21,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesEnvironmentProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.WorkspaceVolumesStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.CertificateProvisioner; -import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GitUserProfileProvisioner; +import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GitConfigProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ImagePullSecretProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.LogsVolumeMachineProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.PodTerminationGracePeriodProvisioner; @@ -29,6 +29,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ServiceAccountProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.UniqueNamesProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSshKeysProvisioner; +import org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSslCertificateProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.env.EnvVarsConverter; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.limits.ram.RamLimitRequestProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.restartpolicy.RestartPolicyRewriter; @@ -69,8 +70,9 @@ public class OpenShiftEnvironmentProvisioner private final ServiceAccountProvisioner serviceAccountProvisioner; private final CertificateProvisioner certificateProvisioner; private final VcsSshKeysProvisioner vcsSshKeysProvisioner; - private final GitUserProfileProvisioner gitUserProfileProvisioner; + private final GitConfigProvisioner gitConfigProvisioner; private final PreviewUrlExposer previewUrlExposer; + private final VcsSslCertificateProvisioner vcsSslCertificateProvisioner; @Inject public OpenShiftEnvironmentProvisioner( @@ -89,8 +91,9 @@ public OpenShiftEnvironmentProvisioner( ServiceAccountProvisioner serviceAccountProvisioner, CertificateProvisioner certificateProvisioner, VcsSshKeysProvisioner vcsSshKeysProvisioner, - GitUserProfileProvisioner gitUserProfileProvisioner, - OpenShiftPreviewUrlExposer previewUrlEndpointsProvisioner) { + GitConfigProvisioner gitConfigProvisioner, + OpenShiftPreviewUrlExposer previewUrlEndpointsProvisioner, + VcsSslCertificateProvisioner vcsSslCertificateProvisioner) { this.pvcEnabled = pvcEnabled; this.volumesStrategy = volumesStrategy; this.uniqueNamesProvisioner = uniqueNamesProvisioner; @@ -106,8 +109,9 @@ public OpenShiftEnvironmentProvisioner( this.serviceAccountProvisioner = serviceAccountProvisioner; this.certificateProvisioner = certificateProvisioner; this.vcsSshKeysProvisioner = vcsSshKeysProvisioner; - this.gitUserProfileProvisioner = gitUserProfileProvisioner; + this.gitConfigProvisioner = gitConfigProvisioner; this.previewUrlExposer = previewUrlEndpointsProvisioner; + this.vcsSslCertificateProvisioner = vcsSslCertificateProvisioner; } @Override @@ -143,7 +147,8 @@ public void provision(OpenShiftEnvironment osEnv, RuntimeIdentity identity) serviceAccountProvisioner.provision(osEnv, identity); certificateProvisioner.provision(osEnv, identity); vcsSshKeysProvisioner.provision(osEnv, identity); - gitUserProfileProvisioner.provision(osEnv, identity); + vcsSslCertificateProvisioner.provision(osEnv, identity); + gitConfigProvisioner.provision(osEnv, identity); LOG.debug( "Provisioning OpenShift environment done for workspace '{}'", identity.getWorkspaceId()); } diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisionerTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisionerTest.java index 53b7f01c381..50ad61c3653 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisionerTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisionerTest.java @@ -17,13 +17,14 @@ import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.WorkspaceVolumesStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.CertificateProvisioner; -import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GitUserProfileProvisioner; +import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GitConfigProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ImagePullSecretProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.LogsVolumeMachineProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.PodTerminationGracePeriodProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ProxySettingsProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ServiceAccountProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSshKeysProvisioner; +import org.eclipse.che.workspace.infrastructure.kubernetes.provision.VcsSslCertificateProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.env.EnvVarsConverter; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.limits.ram.RamLimitRequestProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.restartpolicy.RestartPolicyRewriter; @@ -63,8 +64,9 @@ public class OpenShiftEnvironmentProvisionerTest { @Mock private ServiceAccountProvisioner serviceAccountProvisioner; @Mock private CertificateProvisioner certificateProvisioner; @Mock private VcsSshKeysProvisioner vcsSshKeysProvisioner; - @Mock private GitUserProfileProvisioner gitUserProfileProvisioner; + @Mock private GitConfigProvisioner gitConfigProvisioner; @Mock private OpenShiftPreviewUrlExposer previewUrlEndpointsProvisioner; + @Mock private VcsSslCertificateProvisioner vcsSslCertificateProvisioner; private OpenShiftEnvironmentProvisioner osInfraProvisioner; @@ -89,8 +91,9 @@ public void setUp() { serviceAccountProvisioner, certificateProvisioner, vcsSshKeysProvisioner, - gitUserProfileProvisioner, - previewUrlEndpointsProvisioner); + gitConfigProvisioner, + previewUrlEndpointsProvisioner, + vcsSslCertificateProvisioner); provisionOrder = inOrder( logsVolumeMachineProvisioner, @@ -107,7 +110,8 @@ public void setUp() { serviceAccountProvisioner, certificateProvisioner, vcsSshKeysProvisioner, - gitUserProfileProvisioner, + vcsSslCertificateProvisioner, + gitConfigProvisioner, previewUrlEndpointsProvisioner); } @@ -131,7 +135,8 @@ public void performsOrderedProvisioning() throws Exception { provisionOrder.verify(serviceAccountProvisioner).provision(eq(osEnv), eq(runtimeIdentity)); provisionOrder.verify(certificateProvisioner).provision(eq(osEnv), eq(runtimeIdentity)); provisionOrder.verify(vcsSshKeysProvisioner).provision(eq(osEnv), eq(runtimeIdentity)); - provisionOrder.verify(gitUserProfileProvisioner).provision(eq(osEnv), eq(runtimeIdentity)); + provisionOrder.verify(vcsSslCertificateProvisioner).provision(eq(osEnv), eq(runtimeIdentity)); + provisionOrder.verify(gitConfigProvisioner).provision(eq(osEnv), eq(runtimeIdentity)); provisionOrder.verifyNoMoreInteractions(); } }