Skip to content

Commit

Permalink
Fix EphemeralWorkspaceAdapter to accomodate plugin PVCs support
Browse files Browse the repository at this point in the history
Commit dd47af6 changes the way that certain volumes are handled;
rather than storing the volume information in the relevant container's
machine, it is stored in the environment's pods directly. To accomodate
this, the EphemeralWorkspaceAdapter needs to pull the list of PVCs it
provisions from the environment rather than machines.

Signed-off-by: Angel Misevski <amisevsk@redhat.com>
  • Loading branch information
amisevsk committed Oct 7, 2019
1 parent 88b869c commit 027f2f8
Showing 1 changed file with 23 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,15 @@
*/
package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc;

import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesObjectUtil.newVolumeMount;
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.EmptyDirVolumeSource;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.VolumeBuilder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.model.workspace.config.Volume;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
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;
Expand All @@ -51,95 +42,43 @@ public class EphemeralWorkspaceAdapter {

private static final Logger LOG = LoggerFactory.getLogger(CommonPVCStrategy.class);

private static final String EPHEMERAL_VOLUME_NAME_PREFIX = "ephemeral-che-workspace-";
private final PVCProvisioner pvcProvisioner;
private final SubPathPrefixes subPathPrefixes;

@Inject
public EphemeralWorkspaceAdapter(PVCProvisioner pvcProvisioner, SubPathPrefixes subPathPrefixes) {
this.pvcProvisioner = pvcProvisioner;
this.subPathPrefixes = subPathPrefixes;
}

public void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity)
throws InfrastructureException {
LOG.debug("Provisioning PVC strategy for workspace '{}'", identity.getWorkspaceId());

Map<String, PersistentVolumeClaim> userDefinedPVCs =
new HashMap<>(k8sEnv.getPersistentVolumeClaims());

k8sEnv.getPersistentVolumeClaims().clear();
for (PodData pod : k8sEnv.getPodsData().values()) {
PodSpec podSpec = pod.getSpec();
pvcProvisioner.provision(k8sEnv, userDefinedPVCs);
pvcProvisioner.convertCheVolumes(k8sEnv, identity.getWorkspaceId());
subPathPrefixes.prefixVolumeMountsSubpaths(k8sEnv, identity.getWorkspaceId());

// To ensure same volumes get mounted correctly in different containers, we need to track
// which volumes have been "created"
Map<String, String> cheVolumeNameToPodVolumeName = new HashMap<>();
replacePVCsWithEmptyDir(k8sEnv);
k8sEnv.getPersistentVolumeClaims().clear();
}

private void replacePVCsWithEmptyDir(KubernetesEnvironment k8sEnv) {
for (PodData pod : k8sEnv.getPodsData().values()) {
PodSpec podSpec = pod.getSpec();
podSpec
.getVolumes()
.stream()
.filter(v -> v.getPersistentVolumeClaim() != null)
.forEach(
v -> {
String claimName = v.getPersistentVolumeClaim().getClaimName();
cheVolumeNameToPodVolumeName.put(claimName, v.getName());
v.setPersistentVolumeClaim(null);
v.setEmptyDir(new EmptyDirVolumeSource());
});

List<Container> containers = new ArrayList<>();
containers.addAll(podSpec.getContainers());
containers.addAll(podSpec.getInitContainers());
for (Container container : containers) {
String machineName = Names.machineName(pod, container);
InternalMachineConfig machineConfig = k8sEnv.getMachines().get(machineName);
if (machineConfig == null) {
return;
}
Map<String, Volume> volumes = machineConfig.getVolumes();
if (volumes.isEmpty()) {
continue;
}

for (Entry<String, Volume> volumeEntry : volumes.entrySet()) {
final String volumePath = volumeEntry.getValue().getPath();
final String cheVolumeName =
LOGS_VOLUME_NAME.equals(volumeEntry.getKey())
? volumeEntry.getKey() + '-' + pod.getMetadata().getName()
: volumeEntry.getKey();

final String uniqueVolumeName;
if (cheVolumeNameToPodVolumeName.containsKey(cheVolumeName)) {
uniqueVolumeName = cheVolumeNameToPodVolumeName.get(cheVolumeName);
} else {
uniqueVolumeName = Names.generateName(EPHEMERAL_VOLUME_NAME_PREFIX);
cheVolumeNameToPodVolumeName.put(cheVolumeName, uniqueVolumeName);
}
// binds volume to pod and container
container
.getVolumeMounts()
.add(
newVolumeMount(
uniqueVolumeName,
volumePath,
getSubPath(identity.getWorkspaceId(), cheVolumeName, machineName)));
addEmptyDirVolumeIfAbsent(pod.getSpec(), uniqueVolumeName);
}
}
}
}

private void addEmptyDirVolumeIfAbsent(PodSpec podSpec, String uniqueVolumeName) {
if (podSpec
.getVolumes()
.stream()
.noneMatch(volume -> volume.getName().equals(uniqueVolumeName))) {
podSpec
.getVolumes()
.add(
new VolumeBuilder()
.withName(uniqueVolumeName)
.withNewEmptyDir()
.endEmptyDir()
.build());
}
}

private String getSubPath(String workspaceId, String volumeName, String machineName) {
// logs must be located inside the folder related to the machine because few machines can
// contain the identical agents and in this case, a conflict is possible.
if (LOGS_VOLUME_NAME.equals(volumeName)) {
return workspaceId + '/' + volumeName + '/' + machineName;
}
return workspaceId + '/' + volumeName;
}
}

0 comments on commit 027f2f8

Please sign in to comment.