From e541b7ee8d69eef4131165db982227c88f364329 Mon Sep 17 00:00:00 2001 From: Michal Vala Date: Thu, 1 Oct 2020 12:03:42 +0200 Subject: [PATCH] fixes #17840 - SingleHost devfile endpoints on subdomains (#17928) Signed-off-by: Michal Vala --- .../webapp/WEB-INF/classes/che/che.properties | 6 ++ .../model/workspace/config/ServerConfig.java | 5 + .../kubernetes/KubernetesInfraModule.java | 19 +++- ...ockerimageComponentToWorkspaceApplier.java | 3 +- ...KubernetesComponentToWorkspaceApplier.java | 3 +- .../CombinedSingleHostServerExposer.java | 98 +++++++++++++++++++ .../ExternalServerExposerProvider.java | 28 +----- .../server/external/IngressServerExposer.java | 5 +- ...bernetesExternalServerExposerProvider.java | 67 +++++++++++++ ...tiHostExternalServiceExposureStrategy.java | 2 +- .../MultihostIngressServerExposer.java | 38 +++++++ .../resolver/AbstractServerResolver.java | 8 -- .../resolver/ConfigMapServerResolver.java | 26 +++-- .../resolver/IngressServerResolver.java | 2 +- .../KubernetesServerResolverFactory.java | 5 +- .../server/resolver/ServerResolver.java | 8 ++ ...rimageComponentToWorkspaceApplierTest.java | 1 + ...rnetesComponentToWorkspaceApplierTest.java | 3 + .../CombinedSingleHostServerExposerTest.java | 62 ++++++++++++ .../resolver/ConfigMapServerResolverTest.java | 49 ++++++++++ .../openshift/OpenShiftInfraModule.java | 14 ++- .../OpenShiftServerResolverFactory.java | 4 +- .../openshift/server/RouteServerResolver.java | 2 +- ...penShiftExternalServerExposerProvider.java | 46 +++++++++ .../server/model/impl/ServerConfigImpl.java | 10 +- .../model/impl/ServerConfigImplTest.java | 27 +++++ 26 files changed, 482 insertions(+), 59 deletions(-) create mode 100644 infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/CombinedSingleHostServerExposer.java create mode 100644 infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/KubernetesExternalServerExposerProvider.java create mode 100644 infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultihostIngressServerExposer.java create mode 100644 infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/CombinedSingleHostServerExposerTest.java create mode 100644 infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ConfigMapServerResolverTest.java create mode 100644 infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/external/OpenShiftExternalServerExposerProvider.java diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties index 071aa25ceb..bf22e1ff73 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties @@ -257,6 +257,12 @@ che.infra.kubernetes.server_strategy=multi-host # - 'gateway': Exposes servers using reverse-proxy gateway. che.infra.kubernetes.singlehost.workspace.exposure=native +# Defines the way how to expose devfile endpoints, thus end-user's applications, in single-host server strategy. +# They can either follow the single-host strategy and be exposed on subpaths, or they can be exposed on subdomains. +# - 'multi-host': expose on subdomains +# - 'single-host': expose on subpaths +che.infra.kubernetes.singlehost.workspace.devfile_endpoint_exposure=multi-host + # Defines labels which will be set to ConfigMaps configuring single-host gateway. che.infra.kubernetes.singlehost.gateway.configmap_labels=app=che,component=che-gateway-config 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 d01f4ef36a..e376378709 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 @@ -91,6 +91,11 @@ public interface ServerConfig { */ String SERVICE_PORT_ATTRIBUTE = "servicePort"; + /** + * This attributes is marking that server come from the devfile endpoint. Used internally only. + */ + String DEVFILE_ENDPOINT = "devfileEndpoint"; + /** * Port used by server. * diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java index 6e6131093f..7a64d8ca79 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java @@ -69,10 +69,13 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.server.PreviewUrlExpo import org.eclipse.che.workspace.infrastructure.kubernetes.server.WorkspaceExposureType; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.DefaultHostExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposer; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerProvider; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.GatewayServerExposer; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.IngressServerExposer; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.KubernetesExternalServerExposerProvider; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostExternalServiceExposureStrategy; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultihostIngressServerExposer; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ServiceExposureStrategyProvider; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer; @@ -162,15 +165,21 @@ public class KubernetesInfraModule extends AbstractModule { MapBinder> exposureStrategies = - MapBinder.newMapBinder( - binder(), - new TypeLiteral() {}, - new TypeLiteral>() {}); - exposureStrategies.addBinding(WorkspaceExposureType.NATIVE).to(IngressServerExposer.class); + MapBinder.newMapBinder(binder(), new TypeLiteral<>() {}, new TypeLiteral<>() {}); + exposureStrategies + .addBinding(WorkspaceExposureType.NATIVE) + .to(new TypeLiteral>() {}); exposureStrategies .addBinding(WorkspaceExposureType.GATEWAY) .to(new TypeLiteral>() {}); + bind(new TypeLiteral>() {}) + .annotatedWith(com.google.inject.name.Names.named("multihost-exposer")) + .to(new TypeLiteral>() {}); + + bind(new TypeLiteral>() {}) + .to(new TypeLiteral>() {}); + bind(ServersConverter.class).to(new TypeLiteral>() {}); bind(PreviewUrlExposer.class) .to(new TypeLiteral>() {}); 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 0a87939699..1c9bfbc717 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 @@ -141,7 +141,8 @@ public class DockerimageComponentToWorkspaceApplier implements ComponentToWorksp .getEndpoints() .stream() .collect( - Collectors.toMap(Endpoint::getName, ServerConfigImpl::createFromEndpoint))); + Collectors.toMap( + Endpoint::getName, e -> ServerConfigImpl.createFromEndpoint(e, true)))); dockerimageComponent .getVolumes() 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 4d85443f19..5b571c6c22 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 @@ -264,7 +264,8 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa .getEndpoints() .stream() .collect( - Collectors.toMap(Endpoint::getName, ServerConfigImpl::createFromEndpoint))); + Collectors.toMap( + Endpoint::getName, e -> ServerConfigImpl.createFromEndpoint(e, true)))); } private void provisionVolumes( diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/CombinedSingleHostServerExposer.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/CombinedSingleHostServerExposer.java new file mode 100644 index 0000000000..557c677c72 --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/CombinedSingleHostServerExposer.java @@ -0,0 +1,98 @@ +/* + * 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.server.external; + +import static java.lang.Boolean.FALSE; +import static org.eclipse.che.api.core.model.workspace.config.ServerConfig.DEVFILE_ENDPOINT; + +import io.fabric8.kubernetes.api.model.ServicePort; +import java.util.HashMap; +import java.util.Map; +import org.eclipse.che.api.core.model.workspace.config.ServerConfig; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; + +/** + * This {@link ExternalServerExposer} is used in single-host mode when we need to expose some + * servers on subdomain, instead of subpaths. + * + *

