fixes #17840 - SingleHost devfile endpoints on subdomains (#17928)

Signed-off-by: Michal Vala <mvala@redhat.com>
7.20.x
Michal Vala 2020-10-01 12:03:42 +02:00 committed by GitHub
parent 917425bb73
commit e541b7ee8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 482 additions and 59 deletions

View File

@ -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

View File

@ -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.
*

View File

@ -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<WorkspaceExposureType, ExternalServerExposer<KubernetesEnvironment>>
exposureStrategies =
MapBinder.newMapBinder(
binder(),
new TypeLiteral<WorkspaceExposureType>() {},
new TypeLiteral<ExternalServerExposer<KubernetesEnvironment>>() {});
exposureStrategies.addBinding(WorkspaceExposureType.NATIVE).to(IngressServerExposer.class);
MapBinder.newMapBinder(binder(), new TypeLiteral<>() {}, new TypeLiteral<>() {});
exposureStrategies
.addBinding(WorkspaceExposureType.NATIVE)
.to(new TypeLiteral<IngressServerExposer<KubernetesEnvironment>>() {});
exposureStrategies
.addBinding(WorkspaceExposureType.GATEWAY)
.to(new TypeLiteral<GatewayServerExposer<KubernetesEnvironment>>() {});
bind(new TypeLiteral<ExternalServerExposer<KubernetesEnvironment>>() {})
.annotatedWith(com.google.inject.name.Names.named("multihost-exposer"))
.to(new TypeLiteral<MultihostIngressServerExposer<KubernetesEnvironment>>() {});
bind(new TypeLiteral<ExternalServerExposerProvider<KubernetesEnvironment>>() {})
.to(new TypeLiteral<KubernetesExternalServerExposerProvider<KubernetesEnvironment>>() {});
bind(ServersConverter.class).to(new TypeLiteral<ServersConverter<KubernetesEnvironment>>() {});
bind(PreviewUrlExposer.class)
.to(new TypeLiteral<PreviewUrlExposer<KubernetesEnvironment>>() {});

View File

@ -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()

View File

@ -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(

View File

@ -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.
*
* <p>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 <T> environment type
*/
public class CombinedSingleHostServerExposer<T extends KubernetesEnvironment>
implements ExternalServerExposer<T> {
private final ExternalServerExposer<T> subdomainServerExposer;
private final ExternalServerExposer<T> subpathServerExposer;
public CombinedSingleHostServerExposer(
ExternalServerExposer<T> subdomainServerExposer,
ExternalServerExposer<T> 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<String, ServerConfig> externalServers) {
if (serverId == null) {
// this is the ID for non-unique servers
serverId = servicePort.getName();
}
Map<String, ServerConfig> subpathServers = new HashMap<>();
Map<String, ServerConfig> 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);
}
}
}

View File

@ -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 <T> type of environment
*/
@Singleton
public class ExternalServerExposerProvider<T extends KubernetesEnvironment>
extends AbstractExposureStrategyAwareProvider<ExternalServerExposer<T>> {
@Inject
public ExternalServerExposerProvider(
@Named("che.infra.kubernetes.server_strategy") String exposureStrategy,
@Named("che.infra.kubernetes.singlehost.workspace.exposure") String exposureType,
Map<WorkspaceExposureType, ExternalServerExposer<T>> exposers) {
super(
exposureStrategy,
exposureType,
exposers,
"Could not find an external server exposer implementation for the exposure type '%s'.");
}
}
public interface ExternalServerExposerProvider<T extends KubernetesEnvironment>
extends Provider<ExternalServerExposer<T>> {}

View File

