workspaceAttributeValidators =
Multibinder.newSetBinder(binder(), WorkspaceAttributeValidator.class);
workspaceAttributeValidators.addBinding().to(K8sInfraNamespaceWsAttributeValidator.class);
- workspaceAttributeValidators.addBinding().to(AsyncStorageModeValidator.class);
// order matters here!
// We first need to grant permissions to user, only then we can run other configurators with
@@ -249,6 +247,5 @@ public class KubernetesInfraModule extends AbstractModule {
binder(), KubernetesEnvironment.TYPE);
bind(NonTlsDistributedClusterModeNotifier.class);
- bind(AsyncStorageProvisioner.class);
}
}
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
deleted file mode 100644
index 5a5670c70c..0000000000
--- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/CommonPVCStrategy.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (c) 2012-2022 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc;
-
-import static com.google.common.base.Strings.isNullOrEmpty;
-import static java.lang.String.format;
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toSet;
-import static org.eclipse.che.api.user.server.UserManager.PERSONAL_ACCOUNT;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesObjectUtil.newPVC;
-
-import com.google.inject.Inject;
-import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
-import io.fabric8.kubernetes.api.model.VolumeMount;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-import org.eclipse.che.account.spi.AccountImpl;
-import org.eclipse.che.api.core.Page;
-import org.eclipse.che.api.core.ServerException;
-import org.eclipse.che.api.core.model.workspace.Workspace;
-import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
-import org.eclipse.che.api.workspace.server.WorkspaceManager;
-import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
-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.namespace.KubernetesNamespace;
-import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
-import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesPersistentVolumeClaims;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Provides common PVC for all workspaces in one Kubernetes namespace.
- *
- * This strategy indirectly affects the workspace limits.
- * The number of workspaces that can simultaneously store backups in one PV is limited only by the
- * storage capacity. The number of workspaces that can be running simultaneously depends on access
- * mode configuration and Che configuration limits.
- *
- *
Used subpaths:
- *
- *
This strategy uses subpaths for resolving backed up data paths collisions.
- * Subpaths have the following format: '{workspaceId}/{volumeName}'.
- * Note that logs volume has the special format: '{workspaceId}/{volumeName}/{machineName}'. It is
- * done in this way to avoid conflicts e.g. two identical agents inside different machines produce
- * the same log file.
- *
- *
How user-defined PVCs are processed:
- *
- *
How user-defined PVCs are processed: User-defined PVCs are removed from environment. Pods
- * volumes that reference PVCs are replaced with volume that references common PVC. The
- * corresponding containers volume mounts are relinked to common volume and subpaths are prefixed
- * with `'{workspaceId}/{originalPVCName}'`.
- *
- *
User-defined PVC name is used as Che Volume name. It means that if Machine is configured to
- * use Che Volume with the same name as user-defined PVC has then they will use the same shared
- * folder in common PVC.
- *
- *
Note that quantity and access mode of user-defined PVCs are ignored since common PVC is used
- * and it has preconfigured configuration.
- *
- * @author Anton Korneta
- * @author Alexander Garagatyi
- */
-public class CommonPVCStrategy implements WorkspaceVolumesStrategy {
- // use non-static variable to reuse child class logger
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- public static final String COMMON_STRATEGY = "common";
-
- /**
- * The additional property name with the wildcard reserved for workspace id. Formatted property
- * with the real workspace id is used to get workspace subpaths directories. The value of this
- * property represents the String array of subpaths that are used to create folders in PV with
- * user rights. Note that the value would not be stored and it is removed before PVC creation.
- */
- static final String SUBPATHS_PROPERTY_FMT = "che.workspace.%s.subpaths";
-
- private final boolean preCreateDirs;
- private final String pvcQuantity;
- private final String configuredPVCName;
- private final String pvcAccessMode;
- private final String pvcStorageClassName;
- private final PVCSubPathHelper pvcSubPathHelper;
- private final KubernetesNamespaceFactory factory;
- private final EphemeralWorkspaceAdapter ephemeralWorkspaceAdapter;
- private final PVCProvisioner pvcProvisioner;
- private final PodsVolumes podsVolumes;
- private final SubPathPrefixes subpathPrefixes;
- private final boolean waitBound;
- private final WorkspaceManager workspaceManager;
-
- @Inject
- public CommonPVCStrategy(
- PVCSubPathHelper pvcSubPathHelper,
- KubernetesNamespaceFactory factory,
- EphemeralWorkspaceAdapter ephemeralWorkspaceAdapter,
- PVCProvisioner pvcProvisioner,
- PodsVolumes podsVolumes,
- SubPathPrefixes subpathPrefixes,
- WorkspaceManager workspaceManager) {
- this.configuredPVCName = "test";
- this.pvcQuantity = "test";
- this.pvcAccessMode = "TEST";
- this.preCreateDirs = true;
- this.pvcStorageClassName = "TEST";
- this.waitBound = true;
- this.pvcSubPathHelper = pvcSubPathHelper;
- this.factory = factory;
- this.ephemeralWorkspaceAdapter = ephemeralWorkspaceAdapter;
- this.pvcProvisioner = pvcProvisioner;
- this.podsVolumes = podsVolumes;
- this.subpathPrefixes = subpathPrefixes;
- this.workspaceManager = workspaceManager;
- }
-
- /**
- * Creates new instance of PVC object that should be used for the specified workspace.
- *
- *
May be overridden by child class for changing common scope. Like common per user or common
- * per workspace.
- *
- * @param workspaceId workspace that needs PVC
- * @return pvc that should be used for the specified runtime identity
- */
- protected PersistentVolumeClaim createCommonPVC(String workspaceId) {
- return newPVC(configuredPVCName, pvcAccessMode, pvcQuantity, pvcStorageClassName);
- }
-
- @Override
- public void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity)
- throws InfrastructureException {
- final String workspaceId = identity.getWorkspaceId();
- if (EphemeralWorkspaceUtility.isEphemeral(k8sEnv.getAttributes())) {
- ephemeralWorkspaceAdapter.provision(k8sEnv, identity);
- return;
- }
- log.debug("Provisioning PVC strategy for workspace '{}'", workspaceId);
-
- pvcProvisioner.convertCheVolumes(k8sEnv, workspaceId);
-
- // Note that PVC name is used during prefixing
- // It MUST be done before changing all PVCs references to common PVC
- subpathPrefixes.prefixVolumeMountsSubpaths(k8sEnv, identity.getWorkspaceId());
-
- PersistentVolumeClaim commonPVC = replacePVCsWithCommon(k8sEnv, identity);
-
- podsVolumes.replacePVCVolumesWithCommon(
- k8sEnv.getPodsData(), commonPVC.getMetadata().getName());
-
- if (preCreateDirs) {
- Set subPaths = combineVolumeMountsSubpaths(k8sEnv);
- if (!subPaths.isEmpty()) {
- commonPVC.setAdditionalProperty(
- format(SUBPATHS_PROPERTY_FMT, workspaceId),
- subPaths.toArray(new String[subPaths.size()]));
- }
- }
- log.debug("PVC strategy provisioning done for workspace '{}'", workspaceId);
- }
-
- @Override
- @Traced
- public void prepare(
- KubernetesEnvironment k8sEnv,
- RuntimeIdentity identity,
- long timeoutMillis,
- Map startOptions)
- throws InfrastructureException {
- String workspaceId = identity.getWorkspaceId();
-
- TracingTags.WORKSPACE_ID.set(workspaceId);
-
- if (EphemeralWorkspaceUtility.isEphemeral(k8sEnv.getAttributes())) {
- return;
- }
-
- log.debug("Preparing PVC started for workspace '{}'", workspaceId);
-
- Map claims = k8sEnv.getPersistentVolumeClaims();
- if (claims.isEmpty()) {
- return;
- }
- if (claims.size() > 1) {
- throw new InfrastructureException(
- format(
- "The only one PVC MUST be present in common strategy while it contains: %s.",
- claims.keySet().stream().collect(joining(", "))));
- }
-
- PersistentVolumeClaim commonPVC = claims.values().iterator().next();
-
- final KubernetesNamespace namespace = factory.getOrCreate(identity);
- final KubernetesPersistentVolumeClaims pvcs = namespace.persistentVolumeClaims();
- final Set existing =
- pvcs.get().stream().map(p -> p.getMetadata().getName()).collect(toSet());
- if (!existing.contains(commonPVC.getMetadata().getName())) {
- log.debug("Creating PVC for workspace '{}'", workspaceId);
- pvcs.create(commonPVC);
- if (waitBound) {
- log.debug("Waiting for PVC for workspace '{}' to be bound", workspaceId);
- pvcs.waitBound(commonPVC.getMetadata().getName(), timeoutMillis);
- }
- }
-
- final String[] subpaths =
- (String[])
- commonPVC.getAdditionalProperties().remove(format(SUBPATHS_PROPERTY_FMT, workspaceId));
- if (preCreateDirs && subpaths != null) {
- pvcSubPathHelper.createDirs(
- identity, workspaceId, commonPVC.getMetadata().getName(), startOptions, subpaths);
- }
-
- log.debug("Preparing PVC done for workspace '{}'", workspaceId);
- }
-
- @Override
- public void cleanup(Workspace workspace) throws InfrastructureException {
- AccountImpl account = ((WorkspaceImpl) workspace).getAccount();
- if (isPersonalAccount(account) && accountHasNoWorkspaces(account)) {
- log.debug("Deleting the common PVC: '{}',", configuredPVCName);
- deleteCommonPVC(workspace);
- return;
- }
-
- if (EphemeralWorkspaceUtility.isEphemeral(workspace)) {
- return;
- }
-
- String workspaceId = workspace.getId();
- PersistentVolumeClaim pvc = createCommonPVC(workspaceId);
- pvcSubPathHelper.removeDirsAsync(
- workspaceId,
- factory.get(workspace).getName(),
- pvc.getMetadata().getName(),
- subpathPrefixes.getWorkspaceSubPath(workspaceId));
- }
-
- private PersistentVolumeClaim replacePVCsWithCommon(
- KubernetesEnvironment k8sEnv, RuntimeIdentity identity) {
- final PersistentVolumeClaim commonPVC = createCommonPVC(identity.getWorkspaceId());
- k8sEnv.getPersistentVolumeClaims().clear();
- k8sEnv.getPersistentVolumeClaims().put(commonPVC.getMetadata().getName(), commonPVC);
- return commonPVC;
- }
-
- private Set combineVolumeMountsSubpaths(KubernetesEnvironment k8sEnv) {
- return k8sEnv.getPodsData().values().stream()
- .flatMap(p -> p.getSpec().getContainers().stream())
- .flatMap(c -> c.getVolumeMounts().stream())
- .map(VolumeMount::getSubPath)
- .filter(subpath -> !isNullOrEmpty(subpath))
- .collect(Collectors.toSet());
- }
-
- private void deleteCommonPVC(Workspace workspace) throws InfrastructureException {
- factory.get(workspace).persistentVolumeClaims().delete(configuredPVCName);
- }
-
- /**
- * @param account the account of interest
- * @return true, if the given account is a personal account, false otherwise
- */
- private boolean isPersonalAccount(AccountImpl account) {
- return PERSONAL_ACCOUNT.equals(account.getType());
- }
-
- /**
- * @param account the account of interest
- * @return true, if the given account has no workspaces, false otherwise
- * @throws InfrastructureException
- */
- private boolean accountHasNoWorkspaces(AccountImpl account) throws InfrastructureException {
- try {
- Page workspaces = workspaceManager.getWorkspaces(account.getId(), false, 1, 0);
- if (workspaces.isEmpty()) {
- log.debug("User '{}' has no more workspaces left", account.getId());
- return true;
- }
- } catch (ServerException e) {
- // should never happen
- throw new InfrastructureException(e.getLocalizedMessage(), e);
- }
- return false;
- }
-}
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
deleted file mode 100644
index 11516f403c..0000000000
--- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/EphemeralWorkspaceAdapter.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2012-2021 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc;
-
-import io.fabric8.kubernetes.api.model.EmptyDirVolumeSource;
-import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
-import io.fabric8.kubernetes.api.model.PodSpec;
-import java.util.HashMap;
-import java.util.Map;
-import javax.inject.Inject;
-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;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Allows to create ephemeral workspaces (with no PVC attached) based on workspace config
- * `persistVolumes` attribute. If `persistVolumes` attribute is set to false, workspace volumes
- * would be created as `emptyDir` regardless of the PVC strategy. User-defined PVCs will be removed
- * from environment and the corresponding PVC volumes in Pods will be replaced with `emptyDir`
- * volumes. When a workspace Pod is removed for any reason, the data in the `emptyDir` volume is
- * deleted forever.
- *
- * @see emptyDir
- * @author Ilya Buziuk
- * @author Angel Misevski
- */
-@Singleton
-public class EphemeralWorkspaceAdapter {
-
- private static final Logger LOG = LoggerFactory.getLogger(CommonPVCStrategy.class);
-
- 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 userDefinedPVCs =
- new HashMap<>(k8sEnv.getPersistentVolumeClaims());
-
- k8sEnv.getPersistentVolumeClaims().clear();
- pvcProvisioner.provision(k8sEnv, userDefinedPVCs);
- pvcProvisioner.convertCheVolumes(k8sEnv, identity.getWorkspaceId());
- subPathPrefixes.prefixVolumeMountsSubpaths(k8sEnv, identity.getWorkspaceId());
-
- 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 -> {
- v.setPersistentVolumeClaim(null);
- v.setEmptyDir(new EmptyDirVolumeSource());
- });
- }
- }
-}
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/PerWorkspacePVCStrategy.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/PerWorkspacePVCStrategy.java
deleted file mode 100644
index 92b7f70ccb..0000000000
--- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/PerWorkspacePVCStrategy.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2012-2022 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc;
-
-import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_WORKSPACE_ID_LABEL;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesObjectUtil.newPVC;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesObjectUtil.putLabel;
-
-import com.google.common.collect.ImmutableMap;
-import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
-import javax.inject.Inject;
-import org.eclipse.che.api.core.model.workspace.Workspace;
-import org.eclipse.che.api.workspace.server.WorkspaceManager;
-import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
-import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
-
-/**
- * Provides common PVC per each workspace.
- *
- * Names for PVCs are evaluated as: '{configured_prefix}' + '-' +'{workspaceId}' to avoid naming
- * collisions inside of one Kubernetes namespace.
- *
- *
This strategy uses subpaths to do the same as {@link CommonPVCStrategy} does and make easier
- * data migration if it will be needed.
- * Subpaths have the following format: '{workspaceId}/{volumeName}'.
- * Note that logs volume has the special format: '{workspaceId}/{volumeName}/{machineName}'. It is
- * done in this way to avoid conflicts e.g. two identical agents inside different machines produce
- * the same log file.
- *
- * @author Sergii Leshchenko
- * @author Masaki Muranaka
- */
-public class PerWorkspacePVCStrategy extends CommonPVCStrategy {
-
- public static final String PER_WORKSPACE_STRATEGY = "per-workspace";
-
- private final KubernetesNamespaceFactory factory;
- private final String pvcNamePrefix;
- private final String pvcAccessMode;
- private final String pvcQuantity;
- private final String pvcStorageClassName;
-
- @Inject
- public PerWorkspacePVCStrategy(
- PVCSubPathHelper pvcSubPathHelper,
- KubernetesNamespaceFactory factory,
- EphemeralWorkspaceAdapter ephemeralWorkspaceAdapter,
- PVCProvisioner pvcProvisioner,
- PodsVolumes podsVolumes,
- SubPathPrefixes subpathPrefixes,
- WorkspaceManager workspaceManager) {
- super(
- pvcSubPathHelper,
- factory,
- ephemeralWorkspaceAdapter,
- pvcProvisioner,
- podsVolumes,
- subpathPrefixes,
- workspaceManager);
- this.pvcNamePrefix = "TEST";
- this.factory = factory;
- this.pvcAccessMode = "TEST";
- this.pvcQuantity = "test";
- this.pvcStorageClassName = "TEST";
- }
-
- @Override
- protected PersistentVolumeClaim createCommonPVC(String workspaceId) {
- String pvcName = pvcNamePrefix + '-' + workspaceId;
-
- PersistentVolumeClaim perWorkspacePVC =
- newPVC(pvcName, pvcAccessMode, pvcQuantity, pvcStorageClassName);
- putLabel(perWorkspacePVC.getMetadata(), CHE_WORKSPACE_ID_LABEL, workspaceId);
- return perWorkspacePVC;
- }
-
- @Override
- public void cleanup(Workspace workspace) throws InfrastructureException {
- if (EphemeralWorkspaceUtility.isEphemeral(workspace)) {
- return;
- }
- final String workspaceId = workspace.getId();
- factory
- .get(workspace)
- .persistentVolumeClaims()
- .delete(ImmutableMap.of(CHE_WORKSPACE_ID_LABEL, workspaceId));
- }
-}
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
deleted file mode 100644
index a60ff39641..0000000000
--- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/UniqueWorkspacePVCStrategy.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (c) 2012-2022 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc;
-
-import static java.util.stream.Collectors.toMap;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_WORKSPACE_ID_LABEL;
-
-import com.google.common.collect.ImmutableMap;
-import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Function;
-import javax.inject.Inject;
-import org.eclipse.che.api.core.model.workspace.Workspace;
-import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
-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.namespace.KubernetesNamespaceFactory;
-import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesPersistentVolumeClaims;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Provides a unique PVC for each volume of a workspace.
- *
- *
Names for PVCs are evaluated as: '{configured_prefix}' + '-' +'{generated_8_chars}' to avoid
- * naming collisions inside of one Kubernetes namespace.
- *
- *
Note that for in this strategy number of simultaneously used volumes by workspaces can not be
- * greater than the number of available PVCs in Kubernetes namespace.
- *
- *
Used subpaths:
- *
- *
This strategy uses subpaths to do the same as {@link CommonPVCStrategy} does and make easier
- * data migration if it will be needed.
- * Subpaths have the following format: '{workspaceId}/{volume/PVC name}'.
- * Note that logs volume has the special format: '{workspaceId}/{volumeName}/{machineName}'. It is
- * done in this way to avoid conflicts e.g. two identical agents inside different machines produce
- * the same log file.
- *
- *
How user-defined PVCs are processed:
- *
- *
User-defined PVCs are provisioned with generated unique names. Pods volumes that reference
- * PVCs are updated accordingly. Subpaths of the corresponding containers volume mounts are prefixed
- * with `'{workspaceId}/{originalPVCName}'`.
- *
- *
User-defined PVC name is used as Che Volume name. It means that if Machine is configured to
- * use Che Volume with the same name as user-defined PVC has then Che Volume will reuse user-defined
- * PVC.
- *
- *
Note that quantity and access mode of user-defined PVCs are not overridden with Che Server
- * configured.
- *
- *
Clean up:
- *
- *
Cleanup of backed up data is performed by removing of PVCs related to the workspace but when
- * the volume or machine name is changed then related PVC would not be removed.
- *
- * @author Anton Korneta
- * @author Alexander Garagatyi
- */
-public class UniqueWorkspacePVCStrategy implements WorkspaceVolumesStrategy {
-
- private static final Logger LOG = LoggerFactory.getLogger(UniqueWorkspacePVCStrategy.class);
-
- public static final String UNIQUE_STRATEGY = "unique";
-
- private final KubernetesNamespaceFactory factory;
- private final EphemeralWorkspaceAdapter ephemeralWorkspaceAdapter;
- private final PVCProvisioner pvcProvisioner;
- private final SubPathPrefixes subpathPrefixes;
- private final boolean waitBound;
-
- @Inject
- public UniqueWorkspacePVCStrategy(
- KubernetesNamespaceFactory factory,
- EphemeralWorkspaceAdapter ephemeralWorkspaceAdapter,
- PVCProvisioner pvcProvisioner,
- SubPathPrefixes subpathPrefixes) {
- this.waitBound = true;
- this.factory = factory;
- this.ephemeralWorkspaceAdapter = ephemeralWorkspaceAdapter;
- this.pvcProvisioner = pvcProvisioner;
- this.subpathPrefixes = subpathPrefixes;
- }
-
- @Override
- public void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity)
- throws InfrastructureException {
- final String workspaceId = identity.getWorkspaceId();
- if (EphemeralWorkspaceUtility.isEphemeral(k8sEnv.getAttributes())) {
- ephemeralWorkspaceAdapter.provision(k8sEnv, identity);
- return;
- }
-
- LOG.debug("Provisioning PVC strategy for workspace '{}'", workspaceId);
-
- Map userDefinedPVCs =
- new HashMap<>(k8sEnv.getPersistentVolumeClaims());
-
- k8sEnv.getPersistentVolumeClaims().clear();
- fillInExistingPVCs(k8sEnv, identity);
-
- pvcProvisioner.provision(k8sEnv, userDefinedPVCs);
-
- pvcProvisioner.convertCheVolumes(k8sEnv, workspaceId);
-
- subpathPrefixes.prefixVolumeMountsSubpaths(k8sEnv, identity.getWorkspaceId());
-
- provisionWorkspaceIdLabel(k8sEnv.getPersistentVolumeClaims(), identity.getWorkspaceId());
-
- LOG.debug("PVC strategy provisioning done for workspace '{}'", workspaceId);
- }
-
- @Traced
- @Override
- public void prepare(
- KubernetesEnvironment k8sEnv,
- RuntimeIdentity identity,
- long timeoutMillis,
- Map startOptions)
- throws InfrastructureException {
- String workspaceId = identity.getWorkspaceId();
-
- TracingTags.WORKSPACE_ID.set(workspaceId);
-
- if (EphemeralWorkspaceUtility.isEphemeral(k8sEnv.getAttributes())) {
- return;
- }
-
- if (k8sEnv.getPersistentVolumeClaims().isEmpty()) {
- // no PVCs to prepare
- return;
- }
-
- final KubernetesPersistentVolumeClaims k8sClaims =
- factory.getOrCreate(identity).persistentVolumeClaims();
- LOG.debug("Creating PVCs for workspace '{}'", workspaceId);
- k8sClaims.createIfNotExist(k8sEnv.getPersistentVolumeClaims().values());
-
- if (waitBound) {
- LOG.debug("Waiting for PVC(s) of workspace '{}' to be bound", workspaceId);
- for (PersistentVolumeClaim pvc : k8sEnv.getPersistentVolumeClaims().values()) {
- k8sClaims.waitBound(pvc.getMetadata().getName(), timeoutMillis);
- }
- }
- LOG.debug("Preparing PVCs done for workspace '{}'", workspaceId);
- }
-
- @Override
- public void cleanup(Workspace workspace) throws InfrastructureException {
- if (EphemeralWorkspaceUtility.isEphemeral(workspace)) {
- return;
- }
- factory
- .get(workspace)
- .persistentVolumeClaims()
- .delete(ImmutableMap.of(CHE_WORKSPACE_ID_LABEL, workspace.getId()));
- }
-
- private void fillInExistingPVCs(KubernetesEnvironment k8sEnv, RuntimeIdentity identity)
- throws InfrastructureException {
- Map existingPVCs =
- factory
- .getOrCreate(identity)
- .persistentVolumeClaims()
- .getByLabel(CHE_WORKSPACE_ID_LABEL, identity.getWorkspaceId())
- .stream()
- .collect(toMap(pvc -> pvc.getMetadata().getName(), Function.identity()));
-
- k8sEnv.getPersistentVolumeClaims().putAll(existingPVCs);
- }
-
- private void provisionWorkspaceIdLabel(
- Map pvcs, String workspaceId) {
- pvcs.values()
- .forEach(pvc -> pvc.getMetadata().getLabels().put(CHE_WORKSPACE_ID_LABEL, workspaceId));
- }
-}
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodInterceptor.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodInterceptor.java
deleted file mode 100644
index 991392b6bd..0000000000
--- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodInterceptor.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (c) 2012-2022 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.provision;
-
-import static io.fabric8.kubernetes.api.model.DeletionPropagation.BACKGROUND;
-import static java.lang.String.format;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.CommonPVCStrategy.COMMON_STRATEGY;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.EphemeralWorkspaceUtility.isEphemeral;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStorageProvisioner.ASYNC_STORAGE;
-
-import io.fabric8.kubernetes.api.model.Pod;
-import io.fabric8.kubernetes.api.model.apps.Deployment;
-import io.fabric8.kubernetes.client.KubernetesClientException;
-import io.fabric8.kubernetes.client.Watch;
-import io.fabric8.kubernetes.client.Watcher;
-import io.fabric8.kubernetes.client.WatcherException;
-import io.fabric8.kubernetes.client.dsl.PodResource;
-import io.fabric8.kubernetes.client.dsl.RollableScalableResource;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import javax.inject.Inject;
-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.KubernetesClientFactory;
-import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfrastructureException;
-import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This interceptor checks whether the starting workspace is configured with persistent storage and
- * makes sure to stop the async storage deployment (if any is running) to prevent "Multi-Attach
- * error for volume". After the async storage deployment is stopped and deleted, the workspace start
- * is resumed.
- */
-@Singleton
-public class AsyncStoragePodInterceptor {
-
- private static final Logger LOG = LoggerFactory.getLogger(AsyncStoragePodInterceptor.class);
- private static final int DELETE_DEPLOYMENT_TIMEOUT_IN_MIN = 5;
-
- private final KubernetesClientFactory kubernetesClientFactory;
- private final String pvcStrategy;
-
- @Inject
- public AsyncStoragePodInterceptor(KubernetesClientFactory kubernetesClientFactory) {
- this.pvcStrategy = "TEST";
- this.kubernetesClientFactory = kubernetesClientFactory;
- }
-
- public void intercept(KubernetesEnvironment k8sEnv, RuntimeIdentity identity)
- throws InfrastructureException {
- if (!COMMON_STRATEGY.equals(pvcStrategy) || isEphemeral(k8sEnv.getAttributes())) {
- return;
- }
-
- removeAsyncStoragePodWithoutDeployment(identity);
-
- String namespace = identity.getInfrastructureNamespace();
- String workspaceId = identity.getWorkspaceId();
-
- RollableScalableResource asyncStorageDeploymentResource =
- getAsyncStorageDeploymentResource(namespace, workspaceId);
-
- if (asyncStorageDeploymentResource.get() == null) { // deployment doesn't exist
- return;
- }
-
- try {
- deleteAsyncStorageDeployment(asyncStorageDeploymentResource)
- .get(DELETE_DEPLOYMENT_TIMEOUT_IN_MIN, TimeUnit.MINUTES);
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- throw new InfrastructureException(
- format(
- "Interrupted while waiting for deployment '%s' removal. " + ex.getMessage(),
- ASYNC_STORAGE),
- ex);
- } catch (ExecutionException ex) {
- throw new InfrastructureException(
- format(
- "Error occurred while waiting for deployment '%s' removal. " + ex.getMessage(),
- ASYNC_STORAGE),
- ex);
- } catch (TimeoutException ex) {
- throw new InfrastructureException(
- format("Pod '%s' removal timeout reached " + ex.getMessage(), ASYNC_STORAGE), ex);
- }
- }
-
- private RollableScalableResource getAsyncStorageDeploymentResource(
- String namespace, String workspaceId) throws InfrastructureException {
- return kubernetesClientFactory
- .create(workspaceId)
- .apps()
- .deployments()
- .inNamespace(namespace)
- .withName(ASYNC_STORAGE);
- }
-
- private CompletableFuture deleteAsyncStorageDeployment(
- RollableScalableResource resource) throws InfrastructureException {
- Watch toCloseOnException = null;
- try {
- final CompletableFuture deleteFuture = new CompletableFuture<>();
- final Watch watch = resource.watch(new DeleteWatcher<>(deleteFuture));
- toCloseOnException = watch;
-
- Boolean deleteSucceeded = resource.withPropagationPolicy(BACKGROUND).delete();
- if (deleteSucceeded == null || !deleteSucceeded) {
- deleteFuture.complete(null);
- }
- return deleteFuture.whenComplete(
- (v, e) -> {
- if (e != null) {
- LOG.warn("Failed to remove deployment {} cause {}", ASYNC_STORAGE, e.getMessage());
- }
- watch.close();
- });
- } catch (KubernetesClientException e) {
- if (toCloseOnException != null) {
- toCloseOnException.close();
- }
- throw new KubernetesInfrastructureException(e);
- } catch (Exception e) {
- if (toCloseOnException != null) {
- toCloseOnException.close();
- }
- throw e;
- }
- }
-
- /**
- * Cleanup existed Async Storage pods running without Deployment see
- * https://github.com/eclipse/che/issues/17616. Method can be removed in 7.20.x
- */
- private void removeAsyncStoragePodWithoutDeployment(RuntimeIdentity identity)
- throws InfrastructureException {
- String namespace = identity.getInfrastructureNamespace();
- String workspaceId = identity.getWorkspaceId();
-
- PodResource asyncStoragePodResource =
- kubernetesClientFactory
- .create(workspaceId)
- .pods()
- .inNamespace(namespace)
- .withName(ASYNC_STORAGE);
-
- if (asyncStoragePodResource.get()
- != null) { // remove existed pod to replace it with deployment on provision step
- deleteAsyncStoragePod(asyncStoragePodResource);
- }
- }
-
- private CompletableFuture deleteAsyncStoragePod(PodResource podResource)
- throws InfrastructureException {
- Watch toCloseOnException = null;
- try {
- final CompletableFuture deleteFuture = new CompletableFuture<>();
- final Watch watch = podResource.watch(new DeleteWatcher<>(deleteFuture));
- toCloseOnException = watch;
-
- Boolean deleteSucceeded = podResource.withPropagationPolicy(BACKGROUND).delete();
- if (deleteSucceeded == null || !deleteSucceeded) {
- deleteFuture.complete(null);
- }
- return deleteFuture.whenComplete(
- (v, e) -> {
- if (e != null) {
- LOG.warn("Failed to remove pod {} cause {}", ASYNC_STORAGE, e.getMessage());
- }
- watch.close();
- });
- } catch (KubernetesClientException e) {
- if (toCloseOnException != null) {
- toCloseOnException.close();
- }
- throw new KubernetesInfrastructureException(e);
- } catch (Exception e) {
- if (toCloseOnException != null) {
- toCloseOnException.close();
- }
- throw e;
- }
- }
-
- private static class DeleteWatcher implements Watcher {
-
- private final CompletableFuture future;
-
- private DeleteWatcher(CompletableFuture future) {
- this.future = future;
- }
-
- @Override
- public void eventReceived(Action action, T hasMetadata) {
- if (action == Action.DELETED) {
- future.complete(null);
- }
- }
-
- @Override
- public void onClose(WatcherException e) {
- // if event about removing is received then this completion has no effect
- future.completeExceptionally(
- new RuntimeException(
- "WebSocket connection is closed. But event about removing is not received.", e));
- }
- }
-}
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodWatcher.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodWatcher.java
deleted file mode 100644
index 5d7bd57304..0000000000
--- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodWatcher.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (c) 2012-2022 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.provision;
-
-import static com.google.common.base.Strings.isNullOrEmpty;
-import static java.lang.Long.parseLong;
-import static java.time.Instant.now;
-import static java.time.Instant.ofEpochSecond;
-import static java.util.concurrent.TimeUnit.MINUTES;
-import static org.eclipse.che.api.core.Pages.iterateLazily;
-import static org.eclipse.che.api.workspace.shared.Constants.LAST_ACTIVE_INFRASTRUCTURE_NAMESPACE;
-import static org.eclipse.che.api.workspace.shared.Constants.LAST_ACTIVITY_TIME;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.CommonPVCStrategy.COMMON_STRATEGY;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStorageProvisioner.ASYNC_STORAGE;
-
-import io.fabric8.kubernetes.api.model.Pod;
-import io.fabric8.kubernetes.api.model.apps.Deployment;
-import io.fabric8.kubernetes.client.dsl.PodResource;
-import io.fabric8.kubernetes.client.dsl.RollableScalableResource;
-import java.time.Instant;
-import java.util.Map;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-import org.eclipse.che.api.core.ServerException;
-import org.eclipse.che.api.core.model.user.User;
-import org.eclipse.che.api.user.server.PreferenceManager;
-import org.eclipse.che.api.user.server.UserManager;
-import org.eclipse.che.api.workspace.server.WorkspaceRuntimes;
-import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
-import org.eclipse.che.commons.annotation.Nullable;
-import org.eclipse.che.commons.schedule.ScheduleDelay;
-import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Periodically checks ability to stop Asynchronous Storage Pod. It will periodically revise
- * UserPreferences of all registered user and check specialized preferences. Preferences should be
- * recorded if last workspace stopped and cleanup on start any workspace. Required preferences to
- * initiate stop procedure for Asynchronous Storage Pod : {@link
- * org.eclipse.che.api.workspace.shared.Constants#LAST_ACTIVE_INFRASTRUCTURE_NAMESPACE} : should
- * contain last used infrastructure namespace {@link
- * org.eclipse.che.api.workspace.shared.Constants#LAST_ACTIVITY_TIME} : seconds then workspace
- * stopped in the Java epoch time format (aka Unix time)
- */
-@Singleton
-public class AsyncStoragePodWatcher {
-
- private static final Logger LOG = LoggerFactory.getLogger(AsyncStoragePodWatcher.class);
-
- private final KubernetesClientFactory kubernetesClientFactory;
- private final UserManager userManager;
- private final PreferenceManager preferenceManager;
- private final WorkspaceRuntimes runtimes;
- private final long shutdownTimeoutSec;
- private final boolean isAsyncStoragePodCanBeRun;
-
- @Inject
- public AsyncStoragePodWatcher(
- KubernetesClientFactory kubernetesClientFactory,
- UserManager userManager,
- PreferenceManager preferenceManager,
- WorkspaceRuntimes runtimes,
- @Named("che.infra.kubernetes.async.storage.shutdown_timeout_min") long shutdownTimeoutMin,
- @Nullable @Named("che.infra.kubernetes.namespace.default") String defaultNamespaceName,
- @Named("che.limits.user.workspaces.run.count") int runtimesPerUser) {
- this.kubernetesClientFactory = kubernetesClientFactory;
- this.userManager = userManager;
- this.preferenceManager = preferenceManager;
- this.runtimes = runtimes;
- this.shutdownTimeoutSec = MINUTES.toSeconds(shutdownTimeoutMin);
-
- isAsyncStoragePodCanBeRun =
- isAsyncStoragePodCanBeRun("TEST", defaultNamespaceName, runtimesPerUser);
- }
-
- /**
- * Checking current system configuration on ability to run Async Storage Pod. Will be checked next
- * value of properties:
- *
- *
- * - che.infra.kubernetes.namespace.default=-che
- *
- che.infra.kubernetes.pvc.strategy=common
- *
- che.limits.user.workspaces.run.count=1
- *
- */
- private boolean isAsyncStoragePodCanBeRun(
- String pvcStrategy, String defaultNamespaceName, int runtimesPerUser) {
- return COMMON_STRATEGY.equals(pvcStrategy)
- && runtimesPerUser == 1
- && !isNullOrEmpty(defaultNamespaceName)
- && defaultNamespaceName.contains("");
- }
-
- @ScheduleDelay(
- unit = MINUTES,
- initialDelay = 1,
- delayParameterName = "che.infra.kubernetes.async.storage.shutdown_check_period_min")
- public void check() {
- if (isAsyncStoragePodCanBeRun
- && shutdownTimeoutSec
- > 0) { // if system not support async storage mode or idling time set to 0 or less
- // do nothing
- for (User user :
- iterateLazily((maxItems, skipCount) -> userManager.getAll(maxItems, skipCount))) {
- try {
- String owner = user.getId();
- Map preferences = preferenceManager.find(owner);
- String lastTimeAccess = preferences.get(LAST_ACTIVITY_TIME);
- String namespace = preferences.get(LAST_ACTIVE_INFRASTRUCTURE_NAMESPACE);
-
- if (isNullOrEmpty(namespace)
- || isNullOrEmpty(lastTimeAccess)
- || !runtimes.getInProgress(owner).isEmpty()) {
- continue;
- }
- long lastTimeAccessSec = parseLong(lastTimeAccess);
- Instant expectedShutdownAfter =
- ofEpochSecond(lastTimeAccessSec).plusSeconds(shutdownTimeoutSec);
- if (now().isAfter(expectedShutdownAfter)) {
- removeAsyncStoragePodWithoutDeployment(namespace);
- RollableScalableResource doneableResource =
- kubernetesClientFactory
- .create()
- .apps()
- .deployments()
- .inNamespace(namespace)
- .withName(ASYNC_STORAGE);
- if (doneableResource.get() != null) {
- doneableResource.delete();
- }
- }
- } catch (InfrastructureException | ServerException e) {
- LOG.error(e.getMessage(), e);
- }
- }
- }
- }
-
- /**
- * Cleanup existed Async Storage pods running without Deployment see
- * https://github.com/eclipse/che/issues/17616. Method can be removed in 7.20.x
- *
- * @param namespace
- * @throws InfrastructureException
- */
- private void removeAsyncStoragePodWithoutDeployment(String namespace)
- throws InfrastructureException {
- PodResource doneablePodResource =
- kubernetesClientFactory.create().pods().inNamespace(namespace).withName(ASYNC_STORAGE);
- if (doneablePodResource.get() != null) {
- doneablePodResource.delete();
- }
- }
-}
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStorageProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStorageProvisioner.java
deleted file mode 100644
index 7bbb439e86..0000000000
--- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStorageProvisioner.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (c) 2012-2022 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.provision;
-
-import static com.google.common.collect.ImmutableMap.of;
-import static java.lang.Boolean.parseBoolean;
-import static java.lang.String.format;
-import static java.lang.String.valueOf;
-import static java.util.Collections.singletonList;
-import static org.eclipse.che.api.workspace.shared.Constants.ASYNC_PERSIST_ATTRIBUTE;
-import static org.eclipse.che.api.workspace.shared.Constants.PERSIST_VOLUMES_ATTRIBUTE;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_DEPLOYMENT_NAME_LABEL;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_USER_ID_LABEL;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.Warnings.NOT_ABLE_TO_PROVISION_SSH_KEYS;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.Warnings.NOT_ABLE_TO_PROVISION_SSH_KEYS_MESSAGE;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.CommonPVCStrategy.COMMON_STRATEGY;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.EphemeralWorkspaceUtility.isEphemeral;
-
-import io.fabric8.kubernetes.api.model.ConfigMap;
-import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
-import io.fabric8.kubernetes.api.model.ConfigMapVolumeSourceBuilder;
-import io.fabric8.kubernetes.api.model.Container;
-import io.fabric8.kubernetes.api.model.ContainerBuilder;
-import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
-import io.fabric8.kubernetes.api.model.IntOrString;
-import io.fabric8.kubernetes.api.model.IntOrStringBuilder;
-import io.fabric8.kubernetes.api.model.ObjectMeta;
-import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
-import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
-import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSourceBuilder;
-import io.fabric8.kubernetes.api.model.PodSpec;
-import io.fabric8.kubernetes.api.model.PodSpecBuilder;
-import io.fabric8.kubernetes.api.model.Quantity;
-import io.fabric8.kubernetes.api.model.Service;
-import io.fabric8.kubernetes.api.model.ServicePort;
-import io.fabric8.kubernetes.api.model.ServicePortBuilder;
-import io.fabric8.kubernetes.api.model.ServiceSpec;
-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.apps.Deployment;
-import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
-import io.fabric8.kubernetes.client.KubernetesClient;
-import io.fabric8.kubernetes.client.dsl.Resource;
-import io.fabric8.kubernetes.client.dsl.RollableScalableResource;
-import io.fabric8.kubernetes.client.dsl.ServiceResource;
-import java.util.List;
-import java.util.Map;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-import org.eclipse.che.api.core.ConflictException;
-import org.eclipse.che.api.core.ServerException;
-import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
-import org.eclipse.che.api.ssh.server.SshManager;
-import org.eclipse.che.api.ssh.server.model.impl.SshPairImpl;
-import org.eclipse.che.api.ssh.shared.model.SshPair;
-import org.eclipse.che.api.workspace.server.model.impl.WarningImpl;
-import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
-import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
-import org.eclipse.che.workspace.infrastructure.kubernetes.Names;
-import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
-import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesObjectUtil;
-import org.eclipse.che.workspace.infrastructure.kubernetes.server.ServerServiceBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Configure environment for Async Storage feature (details described in issue
- * https://github.com/eclipse/che/issues/15384) This environment will allow backup on workspace stop
- * event and restore on restart created earlier.
- * Will apply only in case workspace has attributes: asyncPersist: true - persistVolumes:
- * false. In case workspace has attributes: asyncPersist: true - persistVolumes: true will
- * throw exception. Feature enabled only for 'common' PVC strategy, in other cases will throw
- * exception. During provision will be created: - storage Pod - service for rsync connection
- * via SSH - configmap, with public part of SSH key - PVC for storing backups;
- */
-@Singleton
-public class AsyncStorageProvisioner {
-
- private static final int SERVICE_PORT = 2222;
- /**
- * The authorized_keys file in SSH specifies the SSH keys that can be used for logging into the
- * user account for which the file is configured.
- */
- private static final String AUTHORIZED_KEYS = "authorized_keys";
- /**
- * Name of the asynchronous storage Pod and Service. Rsync command will use this Service name for
- * communications: e.g.: rsync ${RSYNC_OPTIONS} --rsh="ssh ${SSH_OPTIONS}" async-storage:/{PATH}
- */
- static final String ASYNC_STORAGE = "async-storage";
- /** The name suffix for ConfigMap with SSH configuration */
- static final String ASYNC_STORAGE_CONFIG = "async-storage-config";
- /** The path of mount storage volume for file persist */
- private static final String ASYNC_STORAGE_DATA_PATH = "/" + ASYNC_STORAGE;
- /** The path to the authorized_keys */
- private static final String SSH_KEY_PATH = "/.ssh/" + AUTHORIZED_KEYS;
- /** The name of SSH key pair for rsync */
- static final String SSH_KEY_NAME = "rsync-via-ssh";
- /** The name of volume for mounting configuration map and authorized_keys */
- private static final String CONFIG_MAP_VOLUME_NAME = "async-storage-configvolume";
- /** */
- private static final String STORAGE_VOLUME = "async-storage-data";
-
- private static final Logger LOG = LoggerFactory.getLogger(AsyncStorageProvisioner.class);
-
- private final String sidecarImagePullPolicy;
- private final String pvcQuantity;
- private final String asyncStorageImage;
- private final String pvcAccessMode;
- private final String pvcStrategy;
- private final String pvcName;
- private final String pvcStorageClassName;
- private final SshManager sshManager;
- private final KubernetesClientFactory kubernetesClientFactory;
-
- @Inject
- public AsyncStorageProvisioner(
- @Named("che.workspace.sidecar.image_pull_policy") String sidecarImagePullPolicy,
- @Named("che.infra.kubernetes.async.storage.image") String asyncStorageImage,
- SshManager sshManager,
- KubernetesClientFactory kubernetesClientFactory) {
- this.sidecarImagePullPolicy = sidecarImagePullPolicy;
- this.pvcQuantity = "test";
- this.asyncStorageImage = asyncStorageImage;
- this.pvcAccessMode = "TEST";
- this.pvcStrategy = "TEST";
- this.pvcName = "TEST";
- this.pvcStorageClassName = "TEST";
- this.sshManager = sshManager;
- this.kubernetesClientFactory = kubernetesClientFactory;
- }
-
- public void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity)
- throws InfrastructureException {
- if (!parseBoolean(k8sEnv.getAttributes().get(ASYNC_PERSIST_ATTRIBUTE))) {
- return;
- }
-
- if (!COMMON_STRATEGY.equals(pvcStrategy)) {
- String message =
- format(
- "Workspace configuration not valid: Asynchronous storage available only for 'common' PVC strategy, but got %s",
- pvcStrategy);
- LOG.warn(message);
- k8sEnv.addWarning(new WarningImpl(4200, message));
- throw new InfrastructureException(message);
- }
-
- if (!isEphemeral(k8sEnv.getAttributes())) {
- String message =
- format(
- "Workspace configuration not valid: Asynchronous storage available only if '%s' attribute set to false",
- PERSIST_VOLUMES_ATTRIBUTE);
- LOG.warn(message);
- k8sEnv.addWarning(new WarningImpl(4200, message));
- throw new InfrastructureException(message);
- }
-
- String namespace = identity.getInfrastructureNamespace();
- String userId = identity.getOwnerId();
- KubernetesClient k8sClient = kubernetesClientFactory.create(identity.getWorkspaceId());
- String configMapName = namespace + ASYNC_STORAGE_CONFIG;
-
- createPvcIfNotExist(k8sClient, namespace, userId);
- createConfigMapIfNotExist(k8sClient, namespace, configMapName, userId, k8sEnv);
- createAsyncStoragePodIfNotExist(k8sClient, namespace, configMapName, userId);
- createStorageServiceIfNotExist(k8sClient, namespace, userId);
- }
-
- private void createPvcIfNotExist(KubernetesClient k8sClient, String namespace, String userId) {
- Resource claimResource =
- k8sClient.persistentVolumeClaims().inNamespace(namespace).withName(pvcName);
-
- if (claimResource.get() != null) {
- return; // pvc already exist
- }
- PersistentVolumeClaim pvc =
- KubernetesObjectUtil.newPVC(pvcName, pvcAccessMode, pvcQuantity, pvcStorageClassName);
- KubernetesObjectUtil.putLabel(pvc.getMetadata(), CHE_USER_ID_LABEL, userId);
- k8sClient.persistentVolumeClaims().inNamespace(namespace).create(pvc);
- }
-
- /** Get or create new pair of SSH keys, this is need for securing rsync connection */
- private List getOrCreateSshPairs(String userId, KubernetesEnvironment k8sEnv)
- throws InfrastructureException {
- List sshPairs;
- try {
- sshPairs = sshManager.getPairs(userId, "internal");
- } catch (ServerException e) {
- String message = format("Unable to get SSH Keys. Cause: %s", e.getMessage());
- LOG.warn(message);
- k8sEnv.addWarning(
- new WarningImpl(
- NOT_ABLE_TO_PROVISION_SSH_KEYS,
- format(NOT_ABLE_TO_PROVISION_SSH_KEYS_MESSAGE, message)));
- throw new InfrastructureException(e);
- }
- if (sshPairs.isEmpty()) {
- try {
- sshPairs = singletonList(sshManager.generatePair(userId, "internal", SSH_KEY_NAME));
- } catch (ServerException | ConflictException e) {
- String message =
- format(
- "Unable to generate the SSH key for async storage service. Cause: %S",
- e.getMessage());
- LOG.warn(message);
- k8sEnv.addWarning(
- new WarningImpl(
- NOT_ABLE_TO_PROVISION_SSH_KEYS,
- format(NOT_ABLE_TO_PROVISION_SSH_KEYS_MESSAGE, message)));
- throw new InfrastructureException(e);
- }
- }
- return sshPairs;
- }
-
- /** Create configmap with public part of SSH key */
- private void createConfigMapIfNotExist(
- KubernetesClient k8sClient,
- String namespace,
- String configMapName,
- String userId,
- KubernetesEnvironment k8sEnv)
- throws InfrastructureException {
- Resource mapResource =
- k8sClient.configMaps().inNamespace(namespace).withName(configMapName);
- if (mapResource.get() != null) { // map already exist
- return;
- }
-
- List sshPairs = getOrCreateSshPairs(userId, k8sEnv);
- if (sshPairs == null) {
- return;
- }
- SshPair sshPair = sshPairs.get(0);
- Map sshConfigData = of(AUTHORIZED_KEYS, sshPair.getPublicKey() + "\n");
- ConfigMap configMap =
- new ConfigMapBuilder()
- .withNewMetadata()
- .withName(configMapName)
- .withNamespace(namespace)
- .withLabels(of(CHE_USER_ID_LABEL, userId))
- .endMetadata()
- .withData(sshConfigData)
- .build();
- k8sClient.configMaps().inNamespace(namespace).create(configMap);
- }
-
- /**
- * Create storage Pod with container with mounted volume for storing project source backups, SSH
- * key and exposed port for rsync connection
- */
- private void createAsyncStoragePodIfNotExist(
- KubernetesClient k8sClient, String namespace, String configMap, String userId) {
-
- RollableScalableResource resource =
- k8sClient.apps().deployments().inNamespace(namespace).withName(ASYNC_STORAGE);
- if (resource.get() != null) {
- return; // deployment already exist
- }
-
- String containerName = Names.generateName(ASYNC_STORAGE);
-
- Volume storageVolume =
- new VolumeBuilder()
- .withName(STORAGE_VOLUME)
- .withPersistentVolumeClaim(
- new PersistentVolumeClaimVolumeSourceBuilder()
- .withClaimName(pvcName)
- .withReadOnly(false)
- .build())
- .build();
-
- Volume sshKeyVolume =
- new VolumeBuilder()
- .withName(CONFIG_MAP_VOLUME_NAME)
- .withConfigMap(
- new ConfigMapVolumeSourceBuilder()
- .withName(configMap)
- .withDefaultMode(0600)
- .build())
- .build();
-
- VolumeMount storageVolumeMount =
- new VolumeMountBuilder()
- .withMountPath(ASYNC_STORAGE_DATA_PATH)
- .withName(STORAGE_VOLUME)
- .withReadOnly(false)
- .build();
-
- VolumeMount sshVolumeMount =
- new VolumeMountBuilder()
- .withMountPath(SSH_KEY_PATH)
- .withSubPath(AUTHORIZED_KEYS)
- .withName(CONFIG_MAP_VOLUME_NAME)
- .withReadOnly(true)
- .build();
-
- Container container =
- new ContainerBuilder()
- .withName(containerName)
- .withImage(asyncStorageImage)
- .withImagePullPolicy(sidecarImagePullPolicy)
- .withNewResources()
- .addToLimits("memory", new Quantity("512Mi"))
- .addToRequests("memory", new Quantity("256Mi"))
- .endResources()
- .withPorts(
- new ContainerPortBuilder()
- .withContainerPort(SERVICE_PORT)
- .withProtocol("TCP")
- .build())
- .withVolumeMounts(storageVolumeMount, sshVolumeMount)
- .build();
-
- PodSpecBuilder podSpecBuilder = new PodSpecBuilder();
- PodSpec podSpec =
- podSpecBuilder.withContainers(container).withVolumes(storageVolume, sshKeyVolume).build();
-
- ObjectMetaBuilder metaBuilder = new ObjectMetaBuilder();
- ObjectMeta meta =
- metaBuilder
- .withLabels(
- of(
- "app",
- "che",
- CHE_USER_ID_LABEL,
- userId,
- CHE_DEPLOYMENT_NAME_LABEL,
- ASYNC_STORAGE))
- .withNamespace(namespace)
- .withName(ASYNC_STORAGE)
- .build();
-
- Deployment deployment =
- new DeploymentBuilder()
- .withMetadata(meta)
- .withNewSpec()
- .withNewSelector()
- .withMatchLabels(meta.getLabels())
- .endSelector()
- .withReplicas(1)
- .withNewTemplate()
- .withMetadata(meta)
- .withSpec(podSpec)
- .endTemplate()
- .endSpec()
- .build();
-
- k8sClient.apps().deployments().inNamespace(namespace).create(deployment);
- }
-
- /** Create service for serving rsync connection */
- private void createStorageServiceIfNotExist(
- KubernetesClient k8sClient, String namespace, String userId) {
- ServiceResource serviceResource =
- k8sClient.services().inNamespace(namespace).withName(ASYNC_STORAGE);
- if (serviceResource.get() != null) {
- return; // service already exist
- }
-
- ObjectMeta meta = new ObjectMeta();
- meta.setName(ASYNC_STORAGE);
- meta.setNamespace(namespace);
- meta.setLabels(of(CHE_USER_ID_LABEL, userId));
-
- IntOrString targetPort =
- new IntOrStringBuilder().withIntVal(SERVICE_PORT).withStrVal(valueOf(SERVICE_PORT)).build();
-
- ServicePort port =
- new ServicePortBuilder()
- .withName("rsync-port")
- .withProtocol("TCP")
- .withPort(SERVICE_PORT)
- .withTargetPort(targetPort)
- .build();
- ServiceSpec spec = new ServiceSpec();
- spec.setPorts(singletonList(port));
- spec.setSelector(of(CHE_DEPLOYMENT_NAME_LABEL, ASYNC_STORAGE));
-
- ServerServiceBuilder serviceBuilder = new ServerServiceBuilder();
- Service service =
- serviceBuilder
- .withPorts(singletonList(port))
- .withSelectorEntry(CHE_DEPLOYMENT_NAME_LABEL, ASYNC_STORAGE)
- .withName(ASYNC_STORAGE)
- .build();
-
- k8sClient.services().inNamespace(namespace).create(service);
- }
-}
diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/AsyncStorageModeValidatorTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/AsyncStorageModeValidatorTest.java
deleted file mode 100644
index 6d4e417bad..0000000000
--- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/AsyncStorageModeValidatorTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2012-2022 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes;
-
-import static com.google.common.collect.ImmutableMap.of;
-import static org.eclipse.che.api.workspace.shared.Constants.ASYNC_PERSIST_ATTRIBUTE;
-import static org.eclipse.che.api.workspace.shared.Constants.PERSIST_VOLUMES_ATTRIBUTE;
-
-import org.eclipse.che.api.core.ValidationException;
-import org.testng.annotations.Test;
-
-public class AsyncStorageModeValidatorTest {
-
- @Test
- public void shouldBeFineForEphemeralMode() throws ValidationException {
- AsyncStorageModeValidator validator = new AsyncStorageModeValidator("-che", 1);
-
- validator.validate(of(PERSIST_VOLUMES_ATTRIBUTE, "false"));
- }
-
- @Test
- public void shouldBeFineForPersistentMode() throws ValidationException {
- AsyncStorageModeValidator validator = new AsyncStorageModeValidator("-che", 1);
-
- validator.validate(of(PERSIST_VOLUMES_ATTRIBUTE, "true"));
- }
-
- @Test
- public void shouldBeFineForEmptyAttribute() throws ValidationException {
- AsyncStorageModeValidator validator = new AsyncStorageModeValidator("-che", 1);
-
- validator.validate(of());
- }
-
- @Test(
- expectedExceptions = ValidationException.class,
- expectedExceptionsMessageRegExp =
- "Workspace configuration not valid: Asynchronous storage available only for NOT persistent storage")
- public void shouldThrowExceptionIfAsyncAttributeForNotEphemeral() throws ValidationException {
- AsyncStorageModeValidator validator = new AsyncStorageModeValidator("-che", 1);
-
- validator.validate(of(ASYNC_PERSIST_ATTRIBUTE, "true", PERSIST_VOLUMES_ATTRIBUTE, "true"));
- }
-
- @Test
- public void shouldBeFineForEphemeralModeUpdate() throws ValidationException {
- AsyncStorageModeValidator validator = new AsyncStorageModeValidator("-che", 1);
-
- validator.validateUpdate(of(), of(PERSIST_VOLUMES_ATTRIBUTE, "false"));
- }
-
- @Test
- public void shouldBeFineForPersistentModeUpdate() throws ValidationException {
- AsyncStorageModeValidator validator = new AsyncStorageModeValidator("-che", 1);
-
- validator.validateUpdate(of(), of(PERSIST_VOLUMES_ATTRIBUTE, "true"));
- }
-
- @Test
- public void shouldBeFineForEmptyAttributeUpdate() throws ValidationException {
- AsyncStorageModeValidator validator = new AsyncStorageModeValidator("-che", 1);
-
- validator.validateUpdate(of(), of());
- }
-
- @Test(
- expectedExceptions = ValidationException.class,
- expectedExceptionsMessageRegExp =
- "Workspace configuration not valid: Asynchronous storage available only for NOT persistent storage")
- public void shouldThrowExceptionIfAsyncAttributeForNotEphemeralUpdate()
- throws ValidationException {
- AsyncStorageModeValidator validator = new AsyncStorageModeValidator("-che", 1);
-
- validator.validateUpdate(
- of(), of(ASYNC_PERSIST_ATTRIBUTE, "true", PERSIST_VOLUMES_ATTRIBUTE, "true"));
- }
-
- @Test(
- expectedExceptions = ValidationException.class,
- expectedExceptionsMessageRegExp =
- "Workspace configuration not valid: Asynchronous storage available only for NOT persistent storage")
- public void shouldThrowExceptionIfAsyncAttributeForNotEphemeralUpdate2()
- throws ValidationException {
- AsyncStorageModeValidator validator = new AsyncStorageModeValidator("-che", 1);
-
- validator.validateUpdate(
- of(PERSIST_VOLUMES_ATTRIBUTE, "true"), of(ASYNC_PERSIST_ATTRIBUTE, "true"));
- }
-}
diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisionerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisionerTest.java
index c87c8b99fc..1b0735ca63 100644
--- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisionerTest.java
+++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesEnvironmentProvisionerTest.java
@@ -18,8 +18,6 @@ import static org.mockito.Mockito.when;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesEnvironmentProvisioner.KubernetesEnvironmentProvisionerImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
-import org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStoragePodInterceptor;
-import org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStorageProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.CertificateProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GatewayRouterProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GitConfigProvisioner;
@@ -75,8 +73,6 @@ public class KubernetesEnvironmentProvisionerTest {
@Mock private ImagePullSecretProvisioner imagePullSecretProvisioner;
@Mock private ProxySettingsProvisioner proxySettingsProvisioner;
@Mock private ServiceAccountProvisioner serviceAccountProvisioner;
- @Mock private AsyncStorageProvisioner asyncStorageProvisioner;
- @Mock private AsyncStoragePodInterceptor asyncStoragePodObserver;
@Mock private CertificateProvisioner certificateProvisioner;
@Mock private SshKeysProvisioner sshKeysProvisioner;
@Mock private GitConfigProvisioner gitConfigProvisioner;
@@ -102,7 +98,6 @@ public class KubernetesEnvironmentProvisionerTest {
envVarsProvisioner,
restartPolicyRewriter,
ramLimitProvisioner,
- logsVolumeMachineProvisioner,
securityContextProvisioner,
podTerminationGracePeriodProvisioner,
externalServerIngressTlsProvisionerProvider,
@@ -110,8 +105,6 @@ public class KubernetesEnvironmentProvisionerTest {
proxySettingsProvisioner,
nodeSelectorProvisioner,
tolerationsProvisioner,
- asyncStorageProvisioner,
- asyncStoragePodObserver,
serviceAccountProvisioner,
certificateProvisioner,
sshKeysProvisioner,
@@ -122,7 +115,6 @@ public class KubernetesEnvironmentProvisionerTest {
trustedCAProvisioner);
provisionOrder =
inOrder(
- logsVolumeMachineProvisioner,
uniqueNamesProvisioner,
serversProvisioner,
envVarsProvisioner,
diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/EphemeralWorkspaceAdapterTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/EphemeralWorkspaceAdapterTest.java
deleted file mode 100644
index 7759187a58..0000000000
--- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/EphemeralWorkspaceAdapterTest.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (c) 2012-2021 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc;
-
-import static org.eclipse.che.api.workspace.shared.Constants.PERSIST_VOLUMES_ATTRIBUTE;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.lenient;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
-import com.google.common.collect.ImmutableMap;
-import io.fabric8.kubernetes.api.model.EmptyDirVolumeSource;
-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.VolumeBuilder;
-import java.util.Collections;
-import java.util.Map;
-import org.eclipse.che.api.core.model.workspace.Workspace;
-import org.eclipse.che.api.core.model.workspace.WorkspaceConfig;
-import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
-import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.testng.MockitoTestNGListener;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Listeners;
-import org.testng.annotations.Test;
-
-/**
- * Tests {@link EphemeralWorkspaceAdapter}.
- *
- * @author Ilya Buziuk
- * @author Angel Misevski
- */
-@Listeners(MockitoTestNGListener.class)
-public class EphemeralWorkspaceAdapterTest {
- private static final String EPHEMERAL_WORKSPACE_ID = "workspace123";
- private static final String NON_EPHEMERAL_WORKSPACE_ID = "workspace234";
- private static final String POD_NAME = "pod1";
-
- @Mock private Workspace nonEphemeralWorkspace;
- @Mock private Workspace ephemeralWorkspace;
-
- @Mock private PVCProvisioner pvcProvisioner;
- @Mock private SubPathPrefixes subPathPrefixes;
-
- private KubernetesEnvironment k8sEnv;
- @Mock private RuntimeIdentity identity;
-
- private InOrder provisionOrder;
- @Captor private ArgumentCaptor k8sEnvCaptor;
-
- private EphemeralWorkspaceAdapter ephemeralWorkspaceAdapter;
-
- @BeforeMethod
- public void setup() throws Exception {
- ephemeralWorkspaceAdapter = new EphemeralWorkspaceAdapter(pvcProvisioner, subPathPrefixes);
-
- // ephemeral workspace configuration
- lenient().when(ephemeralWorkspace.getId()).thenReturn(EPHEMERAL_WORKSPACE_ID);
- WorkspaceConfig ephemeralWorkspaceConfig = mock(WorkspaceConfig.class);
- lenient().when(ephemeralWorkspace.getConfig()).thenReturn(ephemeralWorkspaceConfig);
- Map ephemeralConfigAttributes =
- Collections.singletonMap(PERSIST_VOLUMES_ATTRIBUTE, "false");
- lenient().when(ephemeralWorkspaceConfig.getAttributes()).thenReturn(ephemeralConfigAttributes);
-
- // regular / non-ephemeral workspace configuration
- lenient().when(nonEphemeralWorkspace.getId()).thenReturn(NON_EPHEMERAL_WORKSPACE_ID);
- WorkspaceConfig nonEphemeralWorkspaceConfig = mock(WorkspaceConfig.class);
- lenient().when(nonEphemeralWorkspace.getConfig()).thenReturn(nonEphemeralWorkspaceConfig);
- Map nonEphemeralConfigAttributes = Collections.emptyMap();
- lenient().when(nonEphemeralWorkspace.getAttributes()).thenReturn(nonEphemeralConfigAttributes);
-
- k8sEnv = KubernetesEnvironment.builder().build();
- provisionOrder = inOrder(pvcProvisioner, subPathPrefixes);
- }
-
- @Test
- public void testIsEphemeralWorkspace() throws Exception {
- assertTrue(EphemeralWorkspaceUtility.isEphemeral(ephemeralWorkspace));
- assertFalse(EphemeralWorkspaceUtility.isEphemeral(nonEphemeralWorkspace));
- }
-
- @Test
- public void testProvisioningAllPVCsInWorkspace() throws Exception {
- // given
- PersistentVolumeClaim pvc1 = UniqueWorkspacePVCStrategyTest.newPVC("pvc1");
- PersistentVolumeClaim pvc2 = UniqueWorkspacePVCStrategyTest.newPVC("pvc2");
- k8sEnv.getPersistentVolumeClaims().put("pvc1", pvc1);
- k8sEnv.getPersistentVolumeClaims().put("pvc2", pvc2);
- when(identity.getWorkspaceId()).thenReturn(EPHEMERAL_WORKSPACE_ID);
-
- // when
- ephemeralWorkspaceAdapter.provision(k8sEnv, identity);
-
- // then
- provisionOrder
- .verify(pvcProvisioner)
- .provision(k8sEnv, ImmutableMap.of("pvc1", pvc1, "pvc2", pvc2));
- provisionOrder.verify(pvcProvisioner).convertCheVolumes(k8sEnv, EPHEMERAL_WORKSPACE_ID);
- provisionOrder
- .verify(subPathPrefixes)
- .prefixVolumeMountsSubpaths(k8sEnv, EPHEMERAL_WORKSPACE_ID);
- }
-
- @Test
- public void testConvertsAllPVCsToEmptyDir() throws Exception {
- // given
- k8sEnv.getPersistentVolumeClaims().put("pvc1", mock(PersistentVolumeClaim.class));
- k8sEnv.getPersistentVolumeClaims().put("pvc2", mock(PersistentVolumeClaim.class));
-
- io.fabric8.kubernetes.api.model.Volume configMapVolume =
- new VolumeBuilder().withNewConfigMap().withName("configMap").endConfigMap().build();
- io.fabric8.kubernetes.api.model.Volume emptyDirVolume =
- new VolumeBuilder().withNewEmptyDir().endEmptyDir().build();
- io.fabric8.kubernetes.api.model.Volume pvcVolume =
- new VolumeBuilder()
- .withNewPersistentVolumeClaim()
- .withClaimName("pvc1")
- .endPersistentVolumeClaim()
- .build();
- Pod pod =
- new PodBuilder()
- .withNewMetadata()
- .withName(POD_NAME)
- .endMetadata()
- .withNewSpec()
- .withVolumes(
- new VolumeBuilder(pvcVolume).build(),
- new VolumeBuilder(configMapVolume).build(),
- new VolumeBuilder(emptyDirVolume).build())
- .endSpec()
- .build();
-
- k8sEnv.addPod(pod);
-
- ephemeralWorkspaceAdapter.provision(k8sEnv, identity);
-
- assertTrue(k8sEnv.getPersistentVolumeClaims().isEmpty());
- assertNull(pod.getSpec().getVolumes().get(0).getPersistentVolumeClaim());
- assertEquals(pod.getSpec().getVolumes().get(0).getEmptyDir(), new EmptyDirVolumeSource());
- assertEquals(pod.getSpec().getVolumes().get(1), configMapVolume);
- assertEquals(pod.getSpec().getVolumes().get(2), emptyDirVolume);
- }
-}
diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/UniqueWorkspacePVCStrategyTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/UniqueWorkspacePVCStrategyTest.java
deleted file mode 100644
index b4f2ba4f4d..0000000000
--- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/pvc/UniqueWorkspacePVCStrategyTest.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (c) 2012-2022 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc;
-
-import static java.util.Collections.emptyMap;
-import static java.util.Collections.singletonList;
-import static java.util.Collections.singletonMap;
-import static org.eclipse.che.api.workspace.shared.Constants.PERSIST_VOLUMES_ATTRIBUTE;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_WORKSPACE_ID_LABEL;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.lenient;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-
-import com.google.common.collect.ImmutableMap;
-import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
-import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
-import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder;
-import java.util.HashMap;
-import java.util.Map;
-import org.eclipse.che.api.core.model.workspace.Workspace;
-import org.eclipse.che.api.core.model.workspace.WorkspaceConfig;
-import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
-import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
-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.namespace.KubernetesNamespace;
-import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
-import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesPersistentVolumeClaims;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.testng.MockitoTestNGListener;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Listeners;
-import org.testng.annotations.Test;
-
-/**
- * Tests {@link UniqueWorkspacePVCStrategy}.
- *
- * @author Anton Korneta
- * @author Sergii Leshchenko
- */
-@Listeners(MockitoTestNGListener.class)
-public class UniqueWorkspacePVCStrategyTest {
-
- private static final String WORKSPACE_ID = "workspace123";
- private static final String NAMESPACE = "infraNamespace";
- private static final String PVC_NAME_PREFIX = "che-claim";
-
- private static final RuntimeIdentity IDENTITY =
- new RuntimeIdentityImpl(WORKSPACE_ID, "env1", "id1", NAMESPACE);
-
- private KubernetesEnvironment k8sEnv;
-
- @Mock private KubernetesNamespaceFactory factory;
- @Mock private KubernetesNamespace k8sNamespace;
- @Mock private KubernetesPersistentVolumeClaims pvcs;
- @Mock private EphemeralWorkspaceAdapter ephemeralWorkspaceAdapter;
- @Mock private PVCProvisioner pvcProvisioner;
- @Mock private PodsVolumes podsVolumes;
- @Mock private SubPathPrefixes subpathPrefixes;
- @Captor private ArgumentCaptor k8sEnvCaptor;
-
- private InOrder provisionOrder;
-
- private UniqueWorkspacePVCStrategy strategy;
-
- @BeforeMethod
- public void setup() throws Exception {
- strategy =
- new UniqueWorkspacePVCStrategy(
- factory, ephemeralWorkspaceAdapter, pvcProvisioner, subpathPrefixes);
-
- k8sEnv = KubernetesEnvironment.builder().build();
-
- provisionOrder = inOrder(pvcProvisioner, subpathPrefixes, podsVolumes);
-
- lenient().when(factory.getOrCreate(eq(IDENTITY))).thenReturn(k8sNamespace);
- lenient().when(factory.get(any(Workspace.class))).thenReturn(k8sNamespace);
- lenient().when(k8sNamespace.persistentVolumeClaims()).thenReturn(pvcs);
- }
-
- @Test
- public void testProvisionVolumesIntoKubernetesEnvironment() throws Exception {
- // given
- PersistentVolumeClaim pvc1 = newPVC("pvc1");
- PersistentVolumeClaim pvc2 = newPVC("pvc2");
- k8sEnv.getPersistentVolumeClaims().put("pvc1", pvc1);
- k8sEnv.getPersistentVolumeClaims().put("pvc2", pvc2);
-
- PersistentVolumeClaim existingPVC = newPVC("existingPVC");
- when(pvcs.getByLabel(CHE_WORKSPACE_ID_LABEL, WORKSPACE_ID))
- .thenReturn(singletonList(existingPVC));
-
- // when
- strategy.provision(k8sEnv, IDENTITY);
-
- // then
- provisionOrder
- .verify(pvcProvisioner)
- .provision(k8sEnvCaptor.capture(), eq(ImmutableMap.of("pvc1", pvc1, "pvc2", pvc2)));
- provisionOrder.verify(pvcProvisioner).convertCheVolumes(k8sEnv, WORKSPACE_ID);
- provisionOrder.verify(subpathPrefixes).prefixVolumeMountsSubpaths(k8sEnv, WORKSPACE_ID);
-
- assertEquals(k8sEnv.getPersistentVolumeClaims().size(), 1);
- assertNotNull(k8sEnv.getPersistentVolumeClaims().get("existingPVC"));
- ;
- }
-
- @Test
- public void shouldProvisionWorkspaceIdLabelToPVCs() throws Exception {
- // given
- PersistentVolumeClaim existingPVC = newPVC("existingPVC");
- when(pvcs.getByLabel(CHE_WORKSPACE_ID_LABEL, WORKSPACE_ID))
- .thenReturn(singletonList(existingPVC));
-
- // when
- strategy.provision(k8sEnv, IDENTITY);
-
- // then
- assertEquals(k8sEnv.getPersistentVolumeClaims().size(), 1);
- PersistentVolumeClaim pvc = k8sEnv.getPersistentVolumeClaims().get("existingPVC");
- assertNotNull(pvc);
- assertEquals(pvc.getMetadata().getLabels().get(CHE_WORKSPACE_ID_LABEL), WORKSPACE_ID);
- }
-
- @Test
- public void testCreatesProvisionedPVCsOnPrepare() throws Exception {
- final String uniqueName = PVC_NAME_PREFIX + "-3121";
- final PersistentVolumeClaim pvc = newPVC(uniqueName);
- k8sEnv.getPersistentVolumeClaims().clear();
- k8sEnv.getPersistentVolumeClaims().putAll(singletonMap(uniqueName, pvc));
-
- strategy.prepare(k8sEnv, IDENTITY, 100, emptyMap());
-
- verify(pvcs).createIfNotExist(any());
- verify(pvcs).waitBound(uniqueName, 100);
- }
-
- @Test(expectedExceptions = InfrastructureException.class)
- public void throwsInfrastructureExceptionWhenFailedToCreatePVCs() throws Exception {
- final PersistentVolumeClaim pvc = mock(PersistentVolumeClaim.class);
- when(pvc.getMetadata()).thenReturn(new ObjectMetaBuilder().withName(PVC_NAME_PREFIX).build());
- k8sEnv.getPersistentVolumeClaims().clear();
- k8sEnv.getPersistentVolumeClaims().put(PVC_NAME_PREFIX, pvc);
- doThrow(InfrastructureException.class).when(pvcs).createIfNotExist(any());
-
- strategy.prepare(k8sEnv, IDENTITY, 100, emptyMap());
- }
-
- @Test
- public void shouldDeletePVCsIfThereIsNoPersistAttributeInWorkspaceConfigWhenCleanupCalled()
- throws Exception {
- // given
- Workspace workspace = mock(Workspace.class);
- lenient().when(workspace.getId()).thenReturn(WORKSPACE_ID);
-
- WorkspaceConfig workspaceConfig = mock(WorkspaceConfig.class);
- lenient().when(workspace.getConfig()).thenReturn(workspaceConfig);
-
- Map workspaceConfigAttributes = new HashMap<>();
- lenient().when(workspaceConfig.getAttributes()).thenReturn(workspaceConfigAttributes);
-
- // when
- strategy.cleanup(workspace);
-
- // then
- verify(pvcs).delete(ImmutableMap.of(CHE_WORKSPACE_ID_LABEL, WORKSPACE_ID));
- }
-
- @Test
- public void shouldDeletePVCsIfPersistAttributeIsSetToTrueInWorkspaceConfigWhenCleanupCalled()
- throws Exception {
- // given
- Workspace workspace = mock(Workspace.class);
- lenient().when(workspace.getId()).thenReturn(WORKSPACE_ID);
-
- WorkspaceConfig workspaceConfig = mock(WorkspaceConfig.class);
- lenient().when(workspace.getConfig()).thenReturn(workspaceConfig);
-
- Map workspaceConfigAttributes = new HashMap<>();
- lenient().when(workspaceConfig.getAttributes()).thenReturn(workspaceConfigAttributes);
- workspaceConfigAttributes.put(PERSIST_VOLUMES_ATTRIBUTE, "true");
-
- // when
- strategy.cleanup(workspace);
-
- // then
- verify(pvcs).delete(ImmutableMap.of(CHE_WORKSPACE_ID_LABEL, WORKSPACE_ID));
- }
-
- @Test
- public void shouldDoNothingIfPersistAttributeIsSetToFalseInWorkspaceConfigWhenCleanupCalled()
- throws Exception {
- // given
- Workspace workspace = mock(Workspace.class);
- lenient().when(workspace.getId()).thenReturn(WORKSPACE_ID);
-
- WorkspaceConfig workspaceConfig = mock(WorkspaceConfig.class);
- lenient().when(workspace.getConfig()).thenReturn(workspaceConfig);
-
- Map workspaceConfigAttributes = new HashMap<>();
- lenient().when(workspaceConfig.getAttributes()).thenReturn(workspaceConfigAttributes);
- workspaceConfigAttributes.put(PERSIST_VOLUMES_ATTRIBUTE, "false");
-
- // when
- strategy.cleanup(workspace);
-
- // then
- verify(pvcs, never()).delete(ImmutableMap.of(CHE_WORKSPACE_ID_LABEL, WORKSPACE_ID));
- }
-
- static PersistentVolumeClaim newPVC(String name) {
- return newPVC(name, new HashMap<>());
- }
-
- static PersistentVolumeClaim newPVC(String name, Map labels) {
- return new PersistentVolumeClaimBuilder()
- .withNewMetadata()
- .withName(name)
- .withLabels(labels)
- .endMetadata()
- .withNewSpec()
- .endSpec()
- .build();
- }
-}
diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodInterceptorTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodInterceptorTest.java
deleted file mode 100644
index e8b7a1b71f..0000000000
--- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodInterceptorTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2012-2022 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.provision;
-
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import io.fabric8.kubernetes.api.model.Pod;
-import io.fabric8.kubernetes.api.model.apps.Deployment;
-import io.fabric8.kubernetes.client.KubernetesClient;
-import io.fabric8.kubernetes.client.dsl.AppsAPIGroupDSL;
-import io.fabric8.kubernetes.client.dsl.EditReplacePatchDeletable;
-import io.fabric8.kubernetes.client.dsl.MixedOperation;
-import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
-import io.fabric8.kubernetes.client.dsl.PodResource;
-import io.fabric8.kubernetes.client.dsl.RollableScalableResource;
-import java.util.UUID;
-import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
-import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
-import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
-import org.mockito.Mock;
-import org.mockito.testng.MockitoTestNGListener;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Listeners;
-import org.testng.annotations.Test;
-
-@Listeners(MockitoTestNGListener.class)
-public class AsyncStoragePodInterceptorTest {
-
- private static final String WORKSPACE_ID = UUID.randomUUID().toString();
- private static final String NAMESPACE = UUID.randomUUID().toString();
-
- @Mock private KubernetesEnvironment kubernetesEnvironment;
- @Mock private RuntimeIdentity identity;
- @Mock private KubernetesClientFactory clientFactory;
- @Mock private KubernetesClient kubernetesClient;
- @Mock private RollableScalableResource deploymentResource;
- @Mock private PodResource podResource;
- @Mock private MixedOperation mixedOperation;
- @Mock private MixedOperation mixedOperationPod;
- @Mock private NonNamespaceOperation namespaceOperation;
- @Mock private NonNamespaceOperation namespacePodOperation;
- @Mock private EditReplacePatchDeletable deletable;
- @Mock private AppsAPIGroupDSL apps;
-
- private AsyncStoragePodInterceptor asyncStoragePodInterceptor;
-
- @BeforeMethod
- public void setUp() {
- asyncStoragePodInterceptor = new AsyncStoragePodInterceptor(clientFactory);
- }
-
- @Test
- public void shouldDoNothingIfNotCommonStrategy() throws Exception {
- AsyncStoragePodInterceptor asyncStoragePodInterceptor =
- new AsyncStoragePodInterceptor(clientFactory);
- asyncStoragePodInterceptor.intercept(kubernetesEnvironment, identity);
- verifyNoMoreInteractions(clientFactory);
- verifyNoMoreInteractions(identity);
- }
-}
diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodWatcherTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodWatcherTest.java
deleted file mode 100644
index f38c6fe877..0000000000
--- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/provision/AsyncStoragePodWatcherTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (c) 2012-2022 Red Hat, Inc.
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- */
-package org.eclipse.che.workspace.infrastructure.kubernetes.provision;
-
-import static java.time.Instant.now;
-import static java.util.UUID.randomUUID;
-import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStorageProvisioner.ASYNC_STORAGE;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.lenient;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import io.fabric8.kubernetes.api.model.Pod;
-import io.fabric8.kubernetes.api.model.apps.Deployment;
-import io.fabric8.kubernetes.client.KubernetesClient;
-import io.fabric8.kubernetes.client.dsl.AppsAPIGroupDSL;
-import io.fabric8.kubernetes.client.dsl.MixedOperation;
-import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
-import io.fabric8.kubernetes.client.dsl.PodResource;
-import io.fabric8.kubernetes.client.dsl.RollableScalableResource;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import org.eclipse.che.api.core.Page;
-import org.eclipse.che.api.user.server.PreferenceManager;
-import org.eclipse.che.api.user.server.UserManager;
-import org.eclipse.che.api.user.server.model.impl.UserImpl;
-import org.eclipse.che.api.workspace.server.WorkspaceRuntimes;
-import org.eclipse.che.api.workspace.shared.Constants;
-import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
-import org.mockito.Mock;
-import org.mockito.testng.MockitoTestNGListener;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Listeners;
-import org.testng.annotations.Test;
-
-@Listeners(value = {MockitoTestNGListener.class})
-public class AsyncStoragePodWatcherTest {
-
- private final String NAMESPACE = randomUUID().toString();
- private final String WORKSPACE_ID = randomUUID().toString();
- private final String USER_ID = randomUUID().toString();
-
- private Map userPref;
-
- @Mock private KubernetesClientFactory kubernetesClientFactory;
- @Mock private UserManager userManager;
- @Mock private PreferenceManager preferenceManager;
- @Mock private WorkspaceRuntimes runtimes;
- @Mock private KubernetesClient kubernetesClient;
- @Mock private RollableScalableResource deploymentResource;
- @Mock private MixedOperation mixedOperation;
- @Mock private NonNamespaceOperation namespaceOperation;
- @Mock private PodResource podResource;
- @Mock private MixedOperation mixedOperationPod;
- @Mock private NonNamespaceOperation namespacePodOperation;
- @Mock private UserImpl user;
- @Mock private AppsAPIGroupDSL apps;
-
- @BeforeMethod
- public void setUp() throws Exception {
- lenient().when(user.getId()).thenReturn(USER_ID);
- userPref = new HashMap<>(3);
- long epochSecond = now().getEpochSecond();
- long activityTime = epochSecond - 600; // stored time 10 minutes early
- userPref.put(Constants.LAST_ACTIVITY_TIME, Long.toString(activityTime));
- userPref.put(Constants.LAST_ACTIVE_INFRASTRUCTURE_NAMESPACE, NAMESPACE);
- lenient().when(preferenceManager.find(USER_ID)).thenReturn(userPref);
-
- Page userPage = new Page<>(Collections.singleton(user), 0, 1, 1);
- lenient().when(userManager.getAll(anyInt(), anyLong())).thenReturn(userPage);
-
- lenient().when(kubernetesClientFactory.create()).thenReturn(kubernetesClient);
- lenient().when(kubernetesClient.apps()).thenReturn(apps);
- lenient().when(apps.deployments()).thenReturn(mixedOperation);
- lenient().when(mixedOperation.inNamespace(NAMESPACE)).thenReturn(namespaceOperation);
- lenient().when(namespaceOperation.withName(ASYNC_STORAGE)).thenReturn(deploymentResource);
-
- lenient().when(kubernetesClient.pods()).thenReturn(mixedOperationPod);
- lenient().when(mixedOperationPod.inNamespace(NAMESPACE)).thenReturn(namespacePodOperation);
- lenient().when(namespacePodOperation.withName(ASYNC_STORAGE)).thenReturn(podResource);
- lenient().when(podResource.get()).thenReturn(null);
- }
-
- @Test
- public void shouldDoNothingIfNotCommonPvcStrategy() throws Exception {
- AsyncStoragePodWatcher watcher =
- new AsyncStoragePodWatcher(
- kubernetesClientFactory, userManager, preferenceManager, runtimes, 1, "", 1);
-
- watcher.check();
-
- verifyNoMoreInteractions(preferenceManager);
- verifyNoMoreInteractions(kubernetesClientFactory);
- verifyNoMoreInteractions(deploymentResource);
- }
-
- @Test
- public void shouldDoNothingIfAllowedUserDefinedNamespaces() throws Exception {
- AsyncStoragePodWatcher watcher =
- new AsyncStoragePodWatcher(
- kubernetesClientFactory, userManager, preferenceManager, runtimes, 1, "", 1);
-
- watcher.check();
-
- verifyNoMoreInteractions(preferenceManager);
- verifyNoMoreInteractions(kubernetesClientFactory);
- verifyNoMoreInteractions(deploymentResource);
- }
-
- @Test
- public void shouldDoNothingIfDefaultNamespaceNotCorrect() throws Exception {
- AsyncStoragePodWatcher watcher =
- new AsyncStoragePodWatcher(
- kubernetesClientFactory, userManager, preferenceManager, runtimes, 1, "", 1);
- watcher.check();
-
- verifyNoMoreInteractions(preferenceManager);
- verifyNoMoreInteractions(kubernetesClientFactory);
- verifyNoMoreInteractions(deploymentResource);
- }
-
- @Test
- public void shouldDoNothingIfAllowMoreThanOneRuntime() throws Exception {
- AsyncStoragePodWatcher watcher =
- new AsyncStoragePodWatcher(
- kubernetesClientFactory, userManager, preferenceManager, runtimes, 1, "", 2);
-
- watcher.check();
-
- verifyNoMoreInteractions(preferenceManager);
- verifyNoMoreInteractions(kubernetesClientFactory);
- verifyNoMoreInteractions(deploymentResource);
- }
-
- @Test
- public void shouldDoNothingIfShutdownTimeSetToZero() throws Exception {
- AsyncStoragePodWatcher watcher =
- new AsyncStoragePodWatcher(
- kubernetesClientFactory, userManager, preferenceManager, runtimes, 0, "", 1);
-
- watcher.check();
-
- verifyNoMoreInteractions(preferenceManager);
- verifyNoMoreInteractions(kubernetesClientFactory);
- verifyNoMoreInteractions(deploymentResource);
- }
-}
diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisioner.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisioner.java
index 43b1670bd7..125cac87db 100644
--- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisioner.java
+++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisioner.java
@@ -19,8 +19,6 @@ import org.eclipse.che.commons.annotation.Traced;
import org.eclipse.che.commons.tracing.TracingTags;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesEnvironmentProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.WorkspaceVolumesStrategy;
-import org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStoragePodInterceptor;
-import org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStorageProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.CertificateProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.DeploymentMetadataProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GatewayRouterProvisioner;
@@ -76,9 +74,7 @@ public class OpenShiftEnvironmentProvisioner
private final ProxySettingsProvisioner proxySettingsProvisioner;
private final NodeSelectorProvisioner nodeSelectorProvisioner;
private final TolerationsProvisioner tolerationsProvisioner;
- private final AsyncStoragePodInterceptor asyncStoragePodInterceptor;
private final ServiceAccountProvisioner serviceAccountProvisioner;
- private final AsyncStorageProvisioner asyncStorageProvisioner;
private final CertificateProvisioner certificateProvisioner;
private final SshKeysProvisioner sshKeysProvisioner;
private final GitConfigProvisioner gitConfigProvisioner;
@@ -103,8 +99,6 @@ public class OpenShiftEnvironmentProvisioner
ProxySettingsProvisioner proxySettingsProvisioner,
NodeSelectorProvisioner nodeSelectorProvisioner,
TolerationsProvisioner tolerationsProvisioner,
- AsyncStorageProvisioner asyncStorageProvisioner,
- AsyncStoragePodInterceptor asyncStoragePodInterceptor,
ServiceAccountProvisioner serviceAccountProvisioner,
CertificateProvisioner certificateProvisioner,
SshKeysProvisioner sshKeysProvisioner,
@@ -128,8 +122,6 @@ public class OpenShiftEnvironmentProvisioner
this.proxySettingsProvisioner = proxySettingsProvisioner;
this.nodeSelectorProvisioner = nodeSelectorProvisioner;
this.tolerationsProvisioner = tolerationsProvisioner;
- this.asyncStorageProvisioner = asyncStorageProvisioner;
- this.asyncStoragePodInterceptor = asyncStoragePodInterceptor;
this.serviceAccountProvisioner = serviceAccountProvisioner;
this.certificateProvisioner = certificateProvisioner;
this.sshKeysProvisioner = sshKeysProvisioner;
@@ -151,7 +143,7 @@ public class OpenShiftEnvironmentProvisioner
"Start provisioning OpenShift environment for workspace '{}'", identity.getWorkspaceId());
// 1 stage - update environment according Infrastructure specific
if (pvcEnabled) {
- asyncStoragePodInterceptor.intercept(osEnv, identity);
+ // TODO: Remove things related to pvcEnabled boolean
logsVolumeMachineProvisioner.provision(osEnv, identity);
}
@@ -173,7 +165,6 @@ public class OpenShiftEnvironmentProvisioner
imagePullSecretProvisioner.provision(osEnv, identity);
proxySettingsProvisioner.provision(osEnv, identity);
serviceAccountProvisioner.provision(osEnv, identity);
- asyncStorageProvisioner.provision(osEnv, identity);
certificateProvisioner.provision(osEnv, identity);
sshKeysProvisioner.provision(osEnv, identity);
vcsSslCertificateProvisioner.provision(osEnv, identity);
diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java
index 7916b93bb5..b2dc32eb61 100644
--- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java
+++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java
@@ -34,7 +34,6 @@ import org.eclipse.che.api.workspace.server.spi.provision.env.CheApiInternalEnvV
import org.eclipse.che.api.workspace.server.spi.provision.env.EnvVarProvider;
import org.eclipse.che.api.workspace.server.wsplugins.ChePluginsApplier;
import org.eclipse.che.api.workspace.shared.Constants;
-import org.eclipse.che.workspace.infrastructure.kubernetes.AsyncStorageModeValidator;
import org.eclipse.che.workspace.infrastructure.kubernetes.InconsistentRuntimesDetector;
import org.eclipse.che.workspace.infrastructure.kubernetes.K8sInfraNamespaceWsAttributeValidator;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
@@ -58,9 +57,6 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurato
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserPreferencesConfigurator;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserProfileConfigurator;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.WorkspacePVCCleaner;
-import org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStoragePodInterceptor;
-import org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStoragePodWatcher;
-import org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStorageProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GatewayTlsProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.KubernetesCheApiExternalEnvVarProvider;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.KubernetesCheApiInternalEnvVarProvider;
@@ -109,7 +105,6 @@ public class OpenShiftInfraModule extends AbstractModule {
Multibinder workspaceAttributeValidators =
Multibinder.newSetBinder(binder(), WorkspaceAttributeValidator.class);
workspaceAttributeValidators.addBinding().to(K8sInfraNamespaceWsAttributeValidator.class);
- workspaceAttributeValidators.addBinding().to(AsyncStorageModeValidator.class);
Multibinder namespaceConfigurators =
Multibinder.newSetBinder(binder(), NamespaceConfigurator.class);
@@ -251,8 +246,5 @@ public class OpenShiftInfraModule extends AbstractModule {
bind(ExternalServiceExposureStrategy.class).toProvider(ServiceExposureStrategyProvider.class);
bind(CookiePathStrategy.class).to(OpenShiftCookiePathStrategy.class);
bind(NonTlsDistributedClusterModeNotifier.class);
- bind(AsyncStorageProvisioner.class);
- bind(AsyncStoragePodInterceptor.class);
- bind(AsyncStoragePodWatcher.class);
}
}
diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisionerTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisionerTest.java
index 117edbbd69..5bd9244d8d 100644
--- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisionerTest.java
+++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftEnvironmentProvisionerTest.java
@@ -17,8 +17,6 @@ import static org.mockito.Mockito.when;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.WorkspaceVolumesStrategy;
-import org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStoragePodInterceptor;
-import org.eclipse.che.workspace.infrastructure.kubernetes.provision.AsyncStorageProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.CertificateProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.DeploymentMetadataProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.GatewayRouterProvisioner;
@@ -72,8 +70,6 @@ public class OpenShiftEnvironmentProvisionerTest {
@Mock private ImagePullSecretProvisioner imagePullSecretProvisioner;
@Mock private ProxySettingsProvisioner proxySettingsProvisioner;
@Mock private ServiceAccountProvisioner serviceAccountProvisioner;
- @Mock private AsyncStorageProvisioner asyncStorageProvisioner;
- @Mock private AsyncStoragePodInterceptor asyncStoragePodObserver;
@Mock private CertificateProvisioner certificateProvisioner;
@Mock private SshKeysProvisioner sshKeysProvisioner;
@Mock private GitConfigProvisioner gitConfigProvisioner;
@@ -107,8 +103,6 @@ public class OpenShiftEnvironmentProvisionerTest {
proxySettingsProvisioner,
nodeSelectorProvisioner,
tolerationsProvisioner,
- asyncStorageProvisioner,
- asyncStoragePodObserver,
serviceAccountProvisioner,
certificateProvisioner,
sshKeysProvisioner,