From 7a6c83b19fbaad20c15e65fef10b18a3e4dc10bf Mon Sep 17 00:00:00 2001 From: Michal Vala Date: Mon, 20 Apr 2020 11:40:34 +0200 Subject: [PATCH] Support `endpoints` for components of type kubernetes/openshift (#16529) * prepare devfile for k8s/openshift endpoints Signed-off-by: Michal Vala * include endpoints for k8s components Signed-off-by: Michal Vala make it possible to have public and discoverable k8s component endpoints Signed-off-by: Michal Vala test k8s/openshift devfile endpoint validation with attributes Signed-off-by: Michal Vala code cleanup, util class to avoid duplicate logic, more tests, remove unused code, ... Signed-off-by: Michal Vala * update test devfile for kubernetes endpoint Signed-off-by: Michal Vala * import cleanup Signed-off-by: Michal Vala * remove unused dependency Signed-off-by: Michal Vala * fix missing license headers Signed-off-by: Michal Vala * Revert "remove unused dependency" This reverts commit eeb250b60f122ffcb7c70b47180b98147db726d7. * endpoints extractor for k8s and dockerimage components Signed-off-by: Michal Vala * k8s/openshift components refactored one more time Signed-off-by: Michal Vala * rename extractor->converter Signed-off-by: Michal Vala * prepare devfile for k8s/openshift endpoints Signed-off-by: Michal Vala * include endpoints for k8s components Signed-off-by: Michal Vala make it possible to have public and discoverable k8s component endpoints Signed-off-by: Michal Vala test k8s/openshift devfile endpoint validation with attributes Signed-off-by: Michal Vala code cleanup, util class to avoid duplicate logic, more tests, remove unused code, ... Signed-off-by: Michal Vala * update test devfile for kubernetes endpoint Signed-off-by: Michal Vala * import cleanup Signed-off-by: Michal Vala * remove unused dependency Signed-off-by: Michal Vala * fix missing license headers Signed-off-by: Michal Vala * Revert "remove unused dependency" This reverts commit eeb250b60f122ffcb7c70b47180b98147db726d7. * endpoints extractor for k8s and dockerimage components Signed-off-by: Michal Vala * k8s/openshift components refactored one more time Signed-off-by: Michal Vala * rename extractor->converter Signed-off-by: Michal Vala * docs, tests Signed-off-by: Michal Vala * add label param to create service method, fix tests Signed-off-by: Michal Vala * format, license, Openshift applier fix Signed-off-by: Michal Vala * cleanup Signed-off-by: Michal Vala * move service creation for discoverable endpoints to later phase, together with all services Signed-off-by: Michal Vala * format, cleanup Signed-off-by: Michal Vala * tests Signed-off-by: Michal Vala * tests, comments Signed-off-by: Michal Vala * fix tests Signed-off-by: Michal Vala * javadoc and clean the serverconfig from serverName attribute Signed-off-by: Michal Vala --- .../model/workspace/config/ServerConfig.java | 30 +++++ ...ockerimageComponentToWorkspaceApplier.java | 108 ++++++----------- ...KubernetesComponentToWorkspaceApplier.java | 45 ++++++-- .../server/KubernetesServerExposer.java | 40 +++++++ ...rimageComponentToWorkspaceApplierTest.java | 19 +-- ...rnetesComponentToWorkspaceApplierTest.java | 105 ++++++++++++++++- .../server/KubernetesServerExposerTest.java | 40 +++++++ .../test/resources/devfile/petclinicPods.yaml | 76 ++++++++++++ .../server/model/impl/ServerConfigImpl.java | 23 ++++ .../main/resources/schema/1.0.0/devfile.json | 102 ++++++++-------- .../validator/DevfileSchemaValidatorTest.java | 1 + .../model/impl/ServerConfigImplTest.java | 109 ++++++++++++++++++ ...8s_openshift_component_with_endpoints.yaml | 39 +++++++ 13 files changed, 587 insertions(+), 150 deletions(-) create mode 100644 infrastructures/kubernetes/src/test/resources/devfile/petclinicPods.yaml create mode 100644 wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/model/impl/ServerConfigImplTest.java create mode 100644 wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/kubernetes_openshift_component/devfile_k8s_openshift_component_with_endpoints.yaml diff --git a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/ServerConfig.java b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/ServerConfig.java index a58f56cfb1..ca264acb7e 100644 --- a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/ServerConfig.java +++ b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/ServerConfig.java @@ -17,6 +17,7 @@ import static java.util.Collections.emptyList; import java.util.Arrays; import java.util.List; import java.util.Map; +import org.eclipse.che.api.core.model.workspace.devfile.Endpoint; import org.eclipse.che.api.core.model.workspace.runtime.Server; import org.eclipse.che.commons.annotation.Nullable; @@ -62,6 +63,20 @@ public interface ServerConfig { */ String UNIQUE_SERVER_ATTRIBUTE = "unique"; + /** + * {@link ServerConfig} and {@link Server} attribute name which can identify endpoint as + * discoverable(i.e. it is accessible by its name from workspace's containers). Attribute value + * {@code true} makes a endpoint discoverable, any other value or lack of the attribute makes the + * server non-discoverable. + */ + String DISCOVERABLE_SERVER_ATTRIBUTE = "discoverable"; + + /** + * This attribute is used to remember {@link Endpoint#getName()} inside {@link ServerConfig} for + * internal use. + */ + String SERVER_NAME_ATTRIBUTE = "serverName"; + /** * Port used by server. * @@ -156,6 +171,16 @@ public interface ServerConfig { attributes.put(UNIQUE_SERVER_ATTRIBUTE, Boolean.toString(value)); } + /** + * Determines whether the attributes configure the server to be discoverable. + * + * @param attributes the attributes with additional server configuration + * @see #DISCOVERABLE_SERVER_ATTRIBUTE + */ + static boolean isDiscoverable(Map attributes) { + return AttributesEvaluator.booleanAttr(attributes, DISCOVERABLE_SERVER_ATTRIBUTE, false); + } + /** * Determines whether the attributes configure the server to be authenticated using JWT cookies. A * null value means that the attributes don't require any particular authentication. @@ -229,6 +254,11 @@ public interface ServerConfig { default List getUnsecuredPaths() { return getUnsecuredPaths(getAttributes()); } + + /** @see #isDiscoverable(Map) */ + default boolean isDiscoverable() { + return isDiscoverable(getAttributes()); + } } // helper class for the default methods in the above interface diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/DockerimageComponentToWorkspaceApplier.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/DockerimageComponentToWorkspaceApplier.java index 536235aac2..0a87939699 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/DockerimageComponentToWorkspaceApplier.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/DockerimageComponentToWorkspaceApplier.java @@ -14,12 +14,9 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.devfile; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; import static java.lang.String.format; -import static java.util.Collections.singletonList; 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.DISCOVERABLE_ENDPOINT_ATTRIBUTE; import static org.eclipse.che.api.workspace.server.devfile.Constants.DOCKERIMAGE_COMPONENT_TYPE; -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 com.google.common.annotations.VisibleForTesting; @@ -28,19 +25,13 @@ import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.ContainerBuilder; import io.fabric8.kubernetes.api.model.EnvVar; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.ServiceBuilder; -import io.fabric8.kubernetes.api.model.ServicePort; -import io.fabric8.kubernetes.api.model.ServicePortBuilder; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; import java.util.ArrayList; -import java.util.HashMap; 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.config.ServerConfig; 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; @@ -120,10 +111,37 @@ public class DockerimageComponentToWorkspaceApplier implements ComponentToWorksp String machineName = componentAlias == null ? toMachineName(dockerimageComponent.getImage()) : componentAlias; + MachineConfigImpl machineConfig = createMachineConfig(dockerimageComponent, componentAlias); + List componentObjects = createComponentObjects(dockerimageComponent, machineName); + + k8sEnvProvisioner.provision( + workspaceConfig, + KubernetesEnvironment.TYPE, + componentObjects, + ImmutableMap.of(machineName, machineConfig)); + + workspaceConfig + .getCommands() + .stream() + .filter( + c -> + componentAlias != null + && componentAlias.equals( + c.getAttributes().get(Constants.COMPONENT_ALIAS_COMMAND_ATTRIBUTE))) + .forEach(c -> c.getAttributes().put(MACHINE_NAME_ATTRIBUTE, machineName)); + } + + private MachineConfigImpl createMachineConfig( + ComponentImpl dockerimageComponent, String componentAlias) { MachineConfigImpl machineConfig = new MachineConfigImpl(); - dockerimageComponent - .getEndpoints() - .forEach(e -> machineConfig.getServers().put(e.getName(), toServerConfig(e))); + machineConfig + .getServers() + .putAll( + dockerimageComponent + .getEndpoints() + .stream() + .collect( + Collectors.toMap(Endpoint::getName, ServerConfigImpl::createFromEndpoint))); dockerimageComponent .getVolumes() @@ -143,6 +161,11 @@ public class DockerimageComponentToWorkspaceApplier implements ComponentToWorksp machineConfig.getAttributes().put(DEVFILE_COMPONENT_ALIAS_ATTRIBUTE, componentAlias); } + return machineConfig; + } + + private List createComponentObjects( + ComponentImpl dockerimageComponent, String machineName) { List componentObjects = new ArrayList<>(); Deployment deployment = buildDeployment( @@ -160,27 +183,7 @@ public class DockerimageComponentToWorkspaceApplier implements ComponentToWorksp dockerimageComponent.getArgs()); componentObjects.add(deployment); - dockerimageComponent - .getEndpoints() - .stream() - .filter(e -> "true".equals(e.getAttributes().get(DISCOVERABLE_ENDPOINT_ATTRIBUTE))) - .forEach(e -> componentObjects.add(createService(deployment, e))); - - k8sEnvProvisioner.provision( - workspaceConfig, - KubernetesEnvironment.TYPE, - componentObjects, - ImmutableMap.of(machineName, machineConfig)); - - workspaceConfig - .getCommands() - .stream() - .filter( - c -> - componentAlias != null - && componentAlias.equals( - c.getAttributes().get(Constants.COMPONENT_ALIAS_COMMAND_ATTRIBUTE))) - .forEach(c -> c.getAttributes().put(MACHINE_NAME_ATTRIBUTE, machineName)); + return componentObjects; } private Deployment buildDeployment( @@ -232,45 +235,6 @@ public class DockerimageComponentToWorkspaceApplier implements ComponentToWorksp .build(); } - private ServerConfigImpl toServerConfig(Endpoint endpoint) { - HashMap attributes = new HashMap<>(endpoint.getAttributes()); - - String protocol = attributes.remove("protocol"); - if (isNullOrEmpty(protocol)) { - protocol = "http"; - } - - String path = attributes.remove("path"); - - String isPublic = attributes.remove(PUBLIC_ENDPOINT_ATTRIBUTE); - if ("false".equals(isPublic)) { - ServerConfig.setInternal(attributes, true); - } - - return new ServerConfigImpl(Integer.toString(endpoint.getPort()), protocol, path, attributes); - } - - private Service createService(Deployment deployment, Endpoint endpoint) { - ServicePort servicePort = - new ServicePortBuilder() - .withPort(endpoint.getPort()) - .withProtocol("TCP") - .withNewTargetPort(endpoint.getPort()) - .build(); - return new ServiceBuilder() - .withNewMetadata() - .withName(endpoint.getName()) - .endMetadata() - .withNewSpec() - .withSelector( - ImmutableMap.of( - CHE_COMPONENT_NAME_LABEL, - deployment.getSpec().getTemplate().getMetadata().getName())) - .withPorts(singletonList(servicePort)) - .endSpec() - .build(); - } - @VisibleForTesting static String toMachineName(String imageName) throws DevfileException { if (imageName.isEmpty()) { diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/KubernetesComponentToWorkspaceApplier.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/KubernetesComponentToWorkspaceApplier.java index 515aa313c0..4d85443f19 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/KubernetesComponentToWorkspaceApplier.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/KubernetesComponentToWorkspaceApplier.java @@ -39,11 +39,13 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; 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; @@ -51,6 +53,7 @@ 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; @@ -152,15 +155,10 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa String componentContent = retrieveContent(k8sComponent, contentProvider); - List componentObjects = - new ArrayList<>(unmarshalComponentObjects(k8sComponent, componentContent)); - - if (!k8sComponent.getSelector().isEmpty()) { - componentObjects = SelectorFilter.filter(componentObjects, k8sComponent.getSelector()); - } + final List componentObjects = + prepareComponentObjects(k8sComponent, componentContent); List podsData = getPodDatas(componentObjects); - podsData .stream() .flatMap(e -> e.getSpec().getContainers().stream()) @@ -174,15 +172,29 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa podsData.forEach(p -> envVars.apply(p, k8sComponent.getEnv())); } - applyEntrypoints(k8sComponent.getEntrypoints(), componentObjects); - Map machineConfigs = prepareMachineConfigs(podsData, k8sComponent); - linkCommandsToMachineName(workspaceConfig, k8sComponent, machineConfigs.keySet()); k8sEnvProvisioner.provision(workspaceConfig, environmentType, componentObjects, machineConfigs); } + private List prepareComponentObjects(Component k8sComponent, String componentContent) + throws DevfileRecipeFormatException { + final List componentObjects; + + if (!k8sComponent.getSelector().isEmpty()) { + componentObjects = + SelectorFilter.filter( + new ArrayList<>(unmarshalComponentObjects(k8sComponent, componentContent)), + k8sComponent.getSelector()); + } else { + componentObjects = new ArrayList<>(unmarshalComponentObjects(k8sComponent, componentContent)); + } + + applyEntrypoints(k8sComponent.getEntrypoints(), componentObjects); + return componentObjects; + } + private void applyProjectsVolumes(List podsData, List componentObjects) { if (componentObjects .stream() @@ -236,12 +248,25 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa config.getAttributes().put(DEVFILE_COMPONENT_ALIAS_ATTRIBUTE, component.getAlias()); } provisionVolumes(component, container, config); + provisionEndpoints(component, config); + machineConfigs.put(machineName, config); } } return machineConfigs; } + private void provisionEndpoints(Component component, MachineConfigImpl config) { + config + .getServers() + .putAll( + component + .getEndpoints() + .stream() + .collect( + Collectors.toMap(Endpoint::getName, ServerConfigImpl::createFromEndpoint))); + } + private void provisionVolumes( ComponentImpl component, Container container, MachineConfigImpl config) throws DevfileException { diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposer.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposer.java index 096922b367..0bd5c3903e 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposer.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposer.java @@ -13,6 +13,7 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.server; import static java.lang.Integer.parseInt; import static java.util.stream.Collectors.toMap; +import static org.eclipse.che.api.core.model.workspace.config.ServerConfig.SERVER_NAME_ATTRIBUTE; import static org.eclipse.che.commons.lang.NameGenerator.generate; import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_ORIGINAL_NAME_LABEL; @@ -25,6 +26,7 @@ import io.fabric8.kubernetes.api.model.ServicePort; import io.fabric8.kubernetes.api.model.ServicePortBuilder; import io.fabric8.kubernetes.api.model.extensions.Ingress; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -39,6 +41,8 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.provision.Configurati import org.eclipse.che.workspace.infrastructure.kubernetes.provision.UniqueNamesProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposer; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Helps to modify {@link KubernetesEnvironment} to make servers that are configured by {@link @@ -98,6 +102,8 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureS */ public class KubernetesServerExposer { + private static final Logger LOG = LoggerFactory.getLogger(KubernetesServerExposer.class); + public static final int SERVER_UNIQUE_PART_SIZE = 8; public static final String SERVER_PREFIX = "server"; @@ -178,11 +184,45 @@ public class KubernetesServerExposer { splitServersAndPortsByExposureType( servers, internalServers, externalServers, secureServers, unsecuredPorts, securedPorts); + provisionServicesForDiscoverableServers(servers); + exposeNonSecureServers(internalServers, externalServers, unsecuredPorts); exposeSecureServers(secureServers, securedPorts); } + // TODO: this creates discoverable services as an extra services. Service for same {@link + // ServerConfig} is also created later in in {@link #exposeNonSecureServers(Map, Map, Map)} or + // {@link #exposeSecureServers(Map, Map)} as a non-discoverable one. This was added during + // working on adding endpoints for kubernetes/openshift components, to keep behavior consistent. + // However, this logic is probably broken and should be changed. + /** + * Creates services with defined names for discoverable {@link ServerConfig}s. The name is taken + * from {@link ServerConfig}'s attributes under {@link ServerConfig#SERVER_NAME_ATTRIBUTE} and + * must be set, otherwise service won't be created. + */ + private void provisionServicesForDiscoverableServers( + Map servers) { + for (String serverName : servers.keySet()) { + ServerConfig server = servers.get(serverName); + if (server.getAttributes().containsKey(SERVER_NAME_ATTRIBUTE)) { + // remove the name from attributes so we don't send it to the client + String endpointName = server.getAttributes().remove(SERVER_NAME_ATTRIBUTE); + if (server.isDiscoverable()) { + Service service = + new ServerServiceBuilder() + .withName(endpointName) + .withMachineName(machineName) + .withSelectorEntry(CHE_ORIGINAL_NAME_LABEL, pod.getMetadata().getName()) + .withPorts(Collections.singletonList(getServicePort(server))) + .withServers(Collections.singletonMap(serverName, server)) + .build(); + k8sEnv.getServices().put(service.getMetadata().getName(), service); + } + } + } + } + private void splitServersAndPortsByExposureType( Map servers, Map internalServers, diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/DockerimageComponentToWorkspaceApplierTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/DockerimageComponentToWorkspaceApplierTest.java index 9530385b50..66d52b5771 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/DockerimageComponentToWorkspaceApplierTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/DockerimageComponentToWorkspaceApplierTest.java @@ -14,7 +14,6 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.devfile; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; 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.DISCOVERABLE_ENDPOINT_ATTRIBUTE; import static org.eclipse.che.api.workspace.server.devfile.Constants.DOCKERIMAGE_COMPONENT_TYPE; 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; @@ -31,12 +30,9 @@ import com.google.common.collect.ImmutableMap; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.EnvVar; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.IntOrString; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.Quantity; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.ServicePort; import io.fabric8.kubernetes.api.model.apps.Deployment; import java.util.Arrays; import java.util.HashMap; @@ -352,7 +348,6 @@ public class DockerimageComponentToWorkspaceApplierTest { assertEquals(serverConfig.getPath(), "/ls"); assertEquals(serverConfig.getPort(), "4923"); Map attributes = serverConfig.getAttributes(); - assertEquals(attributes.size(), 2); assertEquals(attributes.get(ServerConfig.INTERNAL_SERVER_ATTRIBUTE), "true"); assertEquals(attributes.get("secure"), "false"); } @@ -373,7 +368,7 @@ public class DockerimageComponentToWorkspaceApplierTest { "false", "secure", "false", - DISCOVERABLE_ENDPOINT_ATTRIBUTE, + "discoverable", "true")); ComponentImpl dockerimageComponent = new ComponentImpl(); dockerimageComponent.setAlias("jdk"); @@ -394,22 +389,12 @@ public class DockerimageComponentToWorkspaceApplierTest { machinesCaptor.capture()); List objects = objectsCaptor.getValue(); - assertEquals(objects.size(), 2); + assertEquals(objects.size(), 1); assertTrue(objects.get(0) instanceof Deployment); Deployment deployment = (Deployment) objects.get(0); assertEquals( deployment.getSpec().getTemplate().getMetadata().getLabels().get(CHE_COMPONENT_NAME_LABEL), "jdk"); - - assertTrue(objects.get(1) instanceof Service); - Service service = (Service) objects.get(1); - assertEquals(service.getMetadata().getName(), "jdk-ls"); - assertEquals(service.getSpec().getSelector(), ImmutableMap.of(CHE_COMPONENT_NAME_LABEL, "jdk")); - List ports = service.getSpec().getPorts(); - assertEquals(ports.size(), 1); - ServicePort port = ports.get(0); - assertEquals(port.getPort(), Integer.valueOf(4923)); - assertEquals(port.getTargetPort(), new IntOrString(4923)); } @Test diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/KubernetesComponentToWorkspaceApplierTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/KubernetesComponentToWorkspaceApplierTest.java index 33bdd218be..99847af04b 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/KubernetesComponentToWorkspaceApplierTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/devfile/KubernetesComponentToWorkspaceApplierTest.java @@ -13,6 +13,7 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.devfile; import static io.fabric8.kubernetes.client.utils.Serialization.unmarshal; import static java.util.Arrays.asList; +import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.eclipse.che.api.core.model.workspace.config.Command.MACHINE_NAME_ATTRIBUTE; @@ -32,6 +33,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; +import com.google.common.collect.ImmutableMap; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.KubernetesList; @@ -51,11 +53,14 @@ 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; 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.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.model.impl.devfile.EntrypointImpl; import org.eclipse.che.api.workspace.server.model.impl.devfile.EnvImpl; import org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl; +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; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesRecipeParser; @@ -556,7 +561,6 @@ public class KubernetesComponentToWorkspaceApplierTest { k8sBasedComponents); String yamlRecipeContent = getResource("devfile/petclinic.yaml"); doReturn(toK8SList(yamlRecipeContent).getItems()).when(k8sRecipeParser).parse(anyString()); - List command = asList("teh", "command"); ComponentImpl component = new ComponentImpl(); component.setType(KUBERNETES_COMPONENT_TYPE); component.setReference(REFERENCE_FILENAME); @@ -583,6 +587,105 @@ public class KubernetesComponentToWorkspaceApplierTest { } } + @Test + public void shouldProvisionEndpointsToAllMachines() + throws IOException, ValidationException, InfrastructureException, DevfileException { + // given + String endpointName = "petclinic-endpoint"; + Integer endpointPort = 8081; + + String yamlRecipeContent = getResource("devfile/petclinicPods.yaml"); + doReturn(toK8SList(yamlRecipeContent).getItems()).when(k8sRecipeParser).parse(anyString()); + + ComponentImpl component = new ComponentImpl(); + component.setType(KUBERNETES_COMPONENT_TYPE); + component.setReference(REFERENCE_FILENAME); + component.setAlias(COMPONENT_NAME); + component.setEndpoints(singletonList(new EndpointImpl(endpointName, endpointPort, emptyMap()))); + + // when + applier.apply(workspaceConfig, component, s -> yamlRecipeContent); + + // then + @SuppressWarnings("unchecked") + ArgumentCaptor> objectsCaptor = + ArgumentCaptor.forClass(Map.class); + verify(k8sEnvProvisioner).provision(any(), any(), any(), objectsCaptor.capture()); + Map configs = objectsCaptor.getValue(); + assertEquals(configs.size(), 3); + configs + .values() + .forEach( + machineConfig -> { + Map serverConfigs = machineConfig.getServers(); + assertEquals(serverConfigs.size(), 1); + assertTrue(serverConfigs.containsKey(endpointName)); + assertEquals(serverConfigs.get(endpointName).getPort(), endpointPort.toString()); + assertNull(serverConfigs.get(endpointName).getPath()); + }); + } + + @Test + public void shouldProvisionEndpointWithAttributes() + throws IOException, ValidationException, InfrastructureException, DevfileException { + // given + String endpointName = "petclinic-endpoint"; + Integer endpointPort = 8081; + String endpointProtocol = "tcp"; + String endpointPath = "path"; + String endpointPublic = "false"; + String endpointSecure = "false"; + + String yamlRecipeContent = getResource("devfile/petclinicPods.yaml"); + doReturn(toK8SList(yamlRecipeContent).getItems()).when(k8sRecipeParser).parse(anyString()); + + ComponentImpl component = new ComponentImpl(); + component.setType(KUBERNETES_COMPONENT_TYPE); + component.setReference(REFERENCE_FILENAME); + component.setAlias(COMPONENT_NAME); + component.setEndpoints( + singletonList( + new EndpointImpl( + endpointName, + endpointPort, + ImmutableMap.of( + "protocol", + endpointProtocol, + "path", + endpointPath, + "public", + endpointPublic, + "secure", + endpointSecure)))); + + // when + applier.apply(workspaceConfig, component, s -> yamlRecipeContent); + + // then + @SuppressWarnings("unchecked") + ArgumentCaptor> objectsCaptor = + ArgumentCaptor.forClass(Map.class); + verify(k8sEnvProvisioner).provision(any(), any(), any(), objectsCaptor.capture()); + Map configs = objectsCaptor.getValue(); + assertEquals(configs.size(), 3); + configs + .values() + .forEach( + machineConfig -> { + Map serverConfigs = machineConfig.getServers(); + assertEquals(serverConfigs.size(), 1); + assertTrue(serverConfigs.containsKey(endpointName)); + assertEquals(serverConfigs.get(endpointName).getPort(), endpointPort.toString()); + assertEquals(serverConfigs.get(endpointName).getPath(), endpointPath); + assertEquals(serverConfigs.get(endpointName).getProtocol(), endpointProtocol); + assertEquals( + serverConfigs.get(endpointName).isSecure(), Boolean.parseBoolean(endpointSecure)); + assertEquals( + serverConfigs.get(endpointName).isInternal(), + !Boolean.parseBoolean(endpointPublic)); + }); + } + private KubernetesList toK8SList(String content) { return unmarshal(content, KubernetesList.class); } diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposerTest.java index 7da7085e8b..f1fa74e4ae 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposerTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposerTest.java @@ -13,6 +13,8 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.server; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; +import static org.eclipse.che.api.core.model.workspace.config.ServerConfig.DISCOVERABLE_SERVER_ATTRIBUTE; +import static org.eclipse.che.api.core.model.workspace.config.ServerConfig.SERVER_NAME_ATTRIBUTE; import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_PREFIX; import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_UNIQUE_PART_SIZE; import static org.mockito.ArgumentMatchers.any; @@ -20,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.verify; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; @@ -396,6 +399,43 @@ public class KubernetesServerExposerTest { "ws-server", new ServerConfigImpl(wsServerConfig).withAttributes(ATTRIBUTES_MAP))); } + @Test + public void testCreateExtraServiceForDiscoverableServerConfig() throws InfrastructureException { + // given + ServerConfigImpl httpServerConfig = + new ServerConfigImpl( + "8080/tcp", + "http", + "/api", + ImmutableMap.of(SERVER_NAME_ATTRIBUTE, "hello", DISCOVERABLE_SERVER_ATTRIBUTE, "true")); + Map serversToExpose = + ImmutableMap.of("http-server", httpServerConfig); + + // when + serverExposer.expose(serversToExpose); + + assertEquals(kubernetesEnvironment.getServices().size(), 2); + assertTrue(kubernetesEnvironment.getServices().containsKey("hello")); + assertEquals(kubernetesEnvironment.getServices().get("hello").getMetadata().getName(), "hello"); + } + + @Test + public void testDiscoverableServerConfigWithoutNameAttributeIsNotProvisioned() + throws InfrastructureException { + // given + ServerConfigImpl httpServerConfig = + new ServerConfigImpl( + "8080/tcp", "http", "/api", ImmutableMap.of(DISCOVERABLE_SERVER_ATTRIBUTE, "true")); + Map serversToExpose = + ImmutableMap.of("http-server", httpServerConfig); + + // when + serverExposer.expose(serversToExpose); + + assertEquals(kubernetesEnvironment.getServices().size(), 1); + assertFalse(kubernetesEnvironment.getServices().containsKey("hello")); + } + @SuppressWarnings("SameParameterValue") private void assertThatExternalServerIsExposed( String machineName, diff --git a/infrastructures/kubernetes/src/test/resources/devfile/petclinicPods.yaml b/infrastructures/kubernetes/src/test/resources/devfile/petclinicPods.yaml new file mode 100644 index 0000000000..1fa04c4af3 --- /dev/null +++ b/infrastructures/kubernetes/src/test/resources/devfile/petclinicPods.yaml @@ -0,0 +1,76 @@ +# +# 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 +# + +--- +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: + 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 diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/ServerConfigImpl.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/ServerConfigImpl.java index e3e98b2ba8..f0cba0c09d 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/ServerConfigImpl.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/ServerConfigImpl.java @@ -11,6 +11,9 @@ */ package org.eclipse.che.api.workspace.server.model.impl; +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.eclipse.che.api.workspace.server.devfile.Constants.PUBLIC_ENDPOINT_ATTRIBUTE; + import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -25,6 +28,7 @@ import javax.persistence.JoinColumn; import javax.persistence.MapKeyColumn; import javax.persistence.Table; import org.eclipse.che.api.core.model.workspace.config.ServerConfig; +import org.eclipse.che.api.core.model.workspace.devfile.Endpoint; /** @author Alexander Garagatyi */ @Entity(name = "ServerConf") @@ -175,4 +179,23 @@ public class ServerConfigImpl implements ServerConfig { + attributes + '}'; } + + public static ServerConfigImpl createFromEndpoint(Endpoint endpoint) { + HashMap attributes = new HashMap<>(endpoint.getAttributes()); + attributes.put(SERVER_NAME_ATTRIBUTE, endpoint.getName()); + + String protocol = attributes.remove("protocol"); + if (isNullOrEmpty(protocol)) { + protocol = "http"; + } + + String path = attributes.remove("path"); + + String isPublic = attributes.remove(PUBLIC_ENDPOINT_ATTRIBUTE); + if ("false".equals(isPublic)) { + ServerConfig.setInternal(attributes, true); + } + + return new ServerConfigImpl(Integer.toString(endpoint.getPort()), protocol, path, attributes); + } } diff --git a/wsmaster/che-core-api-workspace/src/main/resources/schema/1.0.0/devfile.json b/wsmaster/che-core-api-workspace/src/main/resources/schema/1.0.0/devfile.json index d34fec6608..b27456e8bc 100644 --- a/wsmaster/che-core-api-workspace/src/main/resources/schema/1.0.0/devfile.json +++ b/wsmaster/che-core-api-workspace/src/main/resources/schema/1.0.0/devfile.json @@ -388,6 +388,7 @@ "mountSources": {}, "volumes": {}, "env": {}, + "endpoints": {}, "reference": { "description": "Describes absolute or devfile-relative location of Kubernetes list yaml file. Applicable only for 'kubernetes' and 'openshift' type components", "type": "string", @@ -478,6 +479,7 @@ "cpuLimit": {}, "cpuRequest": {}, "volumes": {}, + "endpoints": {}, "memoryLimit": { "anyOf": [ { @@ -524,56 +526,6 @@ "examples": [ "['-R', '-f']" ] - }, - "endpoints": { - "type": "array", - "description": "Describes dockerimage component endpoints", - "items": { - "name": "object", - "description": "Describes dockerimage component endpoint", - "required": [ - "name", - "port" - ], - "properties": { - "name": { - "type": "string", - "title": "The Endpoint Name", - "description": "The Endpoint name" - }, - "port": { - "type": "integer", - "title": "The Endpoint Port", - "description": "The container port that should be used as endpoint" - }, - "attributes": { - "type": "object", - "public": { - "type": "boolean", - "description": "Identifies endpoint as workspace internally or externally accessible.", - "default": "true" - }, - "secure": { - "type": "boolean", - "description": "Identifies server as secure or non-secure. Requests to secure servers will be authenticated and must contain machine token", - "default": "false" - }, - "discoverable": { - "type": "boolean", - "description": "Identifies endpoint as accessible by its name.", - "default": "false" - }, - "protocol": { - "type": "boolean", - "description": "Defines protocol that should be used for communication with endpoint. Is used for endpoint URL evaluation" - }, - "additionalProperties": { - "type": "string" - }, - "javaType": "java.util.Map" - } - } - } } } } @@ -698,6 +650,56 @@ } } } + }, + "endpoints": { + "type": "array", + "description": "Describes dockerimage component endpoints", + "items": { + "name": "object", + "description": "Describes dockerimage component endpoint", + "required": [ + "name", + "port" + ], + "properties": { + "name": { + "type": "string", + "title": "The Endpoint Name", + "description": "The Endpoint name" + }, + "port": { + "type": "integer", + "title": "The Endpoint Port", + "description": "The container port that should be used as endpoint" + }, + "attributes": { + "type": "object", + "public": { + "type": "boolean", + "description": "Identifies endpoint as workspace internally or externally accessible.", + "default": "true" + }, + "secure": { + "type": "boolean", + "description": "Identifies server as secure or non-secure. Requests to secure servers will be authenticated and must contain machine token", + "default": "false" + }, + "discoverable": { + "type": "boolean", + "description": "Identifies endpoint as accessible by its name.", + "default": "false" + }, + "protocol": { + "type": "boolean", + "description": "Defines protocol that should be used for communication with endpoint. Is used for endpoint URL evaluation" + }, + "additionalProperties": { + "type": "string" + }, + "javaType": "java.util.Map" + } + } + } } }, "additionalProperties": true diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/devfile/validator/DevfileSchemaValidatorTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/devfile/validator/DevfileSchemaValidatorTest.java index a956d2b726..1a3cf05d48 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/devfile/validator/DevfileSchemaValidatorTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/devfile/validator/DevfileSchemaValidatorTest.java @@ -62,6 +62,7 @@ public class DevfileSchemaValidatorTest { "kubernetes_openshift_component/devfile_openshift_component_reference_and_content_as_block.yaml" }, {"kubernetes_openshift_component/devfile_k8s_openshift_component_with_env.yaml"}, + {"kubernetes_openshift_component/devfile_k8s_openshift_component_with_endpoints.yaml"}, {"kubernetes_openshift_component/devfile_openshift_component_content_without_reference.yaml"}, { "kubernetes_openshift_component/devfile_kubernetes_component_content_without_reference.yaml" diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/model/impl/ServerConfigImplTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/model/impl/ServerConfigImplTest.java new file mode 100644 index 0000000000..57f775e47b --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/model/impl/ServerConfigImplTest.java @@ -0,0 +1,109 @@ +/* + * 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.api.workspace.server.model.impl; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.eclipse.che.api.core.model.workspace.config.ServerConfig.INTERNAL_SERVER_ATTRIBUTE; +import static org.eclipse.che.api.core.model.workspace.config.ServerConfig.SERVER_NAME_ATTRIBUTE; +import static org.testng.Assert.*; + +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import org.eclipse.che.api.core.model.workspace.config.ServerConfig; +import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl; +import org.testng.annotations.Test; + +public class ServerConfigImplTest { + + @Test + public void testStoreEndpointNameIntoAttributes() { + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint(new EndpointImpl("blabol", 123, emptyMap())); + + assertEquals(serverConfig.getAttributes().get(SERVER_NAME_ATTRIBUTE), "blabol"); + } + + @Test + public void testCreateFromEndpointMinimalEndpointShouldTranslateToHttpProtocol() { + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint(new EndpointImpl("name", 123, emptyMap())); + + assertEquals(serverConfig.getProtocol(), "http"); + } + + @Test + public void testCreateFromEndpointMinimalEndpointShouldTranslateToNullPath() { + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint(new EndpointImpl("name", 123, emptyMap())); + + assertNull(serverConfig.getPath()); + } + + @Test + public void testCreateFromEndpointCustomAttributesShouldPreserveInAttributes() { + Map customAttributes = ImmutableMap.of("k1", "v1", "k2", "v2"); + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint(new EndpointImpl("name", 123, customAttributes)); + + assertEquals(serverConfig.getAttributes().get("k1"), "v1"); + assertEquals(serverConfig.getAttributes().get("k2"), "v2"); + assertEquals(serverConfig.getAttributes().size(), 3); + } + + @Test + public void testCreateFromEndpointTranslatePath() { + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint( + new EndpointImpl("name", 123, singletonMap("path", "hello"))); + + assertEquals(serverConfig.getPath(), "hello"); + } + + @Test + public void testCreateFromEndpointTranslateProtocol() { + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint( + new EndpointImpl("name", 123, singletonMap("protocol", "hello"))); + + assertEquals(serverConfig.getProtocol(), "hello"); + } + + @Test + public void testCreateFromEndpointTranslatePublicTrue() { + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint( + new EndpointImpl("name", 123, singletonMap("public", "true"))); + + assertFalse(serverConfig.isInternal()); + } + + @Test + public void testCreateFromEndpointTranslatePublicWhatever() { + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint( + new EndpointImpl("name", 123, singletonMap("public", "whatever"))); + + assertFalse(serverConfig.isInternal()); + } + + @Test + public void testCreateFromEndpointTranslatePublicFalse() { + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint( + new EndpointImpl("name", 123, singletonMap("public", "false"))); + + assertFalse(serverConfig.getAttributes().isEmpty()); + assertEquals( + serverConfig.getAttributes().get(INTERNAL_SERVER_ATTRIBUTE), Boolean.TRUE.toString()); + } +} diff --git a/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/kubernetes_openshift_component/devfile_k8s_openshift_component_with_endpoints.yaml b/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/kubernetes_openshift_component/devfile_k8s_openshift_component_with_endpoints.yaml new file mode 100644 index 0000000000..e1fe34992f --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/kubernetes_openshift_component/devfile_k8s_openshift_component_with_endpoints.yaml @@ -0,0 +1,39 @@ +# +# 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 +# + +--- +apiVersion: 1.0.0 +metadata: + name: petclinic-dev-environment +components: + - alias: mysql + type: openshift + reference: petclinic.yaml + endpoints: + - name: test-endpoint-os + port: 1234 + attributes: + protocol: http + secure: 'true' + public: 'true' + discoverable: 'false' + - alias: web-app + type: kubernetes + reference: petclinic.yaml + endpoints: + - name: test-endpoint-k8s + port: 4321 + attributes: + protocol: http + secure: 'true' + public: 'true' + discoverable: 'false'