Add RouteAnnotations & Create one route for the same container port
parent
2c0fd870ef
commit
c62841aa6a
|
|
@ -19,9 +19,5 @@ public final class Constants {
|
|||
|
||||
public static final String CHE_POD_NAME_LABEL = "che.pod.name";
|
||||
|
||||
public static final String CHE_SERVER_NAME_ANNOTATION = "che.server.name";
|
||||
public static final String CHE_SERVER_PROTOCOL_ANNOTATION = "che.server.protocol";
|
||||
public static final String CHE_SERVER_PATH_ANNOTATION = "che.server.path";
|
||||
|
||||
private Constants() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,8 @@ import io.fabric8.kubernetes.api.model.Container;
|
|||
import io.fabric8.kubernetes.api.model.DoneablePod;
|
||||
import io.fabric8.kubernetes.api.model.HasMetadata;
|
||||
import io.fabric8.kubernetes.api.model.KubernetesListBuilder;
|
||||
import io.fabric8.kubernetes.api.model.ContainerPort;
|
||||
import io.fabric8.kubernetes.api.model.IntOrString;
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.ServicePort;
|
||||
import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||
import io.fabric8.kubernetes.client.Watcher;
|
||||
import io.fabric8.kubernetes.client.dsl.PodResource;
|
||||
|
|
@ -28,6 +25,7 @@ import io.fabric8.openshift.client.OpenShiftClient;
|
|||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.Machine;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.MachineStatus;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.ServerStatus;
|
||||
|
|
@ -63,10 +61,6 @@ import static java.util.Collections.emptyMap;
|
|||
import static java.util.stream.Collectors.toSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.eclipse.che.workspace.infrastructure.openshift.Constants.CHE_SERVER_NAME_ANNOTATION;
|
||||
import static org.eclipse.che.workspace.infrastructure.openshift.Constants.CHE_SERVER_PATH_ANNOTATION;
|
||||
import static org.eclipse.che.workspace.infrastructure.openshift.Constants.CHE_SERVER_PROTOCOL_ANNOTATION;
|
||||
|
||||
/**
|
||||
* @author Sergii Leshchenko
|
||||
* @author Anton Korneta
|
||||
|
|
@ -138,19 +132,23 @@ public class OpenShiftInternalRuntime extends InternalRuntime<OpenShiftRuntimeCo
|
|||
|
||||
for (Container container : createdPod.getSpec().getContainers()) {
|
||||
Map<String, ServerImpl> servers = new HashMap<>();
|
||||
Set<String> matchedServices = getMatchedServices(services, toCreate, container).stream()
|
||||
.map(s -> s.getMetadata().getName())
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> matchedServices = ServiceMatcher.from(services)
|
||||
.match(createdPod, container)
|
||||
.stream()
|
||||
.map(s -> s.getMetadata().getName())
|
||||
.collect(Collectors.toSet());
|
||||
for (Route route : routes) {
|
||||
if (matchedServices.contains(route.getSpec().getTo().getName())) {
|
||||
Map<String, String> annotations = route.getMetadata().getAnnotations();
|
||||
String serverName = annotations.get(CHE_SERVER_NAME_ANNOTATION);
|
||||
String serverPath = annotations.get(CHE_SERVER_PATH_ANNOTATION);
|
||||
String serverProtocol = annotations.get(CHE_SERVER_PROTOCOL_ANNOTATION);
|
||||
if (serverName != null) {
|
||||
servers.put(serverName, new ServerImpl(serverProtocol + "://" + route.getSpec().getHost() + serverPath,
|
||||
ServerStatus.UNKNOWN));
|
||||
}
|
||||
RoutesAnnotations.newDeserializer(route.getMetadata().getAnnotations())
|
||||
.servers()
|
||||
.entrySet()
|
||||
.forEach(e -> {
|
||||
String name = e.getKey();
|
||||
ServerConfig config = e.getValue();
|
||||
servers.put(name, new ServerImpl(
|
||||
config.getProtocol() + "://" + route.getSpec().getHost() + config.getPath(),
|
||||
ServerStatus.UNKNOWN));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -193,47 +191,6 @@ public class OpenShiftInternalRuntime extends InternalRuntime<OpenShiftRuntimeCo
|
|||
LOG.info("OpenShift Runtime for workspace {} started", getContext().getIdentity().getWorkspaceId());
|
||||
}
|
||||
|
||||
private List<Service> getMatchedServices(List<Service> services, Pod pod, Container container) {
|
||||
return services.stream()
|
||||
.filter(service -> isExposedByService(pod, service))
|
||||
.filter(service -> isExposedByService(container, service))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static boolean isExposedByService(Pod pod, Service service) {
|
||||
Map<String, String> labels = pod.getMetadata().getLabels();
|
||||
Map<String, String> selectorLabels = service.getSpec().getSelector();
|
||||
if (labels == null) {
|
||||
return false;
|
||||
}
|
||||
for (Map.Entry<String, String> selectorLabelEntry : selectorLabels.entrySet()) {
|
||||
if (!selectorLabelEntry.getValue().equals(labels.get(selectorLabelEntry.getKey()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isExposedByService(Container container, Service service) {
|
||||
for (ServicePort servicePort : service.getSpec().getPorts()) {
|
||||
IntOrString targetPort = servicePort.getTargetPort();
|
||||
if (targetPort.getIntVal() != null) {
|
||||
for (ContainerPort containerPort : container.getPorts()) {
|
||||
if (targetPort.getIntVal().equals(containerPort.getContainerPort())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (ContainerPort containerPort : container.getPorts()) {
|
||||
if (targetPort.getStrVal().equals(containerPort.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ? extends Machine> getInternalMachines() {
|
||||
return ImmutableMap.copyOf(machines);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
package org.eclipse.che.workspace.infrastructure.openshift;
|
||||
|
||||
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
public class RoutesAnnotations {
|
||||
public static final String ANNOTATION_PREFIX = "org.eclipse.che.";
|
||||
|
||||
public static final String SERVER_PORT_ANNOTATION_FMT = ANNOTATION_PREFIX + "server.%s.port";
|
||||
public static final String SERVER_PROTOCOL_ANNOTATION_FMT = ANNOTATION_PREFIX + "server.%s.protocol";
|
||||
public static final String SERVER_PATH_ANNOTATION_FMT = ANNOTATION_PREFIX + "server.%s.path";
|
||||
|
||||
/** Pattern that matches server annotations e.g. "org.eclipse.che.server.exec-agent.port". */
|
||||
private static final Pattern SERVER_ANNOTATION_PATTERN = Pattern.compile("org\\.eclipse\\.che\\.server\\.(?<ref>[\\w-/]+)\\..+");
|
||||
|
||||
public static Serializer newSerializer() { return new Serializer(); }
|
||||
|
||||
public static class Serializer {
|
||||
|
||||
private final Map<String, String> annotations = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Serializes server configuration as docker container annotations.
|
||||
* Appends serialization result to this aggregate.
|
||||
*
|
||||
* @param ref
|
||||
* server reference e.g. "exec-agent"
|
||||
* @param server
|
||||
* server configuration
|
||||
* @return this serializer
|
||||
*/
|
||||
public Serializer server(String ref, ServerConfig server) {
|
||||
annotations.put(String.format(SERVER_PORT_ANNOTATION_FMT, ref), server.getPort());
|
||||
annotations.put(String.format(SERVER_PROTOCOL_ANNOTATION_FMT, ref), server.getProtocol());
|
||||
if (server.getPath() != null) {
|
||||
annotations.put(String.format(SERVER_PATH_ANNOTATION_FMT, ref), server.getPath());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Serializer servers(Map<String, ? extends ServerConfig> servers) {
|
||||
servers.forEach(this::server);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, String> annotations() { return annotations; }
|
||||
}
|
||||
|
||||
public static Deserializer newDeserializer(Map<String, String> annotations) { return new Deserializer(annotations); }
|
||||
|
||||
public static class Deserializer {
|
||||
private final Map<String, String> annotations;
|
||||
|
||||
public Deserializer(Map<String, String> annotations) { this.annotations = Objects.requireNonNull(annotations); }
|
||||
|
||||
/** Retrieves server configuration from route annotations and returns (ref -> server config) map. */
|
||||
public Map<String, ServerConfig> servers() {
|
||||
Map<String, ServerConfig> servers = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : annotations.entrySet()) {
|
||||
Matcher refMatcher = SERVER_ANNOTATION_PATTERN.matcher(entry.getKey());
|
||||
if (refMatcher.matches()) {
|
||||
String ref = refMatcher.group("ref");
|
||||
if (!servers.containsKey(ref)) {
|
||||
servers.put(ref, new ServerConfigImpl(annotations.get(String.format(SERVER_PORT_ANNOTATION_FMT, ref)),
|
||||
annotations.get(String.format(SERVER_PROTOCOL_ANNOTATION_FMT, ref)),
|
||||
annotations.get(String.format(SERVER_PATH_ANNOTATION_FMT, ref))));
|
||||
}
|
||||
}
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
}
|
||||
|
||||
private RoutesAnnotations() {}
|
||||
}
|
||||
|
|
@ -31,7 +31,6 @@ import java.util.Optional;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static org.eclipse.che.workspace.infrastructure.openshift.Constants.CHE_POD_NAME_LABEL;
|
||||
|
||||
/**
|
||||
|
|
@ -117,15 +116,19 @@ public class ServerExposer {
|
|||
|
||||
openShiftEnvironment.getServices().put(service.getMetadata().getName(), service);
|
||||
|
||||
for (Map.Entry<String, ? extends ServerConfig> serverEntry : servers.entrySet()) {
|
||||
String serverName = serverEntry.getKey();
|
||||
ServerConfig serverConfig = serverEntry.getValue();
|
||||
ServicePort servicePort = portToServicePort.get(serverConfig.getPort());
|
||||
|
||||
//TODO 1 route for 1 service port could be enough. Implement it in scope of //TODO https://github.com/eclipse/che/issues/5688
|
||||
Route route = new RouteBuilder().withName(namePrefix + "-" + machineName + "-" + serverName)
|
||||
for (ServicePort servicePort : portToServicePort.values()) {
|
||||
Map<String, ? extends ServerConfig> routesServers = servers.entrySet()
|
||||
.stream()
|
||||
.filter(e -> {
|
||||
String port = e.getValue().getPort();
|
||||
return Integer.parseInt(port.split("/")[0]) == servicePort.getTargetPort().getIntVal();
|
||||
})
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
Route route = new RouteBuilder().withName(namePrefix + "-" + machineName + "-" + servicePort.getName())
|
||||
.withTargetPort(servicePort.getName())
|
||||
.withServer(serverName, serverConfig)
|
||||
.withServers(routesServers)
|
||||
.withTo(service.getMetadata().getName())
|
||||
.build();
|
||||
openShiftEnvironment.getRoutes().put(route.getMetadata().getName(), route);
|
||||
|
|
@ -190,22 +193,23 @@ public class ServerExposer {
|
|||
private Service build() {
|
||||
io.fabric8.kubernetes.api.model.ServiceBuilder builder = new io.fabric8.kubernetes.api.model.ServiceBuilder();
|
||||
return builder.withNewMetadata()
|
||||
.withName(name.replace("/", "-"))
|
||||
.withName(name.replace("/", "-"))
|
||||
.endMetadata()
|
||||
.withNewSpec()
|
||||
.withSelector(selector)
|
||||
.withPorts(ports)
|
||||
.withSelector(selector)
|
||||
.withPorts(ports)
|
||||
.endSpec()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
private static class RouteBuilder {
|
||||
private String name;
|
||||
private String serviceName;
|
||||
private IntOrString targetPort;
|
||||
private String serverName;
|
||||
private ServerConfig serverConfig;
|
||||
private String name;
|
||||
private String serviceName;
|
||||
private IntOrString targetPort;
|
||||
private String serverName;
|
||||
private ServerConfig serverConfig;
|
||||
private Map<String, ? extends ServerConfig> serversConfigs;
|
||||
|
||||
private RouteBuilder withName(String name) {
|
||||
this.name = name;
|
||||
|
|
@ -233,24 +237,27 @@ public class ServerExposer {
|
|||
return this;
|
||||
}
|
||||
|
||||
private RouteBuilder withServers(Map<String, ? extends ServerConfig> serversConfigs) {
|
||||
this.serversConfigs = serversConfigs;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Route build() {
|
||||
io.fabric8.openshift.api.model.RouteBuilder builder = new io.fabric8.openshift.api.model.RouteBuilder();
|
||||
HashMap<String, String> annotations = new HashMap<>();
|
||||
annotations.put(Constants.CHE_SERVER_NAME_ANNOTATION, serverName);
|
||||
annotations.put(Constants.CHE_SERVER_PROTOCOL_ANNOTATION, serverConfig.getProtocol());
|
||||
annotations.put(Constants.CHE_SERVER_PATH_ANNOTATION, nullToEmpty(serverConfig.getPath()));
|
||||
|
||||
return builder.withNewMetadata()
|
||||
.withName(name.replace("/", "-"))
|
||||
.withAnnotations(annotations)
|
||||
.withName(name.replace("/", "-"))
|
||||
.withAnnotations(RoutesAnnotations.newSerializer()
|
||||
.servers(serversConfigs)
|
||||
.annotations())
|
||||
.endMetadata()
|
||||
.withNewSpec()
|
||||
.withNewTo()
|
||||
.withName(serviceName)
|
||||
.endTo()
|
||||
.withNewPort()
|
||||
.withTargetPort(targetPort)
|
||||
.endPort()
|
||||
.withNewTo()
|
||||
.withName(serviceName)
|
||||
.endTo()
|
||||
.withNewPort()
|
||||
.withTargetPort(targetPort)
|
||||
.endPort()
|
||||
.endSpec()
|
||||
.build();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
package org.eclipse.che.workspace.infrastructure.openshift;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Container;
|
||||
import io.fabric8.kubernetes.api.model.ContainerPort;
|
||||
import io.fabric8.kubernetes.api.model.IntOrString;
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.ServicePort;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
public class ServiceMatcher {
|
||||
private final List<Service> services;
|
||||
|
||||
public static ServiceMatcher from(List<Service> services) {
|
||||
return new ServiceMatcher(services);
|
||||
}
|
||||
|
||||
private ServiceMatcher(List<Service> services) {
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
public List<Service> match(Pod pod, Container container) {
|
||||
return services.stream()
|
||||
.filter(service -> isExposedByService(pod, service))
|
||||
.filter(service -> isExposedByService(container, service))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static boolean isExposedByService(Pod pod, Service service) {
|
||||
Map<String, String> labels = pod.getMetadata().getLabels();
|
||||
Map<String, String> selectorLabels = service.getSpec().getSelector();
|
||||
if (labels == null) {
|
||||
return false;
|
||||
}
|
||||
for (Map.Entry<String, String> selectorLabelEntry : selectorLabels.entrySet()) {
|
||||
if (!selectorLabelEntry.getValue().equals(labels.get(selectorLabelEntry.getKey()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isExposedByService(Container container, Service service) {
|
||||
for (ServicePort servicePort : service.getSpec().getPorts()) {
|
||||
IntOrString targetPort = servicePort.getTargetPort();
|
||||
if (targetPort.getIntVal() != null) {
|
||||
for (ContainerPort containerPort : container.getPorts()) {
|
||||
if (targetPort.getIntVal().equals(containerPort.getContainerPort())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (ContainerPort containerPort : container.getPorts()) {
|
||||
if (targetPort.getStrVal().equals(containerPort.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue