From 9dfc0b665411cb9a0d0a55ec5d0a1cdc8705c233 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Thu, 6 Dec 2018 23:41:16 -0500 Subject: [PATCH] Support deployments in Kubernetes/OpenShift recipes Add support of deployments in Kubernetes and OpenShift recipes. Important changes: - Since a (Kubernetes|OpenShift)Environment now can contain *both* pods and deployments simultaneously, we need to: - Introduce a wrapper class, PodData, which stores spec and metadata for either a pod or deployment, allowing provisioners to look in one place for specs that need to be modified - Disallow modifying the results of KubernetesEnvironment.getPods() directly, as this would ignore any deployments in the environment - Add method KubernetesEnvironment.getDeployments(), to obtain deployment objects directly when needed. - Modify workspace start process to start all deployments in environment as well as all pods. Note: In an effort to keep the changes in this PR readable, nothing is done to fix broken tests due to the changes above. This commit will not compile unless test compilation is skipped. The next commit fixes tests. Signed-off-by: Angel Misevski --- .../kubernetes/KubernetesInternalRuntime.java | 78 +++++++---- .../infrastructure/kubernetes/Names.java | 16 ++- .../environment/KubernetesEnvironment.java | 130 +++++++++++++++++- .../KubernetesEnvironmentFactory.java | 6 + .../KubernetesEnvironmentValidator.java | 6 +- .../namespace/KubernetesDeployments.java | 73 ++++++---- .../namespace/KubernetesObjectUtil.java | 15 ++ .../namespace/pvc/CommonPVCStrategy.java | 6 +- .../pvc/EphemeralWorkspaceAdapter.java | 4 +- .../namespace/pvc/PVCSubPathHelper.java | 2 +- .../pvc/UniqueWorkspacePVCStrategy.java | 6 +- .../provision/CertificateProvisioner.java | 4 +- .../provision/ImagePullSecretProvisioner.java | 22 +-- .../InstallerServersPortProvisioner.java | 4 +- .../PodTerminationGracePeriodProvisioner.java | 11 +- .../provision/ProxySettingsProvisioner.java | 4 +- .../provision/SecurityContextProvisioner.java | 11 +- .../provision/ServiceAccountProvisioner.java | 4 +- .../provision/UniqueNamesProvisioner.java | 12 +- .../provision/env/EnvVarsConverter.java | 4 +- .../ram/RamLimitRequestProvisioner.java | 4 +- .../restartpolicy/RestartPolicyRewriter.java | 4 +- .../provision/server/ServersConverter.java | 4 +- .../server/KubernetesServerExposer.java | 6 +- .../secure/jwtproxy/JwtProxyProvisioner.java | 2 +- .../KubernetesBrokerInitContainerApplier.java | 10 +- .../KubernetesPluginsToolingApplier.java | 12 +- .../wsplugins/brokerphases/DeployBroker.java | 2 +- .../KubernetesEnvironmentFactoryTest.java | 115 ++++++++++++++++ .../openshift/OpenShiftInternalRuntime.java | 11 +- .../environment/OpenShiftEnvironment.java | 62 ++++++++- .../OpenShiftEnvironmentFactory.java | 6 + .../OpenShiftEnvironmentFactoryTest.java | 115 ++++++++++++++++ 33 files changed, 633 insertions(+), 138 deletions(-) diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInternalRuntime.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInternalRuntime.java index 960eb249e1..d4bc5defdc 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInternalRuntime.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInternalRuntime.java @@ -21,14 +21,18 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodSpec; +import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.extensions.Ingress; import io.fabric8.kubernetes.client.Watcher.Action; import io.opentracing.Span; import io.opentracing.Tracer; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -642,12 +646,14 @@ public class KubernetesInternalRuntime // namespace.pods().watch(new AbnormalStopHandler()); namespace.deployments().watchEvents(new MachineLogsPublisher()); if (unrecoverableEventListenerFactory.isConfigured()) { - Map pods = getContext().getEnvironment().getPods(); + Set toWatch = new HashSet<>(); + KubernetesEnvironment environment = getContext().getEnvironment(); + toWatch.addAll(environment.getPodsCopy().keySet()); + toWatch.addAll(environment.getDeploymentsCopy().keySet()); namespace .deployments() .watchEvents( - unrecoverableEventListenerFactory.create( - pods.keySet(), this::handleUnrecoverableEvent)); + unrecoverableEventListenerFactory.create(toWatch, this::handleUnrecoverableEvent)); } final KubernetesServerResolver serverResolver = @@ -713,39 +719,57 @@ public class KubernetesInternalRuntime final Map machineConfigs = environment.getMachines(); final String workspaceId = getContext().getIdentity().getWorkspaceId(); LOG.debug("Begin pods creation for workspace '{}'", workspaceId); - for (Pod toCreate : environment.getPods().values()) { - startTracingContainersStartup(toCreate); + for (Pod toCreate : environment.getPodsCopy().values()) { + startTracingContainersStartup(toCreate.getSpec(), toCreate.getMetadata()); + ObjectMeta toCreateMeta = toCreate.getMetadata(); final Pod createdPod = namespace.deployments().deploy(toCreate); - LOG.debug( - "Creating pod '{}' in workspace '{}'", toCreate.getMetadata().getName(), workspaceId); - final ObjectMeta podMetadata = createdPod.getMetadata(); - for (Container container : createdPod.getSpec().getContainers()) { - String machineName = Names.machineName(toCreate, container); - - LOG.debug("Creating machine '{}' in workspace '{}'", machineName, workspaceId); - machines.put( - getContext().getIdentity(), - new KubernetesMachineImpl( - workspaceId, - machineName, - podMetadata.getName(), - container.getName(), - MachineStatus.STARTING, - machineConfigs.get(machineName).getAttributes(), - serverResolver.resolve(machineName))); - eventPublisher.sendStartingEvent(machineName, getContext().getIdentity()); - } + LOG.debug("Creating pod '{}' in workspace '{}'", toCreateMeta.getName(), workspaceId); + startMachines(createdPod, toCreateMeta, machineConfigs, serverResolver); + } + for (Deployment toCreate : environment.getDeploymentsCopy().values()) { + PodTemplateSpec template = toCreate.getSpec().getTemplate(); + startTracingContainersStartup(template.getSpec(), template.getMetadata()); + ObjectMeta toCreateMeta = toCreate.getMetadata(); + final Pod createdPod = namespace.deployments().deploy(toCreate); + LOG.debug("Creating deployment '{}' in workspace '{}'", toCreateMeta.getName(), workspaceId); + startMachines(createdPod, toCreateMeta, machineConfigs, serverResolver); } LOG.debug("Pods creation finished in workspace '{}'", workspaceId); } - private void startTracingContainersStartup(Pod pod) { + /** Puts createdPod in the {@code machines} map and sends the starting event for this machine */ + public void startMachines( + Pod createdPod, + ObjectMeta toCreateMeta, + Map machineConfigs, + KubernetesServerResolver serverResolver) + throws InfrastructureException { + final String workspaceId = getContext().getIdentity().getWorkspaceId(); + for (Container container : createdPod.getSpec().getContainers()) { + String machineName = Names.machineName(toCreateMeta, container); + + LOG.debug("Creating machine '{}' in workspace '{}'", machineName, workspaceId); + machines.put( + getContext().getIdentity(), + new KubernetesMachineImpl( + workspaceId, + machineName, + createdPod.getMetadata().getName(), + container.getName(), + MachineStatus.STARTING, + machineConfigs.get(machineName).getAttributes(), + serverResolver.resolve(machineName))); + eventPublisher.sendStartingEvent(machineName, getContext().getIdentity()); + } + } + + private void startTracingContainersStartup(PodSpec podSpec, ObjectMeta podMeta) { if (tracer == null) { return; } - for (Container container : pod.getSpec().getContainers()) { - String machineName = Names.machineName(pod, container); + for (Container container : podSpec.getContainers()) { + String machineName = Names.machineName(podMeta, container); machineStartupTraces.put( machineName, diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/Names.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/Names.java index 1206f7cd23..99be943294 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/Names.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/Names.java @@ -16,9 +16,11 @@ import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_ import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.MACHINE_NAME_ANNOTATION_FMT; import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.Pod; import java.util.Map; import org.eclipse.che.commons.lang.NameGenerator; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Helps to work with Kubernetes objects names. @@ -45,7 +47,15 @@ public class Names { * */ public static String machineName(Pod pod, Container container) { - final Map annotations = pod.getMetadata().getAnnotations(); + return machineName(pod.getMetadata(), container); + } + + public static String machineName(PodData podData, Container container) { + return machineName(podData.getMetadata(), container); + } + + public static String machineName(ObjectMeta podMeta, Container container) { + final Map annotations = podMeta.getAnnotations(); final String machineName; final String containerName = container.getName(); if (annotations != null @@ -55,11 +65,11 @@ public class Names { } final String originalPodName; - final Map labels = pod.getMetadata().getLabels(); + final Map labels = podMeta.getLabels(); if (labels != null && (originalPodName = labels.get(CHE_ORIGINAL_NAME_LABEL)) != null) { return originalPodName + '/' + containerName; } - return pod.getMetadata().getName() + '/' + containerName; + return podMeta.getName() + '/' + containerName; } /** Return pod name that will be unique for a whole namespace. */ diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironment.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironment.java index bd8f5d8206..525d895670 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironment.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironment.java @@ -11,11 +11,16 @@ */ package org.eclipse.che.workspace.infrastructure.kubernetes.environment; +import com.google.common.collect.ImmutableMap; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodSpec; +import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.extensions.Ingress; import java.util.ArrayList; import java.util.HashMap; @@ -37,6 +42,14 @@ public class KubernetesEnvironment extends InternalEnvironment { public static final String TYPE = "kubernetes"; private final Map pods; + private final Map deployments; + /** + * Stores abstracted spec and meta from either a deployment or pod. + * + *

