fix: devfile endpoint with single-host exposure (#157)

Signed-off-by: Michal Vala <mvala@redhat.com>
pull/154/head
Michal Vala 2021-10-06 18:35:15 +02:00 committed by GitHub
parent 63d0ab9c89
commit 3a99240d2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 500 additions and 43 deletions

View File

@ -17,7 +17,9 @@ import static java.lang.String.format;
import static org.eclipse.che.api.core.model.workspace.config.Command.MACHINE_NAME_ATTRIBUTE;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.DEVFILE_COMPONENT_ALIAS_ATTRIBUTE;
import static org.eclipse.che.api.workspace.server.devfile.Constants.DOCKERIMAGE_COMPONENT_TYPE;
import static org.eclipse.che.api.workspace.server.devfile.convert.component.ComponentToWorkspaceApplier.convertEndpointsIntoServers;
import static org.eclipse.che.api.workspace.shared.Constants.PROJECTS_VOLUME_NAME;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostExternalServiceExposureStrategy.SINGLE_HOST_STRATEGY;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
@ -32,13 +34,11 @@ import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.che.api.core.model.workspace.devfile.Endpoint;
import org.eclipse.che.api.workspace.server.devfile.Constants;
import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.convert.component.ComponentToWorkspaceApplier;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
import org.eclipse.che.api.workspace.server.model.impl.MachineConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
@ -65,15 +65,19 @@ public class DockerimageComponentToWorkspaceApplier implements ComponentToWorksp
private final String projectFolderPath;
private final String imagePullPolicy;
private final KubernetesEnvironmentProvisioner k8sEnvProvisioner;
private final String devfileEndpointsExposure;
@Inject
public DockerimageComponentToWorkspaceApplier(
@Named("che.workspace.projects.storage") String projectFolderPath,
@Named("che.workspace.sidecar.image_pull_policy") String imagePullPolicy,
@Named("che.infra.kubernetes.singlehost.workspace.devfile_endpoint_exposure")
String devfileEndpointsExposure,
KubernetesEnvironmentProvisioner k8sEnvProvisioner) {
this.projectFolderPath = projectFolderPath;
this.imagePullPolicy = imagePullPolicy;
this.k8sEnvProvisioner = k8sEnvProvisioner;
this.devfileEndpointsExposure = devfileEndpointsExposure;
}
/**
@ -137,12 +141,9 @@ public class DockerimageComponentToWorkspaceApplier implements ComponentToWorksp
machineConfig
.getServers()
.putAll(
dockerimageComponent
.getEndpoints()
.stream()
.collect(
Collectors.toMap(
Endpoint::getName, e -> ServerConfigImpl.createFromEndpoint(e, true))));
convertEndpointsIntoServers(
dockerimageComponent.getEndpoints(),
!SINGLE_HOST_STRATEGY.equals(devfileEndpointsExposure)));
dockerimageComponent
.getVolumes()

View File

@ -18,12 +18,14 @@ import static java.util.stream.Collectors.toList;
import static org.eclipse.che.api.core.model.workspace.config.Command.MACHINE_NAME_ATTRIBUTE;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.DEVFILE_COMPONENT_ALIAS_ATTRIBUTE;
import static org.eclipse.che.api.workspace.server.devfile.Components.getIdentifiableComponentName;
import static org.eclipse.che.api.workspace.server.devfile.convert.component.ComponentToWorkspaceApplier.convertEndpointsIntoServers;
import static org.eclipse.che.api.workspace.shared.Constants.PROJECTS_VOLUME_NAME;
import static org.eclipse.che.workspace.infrastructure.kubernetes.Names.machineName;
import static org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesDevfileBindings.KUBERNETES_BASED_COMPONENTS_KEY_NAME;
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesObjectUtil.newPVC;
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesObjectUtil.newVolume;
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesObjectUtil.newVolumeMount;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostExternalServiceExposureStrategy.SINGLE_HOST_STRATEGY;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.HasMetadata;
@ -39,14 +41,12 @@ 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;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.che.api.core.model.workspace.WorkspaceConfig;
import org.eclipse.che.api.core.model.workspace.config.Command;
import org.eclipse.che.api.core.model.workspace.devfile.Component;
import org.eclipse.che.api.core.model.workspace.devfile.Endpoint;
import org.eclipse.che.api.core.model.workspace.devfile.Entrypoint;
import org.eclipse.che.api.workspace.server.devfile.Constants;
import org.eclipse.che.api.workspace.server.devfile.DevfileRecipeFormatException;
@ -54,7 +54,6 @@ import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.convert.component.ComponentToWorkspaceApplier;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
import org.eclipse.che.api.workspace.server.model.impl.MachineConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
@ -80,6 +79,7 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa
private final String defaultPVCAccessMode;
private final String pvcStorageClassName;
private final EnvVars envVars;
private final String devfileEndpointsExposure;
@Inject
public KubernetesComponentToWorkspaceApplier(
@ -91,6 +91,8 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa
@Named("che.infra.kubernetes.pvc.access_mode") String defaultPVCAccessMode,
@Named("che.infra.kubernetes.pvc.storage_class_name") String pvcStorageClassName,
@Named("che.workspace.sidecar.image_pull_policy") String imagePullPolicy,
@Named("che.infra.kubernetes.singlehost.workspace.devfile_endpoint_exposure")
String devfileEndpointsExposure,
@Named(KUBERNETES_BASED_COMPONENTS_KEY_NAME) Set<String> kubernetesBasedComponentTypes) {
this(
objectsParser,
@ -102,6 +104,7 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa
defaultPVCAccessMode,
pvcStorageClassName,
imagePullPolicy,
devfileEndpointsExposure,
kubernetesBasedComponentTypes);
}
@ -115,6 +118,7 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa
String defaultPVCAccessMode,
String pvcStorageClassName,
String imagePullPolicy,
String devfileEndpointsExposure,
Set<String> kubernetesBasedComponentTypes) {
this.objectsParser = objectsParser;
this.k8sEnvProvisioner = k8sEnvProvisioner;
@ -126,6 +130,7 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa
this.imagePullPolicy = imagePullPolicy;
this.kubernetesBasedComponentTypes = kubernetesBasedComponentTypes;
this.envVars = envVars;
this.devfileEndpointsExposure = devfileEndpointsExposure;
}
/**
@ -267,12 +272,8 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa
config
.getServers()
.putAll(
component
.getEndpoints()
.stream()
.collect(
Collectors.toMap(
Endpoint::getName, e -> ServerConfigImpl.createFromEndpoint(e, true))));
convertEndpointsIntoServers(
component.getEndpoints(), !SINGLE_HOST_STRATEGY.equals(devfileEndpointsExposure)));
}
private void provisionVolumes(

View File

@ -18,6 +18,8 @@ import static org.eclipse.che.api.workspace.server.devfile.Constants.DOCKERIMAGE
import static org.eclipse.che.api.workspace.server.devfile.Constants.PUBLIC_ENDPOINT_ATTRIBUTE;
import static org.eclipse.che.api.workspace.shared.Constants.PROJECTS_VOLUME_NAME;
import static org.eclipse.che.workspace.infrastructure.kubernetes.devfile.DockerimageComponentToWorkspaceApplier.CHE_COMPONENT_NAME_LABEL;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostExternalServiceExposureStrategy.MULTI_HOST_STRATEGY;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostExternalServiceExposureStrategy.SINGLE_HOST_STRATEGY;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@ -80,7 +82,7 @@ public class DockerimageComponentToWorkspaceApplierTest {
public void setUp() throws Exception {
dockerimageComponentApplier =
new DockerimageComponentToWorkspaceApplier(
PROJECTS_MOUNT_PATH, "Always", k8sEnvProvisioner);
PROJECTS_MOUNT_PATH, "Always", MULTI_HOST_STRATEGY, k8sEnvProvisioner);
workspaceConfig = new WorkspaceConfigImpl();
}
@ -135,7 +137,8 @@ public class DockerimageComponentToWorkspaceApplierTest {
dockerimageComponent.setImage("eclipse/ubuntu_jdk8:latest");
dockerimageComponent.setMemoryLimit("1G");
dockerimageComponentApplier =
new DockerimageComponentToWorkspaceApplier(PROJECTS_MOUNT_PATH, "Never", k8sEnvProvisioner);
new DockerimageComponentToWorkspaceApplier(
PROJECTS_MOUNT_PATH, "Never", MULTI_HOST_STRATEGY, k8sEnvProvisioner);
// when
dockerimageComponentApplier.apply(workspaceConfig, dockerimageComponent, null);
@ -570,6 +573,79 @@ public class DockerimageComponentToWorkspaceApplierTest {
assertEquals(container.getArgs(), args);
}
@Test
public void serverMustHaveRequireSubdomainWhenNonSinglehostDevfileExpose()
throws DevfileException {
dockerimageComponentApplier =
new DockerimageComponentToWorkspaceApplier(
PROJECTS_MOUNT_PATH, "Always", MULTI_HOST_STRATEGY, k8sEnvProvisioner);
// given
EndpointImpl endpoint = new EndpointImpl("jdk-ls", 4923, emptyMap());
ComponentImpl dockerimageComponent = new ComponentImpl();
dockerimageComponent.setAlias("jdk");
dockerimageComponent.setType(DOCKERIMAGE_COMPONENT_TYPE);
dockerimageComponent.setImage("eclipse/ubuntu_jdk8:latest");
dockerimageComponent.setMemoryLimit("100M");
dockerimageComponent.setEndpoints(
Arrays.asList(
new EndpointImpl("e1", 1111, emptyMap()), new EndpointImpl("e2", 2222, emptyMap())));
// when
dockerimageComponentApplier.apply(workspaceConfig, dockerimageComponent, null);
// then
verify(k8sEnvProvisioner)
.provision(
eq(workspaceConfig),
eq(KubernetesEnvironment.TYPE),
objectsCaptor.capture(),
machinesCaptor.capture());
MachineConfigImpl machineConfig = machinesCaptor.getValue().get("jdk");
assertNotNull(machineConfig);
assertEquals(machineConfig.getServers().size(), 2);
assertTrue(
ServerConfig.isRequireSubdomain(machineConfig.getServers().get("e1").getAttributes()));
assertTrue(
ServerConfig.isRequireSubdomain(machineConfig.getServers().get("e2").getAttributes()));
}
@Test
public void serverCantHaveRequireSubdomainWhenSinglehostDevfileExpose() throws DevfileException {
dockerimageComponentApplier =
new DockerimageComponentToWorkspaceApplier(
PROJECTS_MOUNT_PATH, "Always", SINGLE_HOST_STRATEGY, k8sEnvProvisioner);
// given
EndpointImpl endpoint = new EndpointImpl("jdk-ls", 4923, emptyMap());
ComponentImpl dockerimageComponent = new ComponentImpl();
dockerimageComponent.setAlias("jdk");
dockerimageComponent.setType(DOCKERIMAGE_COMPONENT_TYPE);
dockerimageComponent.setImage("eclipse/ubuntu_jdk8:latest");
dockerimageComponent.setMemoryLimit("100M");
dockerimageComponent.setEndpoints(
Arrays.asList(
new EndpointImpl("e1", 1111, emptyMap()), new EndpointImpl("e2", 2222, emptyMap())));
// when
dockerimageComponentApplier.apply(workspaceConfig, dockerimageComponent, null);
// then
verify(k8sEnvProvisioner)
.provision(
eq(workspaceConfig),
eq(KubernetesEnvironment.TYPE),
objectsCaptor.capture(),
machinesCaptor.capture());
MachineConfigImpl machineConfig = machinesCaptor.getValue().get("jdk");
assertNotNull(machineConfig);
assertEquals(machineConfig.getServers().size(), 2);
assertFalse(
ServerConfig.isRequireSubdomain(machineConfig.getServers().get("e1").getAttributes()));
assertFalse(
ServerConfig.isRequireSubdomain(machineConfig.getServers().get("e2").getAttributes()));
}
@Test(dataProvider = "imageNames")
public void testGeneratesValidMachineNameFromImageName(String imageName)
throws ValidationException, DevfileException {

View File

@ -23,6 +23,8 @@ import static org.eclipse.che.api.workspace.server.devfile.Constants.COMPONENT_A
import static org.eclipse.che.api.workspace.server.devfile.Constants.KUBERNETES_COMPONENT_TYPE;
import static org.eclipse.che.api.workspace.server.devfile.Constants.OPENSHIFT_COMPONENT_TYPE;
import static org.eclipse.che.api.workspace.shared.Constants.PROJECTS_VOLUME_NAME;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostExternalServiceExposureStrategy.MULTI_HOST_STRATEGY;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostExternalServiceExposureStrategy.SINGLE_HOST_STRATEGY;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
@ -31,6 +33,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
@ -43,6 +46,7 @@ import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@ -51,6 +55,7 @@ import java.util.Set;
import java.util.stream.Stream;
import org.eclipse.che.api.core.ValidationException;
import org.eclipse.che.api.core.model.workspace.config.MachineConfig;
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
import org.eclipse.che.api.workspace.server.devfile.URLFileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
import org.eclipse.che.api.workspace.server.model.impl.CommandImpl;
@ -110,6 +115,7 @@ public class KubernetesComponentToWorkspaceApplierTest {
"ReadWriteOnce",
"",
"Always",
MULTI_HOST_STRATEGY,
k8sBasedComponents);
workspaceConfig = new WorkspaceConfigImpl();
@ -560,6 +566,7 @@ public class KubernetesComponentToWorkspaceApplierTest {
"ReadWriteOnce",
"",
"Never",
MULTI_HOST_STRATEGY,
k8sBasedComponents);
String yamlRecipeContent = getResource("devfile/petclinic.yaml");
doReturn(toK8SList(yamlRecipeContent).getItems()).when(k8sRecipeParser).parse(anyString());
@ -693,6 +700,110 @@ public class KubernetesComponentToWorkspaceApplierTest {
});
}
@Test
public void serverCantHaveRequireSubdomainWhenSinglehostDevfileExpose()
throws DevfileException, IOException, ValidationException, InfrastructureException {
applier =
new KubernetesComponentToWorkspaceApplier(
k8sRecipeParser,
k8sEnvProvisioner,
envVars,
PROJECT_MOUNT_PATH,
"1Gi",
"ReadWriteOnce",
"",
"Always",
SINGLE_HOST_STRATEGY,
k8sBasedComponents);
String yamlRecipeContent = getResource("devfile/petclinic.yaml");
doReturn(toK8SList(yamlRecipeContent).getItems()).when(k8sRecipeParser).parse(anyString());
// given
ComponentImpl component = new ComponentImpl();
component.setType(KUBERNETES_COMPONENT_TYPE);
component.setReference(REFERENCE_FILENAME);
component.setAlias(COMPONENT_NAME);
component.setEndpoints(
Arrays.asList(
new EndpointImpl("e1", 1111, emptyMap()), new EndpointImpl("e2", 2222, emptyMap())));
// when
applier.apply(workspaceConfig, component, s -> yamlRecipeContent);
// then
@SuppressWarnings("unchecked")
ArgumentCaptor<Map<String, MachineConfigImpl>> objectsCaptor =
ArgumentCaptor.forClass(Map.class);
verify(k8sEnvProvisioner).provision(any(), any(), any(), objectsCaptor.capture());
Map<String, MachineConfigImpl> machineConfigs = objectsCaptor.getValue();
assertEquals(machineConfigs.size(), 4);
machineConfigs
.values()
.forEach(
machineConfig -> {
assertEquals(machineConfig.getServers().size(), 2);
assertFalse(
ServerConfig.isRequireSubdomain(
machineConfig.getServers().get("e1").getAttributes()));
assertFalse(
ServerConfig.isRequireSubdomain(
machineConfig.getServers().get("e2").getAttributes()));
});
}
@Test
public void serverMustHaveRequireSubdomainWhenNonSinglehostDevfileExpose()
throws DevfileException, IOException, ValidationException, InfrastructureException {
applier =
new KubernetesComponentToWorkspaceApplier(
k8sRecipeParser,
k8sEnvProvisioner,
envVars,
PROJECT_MOUNT_PATH,
"1Gi",
"ReadWriteOnce",
"",
"Always",
MULTI_HOST_STRATEGY,
k8sBasedComponents);
String yamlRecipeContent = getResource("devfile/petclinic.yaml");
doReturn(toK8SList(yamlRecipeContent).getItems()).when(k8sRecipeParser).parse(anyString());
// given
ComponentImpl component = new ComponentImpl();
component.setType(KUBERNETES_COMPONENT_TYPE);
component.setReference(REFERENCE_FILENAME);
component.setAlias(COMPONENT_NAME);
component.setEndpoints(
Arrays.asList(
new EndpointImpl("e1", 1111, emptyMap()), new EndpointImpl("e2", 2222, emptyMap())));
// when
applier.apply(workspaceConfig, component, s -> yamlRecipeContent);
// then
@SuppressWarnings("unchecked")
ArgumentCaptor<Map<String, MachineConfigImpl>> objectsCaptor =
ArgumentCaptor.forClass(Map.class);
verify(k8sEnvProvisioner).provision(any(), any(), any(), objectsCaptor.capture());
Map<String, MachineConfigImpl> machineConfigs = objectsCaptor.getValue();
assertEquals(machineConfigs.size(), 4);
machineConfigs
.values()
.forEach(
machineConfig -> {
assertEquals(machineConfig.getServers().size(), 2);
assertTrue(
ServerConfig.isRequireSubdomain(
machineConfig.getServers().get("e1").getAttributes()));
assertTrue(
ServerConfig.isRequireSubdomain(
machineConfig.getServers().get("e2").getAttributes()));
});
}
private KubernetesList toK8SList(String content) {
return unmarshal(content, KubernetesList.class);
}

View File

@ -33,6 +33,8 @@ public class OpenshiftComponentToWorkspaceApplier extends KubernetesComponentToW
@Named("che.infra.kubernetes.pvc.access_mode") String defaultPVCAccessMode,
@Named("che.infra.kubernetes.pvc.storage_class_name") String pvcStorageClassName,
@Named("che.workspace.sidecar.image_pull_policy") String imagePullPolicy,
@Named("che.infra.kubernetes.singlehost.workspace.devfile_endpoint_exposure")
String devfileEndpointsExposure,
@Named(KUBERNETES_BASED_COMPONENTS_KEY_NAME) Set<String> kubernetesBasedComponentTypes) {
super(
objectsParser,
@ -44,6 +46,7 @@ public class OpenshiftComponentToWorkspaceApplier extends KubernetesComponentToW
defaultPVCAccessMode,
pvcStorageClassName,
imagePullPolicy,
devfileEndpointsExposure,
kubernetesBasedComponentTypes);
}
}

View File

@ -11,27 +11,47 @@
*/
package org.eclipse.che.workspace.infrastructure.openshift.devfile;
import static io.fabric8.kubernetes.client.utils.Serialization.unmarshal;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static org.eclipse.che.api.workspace.server.devfile.Constants.KUBERNETES_COMPONENT_TYPE;
import static org.eclipse.che.api.workspace.server.devfile.Constants.OPENSHIFT_COMPONENT_TYPE;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostExternalServiceExposureStrategy.MULTI_HOST_STRATEGY;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostExternalServiceExposureStrategy.SINGLE_HOST_STRATEGY;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import io.fabric8.kubernetes.api.model.KubernetesList;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.che.api.core.ValidationException;
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
import org.eclipse.che.api.workspace.server.model.impl.MachineConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesComponentToWorkspaceApplier;
import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesEnvironmentProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesRecipeParser;
import org.eclipse.che.workspace.infrastructure.kubernetes.util.EnvVars;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import org.testng.reporters.Files;
@Listeners(MockitoTestNGListener.class)
public class OpenshiftComponentToWorkspaceApplierTest {
@ -59,6 +79,7 @@ public class OpenshiftComponentToWorkspaceApplierTest {
"ReadWriteOnce",
"",
"Always",
MULTI_HOST_STRATEGY,
k8sBasedComponents);
workspaceConfig = new WorkspaceConfigImpl();
@ -81,4 +102,120 @@ public class OpenshiftComponentToWorkspaceApplierTest {
verify(k8sEnvProvisioner)
.provision(workspaceConfig, OpenShiftEnvironment.TYPE, emptyList(), emptyMap());
}
@Test
public void serverCantHaveRequireSubdomainWhenSinglehostDevfileExpose()
throws DevfileException, IOException, ValidationException, InfrastructureException {
Set<String> openshiftBasedComponents = new HashSet<>();
openshiftBasedComponents.add(OPENSHIFT_COMPONENT_TYPE);
applier =
new OpenshiftComponentToWorkspaceApplier(
k8sRecipeParser,
k8sEnvProvisioner,
envVars,
"/projects",
"1Gi",
"ReadWriteOnce",
"",
"Always",
SINGLE_HOST_STRATEGY,
openshiftBasedComponents);
String yamlRecipeContent = getResource("devfile/petclinic.yaml");
doReturn(toK8SList(yamlRecipeContent).getItems()).when(k8sRecipeParser).parse(anyString());
// given
ComponentImpl component = new ComponentImpl();
component.setType(OPENSHIFT_COMPONENT_TYPE);
component.setReference(REFERENCE_FILENAME);
component.setAlias(COMPONENT_NAME);
component.setEndpoints(
Arrays.asList(
new EndpointImpl("e1", 1111, emptyMap()), new EndpointImpl("e2", 2222, emptyMap())));
// when
applier.apply(workspaceConfig, component, s -> yamlRecipeContent);
// then
@SuppressWarnings("unchecked")
ArgumentCaptor<Map<String, MachineConfigImpl>> objectsCaptor =
ArgumentCaptor.forClass(Map.class);
verify(k8sEnvProvisioner).provision(any(), any(), any(), objectsCaptor.capture());
Map<String, MachineConfigImpl> machineConfigs = objectsCaptor.getValue();
assertEquals(machineConfigs.size(), 4);
machineConfigs
.values()
.forEach(
machineConfig -> {
assertEquals(machineConfig.getServers().size(), 2);
assertFalse(
ServerConfig.isRequireSubdomain(
machineConfig.getServers().get("e1").getAttributes()));
assertFalse(
ServerConfig.isRequireSubdomain(
machineConfig.getServers().get("e2").getAttributes()));
});
}
@Test
public void serverMustHaveRequireSubdomainWhenNonSinglehostDevfileExpose()
throws DevfileException, IOException, ValidationException, InfrastructureException {
Set<String> openshiftBasedComponents = new HashSet<>();
openshiftBasedComponents.add(OPENSHIFT_COMPONENT_TYPE);
applier =
new OpenshiftComponentToWorkspaceApplier(
k8sRecipeParser,
k8sEnvProvisioner,
envVars,
"/projects",
"1Gi",
"ReadWriteOnce",
"",
"Always",
MULTI_HOST_STRATEGY,
openshiftBasedComponents);
String yamlRecipeContent = getResource("devfile/petclinic.yaml");
doReturn(toK8SList(yamlRecipeContent).getItems()).when(k8sRecipeParser).parse(anyString());
// given
ComponentImpl component = new ComponentImpl();
component.setType(OPENSHIFT_COMPONENT_TYPE);
component.setReference(REFERENCE_FILENAME);
component.setAlias(COMPONENT_NAME);
component.setEndpoints(
Arrays.asList(
new EndpointImpl("e1", 1111, emptyMap()), new EndpointImpl("e2", 2222, emptyMap())));
// when
applier.apply(workspaceConfig, component, s -> yamlRecipeContent);
// then
@SuppressWarnings("unchecked")
ArgumentCaptor<Map<String, MachineConfigImpl>> objectsCaptor =
ArgumentCaptor.forClass(Map.class);
verify(k8sEnvProvisioner).provision(any(), any(), any(), objectsCaptor.capture());
Map<String, MachineConfigImpl> machineConfigs = objectsCaptor.getValue();
assertEquals(machineConfigs.size(), 4);
machineConfigs
.values()
.forEach(
machineConfig -> {
assertEquals(machineConfig.getServers().size(), 2);
assertTrue(
ServerConfig.isRequireSubdomain(
machineConfig.getServers().get("e1").getAttributes()));
assertTrue(
ServerConfig.isRequireSubdomain(
machineConfig.getServers().get("e2").getAttributes()));
});
}
private KubernetesList toK8SList(String content) {
return unmarshal(content, KubernetesList.class);
}
private String getResource(String resourceName) throws IOException {
return Files.readFile(getClass().getClassLoader().getResourceAsStream(resourceName));
}
}

View File

@ -0,0 +1,131 @@
#
# 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/
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# Red Hat, Inc. - initial API and implementation
#
---
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: Pod
metadata:
name: petclinic
labels:
app.kubernetes.io/name: petclinic
app.kubernetes.io/component: webapp
app.kubernetes.io/part-of: petclinic
spec:
initContainers:
- command:
- "echo foo:bar"
image: "openjdk:11-jre-openjdk9"
name: "init"
containers:
- name: server
image: mariolet/petclinic
ports:
- containerPort: 8080
protocol: TCP
resources:
limits:
memory: 512Mi
- apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
app.kubernetes.io/name: mysql
app.kubernetes.io/component: database
app.kubernetes.io/part-of: petclinic
spec:
containers:
- name: mysql
image: centos/mysql-57-centos7
env:
- name: MYSQL_USER
value: petclinic
- name: MYSQL_PASSWORD
value: petclinic
- name: MYSQL_ROOT_PASSWORD
value: petclinic
- name: MYSQL_DATABASE
value: petclinic
ports:
- containerPort: 3306
protocol: TCP
resources:
limits:
memory: 512Mi
- apiVersion: v1
kind: Pod
metadata:
name: withoutLabels
spec:
containers:
- name: server
imagePullPolicy: Always
image: test/petclinic
ports:
- containerPort: 8080
protocol: TCP
resources:
limits:
memory: 512Mi
volumeMounts:
- name: foo_volume
mountPath: /foo/bar
- kind: Service
apiVersion: v1
metadata:
name: mysql
labels:
app.kubernetes.io/name: mysql
app.kubernetes.io/component: database
app.kubernetes.io/part-of: petclinic
spec:
ports:
- name: mysql
port: 3306
targetPort: 3360
selector:
app.kubernetes.io/name: mysql
app.kubernetes.io/component: database
app.kubernetes.io/part-of: petclinic
- kind: Service
apiVersion: v1
metadata:
name: petclinic
labels:
app.kubernetes.io/name: petclinic
app.kubernetes.io/component: webapp
app.kubernetes.io/part-of: petclinic
spec:
ports:
- name: web
port: 8080
targetPort: 8080
selector:
app: petclinic
component: webapp
- kind: Route
apiVersion: v1
metadata:
name: petclinic
labels:
app.kubernetes.io/name: petclinic
app.kubernetes.io/component: webapp
app.kubernetes.io/part-of: petclinic
spec:
to:
kind: Service
name: petclinic
port:
targetPort: web

View File

@ -11,8 +11,14 @@
*/
package org.eclipse.che.api.workspace.server.devfile.convert.component;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
import org.eclipse.che.api.core.model.workspace.devfile.Endpoint;
import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
@ -41,4 +47,18 @@ public interface ComponentToWorkspaceApplier {
ComponentImpl component,
FileContentProvider contentProvider)
throws DevfileException;
static Map<String, ServerConfigImpl> convertEndpointsIntoServers(
List<? extends Endpoint> endpoints, boolean requireSubdomain) {
return endpoints
.stream()
.collect(
Collectors.toMap(
Endpoint::getName,
e -> {
var cfg = ServerConfigImpl.createFromEndpoint(e);
ServerConfig.setRequireSubdomain(cfg.getAttributes(), requireSubdomain);
return cfg;
}));
}
}