It aggregates 2 {@link ExternalServerExposer}s, using one to expose servers on subdomand, and + * 2nd to expose servers on subpaths. It determines which to use for individual server based on some + * attribute in {@link ServerConfig#getAttributes()} (see implementation {@link + * CombinedSingleHostServerExposer#expose(KubernetesEnvironment, String, String, String, + * ServicePort, Map)} for the details). + * + * @param environment type + */ +public class CombinedSingleHostServerExposer + implements ExternalServerExposer { + + private final ExternalServerExposer subdomainServerExposer; + private final ExternalServerExposer subpathServerExposer; + + public CombinedSingleHostServerExposer( + ExternalServerExposer subdomainServerExposer, + ExternalServerExposer subpathServerExposer) { + this.subdomainServerExposer = subdomainServerExposer; + this.subpathServerExposer = subpathServerExposer; + } + + /** + * Exposes given 'externalServers' to either subdomain or subpath, using 2 different {@link + * ExternalServerExposer}s. Which one to use for individual server is determined with {@link + * ServerConfig#DEVFILE_ENDPOINT} attribute. + * + * @param k8sEnv environment + * @param machineName machine containing servers + * @param serviceName service associated with machine, mapping all machine server ports + * @param serverId non-null for a unique server, null for a compound set of servers that should be + * exposed together. + * @param servicePort specific service port to be exposed externally + * @param externalServers server configs of servers to be exposed externally + */ + @Override + public void expose( + T k8sEnv, + String machineName, + String serviceName, + String serverId, + ServicePort servicePort, + Map externalServers) { + + if (serverId == null) { + // this is the ID for non-unique servers + serverId = servicePort.getName(); + } + + Map subpathServers = new HashMap<>(); + Map subdomainServers = new HashMap<>(); + + for (String esKey : externalServers.keySet()) { + ServerConfig serverConfig = externalServers.get(esKey); + if (Boolean.parseBoolean( + serverConfig.getAttributes().getOrDefault(DEVFILE_ENDPOINT, FALSE.toString()))) { + subdomainServers.put(esKey, serverConfig); + } else { + subpathServers.put(esKey, serverConfig); + } + } + + if (!subpathServers.isEmpty()) { + subpathServerExposer.expose( + k8sEnv, machineName, serviceName, serverId, servicePort, subpathServers); + } + + if (!subdomainServers.isEmpty()) { + subdomainServerExposer.expose( + k8sEnv, machineName, serviceName, serverId, servicePort, subdomainServers); + } + } +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServerExposerProvider.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServerExposerProvider.java index 7206a9453a..55b7f7b56f 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServerExposerProvider.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServerExposerProvider.java @@ -11,33 +11,13 @@ */ package org.eclipse.che.workspace.infrastructure.kubernetes.server.external; -import java.util.Map; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; +import javax.inject.Provider; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; -import org.eclipse.che.workspace.infrastructure.kubernetes.server.AbstractExposureStrategyAwareProvider; -import org.eclipse.che.workspace.infrastructure.kubernetes.server.WorkspaceExposureType; /** - * Provides {@link ExternalServerExposer} based on `che.infra.kubernetes.server_strategy` and - * `che.infra.kubernetes.singlehost.workspace.exposure` properties. + * Provides {@link ExternalServerExposer} implementations based on configuration. * * @param type of environment */ -@Singleton -public class ExternalServerExposerProvider - extends AbstractExposureStrategyAwareProvider> { - @Inject - public ExternalServerExposerProvider( - @Named("che.infra.kubernetes.server_strategy") String exposureStrategy, - @Named("che.infra.kubernetes.singlehost.workspace.exposure") String exposureType, - Map> exposers) { - - super( - exposureStrategy, - exposureType, - exposers, - "Could not find an external server exposer implementation for the exposure type '%s'."); - } -} +public interface ExternalServerExposerProvider + extends Provider> {} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServerExposer.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServerExposer.java index 7665bfcbdc..f758d69a32 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServerExposer.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServerExposer.java @@ -31,7 +31,8 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServ * @see ExternalServerExposer */ @Singleton -public class IngressServerExposer implements ExternalServerExposer { +public class IngressServerExposer + implements ExternalServerExposer { /** * A string to look for in the value of the "che.infra.kubernetes.ingress.path_transform" @@ -69,7 +70,7 @@ public class IngressServerExposer implements ExternalServerExposerBased on server strategy, it can create a {@link CombinedSingleHostServerExposer} with + * Kubernetes specific {@link IngressServerExposer} for exposing servers on subdomains. + * + * @param type of environment + */ +@Singleton +public class KubernetesExternalServerExposerProvider + extends AbstractExposureStrategyAwareProvider> + implements ExternalServerExposerProvider { + + private final ExternalServerExposer combinedInstance; + + @Inject + public KubernetesExternalServerExposerProvider( + @Named("che.infra.kubernetes.server_strategy") String exposureStrategy, + @Named("che.infra.kubernetes.singlehost.workspace.exposure") String exposureType, + @Named("che.infra.kubernetes.singlehost.workspace.devfile_endpoint_exposure") + String devfileEndpointsExposure, + @Named("multihost-exposer") ExternalServerExposer multihostExposer, + Map> exposers) { + super( + exposureStrategy, + exposureType, + exposers, + "Could not find an external server exposer implementation for the exposure type '%s'."); + + if (SINGLE_HOST_STRATEGY.equals(exposureStrategy) + && MULTI_HOST_STRATEGY.equals(devfileEndpointsExposure)) { + this.combinedInstance = new CombinedSingleHostServerExposer<>(multihostExposer, instance); + } else { + this.combinedInstance = null; + } + } + + @Override + public ExternalServerExposer get() { + return combinedInstance != null ? combinedInstance : instance; + } +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostExternalServiceExposureStrategy.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostExternalServiceExposureStrategy.java index 3b7225b398..d1fc61a9ba 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostExternalServiceExposureStrategy.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostExternalServiceExposureStrategy.java @@ -46,7 +46,7 @@ import org.eclipse.che.inject.ConfigurationException; public class MultiHostExternalServiceExposureStrategy implements ExternalServiceExposureStrategy { public static final String MULTI_HOST_STRATEGY = "multi-host"; - private static final String INGRESS_DOMAIN_PROPERTY = "che.infra.kubernetes.ingress.domain"; + protected static final String INGRESS_DOMAIN_PROPERTY = "che.infra.kubernetes.ingress.domain"; private final String domain; diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultihostIngressServerExposer.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultihostIngressServerExposer.java new file mode 100644 index 0000000000..4ca4c5fb2e --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultihostIngressServerExposer.java @@ -0,0 +1,38 @@ +/* + * 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.server.external; + +import io.fabric8.kubernetes.api.model.extensions.Ingress; +import java.util.Map; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.commons.annotation.Nullable; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; + +/** + * Uses Kubernetes {@link Ingress}es to expose the services using subdomains a.k.a. multi-host. + * + * @see ExternalServerExposer + */ +@Singleton +public class MultihostIngressServerExposer + extends IngressServerExposer implements ExternalServerExposer { + @Inject + public MultihostIngressServerExposer( + MultiHostExternalServiceExposureStrategy serviceExposureStrategy, + @Named("infra.kubernetes.ingress.annotations") Map annotations, + @Nullable @Named("che.infra.kubernetes.ingress.labels") String labelsProperty, + @Nullable @Named("che.infra.kubernetes.ingress.path_transform") String pathTransformFmt) { + super(serviceExposureStrategy, annotations, labelsProperty, pathTransformFmt); + } +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/AbstractServerResolver.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/AbstractServerResolver.java index a36bb3bcde..14a52db4f1 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/AbstractServerResolver.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/AbstractServerResolver.java @@ -76,12 +76,4 @@ public abstract class AbstractServerResolver implements ServerResolver { .build(), (s1, s2) -> s2)); } - - /** - * Resolve external servers from implementation specific k8s object and it's annotations. - * - * @param machineName machine to resolve servers - * @return resolved servers - */ - protected abstract Map resolveExternalServers(String machineName); } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ConfigMapServerResolver.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ConfigMapServerResolver.java index dc75e024d7..2e83db74ce 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ConfigMapServerResolver.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ConfigMapServerResolver.java @@ -15,6 +15,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.Service; +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; @@ -27,10 +28,15 @@ public class ConfigMapServerResolver extends AbstractServerResolver { private final Multimap configMaps; private final String cheHost; + private final ServerResolver nativeServerResolver; public ConfigMapServerResolver( - Iterable services, Iterable configMaps, String cheHost) { + Iterable services, + Iterable configMaps, + String cheHost, + ServerResolver nativeServerResolver) { super(services); + this.nativeServerResolver = nativeServerResolver; this.cheHost = cheHost; this.configMaps = ArrayListMultimap.create(); @@ -44,13 +50,17 @@ public class ConfigMapServerResolver extends AbstractServerResolver { } @Override - protected Map resolveExternalServers(String machineName) { - return configMaps - .get(machineName) - .stream() - .map(this::fillGatewayServers) - .flatMap(m -> m.entrySet().stream()) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (s1, s2) -> s2)); + public Map resolveExternalServers(String machineName) { + Map serverMap = new HashMap<>(); + serverMap.putAll(nativeServerResolver.resolveExternalServers(machineName)); + serverMap.putAll( + configMaps + .get(machineName) + .stream() + .map(this::fillGatewayServers) + .flatMap(m -> m.entrySet().stream()) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (s1, s2) -> s2))); + return serverMap; } private Map fillGatewayServers(ConfigMap configMap) { diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/IngressServerResolver.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/IngressServerResolver.java index 85bca82cbf..89ef37fccb 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/IngressServerResolver.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/IngressServerResolver.java @@ -61,7 +61,7 @@ public class IngressServerResolver extends AbstractServerResolver { } @Override - protected Map resolveExternalServers(String machineName) { + public Map resolveExternalServers(String machineName) { return ingresses .get(machineName) .stream() diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/KubernetesServerResolverFactory.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/KubernetesServerResolverFactory.java index d31058fdd7..417b211dc1 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/KubernetesServerResolverFactory.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/KubernetesServerResolverFactory.java @@ -38,7 +38,10 @@ public class KubernetesServerResolverFactory extends AbstractServerResolverFacto exposureStrategy, wsExposureType, ImmutableMap.of( - GATEWAY, (ss, is, cs) -> new ConfigMapServerResolver(ss, cs, cheHost), + GATEWAY, + (ss, is, cs) -> + new ConfigMapServerResolver( + ss, cs, cheHost, new IngressServerResolver(pathTransformInverter, ss, is)), NATIVE, (ss, is, cs) -> new IngressServerResolver(pathTransformInverter, ss, is)), "Failed to initialize KubernetesServerResolverFactory for workspace exposure type '%s'."); } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ServerResolver.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ServerResolver.java index a4835199aa..64e41ff2f9 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ServerResolver.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ServerResolver.java @@ -35,4 +35,12 @@ public interface ServerResolver { * @return resolved servers */ Map resolve(String machineName); + + /** + * Resolve external servers from implementation specific k8s object and it's annotations. + * + * @param machineName machine to resolve servers + * @return resolved servers + */ + Map resolveExternalServers(String machineName); } 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 66d52b5771..148b8e85bf 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 @@ -350,6 +350,7 @@ public class DockerimageComponentToWorkspaceApplierTest { Map attributes = serverConfig.getAttributes(); assertEquals(attributes.get(ServerConfig.INTERNAL_SERVER_ATTRIBUTE), "true"); assertEquals(attributes.get("secure"), "false"); + assertEquals(attributes.get(ServerConfig.DEVFILE_ENDPOINT), "true"); } @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 99847af04b..b5bd3d0fd8 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 @@ -18,6 +18,7 @@ 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; import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.DEVFILE_COMPONENT_ALIAS_ATTRIBUTE; +import static org.eclipse.che.api.core.model.workspace.config.ServerConfig.DEVFILE_ENDPOINT; import static org.eclipse.che.api.workspace.server.devfile.Constants.COMPONENT_ALIAS_COMMAND_ATTRIBUTE; 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; @@ -678,6 +679,8 @@ public class KubernetesComponentToWorkspaceApplierTest { assertEquals(serverConfigs.get(endpointName).getPort(), endpointPort.toString()); assertEquals(serverConfigs.get(endpointName).getPath(), endpointPath); assertEquals(serverConfigs.get(endpointName).getProtocol(), endpointProtocol); + assertEquals( + serverConfigs.get(endpointName).getAttributes().get(DEVFILE_ENDPOINT), "true"); assertEquals( serverConfigs.get(endpointName).isSecure(), Boolean.parseBoolean(endpointSecure)); assertEquals( diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/CombinedSingleHostServerExposerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/CombinedSingleHostServerExposerTest.java new file mode 100644 index 0000000000..fbabcd5bd2 --- /dev/null +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/CombinedSingleHostServerExposerTest.java @@ -0,0 +1,62 @@ +/* + * 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.server.external; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.eclipse.che.api.core.model.workspace.config.ServerConfig.DEVFILE_ENDPOINT; +import static org.mockito.Mockito.verify; +import static org.testng.Assert.*; + +import io.fabric8.kubernetes.api.model.ServicePort; +import java.util.Map; +import org.eclipse.che.api.core.model.workspace.config.ServerConfig; +import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class CombinedSingleHostServerExposerTest { + + @Mock private KubernetesEnvironment env; + + private final String MACHINE = "machine"; + private final String SERVICE = "service"; + private final String SERVER = "server"; + private final ServicePort PORT = new ServicePort(); + + @Mock private ExternalServerExposer subdomainExposer; + @Mock private ExternalServerExposer subpathExposer; + + @Test + public void shouldExposeDevfileServersOnSubdomans() { + // given + ServerConfig s1 = new ServerConfigImpl("1", "http", "/", emptyMap()); + ServerConfig s2 = + new ServerConfigImpl("2", "http", "/", singletonMap(DEVFILE_ENDPOINT, "false")); + ServerConfig s3 = + new ServerConfigImpl("3", "http", "/", singletonMap(DEVFILE_ENDPOINT, "true")); + + CombinedSingleHostServerExposer serverExposer = + new CombinedSingleHostServerExposer<>(subdomainExposer, subpathExposer); + + // when + serverExposer.expose(env, MACHINE, SERVICE, SERVER, PORT, Map.of("s1", s1, "s2", s2, "s3", s3)); + + // then + verify(subdomainExposer).expose(env, MACHINE, SERVICE, SERVER, PORT, Map.of("s3", s3)); + verify(subpathExposer).expose(env, MACHINE, SERVICE, SERVER, PORT, Map.of("s1", s1, "s2", s2)); + } +} diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ConfigMapServerResolverTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ConfigMapServerResolverTest.java new file mode 100644 index 0000000000..176473e4f6 --- /dev/null +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/resolver/ConfigMapServerResolverTest.java @@ -0,0 +1,49 @@ +/* + * 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.server.resolver; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.mockito.Mockito.when; +import static org.testng.Assert.*; + +import java.util.Map; +import org.eclipse.che.api.core.model.workspace.runtime.ServerStatus; +import org.eclipse.che.api.workspace.server.model.impl.ServerImpl; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class ConfigMapServerResolverTest { + @Mock private ServerResolver nativeServerResolver; + + @Test + public void shouldIncludeServersFromNativeResolver() { + // given + ServerImpl server = new ServerImpl("server", ServerStatus.UNKNOWN, emptyMap()); + when(nativeServerResolver.resolveExternalServers("test")) + .thenReturn(singletonMap("s1", server)); + + ConfigMapServerResolver serverResolver = + new ConfigMapServerResolver(emptyList(), emptyList(), "che.host", nativeServerResolver); + + // when + Map resolvedServers = serverResolver.resolve("test"); + + // then + assertTrue(resolvedServers.containsKey("s1")); + assertEquals(resolvedServers.get("s1"), server); + } +} diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java index 37970c0f32..6c69f1e2a5 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java @@ -72,6 +72,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.provision.server.Serv import org.eclipse.che.workspace.infrastructure.kubernetes.server.PreviewUrlExposer; import org.eclipse.che.workspace.infrastructure.kubernetes.server.WorkspaceExposureType; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposer; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerProvider; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.GatewayServerExposer; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ServiceExposureStrategyProvider; @@ -101,6 +102,7 @@ import org.eclipse.che.workspace.infrastructure.openshift.server.OpenShiftCookie import org.eclipse.che.workspace.infrastructure.openshift.server.OpenShiftPreviewUrlExposer; import org.eclipse.che.workspace.infrastructure.openshift.server.OpenShiftServerExposureStrategy; import org.eclipse.che.workspace.infrastructure.openshift.server.RouteServerExposer; +import org.eclipse.che.workspace.infrastructure.openshift.server.external.OpenShiftExternalServerExposerProvider; import org.eclipse.che.workspace.infrastructure.openshift.wsplugins.brokerphases.OpenshiftBrokerEnvironmentFactory; /** @author Sergii Leshchenko */ @@ -146,15 +148,19 @@ public class OpenShiftInfraModule extends AbstractModule { MapBinder> exposureStrategies = - MapBinder.newMapBinder( - binder(), - new TypeLiteral() {}, - new TypeLiteral>() {}); + MapBinder.newMapBinder(binder(), new TypeLiteral<>() {}, new TypeLiteral<>() {}); exposureStrategies.addBinding(WorkspaceExposureType.NATIVE).to(RouteServerExposer.class); exposureStrategies .addBinding(WorkspaceExposureType.GATEWAY) .to(new TypeLiteral>() {}); + bind(new TypeLiteral>() {}) + .annotatedWith(com.google.inject.name.Names.named("multihost-exposer")) + .to(RouteServerExposer.class); + + bind(new TypeLiteral>() {}) + .to(OpenShiftExternalServerExposerProvider.class); + bind(ServersConverter.class).to(new TypeLiteral>() {}); bind(PreviewUrlExposer.class).to(new TypeLiteral() {}); bind(PreviewUrlCommandProvisioner.class) diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/OpenShiftServerResolverFactory.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/OpenShiftServerResolverFactory.java index 40a397b33d..a080f39f4a 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/OpenShiftServerResolverFactory.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/OpenShiftServerResolverFactory.java @@ -37,7 +37,9 @@ public class OpenShiftServerResolverFactory extends AbstractServerResolverFactor exposureStrategy, wsExposureType, ImmutableMap.of( - GATEWAY, (ss, rs, cs) -> new ConfigMapServerResolver(ss, cs, cheHost), + GATEWAY, + (ss, rs, cs) -> + new ConfigMapServerResolver(ss, cs, cheHost, new RouteServerResolver(ss, rs)), NATIVE, (ss, rs, cs) -> new RouteServerResolver(ss, rs)), "Failed to initialize OpenShiftServerResolverFactory for workspace exposure type '%s'."); } diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/RouteServerResolver.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/RouteServerResolver.java index 196b4c9fab..68a144c00a 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/RouteServerResolver.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/RouteServerResolver.java @@ -51,7 +51,7 @@ public class RouteServerResolver extends AbstractServerResolver { } @Override - protected Map resolveExternalServers(String machineName) { + public Map resolveExternalServers(String machineName) { return routes .get(machineName) .stream() diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/external/OpenShiftExternalServerExposerProvider.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/external/OpenShiftExternalServerExposerProvider.java new file mode 100644 index 0000000000..8f6abf18b1 --- /dev/null +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/external/OpenShiftExternalServerExposerProvider.java @@ -0,0 +1,46 @@ +/* + * 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.openshift.server.external; + +import java.util.Map; +import javax.inject.Inject; +import javax.inject.Named; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.WorkspaceExposureType; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.CombinedSingleHostServerExposer; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposer; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerProvider; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.KubernetesExternalServerExposerProvider; +import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment; +import org.eclipse.che.workspace.infrastructure.openshift.server.RouteServerExposer; + +/** + * Provides {@link ExternalServerExposer} based on `che.infra.kubernetes.server_strategy` and + * `che.infra.kubernetes.singlehost.workspace.exposure` properties. + * + *

Based on server strategy, it can create a {@link CombinedSingleHostServerExposer} with + * OpenShift specific {@link RouteServerExposer} for exposing servers on subdomains. + */ +public class OpenShiftExternalServerExposerProvider + extends KubernetesExternalServerExposerProvider + implements ExternalServerExposerProvider { + + @Inject + public OpenShiftExternalServerExposerProvider( + @Named("che.infra.kubernetes.server_strategy") String exposureStrategy, + @Named("che.infra.kubernetes.singlehost.workspace.exposure") String exposureType, + @Named("che.infra.kubernetes.singlehost.workspace.devfile_endpoint_exposure") + String devfileEndpointExposure, + @Named("multihost-exposer") ExternalServerExposer multihostExposer, + Map> exposers) { + super(exposureStrategy, exposureType, devfileEndpointExposure, multihostExposer, exposers); + } +} 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 f0cba0c09d..cc2a635430 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 @@ -180,7 +180,7 @@ public class ServerConfigImpl implements ServerConfig { + '}'; } - public static ServerConfigImpl createFromEndpoint(Endpoint endpoint) { + public static ServerConfigImpl createFromEndpoint(Endpoint endpoint, boolean devfileEndpoint) { HashMap attributes = new HashMap<>(endpoint.getAttributes()); attributes.put(SERVER_NAME_ATTRIBUTE, endpoint.getName()); @@ -196,6 +196,14 @@ public class ServerConfigImpl implements ServerConfig { ServerConfig.setInternal(attributes, true); } + if (devfileEndpoint) { + attributes.put(DEVFILE_ENDPOINT, Boolean.TRUE.toString()); + } + return new ServerConfigImpl(Integer.toString(endpoint.getPort()), protocol, path, attributes); } + + public static ServerConfigImpl createFromEndpoint(Endpoint endpoint) { + return createFromEndpoint(endpoint, false); + } } 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 index 57f775e47b..c4ccce21c5 100644 --- 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 @@ -13,11 +13,13 @@ 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.DEVFILE_ENDPOINT; 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.HashMap; 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; @@ -106,4 +108,29 @@ public class ServerConfigImplTest { assertEquals( 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(DEVFILE_ENDPOINT)); + assertTrue(Boolean.parseBoolean(serverConfig.getAttributes().get(DEVFILE_ENDPOINT))); + } + + @Test + public void testCreateFromEndpointDevfileEndpointAttributeNotSet() { + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint(new EndpointImpl("name", 123, new HashMap<>()), false); + + assertFalse(serverConfig.getAttributes().containsKey(DEVFILE_ENDPOINT)); + } + + @Test + public void testCreateFromEndpointDevfileEndpointAttributeNotSetWhenDefault() { + ServerConfig serverConfig = + ServerConfigImpl.createFromEndpoint(new EndpointImpl("name", 123, new HashMap<>())); + + assertFalse(serverConfig.getAttributes().containsKey(DEVFILE_ENDPOINT)); + } }