@ -31,7 +31,8 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServ
* @see ExternalServerExposer
*/
@Singleton
public class IngressServerExposer implements ExternalServerExposer<KubernetesEnvironment> {
public class IngressServerExposer<T extends KubernetesEnvironment>
implements ExternalServerExposer<T> {
/**
* A string to look for in the value of the "che.infra.kubernetes.ingress.path_transform"
@ -69,7 +70,7 @@ public class IngressServerExposer implements ExternalServerExposer<KubernetesEnv
*/
@Override
public void expose(
KubernetesEnvironment k8sEnv,
T k8sEnv,
@Nullable String machineName,
String serviceName,
String serverId,

View File

@ -0,0 +1,67 @@
/*
* 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 org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostExternalServiceExposureStrategy.MULTI_HOST_STRATEGY;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostExternalServiceExposureStrategy.SINGLE_HOST_STRATEGY;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
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.
*
* <p>Based on server strategy, it can create a {@link CombinedSingleHostServerExposer} with
* Kubernetes specific {@link IngressServerExposer} for exposing servers on subdomains.
*
* @param <T> type of environment
*/
@Singleton
public class KubernetesExternalServerExposerProvider<T extends KubernetesEnvironment>
extends AbstractExposureStrategyAwareProvider<ExternalServerExposer<T>>
implements ExternalServerExposerProvider<T> {
private final ExternalServerExposer<T> 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<T> multihostExposer,
Map<WorkspaceExposureType, ExternalServerExposer<T>> 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<T> get() {
return combinedInstance != null ? combinedInstance : instance;
}
}

View File

@ -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;

View File

@ -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<T extends KubernetesEnvironment>
extends IngressServerExposer<T> implements ExternalServerExposer<T> {
@Inject
public MultihostIngressServerExposer(
MultiHostExternalServiceExposureStrategy serviceExposureStrategy,
@Named("infra.kubernetes.ingress.annotations") Map<String, String> 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);
}
}

View File

@ -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<String, ServerImpl> resolveExternalServers(String machineName);
}

View File

@ -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<String, ConfigMap> configMaps;
private final String cheHost;
private final ServerResolver nativeServerResolver;
public ConfigMapServerResolver(
Iterable<Service> services, Iterable<ConfigMap> configMaps, String cheHost) {
Iterable<Service> services,
Iterable<ConfigMap> 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<String, ServerImpl> 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<String, ServerImpl> resolveExternalServers(String machineName) {
Map<String, ServerImpl> 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<String, ServerImpl> fillGatewayServers(ConfigMap configMap) {

View File

@ -61,7 +61,7 @@ public class IngressServerResolver extends AbstractServerResolver {
}
@Override
protected Map<String, ServerImpl> resolveExternalServers(String machineName) {
public Map<String, ServerImpl> resolveExternalServers(String machineName) {
return ingresses
.get(machineName)
.stream()

View File

@ -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'.");
}

View File

@ -35,4 +35,12 @@ public interface ServerResolver {
* @return resolved servers
*/
Map<String, ServerImpl> 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<String, ServerImpl> resolveExternalServers(String machineName);
}

View File

@ -350,6 +350,7 @@ public class DockerimageComponentToWorkspaceApplierTest {
Map<String, String> attributes = serverConfig.getAttributes();
assertEquals(attributes.get(ServerConfig.INTERNAL_SERVER_ATTRIBUTE), "true");
assertEquals(attributes.get("secure"), "false");
assertEquals(attributes.get(ServerConfig.DEVFILE_ENDPOINT), "true");
}
@Test

View File

@ -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(

View File

@ -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<KubernetesEnvironment> subdomainExposer;
@Mock private ExternalServerExposer<KubernetesEnvironment> 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<KubernetesEnvironment> 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));
}
}

View File

@ -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<String, ServerImpl> resolvedServers = serverResolver.resolve("test");
// then
assertTrue(resolvedServers.containsKey("s1"));
assertEquals(resolvedServers.get("s1"), server);
}
}

View File

@ -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<WorkspaceExposureType, ExternalServerExposer<OpenShiftEnvironment>>
exposureStrategies =
MapBinder.newMapBinder(
binder(),
new TypeLiteral<WorkspaceExposureType>() {},
new TypeLiteral<ExternalServerExposer<OpenShiftEnvironment>>() {});
MapBinder.newMapBinder(binder(), new TypeLiteral<>() {}, new TypeLiteral<>() {});
exposureStrategies.addBinding(WorkspaceExposureType.NATIVE).to(RouteServerExposer.class);
exposureStrategies
.addBinding(WorkspaceExposureType.GATEWAY)
.to(new TypeLiteral<GatewayServerExposer<OpenShiftEnvironment>>() {});
bind(new TypeLiteral<ExternalServerExposer<OpenShiftEnvironment>>() {})
.annotatedWith(com.google.inject.name.Names.named("multihost-exposer"))
.to(RouteServerExposer.class);
bind(new TypeLiteral<ExternalServerExposerProvider<OpenShiftEnvironment>>() {})
.to(OpenShiftExternalServerExposerProvider.class);
bind(ServersConverter.class).to(new TypeLiteral<ServersConverter<OpenShiftEnvironment>>() {});
bind(PreviewUrlExposer.class).to(new TypeLiteral<OpenShiftPreviewUrlExposer>() {});
bind(PreviewUrlCommandProvisioner.class)

View File

@ -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'.");
}

View File

@ -51,7 +51,7 @@ public class RouteServerResolver extends AbstractServerResolver {
}
@Override
protected Map<String, ServerImpl> resolveExternalServers(String machineName) {
public Map<String, ServerImpl> resolveExternalServers(String machineName) {
return routes
.get(machineName)
.stream()

View File

@ -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.
*
* <p>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<OpenShiftEnvironment>
implements ExternalServerExposerProvider<OpenShiftEnvironment> {
@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<OpenShiftEnvironment> multihostExposer,
Map<WorkspaceExposureType, ExternalServerExposer<OpenShiftEnvironment>> exposers) {
super(exposureStrategy, exposureType, devfileEndpointExposure, multihostExposer, exposers);
}
}

View File

@ -180,7 +180,7 @@ public class ServerConfigImpl implements ServerConfig {
+ '}';
}
public static ServerConfigImpl createFromEndpoint(Endpoint endpoint) {
public static ServerConfigImpl createFromEndpoint(Endpoint endpoint, boolean devfileEndpoint) {
HashMap<String, String> 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);
}
}

View File

@ -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));
}
}