Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

che #18369 Deleting the common PVC if there are no workspaces left for a given user #18713

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,26 @@ public void delete(Map<String, String> labels) throws InfrastructureException {
}
}

/**
* Removes PVC by name.
*
* @param name the name of the PVC
* @throws InfrastructureException when any error occurs while removing
*/
public void delete(final String name) throws InfrastructureException {
try {
clientFactory
.create(workspaceId)
.persistentVolumeClaims()
.inNamespace(namespace)
.withName(name)
.withPropagationPolicy("Background")
.delete();
} catch (KubernetesClientException e) {
throw new KubernetesInfrastructureException(e);
}
}

/**
* Waits until persistent volume claim state is bound. If used k8s Storage Class has
* 'volumeBindingMode: WaitForFirstConsumer', we don't wait to avoid deadlock.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,17 @@
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Named;
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.Nullable;
import org.eclipse.che.commons.annotation.Traced;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
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;
Expand Down Expand Up @@ -70,7 +77,6 @@
* @author Alexander Garagatyi
*/
public class CommonPVCStrategy implements WorkspaceVolumesStrategy {

// use non-static variable to reuse child class logger
private final Logger log = LoggerFactory.getLogger(getClass());

Expand All @@ -96,6 +102,8 @@ public class CommonPVCStrategy implements WorkspaceVolumesStrategy {
private final PodsVolumes podsVolumes;
private final SubPathPrefixes subpathPrefixes;
private final boolean waitBound;
private final String defaultNamespaceName;
private final WorkspaceManager workspaceManager;

@Inject
public CommonPVCStrategy(
Expand All @@ -105,12 +113,14 @@ public CommonPVCStrategy(
@Named("che.infra.kubernetes.pvc.precreate_subpaths") boolean preCreateDirs,
@Named("che.infra.kubernetes.pvc.storage_class_name") String pvcStorageClassName,
@Named("che.infra.kubernetes.pvc.wait_bound") boolean waitBound,
@Nullable @Named("che.infra.kubernetes.namespace.default") String defaultNamespaceName,
PVCSubPathHelper pvcSubPathHelper,
KubernetesNamespaceFactory factory,
EphemeralWorkspaceAdapter ephemeralWorkspaceAdapter,
PVCProvisioner pvcProvisioner,
PodsVolumes podsVolumes,
SubPathPrefixes subpathPrefixes) {
SubPathPrefixes subpathPrefixes,
WorkspaceManager workspaceManager) {
this.configuredPVCName = configuredPVCName;
this.pvcQuantity = pvcQuantity;
this.pvcAccessMode = pvcAccessMode;
Expand All @@ -123,6 +133,8 @@ public CommonPVCStrategy(
this.pvcProvisioner = pvcProvisioner;
this.podsVolumes = podsVolumes;
this.subpathPrefixes = subpathPrefixes;
this.defaultNamespaceName = defaultNamespaceName;
this.workspaceManager = workspaceManager;
}

/**
Expand Down Expand Up @@ -229,6 +241,10 @@ public void prepare(
public void cleanup(Workspace workspace) throws InfrastructureException {
if (EphemeralWorkspaceUtility.isEphemeral(workspace)) {
return;
} else if (noWorkspacesLeft()) {
log.debug("Deleting the common PVC: '{}',", configuredPVCName);
deleteCommonPVC(workspace);
return;
}
String workspaceId = workspace.getId();
PersistentVolumeClaim pvc = createCommonPVC(workspaceId);
Expand Down Expand Up @@ -258,4 +274,70 @@ private Set<String> combineVolumeMountsSubpaths(KubernetesEnvironment k8sEnv) {
.filter(subpath -> !isNullOrEmpty(subpath))
.collect(Collectors.toSet());
}

private void deleteCommonPVC(Workspace workspace) throws InfrastructureException {
factory.get(workspace).persistentVolumeClaims().delete(configuredPVCName);
}

/**
* @return true, if the common PVC is expected to be deleted, false otherwise. Depending on the
* configuration it could be either the common PVC of a particular user, or the common PVC
* that is shared across all the users (the last setup is considered to be uncommon and
* not-recommended)
*/
private boolean noWorkspacesLeft() {
return defaultNamespaceWithoutPlaceholderIsDefined()
? totalNumberOfWorkspacesIsZero()
: userHasNoWorkspaces();
}

/**
* The method is expected to be used in order to identify if the common PVC is used across all the
* users. The common PVC for all the users will be used if
* 'che.infra.kubernetes.namespace.default' points to a particular namespace e.g. 'che',
* 'workspaces' etc.
*
* @return true, if 'che.infra.kubernetes.namespace.default' is defined and does NOT contain the
* placeholder e.g.: che-workspace-<username>), false otherwise
*/
private boolean defaultNamespaceWithoutPlaceholderIsDefined() {
return defaultNamespaceName != null
&& !defaultNamespaceName.contains("<")
&& !defaultNamespaceName.contains(">");
}

/** @return true, if there are no workspaces left across all the users, false otherwise */
private boolean totalNumberOfWorkspacesIsZero() {
try {
return workspaceManager.getWorkspacesTotalCount() == 0;
} catch (ServerException e) {
log.error("Unable to get the total number of workspaces. Cause: {}", e.getMessage(), e);
}
return false;
}

/**
* @return true, if a given user has no workspaces, false otherwise (or if the subject is
* anonymous)
*/
private boolean userHasNoWorkspaces() {
Subject subject = EnvironmentContext.getCurrent().getSubject();
if (!subject.isAnonymous()) {
String userId = subject.getUserId();
try {
Page<WorkspaceImpl> workspaces = workspaceManager.getWorkspaces(userId, false, 1, 0);
if (workspaces.isEmpty()) {
log.debug("User '{}' has no more workspaces left", userId);
return true;
}
} catch (ServerException e) {
log.error(
"Unable to get the number of workspaces for user '{}'. Cause: {}",
userId,
e.getMessage(),
e);
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import javax.inject.Inject;
import javax.inject.Named;
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.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;

/**
Expand Down Expand Up @@ -57,25 +59,29 @@ public PerWorkspacePVCStrategy(
@Named("che.infra.kubernetes.pvc.precreate_subpaths") boolean preCreateDirs,
@Named("che.infra.kubernetes.pvc.storage_class_name") String pvcStorageClassName,
@Named("che.infra.kubernetes.pvc.wait_bound") boolean waitBound,
@Nullable @Named("che.infra.kubernetes.namespace.default") String defaultNamespaceName,
PVCSubPathHelper pvcSubPathHelper,
KubernetesNamespaceFactory factory,
EphemeralWorkspaceAdapter ephemeralWorkspaceAdapter,
PVCProvisioner pvcProvisioner,
PodsVolumes podsVolumes,
SubPathPrefixes subpathPrefixes) {
SubPathPrefixes subpathPrefixes,
WorkspaceManager workspaceManager) {
super(
pvcName,
pvcQuantity,
pvcAccessMode,
preCreateDirs,
pvcStorageClassName,
waitBound,
defaultNamespaceName,
pvcSubPathHelper,
factory,
ephemeralWorkspaceAdapter,
pvcProvisioner,
podsVolumes,
subpathPrefixes);
subpathPrefixes,
workspaceManager);
this.pvcNamePrefix = pvcName;
this.factory = factory;
this.pvcAccessMode = pvcAccessMode;
Expand Down
Loading