View File

@ -180,7 +180,7 @@ public class ServerConfigImpl implements ServerConfig {
+ '}';
}
public static ServerConfigImpl createFromEndpoint(Endpoint endpoint, boolean devfileEndpoint) {
public static ServerConfigImpl createFromEndpoint(Endpoint endpoint) {
HashMap<String, String> attributes = new HashMap<>(endpoint.getAttributes());
attributes.put(SERVER_NAME_ATTRIBUTE, endpoint.getName());
@ -196,12 +196,6 @@ public class ServerConfigImpl implements ServerConfig {
ServerConfig.setInternal(attributes, true);
}
ServerConfig.setRequireSubdomain(attributes, devfileEndpoint);
return new ServerConfigImpl(Integer.toString(endpoint.getPort()), protocol, path, attributes);
}
public static ServerConfigImpl createFromEndpoint(Endpoint endpoint) {
return createFromEndpoint(endpoint, false);
}
}

View File

@ -109,23 +109,6 @@ public class ServerConfigImplTest {
serverConfig.getAttributes().get(INTERNAL_SERVER_ATTRIBUTE), Boolean.TRUE.toString());
}
@Test
public void testCreateFromEndpointDevfileEndpointAttributeSet() {
ServerConfig serverConfig =
ServerConfigImpl.createFromEndpoint(new EndpointImpl("name", 123, new HashMap<>()), true);
assertTrue(serverConfig.getAttributes().containsKey(REQUIRE_SUBDOMAIN));
assertTrue(Boolean.parseBoolean(serverConfig.getAttributes().get(REQUIRE_SUBDOMAIN)));
}
@Test
public void testCreateFromEndpointDevfileEndpointAttributeNotSet() {
ServerConfig serverConfig =
ServerConfigImpl.createFromEndpoint(new EndpointImpl("name", 123, new HashMap<>()), false);
assertFalse(serverConfig.getAttributes().containsKey(REQUIRE_SUBDOMAIN));
}
@Test
public void testCreateFromEndpointDevfileEndpointAttributeNotSetWhenDefault() {
ServerConfig serverConfig =