{@link PodData} + */ + private final Map podData; + private final Map services; private final Map ingresses; private final Map persistentVolumeClaims; @@ -61,7 +74,9 @@ public class KubernetesEnvironment extends InternalEnvironment { public KubernetesEnvironment(KubernetesEnvironment k8sEnv) { this( k8sEnv, - k8sEnv.getPods(), + k8sEnv.getPodsCopy(), + k8sEnv.getDeploymentsCopy(), + k8sEnv.getPodData(), k8sEnv.getServices(), k8sEnv.getIngresses(), k8sEnv.getPersistentVolumeClaims(), @@ -72,6 +87,8 @@ public class KubernetesEnvironment extends InternalEnvironment { protected KubernetesEnvironment( InternalEnvironment internalEnvironment, Map pods, + Map deployments, + Map podData, Map services, Map ingresses, Map persistentVolumeClaims, @@ -80,6 +97,8 @@ public class KubernetesEnvironment extends InternalEnvironment { super(internalEnvironment); setType(TYPE); this.pods = pods; + this.deployments = deployments; + this.podData = podData; this.services = services; this.ingresses = ingresses; this.persistentVolumeClaims = persistentVolumeClaims; @@ -92,6 +111,8 @@ public class KubernetesEnvironment extends InternalEnvironment { Map machines, List warnings, Map pods, + Map deployments, + Map podData, Map services, Map ingresses, Map persistentVolumeClaims, @@ -100,6 +121,8 @@ public class KubernetesEnvironment extends InternalEnvironment { super(internalRecipe, machines, warnings); setType(TYPE); this.pods = pods; + this.deployments = deployments; + this.podData = podData; this.services = services; this.ingresses = ingresses; this.persistentVolumeClaims = persistentVolumeClaims; @@ -112,9 +135,43 @@ public class KubernetesEnvironment extends InternalEnvironment { return (KubernetesEnvironment) super.setType(type); } - /** Returns pods that should be created when environment starts. */ - public Map getPods() { - return pods; + /** + * Returns pods that should be created when environment starts. + * + *

Note: This map should not be changed, as it will only return pods and not + * deployments. If objects in the map need to be changed, see {@link #getPodData()} + */ + public Map getPodsCopy() { + return ImmutableMap.copyOf(pods); + } + + /** + * Returns deployments that should be created when environment starts. + * + *

Note: This map should not be changed. If objects in the map need to be changed, see + * {@link #getPodData()} + */ + public Map getDeploymentsCopy() { + return ImmutableMap.copyOf(deployments); + } + + /** + * Returns {@link PodData} representing the metadata and pod spec of objects (pods or deployments) + * that should be created when environment starts. The data returned by this method represents all + * deployment and pod objects that form the workspace, and should be used when provisioning or + * performing any action that needs to see every object in the environment. + */ + public Map getPodData() { + return ImmutableMap.copyOf(podData); + } + + /** + * Add a pod to the current environment. This method is necessary as the map returned by {@link + * #getPodsCopy()} is a copy. This method also adds the relevant data to {@link #getPodData()}. + */ + public void addPod(String key, Pod pod) { + pods.put(key, pod); + podData.put(key, new PodData(pod.getSpec(), pod.getMetadata())); } /** Returns services that should be created when environment starts. */ @@ -146,6 +203,8 @@ public class KubernetesEnvironment extends InternalEnvironment { protected final InternalEnvironment internalEnvironment; protected final Map pods = new HashMap<>(); + protected final Map deployments = new HashMap<>(); + protected final Map podData = new HashMap<>(); protected final Map services = new HashMap<>(); protected final Map ingresses = new HashMap<>(); protected final Map pvcs = new HashMap<>(); @@ -183,6 +242,25 @@ public class KubernetesEnvironment extends InternalEnvironment { public Builder setPods(Map pods) { this.pods.putAll(pods); + pods.entrySet() + .forEach( + e -> { + Pod pod = e.getValue(); + podData.put(e.getKey(), new PodData(pod.getSpec(), pod.getMetadata())); + }); + return this; + } + + public Builder setDeployments(Map deployments) { + this.deployments.putAll(deployments); + deployments + .entrySet() + .forEach( + e -> { + PodTemplateSpec podTemplate = e.getValue().getSpec().getTemplate(); + podData.put( + e.getKey(), new PodData(podTemplate.getSpec(), podTemplate.getMetadata())); + }); return this; } @@ -218,7 +296,49 @@ public class KubernetesEnvironment extends InternalEnvironment { public KubernetesEnvironment build() { return new KubernetesEnvironment( - internalEnvironment, pods, services, ingresses, pvcs, secrets, configMaps); + internalEnvironment, + pods, + deployments, + podData, + services, + ingresses, + pvcs, + secrets, + configMaps); + } + } + + /** + * Abstraction of pod, since deployments store pod spec and meta within a PodSpecTemplate instead + * of a pod object. This class allows us to use one class to support passing of the relevant parts + * of a pod or deployment when it comes to provisioning. + * + *

The methods for accessing metadata and spec are identical to that of the Pod class (i.e. + * {@code getSpec()} and {@code getMetadata()} + */ + public static class PodData { + private PodSpec podSpec; + private ObjectMeta podMeta; + + public PodData(PodSpec podSpec, ObjectMeta podMeta) { + this.podSpec = podSpec; + this.podMeta = podMeta; + } + + public PodSpec getSpec() { + return podSpec; + } + + public void setSpec(PodSpec podSpec) { + this.podSpec = podSpec; + } + + public ObjectMeta getMetadata() { + return podMeta; + } + + public void setMetadata(ObjectMeta podMeta) { + this.podMeta = podMeta; } } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentFactory.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentFactory.java index f99e2fb60d..a2baa561e2 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentFactory.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentFactory.java @@ -22,6 +22,7 @@ import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.extensions.Ingress; import java.io.ByteArrayInputStream; import java.util.ArrayList; @@ -100,6 +101,7 @@ public class KubernetesEnvironmentFactory clientFactory.create().lists().load(new ByteArrayInputStream(content.getBytes())).get(); Map pods = new HashMap<>(); + Map deployments = new HashMap<>(); Map services = new HashMap<>(); boolean isAnyIngressPresent = false; boolean isAnyPVCPresent = false; @@ -109,6 +111,9 @@ public class KubernetesEnvironmentFactory if (object instanceof Pod) { Pod pod = (Pod) object; pods.put(pod.getMetadata().getName(), pod); + } else if (object instanceof Deployment) { + Deployment deployment = (Deployment) object; + deployments.put(deployment.getMetadata().getName(), deployment); } else if (object instanceof Service) { Service service = (Service) object; services.put(service.getMetadata().getName(), service); @@ -158,6 +163,7 @@ public class KubernetesEnvironmentFactory .setMachines(machines) .setWarnings(warnings) .setPods(pods) + .setDeployments(deployments) .setServices(services) .setIngresses(new HashMap<>()) .setPersistentVolumeClaims(new HashMap<>()) diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentValidator.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentValidator.java index 26e00285fc..7196e7059e 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentValidator.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentValidator.java @@ -15,11 +15,11 @@ import static java.lang.String.format; import com.google.common.base.Joiner; import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.Pod; import java.util.HashSet; import java.util.Set; import org.eclipse.che.api.core.ValidationException; import org.eclipse.che.workspace.infrastructure.kubernetes.Names; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Validates {@link KubernetesEnvironment}. @@ -35,10 +35,10 @@ public class KubernetesEnvironmentValidator { * @throws ValidationException if the specified {@link KubernetesEnvironment} is invalid */ public void validate(KubernetesEnvironment env) throws ValidationException { - checkArgument(!env.getPods().isEmpty(), "Environment should contain at least 1 pod"); + checkArgument(!env.getPodData().isEmpty(), "Environment should contain at least 1 pod"); Set missingMachines = new HashSet<>(env.getMachines().keySet()); - for (Pod pod : env.getPods().values()) { + for (PodData pod : env.getPodData().values()) { if (pod.getSpec() != null && pod.getSpec().getContainers() != null) { for (Container container : pod.getSpec().getContainers()) { missingMachines.remove(Names.machineName(pod, container)); diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesDeployments.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesDeployments.java index 7af06c880b..cc267ba883 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesDeployments.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesDeployments.java @@ -29,6 +29,7 @@ import io.fabric8.kubernetes.api.model.OwnerReference; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodSpec; import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; import io.fabric8.kubernetes.api.model.apps.DoneableDeployment; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.Watch; @@ -125,38 +126,59 @@ public class KubernetesDeployments { putLabel(pod, CHE_WORKSPACE_ID_LABEL, workspaceId); ObjectMeta metadata = pod.getMetadata(); - // Note: metadata.name will be changed as for pods it is set by the deployment. - String originalName = metadata.getName(); - putLabel(pod, CHE_DEPLOYMENT_NAME_LABEL, originalName); - PodSpec podSpec = pod.getSpec(); podSpec.setRestartPolicy("Always"); // Only allowable value + + Deployment deployment = + new DeploymentBuilder() + .withMetadata(metadata) + .withNewSpec() + .withNewSelector() + .withMatchLabels(metadata.getLabels()) + .endSelector() + .withReplicas(1) + .withNewTemplate() + .withMetadata(metadata) + .withSpec(podSpec) + .endTemplate() + .endSpec() + .build(); + return createDeployment(deployment, workspaceId); + } + + public Pod deploy(Deployment deployment) throws InfrastructureException { + ObjectMeta podMeta = deployment.getSpec().getTemplate().getMetadata(); + putLabel(podMeta, CHE_WORKSPACE_ID_LABEL, workspaceId); + putLabel(podMeta, CHE_DEPLOYMENT_NAME_LABEL, deployment.getMetadata().getName()); + putLabel(deployment.getMetadata(), CHE_WORKSPACE_ID_LABEL, workspaceId); + // Match condition for a deployment is an AND of all labels + deployment.getSpec().getSelector().setMatchLabels(podMeta.getLabels()); + // Avoid accidental setting of multiple replicas + deployment.getSpec().setReplicas(1); + + PodSpec podSpec = deployment.getSpec().getTemplate().getSpec(); + podSpec.setRestartPolicy("Always"); // Only allowable value + + return createDeployment(deployment, workspaceId); + } + + private Pod createDeployment(Deployment deployment, String workspaceId) + throws InfrastructureException { + final String deploymentName = deployment.getMetadata().getName(); final CompletableFuture createFuture = new CompletableFuture<>(); final Watch createWatch = clientFactory .create(workspaceId) .pods() .inNamespace(namespace) - .watch(new CreateWatcher(createFuture, workspaceId, originalName)); + .watch(new CreateWatcher(createFuture, workspaceId, deploymentName)); try { clientFactory .create(workspaceId) .apps() .deployments() .inNamespace(namespace) - .createNew() - .withMetadata(metadata) - .withNewSpec() - .withNewSelector() - .withMatchLabels(metadata.getLabels()) - .endSelector() - .withReplicas(1) - .withNewTemplate() - .withMetadata(metadata) - .withSpec(podSpec) - .endTemplate() - .endSpec() - .done(); + .create(deployment); return createFuture.get(POD_CREATION_TIMEOUT_MIN, TimeUnit.MINUTES); } catch (KubernetesClientException e) { throw new KubernetesInfrastructureException(e); @@ -165,17 +187,17 @@ public class KubernetesDeployments { throw new InfrastructureException( String.format( "Interrupted while waiting for Pod creation. -id: %s -message: %s", - metadata.getName(), e.getMessage())); + deploymentName, e.getMessage())); } catch (ExecutionException e) { throw new InfrastructureException( String.format( "Error occured while waiting for Pod creation. -id: %s -message: %s", - metadata.getName(), e.getCause().getMessage())); + deploymentName, e.getCause().getMessage())); } catch (TimeoutException e) { throw new InfrastructureException( String.format( "Pod creation timeout exceeded. -id: %s -message: %s", - metadata.getName(), e.getMessage())); + deploymentName, e.getMessage())); } finally { createWatch.close(); } @@ -894,19 +916,20 @@ public class KubernetesDeployments { private final CompletableFuture future; private final String workspaceId; - private final String originalName; + private final String deploymentName; - private CreateWatcher(CompletableFuture future, String workspaceId, String originalName) { + private CreateWatcher( + CompletableFuture future, String workspaceId, String deploymentName) { this.future = future; this.workspaceId = workspaceId; - this.originalName = originalName; + this.deploymentName = deploymentName; } @Override public void eventReceived(Action action, Pod resource) { Map labels = resource.getMetadata().getLabels(); if (workspaceId.equals(labels.get(CHE_WORKSPACE_ID_LABEL)) - && originalName.equals(labels.get(CHE_DEPLOYMENT_NAME_LABEL))) { + && deploymentName.equals(labels.get(CHE_DEPLOYMENT_NAME_LABEL))) { future.complete(resource); } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesObjectUtil.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesObjectUtil.java index 48e77460d4..cce24ced71 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesObjectUtil.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesObjectUtil.java @@ -27,6 +27,7 @@ import io.fabric8.kubernetes.api.model.VolumeMount; import io.fabric8.kubernetes.api.model.VolumeMountBuilder; import java.util.HashMap; import java.util.Map; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Helps to work with Kubernetes objects. @@ -45,6 +46,20 @@ public class KubernetesObjectUtil { target.setMetadata(metadata = new ObjectMeta()); } + putLabel(target.getMetadata(), key, value); + } + + public static void putLabel(PodData target, String key, String value) { + ObjectMeta metadata = target.getMetadata(); + + if (metadata == null) { + target.setMetadata(metadata = new ObjectMeta()); + } + + putLabel(metadata, key, value); + } + + public static void putLabel(ObjectMeta metadata, String key, String value) { Map labels = metadata.getLabels(); if (labels == null) { metadata.setLabels(labels = new HashMap<>()); diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/CommonPVCStrategy.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/CommonPVCStrategy.java index 14bbf38539..b1f878b7d1 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/CommonPVCStrategy.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/CommonPVCStrategy.java @@ -21,7 +21,6 @@ import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.Logs import com.google.inject.Inject; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; -import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodSpec; import java.util.ArrayList; import java.util.Collection; @@ -40,6 +39,7 @@ import org.eclipse.che.commons.annotation.Traced; import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespace; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesPersistentVolumeClaims; @@ -128,7 +128,7 @@ public class CommonPVCStrategy implements WorkspaceVolumesStrategy { String commonPVCName = getCommonPVCName(identity); final PersistentVolumeClaim pvc = newPVC(commonPVCName, pvcAccessMode, pvcQuantity); k8sEnv.getPersistentVolumeClaims().put(commonPVCName, pvc); - for (Pod pod : k8sEnv.getPods().values()) { + for (PodData pod : k8sEnv.getPodData().values()) { PodSpec podSpec = pod.getSpec(); List containers = new ArrayList<>(); containers.addAll(podSpec.getContainers()); @@ -198,7 +198,7 @@ public class CommonPVCStrategy implements WorkspaceVolumesStrategy { String pvcName, String workspaceId, Set subPaths, - Pod pod, + PodData pod, Container container, Map volumes) { if (volumes.isEmpty()) { diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/EphemeralWorkspaceAdapter.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/EphemeralWorkspaceAdapter.java index cf22758a96..bc04c3d89f 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/EphemeralWorkspaceAdapter.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/EphemeralWorkspaceAdapter.java @@ -15,7 +15,6 @@ import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.Kube import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.LogsVolumeMachineProvisioner.LOGS_VOLUME_NAME; import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodSpec; import io.fabric8.kubernetes.api.model.VolumeBuilder; import java.util.ArrayList; @@ -29,6 +28,7 @@ import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,7 +51,7 @@ public class EphemeralWorkspaceAdapter { public void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity) throws InfrastructureException { LOG.debug("Provisioning PVC strategy for workspace '{}'", identity.getWorkspaceId()); - for (Pod pod : k8sEnv.getPods().values()) { + for (PodData pod : k8sEnv.getPodData().values()) { PodSpec podSpec = pod.getSpec(); // To ensure same volumes get mounted correctly in different containers, we need to track diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/PVCSubPathHelper.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/PVCSubPathHelper.java index 9aaef06ac6..670642e9d7 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/PVCSubPathHelper.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/PVCSubPathHelper.java @@ -136,7 +136,7 @@ public class PVCSubPathHelper { final String podName = jobName + '-' + workspaceId; final String[] command = buildCommand(commandBase, arguments); final Pod pod = newPod(podName, command); - securityContextProvisioner.provision(pod); + securityContextProvisioner.provision(pod.getSpec()); KubernetesDeployments deployments = null; try { diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/UniqueWorkspacePVCStrategy.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/UniqueWorkspacePVCStrategy.java index d308dc1a81..e4b260e899 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/UniqueWorkspacePVCStrategy.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/UniqueWorkspacePVCStrategy.java @@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableMap; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; -import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodSpec; import java.util.ArrayList; import java.util.Collection; @@ -41,6 +40,7 @@ import org.eclipse.che.commons.annotation.Traced; import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesPersistentVolumeClaims; import org.slf4j.Logger; @@ -115,7 +115,7 @@ public class UniqueWorkspacePVCStrategy implements WorkspaceVolumesStrategy { .create(workspaceId) .persistentVolumeClaims() .getByLabel(CHE_WORKSPACE_ID_LABEL, workspaceId)); - for (Pod pod : k8sEnv.getPods().values()) { + for (PodData pod : k8sEnv.getPodData().values()) { final PodSpec podSpec = pod.getSpec(); List containers = new ArrayList<>(); containers.addAll(podSpec.getContainers()); @@ -158,7 +158,7 @@ public class UniqueWorkspacePVCStrategy implements WorkspaceVolumesStrategy { String workspaceId, Map provisionedClaims, Map existingVolumeName2PVC, - Pod pod, + PodData pod, Container container, Map volumes) throws InfrastructureException { diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/CertificateProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/CertificateProvisioner.java index 986b7ca576..4e4323f1dd 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/CertificateProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/CertificateProvisioner.java @@ -17,7 +17,6 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.SecretBuilder; import io.fabric8.kubernetes.api.model.SecretVolumeSourceBuilder; import io.fabric8.kubernetes.api.model.Volume; @@ -30,6 +29,7 @@ import javax.inject.Singleton; import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Mount configured self-signed certificate as file in each workspace machines if configured. @@ -81,7 +81,7 @@ public class CertificateProvisioner implements ConfigurationProvisioner certVolume = pod.getSpec() .getVolumes() diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ImagePullSecretProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ImagePullSecretProvisioner.java index d2cb279b52..d3e7f48bf4 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ImagePullSecretProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ImagePullSecretProvisioner.java @@ -16,7 +16,7 @@ import com.google.gson.Gson; import com.google.gson.stream.JsonWriter; import io.fabric8.kubernetes.api.model.LocalObjectReference; import io.fabric8.kubernetes.api.model.LocalObjectReferenceBuilder; -import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodSpec; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.SecretBuilder; import java.io.IOException; @@ -93,7 +93,10 @@ public class ImagePullSecretProvisioner implements ConfigurationProvisioner addImagePullSecret(secret.getMetadata().getName(), p)); + k8sEnv + .getPodData() + .values() + .forEach(p -> addImagePullSecret(secret.getMetadata().getName(), p.getSpec())); } /** @@ -160,13 +163,12 @@ public class ImagePullSecretProvisioner implements ConfigurationProvisioner imagePullSecrets = pod.getSpec().getImagePullSecrets(); - pod.getSpec() - .setImagePullSecrets( - ImmutableList.builder() - .add(new LocalObjectReferenceBuilder().withName(secretName).build()) - .addAll(imagePullSecrets) - .build()); + private void addImagePullSecret(String secretName, PodSpec podSpec) { + List imagePullSecrets = podSpec.getImagePullSecrets(); + podSpec.setImagePullSecrets( + ImmutableList.builder() + .add(new LocalObjectReferenceBuilder().withName(secretName).build()) + .addAll(imagePullSecrets) + .build()); } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/InstallerServersPortProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/InstallerServersPortProvisioner.java index 05ea22ac4a..03c8fad1c6 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/InstallerServersPortProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/InstallerServersPortProvisioner.java @@ -16,7 +16,6 @@ import static java.util.stream.Collectors.toList; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; -import io.fabric8.kubernetes.api.model.Pod; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -37,6 +36,7 @@ import org.eclipse.che.commons.lang.Pair; import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Fixes installers servers ports if conflicts are present. @@ -76,7 +76,7 @@ public class InstallerServersPortProvisioner implements ConfigurationProvisioner TracingTags.WORKSPACE_ID.set(identity::getWorkspaceId); - for (Pod pod : k8sEnv.getPods().values()) { + for (PodData pod : k8sEnv.getPodData().values()) { // it is needed to detect conflicts between all containers in a pod // because they use the same ports List podMachines = diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/PodTerminationGracePeriodProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/PodTerminationGracePeriodProvisioner.java index 33cab64fca..e879bf083f 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/PodTerminationGracePeriodProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/PodTerminationGracePeriodProvisioner.java @@ -11,7 +11,7 @@ */ package org.eclipse.che.workspace.infrastructure.kubernetes.provision; -import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodSpec; import javax.inject.Inject; import javax.inject.Named; import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; @@ -19,6 +19,7 @@ import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.commons.annotation.Traced; import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Adds grace termination period to workspace pods. @@ -45,8 +46,8 @@ public class PodTerminationGracePeriodProvisioner implements ConfigurationProvis TracingTags.WORKSPACE_ID.set(identity::getWorkspaceId); - for (Pod pod : k8sEnv.getPods().values()) { - if (!isTerminationGracePeriodSet(pod)) { + for (PodData pod : k8sEnv.getPodData().values()) { + if (!isTerminationGracePeriodSet(pod.getSpec())) { pod.getSpec().setTerminationGracePeriodSeconds(graceTerminationPeriodSec); } } @@ -57,7 +58,7 @@ public class PodTerminationGracePeriodProvisioner implements ConfigurationProvis * @return true if 'terminationGracePeriodSeconds' have been explicitly set in Kubernetes / * OpenShift recipe, false otherwise */ - private boolean isTerminationGracePeriodSet(final Pod pod) { - return pod.getSpec().getTerminationGracePeriodSeconds() != null; + private boolean isTerminationGracePeriodSet(final PodSpec podSpec) { + return podSpec.getTerminationGracePeriodSeconds() != null; } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ProxySettingsProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ProxySettingsProvisioner.java index 62ca842c17..cbafa75029 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ProxySettingsProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ProxySettingsProvisioner.java @@ -12,7 +12,6 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.provision; import io.fabric8.kubernetes.api.model.EnvVar; -import io.fabric8.kubernetes.api.model.Pod; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; @@ -22,6 +21,7 @@ import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.commons.annotation.Traced; import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Add proxy configuration to pod containers @@ -60,7 +60,7 @@ public class ProxySettingsProvisioner implements ConfigurationProvisioner { TracingTags.WORKSPACE_ID.set(identity::getWorkspaceId); if (!proxyEnvVars.isEmpty()) { - for (Pod pod : k8sEnv.getPods().values()) { + for (PodData pod : k8sEnv.getPodData().values()) { pod.getSpec() .getContainers() .forEach( diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/SecurityContextProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/SecurityContextProvisioner.java index 340f3adffd..895f7030c4 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/SecurityContextProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/SecurityContextProvisioner.java @@ -11,8 +11,8 @@ */ package org.eclipse.che.workspace.infrastructure.kubernetes.provision; -import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodSecurityContextBuilder; +import io.fabric8.kubernetes.api.model.PodSpec; import javax.inject.Inject; import javax.inject.Named; import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; @@ -38,13 +38,12 @@ public class SecurityContextProvisioner implements ConfigurationProvisioner provision(p.getSpec())); } } - public void provision(Pod pod) { - pod.getSpec() - .setSecurityContext( - new PodSecurityContextBuilder().withRunAsUser(runAsUser).withFsGroup(fsGroup).build()); + public void provision(PodSpec podSpec) { + podSpec.setSecurityContext( + new PodSecurityContextBuilder().withRunAsUser(runAsUser).withFsGroup(fsGroup).build()); } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ServiceAccountProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ServiceAccountProvisioner.java index 347cf71559..f3d97a6c9e 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ServiceAccountProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/ServiceAccountProvisioner.java @@ -13,7 +13,6 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.provision; import static com.google.common.base.Strings.isNullOrEmpty; -import io.fabric8.kubernetes.api.model.Pod; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; @@ -23,6 +22,7 @@ import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.annotation.Traced; import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Sets the service account to workspace pods if configured. @@ -51,7 +51,7 @@ public class ServiceAccountProvisioner implements ConfigurationProvisioner { TracingTags.WORKSPACE_ID.set(identity::getWorkspaceId); if (!isNullOrEmpty(serviceAccount)) { - for (Pod pod : k8sEnv.getPods().values()) { + for (PodData pod : k8sEnv.getPodData().values()) { pod.getSpec().setServiceAccountName(serviceAccount); pod.getSpec().setAutomountServiceAccountToken(true); } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/UniqueNamesProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/UniqueNamesProvisioner.java index 41a964cc79..11cde3607c 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/UniqueNamesProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/UniqueNamesProvisioner.java @@ -14,8 +14,8 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.provision; import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesObjectUtil.putLabel; import io.fabric8.kubernetes.api.model.ObjectMeta; -import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.extensions.Ingress; +import java.util.Collection; import java.util.HashSet; import java.util.Set; import javax.inject.Singleton; @@ -26,6 +26,7 @@ import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.Constants; import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Makes names of Kubernetes pods and ingresses unique whole namespace by {@link Names}. @@ -48,15 +49,14 @@ public class UniqueNamesProvisioner TracingTags.WORKSPACE_ID.set(workspaceId); - final Set pods = new HashSet<>(k8sEnv.getPods().values()); - k8sEnv.getPods().clear(); - for (Pod pod : pods) { + final Collection podData = k8sEnv.getPodData().values(); + for (PodData pod : podData) { final ObjectMeta podMeta = pod.getMetadata(); - putLabel(pod, Constants.CHE_ORIGINAL_NAME_LABEL, podMeta.getName()); + putLabel(podMeta, Constants.CHE_ORIGINAL_NAME_LABEL, podMeta.getName()); final String podName = Names.uniquePodName(podMeta.getName(), workspaceId); podMeta.setName(podName); - k8sEnv.getPods().put(podName, pod); } + final Set ingresses = new HashSet<>(k8sEnv.getIngresses().values()); k8sEnv.getIngresses().clear(); for (Ingress ingress : ingresses) { diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/env/EnvVarsConverter.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/env/EnvVarsConverter.java index 6db1cebd2a..991415a354 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/env/EnvVarsConverter.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/env/EnvVarsConverter.java @@ -13,7 +13,6 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.provision.env; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.EnvVar; -import io.fabric8.kubernetes.api.model.Pod; import javax.inject.Singleton; import org.eclipse.che.api.core.model.workspace.config.MachineConfig; import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; @@ -23,6 +22,7 @@ import org.eclipse.che.commons.annotation.Traced; import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ConfigurationProvisioner; /** @@ -39,7 +39,7 @@ public class EnvVarsConverter implements ConfigurationProvisioner { TracingTags.WORKSPACE_ID.set(identity::getWorkspaceId); - for (Pod pod : k8sEnv.getPods().values()) { + for (PodData pod : k8sEnv.getPodData().values()) { for (Container container : pod.getSpec().getContainers()) { String machineName = Names.machineName(pod, container); InternalMachineConfig machineConf = k8sEnv.getMachines().get(machineName); diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/limits/ram/RamLimitRequestProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/limits/ram/RamLimitRequestProvisioner.java index eaa30654f7..a11697f032 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/limits/ram/RamLimitRequestProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/limits/ram/RamLimitRequestProvisioner.java @@ -16,7 +16,6 @@ import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMO import static org.eclipse.che.workspace.infrastructure.kubernetes.Names.machineName; import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.Pod; import java.util.Map; import javax.inject.Inject; import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; @@ -26,6 +25,7 @@ import org.eclipse.che.api.workspace.server.spi.environment.MemoryAttributeProvi import org.eclipse.che.commons.annotation.Traced; import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ConfigurationProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.util.Containers; @@ -52,7 +52,7 @@ public class RamLimitRequestProvisioner implements ConfigurationProvisioner { TracingTags.WORKSPACE_ID.set(identity::getWorkspaceId); final Map machines = k8sEnv.getMachines(); - for (Pod pod : k8sEnv.getPods().values()) { + for (PodData pod : k8sEnv.getPodData().values()) { for (Container container : pod.getSpec().getContainers()) { InternalMachineConfig machineConfig = machines.get(machineName(pod, container)); memoryAttributeProvisioner.provision( diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/restartpolicy/RestartPolicyRewriter.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/restartpolicy/RestartPolicyRewriter.java index 1ec7be843b..8ba5dcea41 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/restartpolicy/RestartPolicyRewriter.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/restartpolicy/RestartPolicyRewriter.java @@ -13,7 +13,6 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.provision.restartpol import static java.lang.String.format; -import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodSpec; import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; import org.eclipse.che.api.workspace.server.model.impl.WarningImpl; @@ -22,6 +21,7 @@ import org.eclipse.che.commons.annotation.Traced; import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.Warnings; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ConfigurationProvisioner; /** @@ -40,7 +40,7 @@ public class RestartPolicyRewriter implements ConfigurationProvisioner { TracingTags.WORKSPACE_ID.set(identity::getWorkspaceId); - for (Pod podConfig : k8sEnv.getPods().values()) { + for (PodData podConfig : k8sEnv.getPodData().values()) { final String podName = podConfig.getMetadata().getName(); final PodSpec podSpec = podConfig.getSpec(); rewriteRestartPolicy(podSpec, podName, k8sEnv); diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/server/ServersConverter.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/server/ServersConverter.java index 245b513cb1..20aad0cede 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/server/ServersConverter.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/server/ServersConverter.java @@ -12,7 +12,6 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.provision.server; import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodSpec; import java.util.Map; import javax.inject.Inject; @@ -25,6 +24,7 @@ import org.eclipse.che.commons.annotation.Traced; import org.eclipse.che.commons.tracing.TracingTags; import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ConfigurationProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy; @@ -64,7 +64,7 @@ public class ServersConverter SecureServerExposer secureServerExposer = secureServerExposerFactoryProvider.get(k8sEnv).create(identity); - for (Pod podConfig : k8sEnv.getPods().values()) { + for (PodData podConfig : k8sEnv.getPodData().values()) { final PodSpec podSpec = podConfig.getSpec(); for (Container containerConfig : podSpec.getContainers()) { String machineName = Names.machineName(podConfig, containerConfig); diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposer.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposer.java index 7b0272e733..3fdd06203d 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposer.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/KubernetesServerExposer.java @@ -20,7 +20,6 @@ import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_ import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.ContainerPort; import io.fabric8.kubernetes.api.model.ContainerPortBuilder; -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.api.model.ServicePortBuilder; @@ -38,6 +37,7 @@ import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.workspace.infrastructure.kubernetes.Annotations; import org.eclipse.che.workspace.infrastructure.kubernetes.Constants; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; import org.eclipse.che.workspace.infrastructure.kubernetes.provision.UniqueNamesProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer; @@ -107,14 +107,14 @@ public class KubernetesServerExposer { private final SecureServerExposer secureServerExposer; private final String machineName; private final Container container; - private final Pod pod; + private final PodData pod; private final T k8sEnv; public KubernetesServerExposer( ExternalServerExposerStrategy externalServerExposer, SecureServerExposer secureServerExposer, String machineName, - Pod pod, + PodData pod, Container container, T k8sEnv) { this.externalServerExposer = externalServerExposer; diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java index c7e208d28a..fa44478c44 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java @@ -213,7 +213,7 @@ public class JwtProxyProvisioner { private void ensureJwtProxyInjected(KubernetesEnvironment k8sEnv) throws InfrastructureException { if (!k8sEnv.getMachines().containsKey(JWT_PROXY_MACHINE_NAME)) { k8sEnv.getMachines().put(JWT_PROXY_MACHINE_NAME, createJwtProxyMachine()); - k8sEnv.getPods().put(JWT_PROXY_POD_NAME, createJwtProxyPod()); + k8sEnv.addPod(JWT_PROXY_POD_NAME, createJwtProxyPod()); KeyPair keyPair; try { diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/KubernetesBrokerInitContainerApplier.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/KubernetesBrokerInitContainerApplier.java index e3c21bafe4..620b1cc8a1 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/KubernetesBrokerInitContainerApplier.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/KubernetesBrokerInitContainerApplier.java @@ -14,7 +14,6 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins; import com.google.common.annotations.Beta; import com.google.inject.Inject; import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.Pod; import java.util.Collection; import java.util.List; import java.util.Map; @@ -25,6 +24,7 @@ import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfi import org.eclipse.che.api.workspace.server.wsplugins.model.PluginMeta; import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.brokerphases.BrokerEnvironmentFactory; /** @@ -58,18 +58,18 @@ public class KubernetesBrokerInitContainerApplier workspacePods = workspaceEnvironment.getPods(); + Map workspacePods = workspaceEnvironment.getPodData(); if (workspacePods.size() != 1) { throw new InfrastructureException( "Che plugins tooling configuration can be applied to a workspace with one pod only."); } - Pod workspacePod = workspacePods.values().iterator().next(); + PodData workspacePod = workspacePods.values().iterator().next(); - Map brokerPods = brokerEnvironment.getPods(); + Map brokerPods = brokerEnvironment.getPodData(); if (brokerPods.size() != 1) { throw new InfrastructureException("Broker environment must have only one Pod."); } - Pod brokerPod = brokerPods.values().iterator().next(); + PodData brokerPod = brokerPods.values().iterator().next(); // Add broker machines to workspace environment so that the init containers can be provisioned. List brokerContainers = brokerPod.getSpec().getContainers(); diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/KubernetesPluginsToolingApplier.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/KubernetesPluginsToolingApplier.java index 154d72d875..8f75598440 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/KubernetesPluginsToolingApplier.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/KubernetesPluginsToolingApplier.java @@ -45,6 +45,7 @@ import org.eclipse.che.api.workspace.server.wsplugins.model.Command; import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.Warnings; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Applies Che plugins tooling configuration to a kubernetes internal runtime object. @@ -84,10 +85,11 @@ public class KubernetesPluginsToolingApplier implements ChePluginsApplier { KubernetesEnvironment kubernetesEnvironment = (KubernetesEnvironment) internalEnvironment; - Map pods = kubernetesEnvironment.getPods(); + Map pods = kubernetesEnvironment.getPodData(); switch (pods.size()) { case 0: addToolingPod(kubernetesEnvironment); + pods = kubernetesEnvironment.getPodData(); break; case 1: break; @@ -95,7 +97,7 @@ public class KubernetesPluginsToolingApplier implements ChePluginsApplier { throw new InfrastructureException( "Che plugins tooling configuration can be applied to a workspace with one pod only"); } - Pod pod = pods.values().iterator().next(); + PodData pod = pods.values().iterator().next(); CommandsResolver commandsResolver = new CommandsResolver(kubernetesEnvironment); for (ChePlugin chePlugin : chePlugins) { @@ -125,7 +127,7 @@ public class KubernetesPluginsToolingApplier implements ChePluginsApplier { .endSpec() .build(); - kubernetesEnvironment.getPods().put(CHE_WORKSPACE_POD, pod); + kubernetesEnvironment.addPod(CHE_WORKSPACE_POD, pod); } private void populateWorkspaceEnvVars( @@ -133,7 +135,7 @@ public class KubernetesPluginsToolingApplier implements ChePluginsApplier { List workspaceEnv = toK8sEnvVars(chePlugin.getWorkspaceEnv()); kubernetesEnvironment - .getPods() + .getPodData() .values() .stream() .flatMap(pod -> pod.getSpec().getContainers().stream()) @@ -161,7 +163,7 @@ public class KubernetesPluginsToolingApplier implements ChePluginsApplier { * @throws InfrastructureException when any error occurs */ private void addSidecar( - Pod pod, + PodData pod, CheContainer container, ChePlugin chePlugin, KubernetesEnvironment kubernetesEnvironment, diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/DeployBroker.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/DeployBroker.java index a64d2d65a1..7d3673f5b5 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/DeployBroker.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/wsplugins/brokerphases/DeployBroker.java @@ -75,7 +75,7 @@ public class DeployBroker extends BrokerPhase { namespace.configMaps().create(configMap); } - Pod pluginBrokerPod = getPluginBrokerPod(brokerEnvironment.getPods()); + Pod pluginBrokerPod = getPluginBrokerPod(brokerEnvironment.getPodsCopy()); if (factory.isConfigured()) { UnrecoverablePodEventListener unrecoverableEventListener = diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentFactoryTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentFactoryTest.java index 1d23afc342..1b021fc6e0 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentFactoryTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/environment/KubernetesEnvironmentFactoryTest.java @@ -45,10 +45,15 @@ import io.fabric8.kubernetes.api.model.KubernetesList; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodBuilder; import io.fabric8.kubernetes.api.model.PodSpec; +import io.fabric8.kubernetes.api.model.PodTemplateSpec; +import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; import io.fabric8.kubernetes.api.model.Quantity; import io.fabric8.kubernetes.api.model.ResourceRequirements; import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; import io.fabric8.kubernetes.api.model.extensions.Ingress; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.dsl.KubernetesListMixedOperation; @@ -176,6 +181,116 @@ public class KubernetesEnvironmentFactoryTest { new WarningImpl(CONFIG_MAP_IGNORED_WARNING_CODE, CONFIG_MAP_IGNORED_WARNING_MESSAGE)); } + @Test + public void addPodsWhenRecipeContainsThem() throws Exception { + Pod pod = + new PodBuilder() + .withNewMetadata() + .withName("pod-test") + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + + final List recipeObjects = singletonList(pod); + when(validatedObjects.getItems()).thenReturn(recipeObjects); + + final KubernetesEnvironment parsed = + k8sEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList()); + + assertEquals(parsed.getPodsCopy().size(), 1); + assertEquals( + parsed.getPodsCopy().values().iterator().next().getMetadata().getName(), + pod.getMetadata().getName()); + assertEquals(parsed.getPodData().size(), 1); + assertEquals( + parsed.getPodData().values().iterator().next().getMetadata().getName(), + pod.getMetadata().getName()); + } + + @Test + public void addDeploymentsWhenRecipeContainsThem() throws Exception { + PodTemplateSpec podTemplate = + new PodTemplateSpecBuilder() + .withNewMetadata() + .withName("deployment-pod") + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + Deployment deployment = + new DeploymentBuilder() + .withNewMetadata() + .withName("deployment-test") + .endMetadata() + .withNewSpec() + .withTemplate(podTemplate) + .endSpec() + .build(); + + final List recipeObjects = singletonList(deployment); + when(validatedObjects.getItems()).thenReturn(recipeObjects); + + final KubernetesEnvironment parsed = + k8sEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList()); + + assertEquals(parsed.getDeploymentsCopy().size(), 1); + assertEquals( + parsed.getDeploymentsCopy().values().iterator().next().getMetadata().getName(), + deployment.getMetadata().getName()); + assertEquals(parsed.getPodData().size(), 1); + assertEquals( + parsed.getPodData().values().iterator().next().getMetadata().getName(), + podTemplate.getMetadata().getName()); + } + + @Test + public void bothPodsAndDeploymentsIncludedInPodData() throws Exception { + PodTemplateSpec podTemplate = + new PodTemplateSpecBuilder() + .withNewMetadata() + .withName("deployment-pod") + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + Deployment deployment = + new DeploymentBuilder() + .withNewMetadata() + .withName("deployment-test") + .endMetadata() + .withNewSpec() + .withTemplate(podTemplate) + .endSpec() + .build(); + Pod pod = + new PodBuilder() + .withNewMetadata() + .withName("bare-pod") + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + final List recipeObjects = asList(deployment, pod); + when(validatedObjects.getItems()).thenReturn(recipeObjects); + + final KubernetesEnvironment parsed = + k8sEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList()); + + assertEquals(parsed.getPodData().size(), 2); + assertTrue( + parsed + .getPodData() + .values() + .stream() + .allMatch( + podData -> { + String name = podData.getMetadata().getName(); + return name.equals(podTemplate.getMetadata().getName()) + || name.equals(pod.getMetadata().getName()); + })); + } + @Test public void testProvisionRamAttributesIsInvoked() throws Exception { final long firstMachineRamLimit = 3072; diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInternalRuntime.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInternalRuntime.java index 4d3d961bc6..a561868ea8 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInternalRuntime.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInternalRuntime.java @@ -13,14 +13,13 @@ package org.eclipse.che.workspace.infrastructure.openshift; import com.google.inject.assistedinject.Assisted; import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.Service; import io.fabric8.openshift.api.model.Route; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import javax.inject.Inject; import javax.inject.Named; @@ -123,10 +122,12 @@ public class OpenShiftInternalRuntime extends KubernetesInternalRuntime pods = getContext().getEnvironment().getPods(); + Set toWatch = new HashSet<>(); + OpenShiftEnvironment environment = getContext().getEnvironment(); + toWatch.addAll(environment.getPodsCopy().keySet()); + toWatch.addAll(environment.getDeploymentsCopy().keySet()); UnrecoverablePodEventListener handler = - unrecoverablePodEventListenerFactory.create( - pods.keySet(), this::handleUnrecoverableEvent); + unrecoverablePodEventListenerFactory.create(toWatch, this::handleUnrecoverableEvent); project.deployments().watchEvents(handler); } diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironment.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironment.java index b297ae1d61..db16653c10 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironment.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironment.java @@ -14,8 +14,10 @@ package org.eclipse.che.workspace.infrastructure.openshift.environment; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.extensions.Ingress; import io.fabric8.openshift.api.model.Route; import java.util.HashMap; @@ -27,6 +29,7 @@ import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfi import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.Builder; +import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData; /** * Holds objects of OpenShift environment. @@ -63,13 +66,24 @@ public class OpenShiftEnvironment extends KubernetesEnvironment { public OpenShiftEnvironment( InternalEnvironment internalEnvironment, Map pods, + Map deployments, + Map podData, Map services, Map ingresses, Map pvcs, Map secrets, Map configMaps, Map routes) { - super(internalEnvironment, pods, services, ingresses, pvcs, secrets, configMaps); + super( + internalEnvironment, + pods, + deployments, + podData, + services, + ingresses, + pvcs, + secrets, + configMaps); setType(TYPE); this.routes = routes; } @@ -79,13 +93,26 @@ public class OpenShiftEnvironment extends KubernetesEnvironment { Map machines, List warnings, Map pods, + Map deployments, + Map podData, Map services, Map ingresses, Map pvcs, Map secrets, Map configMaps, Map routes) { - super(internalRecipe, machines, warnings, pods, services, ingresses, pvcs, secrets, configMaps); + super( + internalRecipe, + machines, + warnings, + pods, + deployments, + podData, + services, + ingresses, + pvcs, + secrets, + configMaps); setType(TYPE); this.routes = routes; } @@ -130,6 +157,26 @@ public class OpenShiftEnvironment extends KubernetesEnvironment { @Override public Builder setPods(Map pods) { this.pods.putAll(pods); + pods.entrySet() + .forEach( + e -> { + Pod pod = e.getValue(); + podData.put(e.getKey(), new PodData(pod.getSpec(), pod.getMetadata())); + }); + return this; + } + + @Override + public Builder setDeployments(Map deployments) { + this.deployments.putAll(deployments); + deployments + .entrySet() + .forEach( + e -> { + PodTemplateSpec podTemplate = e.getValue().getSpec().getTemplate(); + podData.put( + e.getKey(), new PodData(podTemplate.getSpec(), podTemplate.getMetadata())); + }); return this; } @@ -176,7 +223,16 @@ public class OpenShiftEnvironment extends KubernetesEnvironment { public OpenShiftEnvironment build() { return new OpenShiftEnvironment( - internalEnvironment, pods, services, ingresses, pvcs, secrets, configMaps, routes); + internalEnvironment, + pods, + deployments, + podData, + services, + ingresses, + pvcs, + secrets, + configMaps, + routes); } } } diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentFactory.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentFactory.java index c728eee88d..238c5a4292 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentFactory.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentFactory.java @@ -22,6 +22,7 @@ import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.openshift.api.model.DeploymentConfig; import io.fabric8.openshift.api.model.Route; import java.io.ByteArrayInputStream; @@ -101,6 +102,7 @@ public class OpenShiftEnvironmentFactory extends InternalEnvironmentFactory pods = new HashMap<>(); + Map deployments = new HashMap<>(); Map services = new HashMap<>(); boolean isAnyRoutePresent = false; boolean isAnyPVCPresent = false; @@ -112,6 +114,9 @@ public class OpenShiftEnvironmentFactory extends InternalEnvironmentFactory()) .setSecrets(new HashMap<>()) diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentFactoryTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentFactoryTest.java index 7b5b55ae67..9ecefffd65 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentFactoryTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentFactoryTest.java @@ -44,10 +44,15 @@ import io.fabric8.kubernetes.api.model.KubernetesList; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodBuilder; import io.fabric8.kubernetes.api.model.PodSpec; +import io.fabric8.kubernetes.api.model.PodTemplateSpec; +import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; import io.fabric8.kubernetes.api.model.Quantity; import io.fabric8.kubernetes.api.model.ResourceRequirements; import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; import io.fabric8.kubernetes.client.dsl.KubernetesListMixedOperation; import io.fabric8.kubernetes.client.dsl.RecreateFromServerGettable; import io.fabric8.openshift.api.model.Route; @@ -175,6 +180,116 @@ public class OpenShiftEnvironmentFactoryTest { new WarningImpl(CONFIG_MAP_IGNORED_WARNING_CODE, CONFIG_MAP_IGNORED_WARNING_MESSAGE)); } + @Test + public void addPodsWhenRecipeContainsThem() throws Exception { + Pod pod = + new PodBuilder() + .withNewMetadata() + .withName("pod-test") + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + + final List recipeObjects = singletonList(pod); + when(validatedObjects.getItems()).thenReturn(recipeObjects); + + final KubernetesEnvironment parsed = + osEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList()); + + assertEquals(parsed.getPodsCopy().size(), 1); + assertEquals( + parsed.getPodsCopy().values().iterator().next().getMetadata().getName(), + pod.getMetadata().getName()); + assertEquals(parsed.getPodData().size(), 1); + assertEquals( + parsed.getPodData().values().iterator().next().getMetadata().getName(), + pod.getMetadata().getName()); + } + + @Test + public void addDeploymentsWhenRecipeContainsThem() throws Exception { + PodTemplateSpec podTemplate = + new PodTemplateSpecBuilder() + .withNewMetadata() + .withName("deployment-pod") + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + Deployment deployment = + new DeploymentBuilder() + .withNewMetadata() + .withName("deployment-test") + .endMetadata() + .withNewSpec() + .withTemplate(podTemplate) + .endSpec() + .build(); + + final List recipeObjects = singletonList(deployment); + when(validatedObjects.getItems()).thenReturn(recipeObjects); + + final KubernetesEnvironment parsed = + osEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList()); + + assertEquals(parsed.getDeploymentsCopy().size(), 1); + assertEquals( + parsed.getDeploymentsCopy().values().iterator().next().getMetadata().getName(), + deployment.getMetadata().getName()); + assertEquals(parsed.getPodData().size(), 1); + assertEquals( + parsed.getPodData().values().iterator().next().getMetadata().getName(), + podTemplate.getMetadata().getName()); + } + + @Test + public void bothPodsAndDeploymentsIncludedInPodData() throws Exception { + PodTemplateSpec podTemplate = + new PodTemplateSpecBuilder() + .withNewMetadata() + .withName("deployment-pod") + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + Deployment deployment = + new DeploymentBuilder() + .withNewMetadata() + .withName("deployment-test") + .endMetadata() + .withNewSpec() + .withTemplate(podTemplate) + .endSpec() + .build(); + Pod pod = + new PodBuilder() + .withNewMetadata() + .withName("bare-pod") + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + final List recipeObjects = asList(deployment, pod); + when(validatedObjects.getItems()).thenReturn(recipeObjects); + + final KubernetesEnvironment parsed = + osEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList()); + + assertEquals(parsed.getPodData().size(), 2); + assertTrue( + parsed + .getPodData() + .values() + .stream() + .allMatch( + podData -> { + String name = podData.getMetadata().getName(); + return name.equals(podTemplate.getMetadata().getName()) + || name.equals(pod.getMetadata().getName()); + })); + } + @Test public void testProvisionRamAttributesIsInvoked() throws Exception { final long firstMachineRamLimit = 3072 * BYTES_IN_MB;