Java tutorial
/******************************************************************************* * Copyright (c) 2012-2017 Red Hat, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.che.plugin.openshift.client; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.StringUtils; import org.eclipse.che.plugin.docker.client.DockerApiVersionPathPrefixProvider; import org.eclipse.che.plugin.docker.client.DockerConnector; import org.eclipse.che.plugin.docker.client.DockerConnectorConfiguration; import org.eclipse.che.plugin.docker.client.DockerRegistryAuthResolver; import org.eclipse.che.plugin.docker.client.connection.DockerConnectionFactory; import org.eclipse.che.plugin.docker.client.json.ContainerCreated; import org.eclipse.che.plugin.docker.client.json.ContainerInfo; import org.eclipse.che.plugin.docker.client.json.ContainerListEntry; import org.eclipse.che.plugin.docker.client.json.ImageConfig; import org.eclipse.che.plugin.docker.client.json.NetworkCreated; import org.eclipse.che.plugin.docker.client.json.PortBinding; import org.eclipse.che.plugin.docker.client.json.network.ContainerInNetwork; import org.eclipse.che.plugin.docker.client.json.network.Ipam; import org.eclipse.che.plugin.docker.client.json.network.IpamConfig; import org.eclipse.che.plugin.docker.client.json.network.Network; import org.eclipse.che.plugin.docker.client.params.CreateContainerParams; import org.eclipse.che.plugin.docker.client.params.GetResourceParams; import org.eclipse.che.plugin.docker.client.params.KillContainerParams; import org.eclipse.che.plugin.docker.client.params.PutResourceParams; import org.eclipse.che.plugin.docker.client.params.RemoveContainerParams; import org.eclipse.che.plugin.docker.client.params.RemoveNetworkParams; import org.eclipse.che.plugin.docker.client.params.StartContainerParams; import org.eclipse.che.plugin.docker.client.params.StopContainerParams; import org.eclipse.che.plugin.docker.client.params.network.ConnectContainerToNetworkParams; import org.eclipse.che.plugin.docker.client.params.network.CreateNetworkParams; import org.eclipse.che.plugin.docker.client.params.network.DisconnectContainerFromNetworkParams; import org.eclipse.che.plugin.docker.client.params.network.InspectNetworkParams; import org.eclipse.che.plugin.openshift.client.exception.OpenShiftException; import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesContainer; import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesEnvVar; import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesLabelConverter; import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.ContainerBuilder; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodList; import io.fabric8.kubernetes.api.model.PodSpec; import io.fabric8.kubernetes.api.model.PodSpecBuilder; import io.fabric8.kubernetes.api.model.Probe; import io.fabric8.kubernetes.api.model.ProbeBuilder; import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.ServiceList; import io.fabric8.kubernetes.api.model.ServicePort; import io.fabric8.kubernetes.api.model.Volume; import io.fabric8.kubernetes.api.model.VolumeBuilder; import io.fabric8.kubernetes.api.model.VolumeMount; import io.fabric8.kubernetes.api.model.VolumeMountBuilder; import io.fabric8.kubernetes.api.model.extensions.Deployment; import io.fabric8.kubernetes.api.model.extensions.DeploymentBuilder; import io.fabric8.kubernetes.api.model.extensions.ReplicaSet; import io.fabric8.kubernetes.api.model.extensions.ReplicaSetList; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.openshift.client.DefaultOpenShiftClient; import io.fabric8.openshift.client.OpenShiftClient; /** * Client for OpenShift API. * * @author Mario Loriedo (mloriedo@redhat.com) * @author Angel Misevski (amisevsk@redhat.com) * @author Ilya Buziuk (ibuziuk@redhat.com) */ @Singleton public class OpenShiftConnector extends DockerConnector { private static final Logger LOG = LoggerFactory.getLogger(OpenShiftConnector.class); private static final String CHE_CONTAINER_IDENTIFIER_LABEL_KEY = "cheContainerIdentifier"; private static final String CHE_DEFAULT_EXTERNAL_ADDRESS = "172.17.0.1"; private static final String CHE_OPENSHIFT_RESOURCES_PREFIX = "che-ws-"; private static final String CHE_WORKSPACE_ID_ENV_VAR = "CHE_WORKSPACE_ID"; private static final int CHE_WORKSPACE_AGENT_PORT = 4401; private static final int CHE_TERMINAL_AGENT_PORT = 4411; private static final String DOCKER_PREFIX = "docker://"; private static final String DOCKER_PROTOCOL_PORT_DELIMITER = "/"; private static final String OPENSHIFT_SERVICE_TYPE_NODE_PORT = "NodePort"; private static final int OPENSHIFT_WAIT_POD_DELAY = 1000; private static final int OPENSHIFT_WAIT_POD_TIMEOUT = 120; private static final String OPENSHIFT_POD_STATUS_RUNNING = "Running"; private static final String OPENSHIFT_DEPLOYMENT_LABEL = "deployment"; private static final String OPENSHIFT_IMAGE_PULL_POLICY_IFNOTPRESENT = "IfNotPresent"; private static final Long UID_ROOT = Long.valueOf(0); private static final Long UID_USER = Long.valueOf(1000); private final OpenShiftClient openShiftClient; private final KubernetesLabelConverter kubernetesLabelConverter; private final KubernetesEnvVar kubernetesEnvVar; private final KubernetesContainer kubernetesContainer; private final KubernetesService kubernetesService; private final String openShiftCheProjectName; private final String openShiftCheServiceAccount; private final int openShiftLivenessProbeDelay; private final int openShiftLivenessProbeTimeout; @Inject public OpenShiftConnector(DockerConnectorConfiguration connectorConfiguration, DockerConnectionFactory connectionFactory, DockerRegistryAuthResolver authResolver, KubernetesLabelConverter kubernetesLabelConverter, KubernetesEnvVar kubernetesEnvVar, KubernetesContainer kubernetesContainer, KubernetesService kubernetesService, DockerApiVersionPathPrefixProvider dockerApiVersionPathPrefixProvider, @Named("che.openshift.endpoint") String openShiftApiEndpoint, @Named("che.openshift.username") String openShiftUserName, @Named("che.openshift.password") String openShiftUserPassword, @Named("che.openshift.project") String openShiftCheProjectName, @Named("che.openshift.serviceaccountname") String openShiftCheServiceAccount, @Named("che.openshift.liveness.probe.delay") int openShiftLivenessProbeDelay, @Named("che.openshift.liveness.probe.timeout") int openShiftLivenessProbeTimeout) { super(connectorConfiguration, connectionFactory, authResolver, dockerApiVersionPathPrefixProvider); this.openShiftCheProjectName = openShiftCheProjectName; this.openShiftCheServiceAccount = openShiftCheServiceAccount; this.kubernetesLabelConverter = kubernetesLabelConverter; this.kubernetesEnvVar = kubernetesEnvVar; this.kubernetesContainer = kubernetesContainer; this.kubernetesService = kubernetesService; this.openShiftLivenessProbeDelay = openShiftLivenessProbeDelay; this.openShiftLivenessProbeTimeout = openShiftLivenessProbeTimeout; Config config = new ConfigBuilder().withMasterUrl(openShiftApiEndpoint).withUsername(openShiftUserName) .withPassword(openShiftUserPassword).build(); this.openShiftClient = new DefaultOpenShiftClient(config); } /** * @param createContainerParams * @return * @throws IOException */ @Override public ContainerCreated createContainer(CreateContainerParams createContainerParams) throws IOException { String containerName = getNormalizedContainerName(createContainerParams); String workspaceID = getCheWorkspaceId(createContainerParams); // Generate workspaceID if CHE_WORKSPACE_ID env var does not exist workspaceID = workspaceID.isEmpty() ? generateWorkspaceID() : workspaceID; String imageName = createContainerParams.getContainerConfig().getImage(); Set<String> containerExposedPorts = createContainerParams.getContainerConfig().getExposedPorts().keySet(); Set<String> imageExposedPorts = inspectImage(imageName).getConfig().getExposedPorts().keySet(); Set<String> exposedPorts = getExposedPorts(containerExposedPorts, imageExposedPorts); boolean runContainerAsRoot = runContainerAsRoot(imageName); String[] envVariables = createContainerParams.getContainerConfig().getEnv(); String[] volumes = createContainerParams.getContainerConfig().getHostConfig().getBinds(); Map<String, String> additionalLabels = createContainerParams.getContainerConfig().getLabels(); createOpenShiftService(workspaceID, exposedPorts, additionalLabels); String deploymentName = createOpenShiftDeployment(workspaceID, imageName, containerName, exposedPorts, envVariables, volumes, runContainerAsRoot); String containerID = waitAndRetrieveContainerID(deploymentName); if (containerID == null) { throw new OpenShiftException("Failed to get the ID of the container running in the OpenShift pod"); } return new ContainerCreated(containerID, null); } @Override public void startContainer(final StartContainerParams params) throws IOException { // Not used in OpenShift } @Override public void stopContainer(StopContainerParams params) throws IOException { // Not used in OpenShift } @Override public int waitContainer(String container) throws IOException { // Not used in OpenShift return 0; } @Override public void killContainer(KillContainerParams params) throws IOException { // Not used in OpenShift } @Override public List<ContainerListEntry> listContainers() throws IOException { // Implement once 'Service Provider Interface' is defined return Collections.emptyList(); } @Override public InputStream getResource(GetResourceParams params) throws IOException { throw new UnsupportedOperationException("'getResource' is currently not supported by OpenShift"); } @Override public void putResource(PutResourceParams params) throws IOException { throw new UnsupportedOperationException("'putResource' is currently not supported by OpenShift"); } /** * @param docker * @param container * @return * @throws IOException */ @Override public ContainerInfo inspectContainer(String container) throws IOException { // Proxy to DockerConnector ContainerInfo info = super.inspectContainer(container); if (info == null) { return null; } Pod pod = getChePodByContainerId(info.getId()); if (pod == null) { LOG.warn("No Pod found by container ID {}", info.getId()); return null; } String deploymentName = pod.getMetadata().getLabels().get(OPENSHIFT_DEPLOYMENT_LABEL); if (deploymentName == null) { LOG.warn("No label {} found for Pod {}", OPENSHIFT_DEPLOYMENT_LABEL, pod.getMetadata().getName()); return null; } Service svc = getCheServiceBySelector(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName); if (svc == null) { LOG.warn("No Service found by selector {}={}", OPENSHIFT_DEPLOYMENT_LABEL, deploymentName); return null; } Map<String, String> annotations = kubernetesLabelConverter .namesToLabels(svc.getMetadata().getAnnotations()); Map<String, String> containerLabels = info.getConfig().getLabels(); Map<String, String> labels = Stream .concat(annotations.entrySet().stream(), containerLabels.entrySet().stream()) .filter(e -> e.getKey().startsWith(kubernetesLabelConverter.getCheServerLabelPrefix())) .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); info.getConfig().setLabels(labels); LOG.info("Container labels:"); info.getConfig().getLabels().entrySet().stream() .forEach(e -> LOG.info("- {}={}", e.getKey(), e.getValue())); replaceNetworkSettings(info); return info; } @Override public void removeContainer(final RemoveContainerParams params) throws IOException { String containerId = params.getContainer(); Pod pod = getChePodByContainerId(containerId); String deploymentName = pod.getMetadata().getLabels().get(OPENSHIFT_DEPLOYMENT_LABEL); Deployment deployment = getDeploymentByName(deploymentName); ReplicaSet replicaSet = getReplicaSetByLabel(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName); Service service = getCheServiceBySelector(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName); if (service != null) { LOG.info("Removing OpenShift Service {}", service.getMetadata().getName()); openShiftClient.resource(service).delete(); } if (deployment != null) { LOG.info("Removing OpenShift Deployment {}", deployment.getMetadata().getName()); openShiftClient.resource(deployment).delete(); } if (replicaSet != null) { LOG.info("Removing Replica Set {}", replicaSet.getMetadata().getName()); openShiftClient.resource(replicaSet).delete(); } LOG.info("Removing OpenShift Pod {}", pod.getMetadata().getName()); openShiftClient.resource(pod).delete(); } @Override public NetworkCreated createNetwork(CreateNetworkParams params) throws IOException { // Not needed in OpenShift return new NetworkCreated().withId(params.getNetwork().getName()); } @Override public void removeNetwork(String netId) throws IOException { // Not needed in OpenShift } @Override public void removeNetwork(RemoveNetworkParams params) throws IOException { // Not needed in OpenShift } @Override public void connectContainerToNetwork(String netId, String containerId) throws IOException { // Not needed in OpenShift } @Override public void connectContainerToNetwork(ConnectContainerToNetworkParams params) throws IOException { // Not used in OpenShift } @Override public void disconnectContainerFromNetwork(String netId, String containerId) throws IOException { // Not needed in OpenShift } @Override public void disconnectContainerFromNetwork(DisconnectContainerFromNetworkParams params) throws IOException { // Not needed in OpenShift } @Override public Network inspectNetwork(String netId) throws IOException { return inspectNetwork(InspectNetworkParams.create(netId)); } @Override public Network inspectNetwork(InspectNetworkParams params) throws IOException { String netId = params.getNetworkId(); ServiceList services = openShiftClient.services().inNamespace(this.openShiftCheProjectName).list(); Map<String, ContainerInNetwork> containers = new HashMap<>(); for (Service svc : services.getItems()) { String selector = svc.getSpec().getSelector().get(OPENSHIFT_DEPLOYMENT_LABEL); if (selector == null || !selector.startsWith(CHE_OPENSHIFT_RESOURCES_PREFIX)) { continue; } PodList pods = openShiftClient.pods().inNamespace(openShiftCheProjectName) .withLabel(OPENSHIFT_DEPLOYMENT_LABEL, selector).list(); for (Pod pod : pods.getItems()) { String podName = pod.getMetadata().getName(); ContainerInNetwork container = new ContainerInNetwork().withName(podName) .withIPv4Address(svc.getSpec().getClusterIP()); String podId = getLabelFromContainerID( pod.getMetadata().getLabels().get(CHE_CONTAINER_IDENTIFIER_LABEL_KEY)); if (podId == null) { continue; } containers.put(podId, container); } } List<IpamConfig> ipamConfig = new ArrayList<>(); Ipam ipam = new Ipam().withDriver("bridge").withOptions(Collections.emptyMap()).withConfig(ipamConfig); return new Network().withName("OpenShift").withId(netId).withContainers(containers) .withLabels(Collections.emptyMap()).withOptions(Collections.emptyMap()).withDriver("default") .withIPAM(ipam).withScope("local").withInternal(false).withEnableIPv6(false); } private Service getCheServiceBySelector(String selectorKey, String selectorValue) { ServiceList svcs = openShiftClient.services().inNamespace(this.openShiftCheProjectName).list(); Service svc = svcs.getItems().stream().filter(s -> s.getSpec().getSelector().containsKey(selectorKey)) .filter(s -> s.getSpec().getSelector().get(selectorKey).equals(selectorValue)).findAny() .orElse(null); if (svc == null) { LOG.warn("No Service with selector {}={} could be found", selectorKey, selectorValue); } return svc; } private Deployment getDeploymentByName(String deploymentName) throws IOException { Deployment deployment = openShiftClient.extensions().deployments().inNamespace(this.openShiftCheProjectName) .withName(deploymentName).get(); if (deployment == null) { LOG.warn("No Deployment with name {} could be found", deploymentName); } return deployment; } private ReplicaSet getReplicaSetByLabel(String labelKey, String labelValue) throws IOException { ReplicaSetList replicaSetList = openShiftClient.extensions().replicaSets() .inNamespace(this.openShiftCheProjectName).withLabel(labelKey, labelValue).list(); List<ReplicaSet> items = replicaSetList.getItems(); if (items.isEmpty()) { LOG.warn("No ReplicaSet with label {}={} could be found", labelKey, labelValue); throw new IOException("No ReplicaSet with label " + labelKey + "=" + labelValue + " could be found"); } if (items.size() > 1) { LOG.warn("Found more than one ReplicaSet with label {}={}", labelKey, labelValue); throw new IOException("Found more than one ReplicaSet with label " + labelValue + "=" + labelValue); } return items.get(0); } private Pod getChePodByContainerId(String containerId) throws IOException { PodList pods = openShiftClient.pods().inNamespace(this.openShiftCheProjectName) .withLabel(CHE_CONTAINER_IDENTIFIER_LABEL_KEY, getLabelFromContainerID(containerId)).list(); List<Pod> items = pods.getItems(); if (items.isEmpty()) { LOG.error("An OpenShift Pod with label {}={} could not be found", CHE_CONTAINER_IDENTIFIER_LABEL_KEY, containerId); throw new IOException("An OpenShift Pod with label " + CHE_CONTAINER_IDENTIFIER_LABEL_KEY + "=" + containerId + " could not be found"); } if (items.size() > 1) { LOG.error("There are {} pod with label {}={} (just one was expeced)", items.size(), CHE_CONTAINER_IDENTIFIER_LABEL_KEY, containerId); throw new IOException("There are " + items.size() + " pod with label " + CHE_CONTAINER_IDENTIFIER_LABEL_KEY + "=" + containerId + " (just one was expeced)"); } return items.get(0); } private String getNormalizedContainerName(CreateContainerParams createContainerParams) { String containerName = createContainerParams.getContainerName(); // The name of a container in Kubernetes should be a // valid hostname as specified by RFC 1123 (i.e. max length // of 63 chars and no underscores) return containerName.substring(9).replace('_', '-'); } protected String getCheWorkspaceId(CreateContainerParams createContainerParams) { Stream<String> env = Arrays.stream(createContainerParams.getContainerConfig().getEnv()); String workspaceID = env.filter(v -> v.startsWith(CHE_WORKSPACE_ID_ENV_VAR) && v.contains("=")) .map(v -> v.split("=", 2)[1]).findFirst().orElse(""); return workspaceID.replaceFirst("workspace", ""); } private void createOpenShiftService(String workspaceID, Set<String> exposedPorts, Map<String, String> additionalLabels) { Map<String, String> selector = Collections.singletonMap(OPENSHIFT_DEPLOYMENT_LABEL, CHE_OPENSHIFT_RESOURCES_PREFIX + workspaceID); List<ServicePort> ports = kubernetesService.getServicePortsFrom(exposedPorts); Service service = openShiftClient.services().inNamespace(this.openShiftCheProjectName).createNew() .withNewMetadata().withName(CHE_OPENSHIFT_RESOURCES_PREFIX + workspaceID) .withAnnotations(kubernetesLabelConverter.labelsToNames(additionalLabels)).endMetadata() .withNewSpec().withType(OPENSHIFT_SERVICE_TYPE_NODE_PORT).withSelector(selector).withPorts(ports) .endSpec().done(); LOG.info("OpenShift service {} created", service.getMetadata().getName()); } private String createOpenShiftDeployment(String workspaceID, String imageName, String sanitizedContainerName, Set<String> exposedPorts, String[] envVariables, String[] volumes, boolean runContainerAsRoot) { String deploymentName = CHE_OPENSHIFT_RESOURCES_PREFIX + workspaceID; LOG.info("Creating OpenShift deployment {}", deploymentName); Map<String, String> selector = Collections.singletonMap(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName); LOG.info("Adding container {} to OpenShift deployment {}", sanitizedContainerName, deploymentName); Long UID = runContainerAsRoot ? UID_ROOT : UID_USER; Container container = new ContainerBuilder().withName(sanitizedContainerName).withImage(imageName) .withEnv(kubernetesEnvVar.getEnvFrom(envVariables)) .withPorts(kubernetesContainer.getContainerPortsFrom(exposedPorts)) .withImagePullPolicy(OPENSHIFT_IMAGE_PULL_POLICY_IFNOTPRESENT).withNewSecurityContext() .withRunAsUser(UID).withPrivileged(true).endSecurityContext() .withLivenessProbe(getLivenessProbeFrom(exposedPorts)) .withVolumeMounts(getVolumeMountsFrom(volumes, workspaceID)).build(); PodSpec podSpec = new PodSpecBuilder().withContainers(container) .withVolumes(getVolumesFrom(volumes, workspaceID)) .withServiceAccountName(this.openShiftCheServiceAccount).build(); Deployment deployment = new DeploymentBuilder().withNewMetadata().withName(deploymentName) .withNamespace(this.openShiftCheProjectName).endMetadata().withNewSpec().withReplicas(1) .withNewSelector().withMatchLabels(selector).endSelector().withNewTemplate().withNewMetadata() .withLabels(selector).endMetadata().withSpec(podSpec).endTemplate().endSpec().build(); deployment = openShiftClient.extensions().deployments().inNamespace(this.openShiftCheProjectName) .create(deployment); LOG.info("OpenShift deployment {} created", deploymentName); return deployment.getMetadata().getName(); } private List<VolumeMount> getVolumeMountsFrom(String[] volumes, String workspaceID) { List<VolumeMount> vms = new ArrayList<>(); for (String volume : volumes) { String mountPath = volume.split(":", 3)[1]; String volumeName = getVolumeName(volume); VolumeMount vm = new VolumeMountBuilder().withMountPath(mountPath) .withName("ws-" + workspaceID + "-" + volumeName).build(); vms.add(vm); } return vms; } private List<Volume> getVolumesFrom(String[] volumes, String workspaceID) { List<Volume> vs = new ArrayList<>(); for (String volume : volumes) { String hostPath = volume.split(":", 3)[0]; String volumeName = getVolumeName(volume); Volume v = new VolumeBuilder().withNewHostPath(hostPath) .withName("ws-" + workspaceID + "-" + volumeName).build(); vs.add(v); } return vs; } private String getVolumeName(String volume) { if (volume.contains("ws-agent")) { return "wsagent-lib"; } if (volume.contains("terminal")) { return "terminal"; } if (volume.contains("workspaces")) { return "project"; } return "unknown-volume"; } private String waitAndRetrieveContainerID(String deploymentName) { for (int i = 0; i < OPENSHIFT_WAIT_POD_TIMEOUT; i++) { try { Thread.sleep(OPENSHIFT_WAIT_POD_DELAY); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } PodList pods = openShiftClient.pods().inNamespace(this.openShiftCheProjectName).list(); for (Pod p : pods.getItems()) { String status = p.getStatus().getPhase(); String dc = p.getMetadata().getLabels().get(OPENSHIFT_DEPLOYMENT_LABEL); if (OPENSHIFT_POD_STATUS_RUNNING.equals(status) && deploymentName.equals(dc)) { String containerID = p.getStatus().getContainerStatuses().get(0).getContainerID(); String normalizedID = normalizeContainerID(containerID); openShiftClient.pods().inNamespace(this.openShiftCheProjectName) .withName(p.getMetadata().getName()).edit().editMetadata() .addToLabels(CHE_CONTAINER_IDENTIFIER_LABEL_KEY, getLabelFromContainerID(normalizedID)) .endMetadata().done(); return normalizedID; } } } return null; } private void replaceNetworkSettings(ContainerInfo info) throws IOException { if (info.getNetworkSettings() == null) { return; } Service service = getCheWorkspaceService(); Map<String, List<PortBinding>> networkSettingsPorts = getCheServicePorts(service); info.getNetworkSettings().setPorts(networkSettingsPorts); } private Service getCheWorkspaceService() throws IOException { ServiceList services = openShiftClient.services().inNamespace(this.openShiftCheProjectName).list(); // TODO: improve how the service is found (e.g. using a label with the workspaceid) Service service = services.getItems().stream() .filter(s -> s.getMetadata().getName().startsWith(CHE_OPENSHIFT_RESOURCES_PREFIX)).findFirst() .orElse(null); if (service == null) { LOG.error("No service with prefix {} found", CHE_OPENSHIFT_RESOURCES_PREFIX); throw new IOException("No service with prefix " + CHE_OPENSHIFT_RESOURCES_PREFIX + " found"); } return service; } /** * Adds OpenShift liveness probe to the container. Liveness probe is configured * via TCP Socket Check - for dev machines by checking Workspace API agent port * (4401), for non-dev by checking Terminal port (4411) * * @param exposedPorts * @see <a href= * "https://docs.openshift.com/enterprise/3.0/dev_guide/application_health.html">OpenShift * Application Health</a> * */ private Probe getLivenessProbeFrom(final Set<String> exposedPorts) { int port = 0; if (isDevMachine(exposedPorts)) { port = CHE_WORKSPACE_AGENT_PORT; } else if (isTerminalAgentInjected(exposedPorts)) { port = CHE_TERMINAL_AGENT_PORT; } if (port != 0) { return new ProbeBuilder().withNewTcpSocket().withNewPort(port).endTcpSocket() .withInitialDelaySeconds(openShiftLivenessProbeDelay) .withTimeoutSeconds(openShiftLivenessProbeTimeout).build(); } return null; } private Map<String, List<PortBinding>> getCheServicePorts(Service service) { Map<String, List<PortBinding>> networkSettingsPorts = new HashMap<>(); List<ServicePort> servicePorts = service.getSpec().getPorts(); LOG.info("Retrieving {} ports exposed by service {}", servicePorts.size(), service.getMetadata().getName()); for (ServicePort servicePort : servicePorts) { String protocol = servicePort.getProtocol(); String targetPort = String.valueOf(servicePort.getTargetPort().getIntVal()); String nodePort = String.valueOf(servicePort.getNodePort()); String portName = servicePort.getName(); LOG.info("Port: {}{}{} ({})", targetPort, DOCKER_PROTOCOL_PORT_DELIMITER, protocol, portName); networkSettingsPorts.put(targetPort + DOCKER_PROTOCOL_PORT_DELIMITER + protocol.toLowerCase(), Collections.singletonList( new PortBinding().withHostIp(CHE_DEFAULT_EXTERNAL_ADDRESS).withHostPort(nodePort))); } return networkSettingsPorts; } /** * @param containerExposedPorts * @param imageExposedPorts * @return ports exposed by both image and container */ private Set<String> getExposedPorts(Set<String> containerExposedPorts, Set<String> imageExposedPorts) { Set<String> exposedPorts = new HashSet<>(); exposedPorts.addAll(containerExposedPorts); exposedPorts.addAll(imageExposedPorts); return exposedPorts; } /** * When container is expected to be run as root, user field from {@link ImageConfig} is empty. * For non-root user it contains "user" value * * @param dockerConnector * @param imageName * @return true if user property from Image config is empty string, false otherwise * @throws IOException */ private boolean runContainerAsRoot(final String imageName) throws IOException { String user = inspectImage(imageName).getConfig().getUser(); return user != null && user.isEmpty(); } /** * @param exposedPorts * @return true if machine exposes 4411/tcp port used by Terminal agent, * false otherwise */ private boolean isTerminalAgentInjected(final Set<String> exposedPorts) { return exposedPorts.contains(CHE_TERMINAL_AGENT_PORT + "/tcp"); } /** * @param exposedPorts * @return true if machine exposes 4401/tcp port used by Worspace API agent, * false otherwise */ private boolean isDevMachine(final Set<String> exposedPorts) { return exposedPorts.contains(CHE_WORKSPACE_AGENT_PORT + "/tcp"); } /** * Che workspace id is used as OpenShift service / deployment config name * and must match the regex [a-z]([-a-z0-9]*[a-z0-9]) e.g. "q5iuhkwjvw1w9emg" * * @return randomly generated workspace id */ private String generateWorkspaceID() { return RandomStringUtils.random(16, true, true).toLowerCase(); } /** * @param containerID * @return label based on 'ContainerID' (first 12 chars of ID) */ private String getLabelFromContainerID(final String containerID) { return StringUtils.substring(containerID, 0, 12); } /** * @param containerID * @return normalized version of 'ContainerID' without 'docker://' prefix and double quotes */ private String normalizeContainerID(final String containerID) { return StringUtils.replaceOnce(containerID, DOCKER_PREFIX, "").replace("\"", ""); } }