Skip to content

Commit 9c5c500

Browse files
committed
fix: PersistentVolumeClaim are not cleaned up after cluster deletion
1 parent 6990ea2 commit 9c5c500

7 files changed

Lines changed: 82 additions & 11 deletions

File tree

stackgres-k8s/e2e/utils/kubernetes

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
export E2E_ENV="${E2E_ENV:-kind}"
44
export KUBECONFIG="${KUBECONFIG:-$HOME/.kube/config}"
5-
export DEFAULT_K8S_VERSION="1.24"
5+
export DEFAULT_K8S_VERSION="1.34"
66
export K8S_VERSION="${K8S_VERSION:-$DEFAULT_K8S_VERSION}"
77
export KUBERNETES_VERSION_NUMBER
88
# When DEBUG is set kubectl output debug messages

stackgres-k8s/src/operator/src/main/java/io/stackgres/operator/conciliation/IgnorePodReconciliationHandler.java renamed to stackgres-k8s/src/operator/src/main/java/io/stackgres/operator/conciliation/IgnoreReconciliationHandler.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@
1010
import org.slf4j.Logger;
1111
import org.slf4j.LoggerFactory;
1212

13-
public abstract class IgnorePodReconciliationHandler<T extends CustomResource<?, ?>>
13+
public abstract class IgnoreReconciliationHandler<T extends CustomResource<?, ?>>
1414
implements ReconciliationHandler<T> {
1515

1616
protected static final Logger LOGGER =
17-
LoggerFactory.getLogger(IgnorePodReconciliationHandler.class);
17+
LoggerFactory.getLogger(IgnoreReconciliationHandler.class);
1818

1919
@Override
2020
public HasMetadata create(T context, HasMetadata resource) {
21-
LOGGER.debug("Skipping creating Pod {}.{}",
21+
LOGGER.debug("Skipping creating {} {}.{}",
22+
resource.getKind(),
2223
resource.getMetadata().getNamespace(),
2324
resource.getMetadata().getName());
2425
return resource;
@@ -27,30 +28,34 @@ public HasMetadata create(T context, HasMetadata resource) {
2728
@Override
2829
public HasMetadata patch(T context, HasMetadata newResource,
2930
HasMetadata oldResource) {
30-
LOGGER.debug("Skipping patching Pod {}.{}",
31+
LOGGER.debug("Skipping patching {} {}.{}",
32+
oldResource.getKind(),
3133
oldResource.getMetadata().getNamespace(),
3234
oldResource.getMetadata().getName());
3335
return oldResource;
3436
}
3537

3638
@Override
3739
public HasMetadata replace(T context, HasMetadata resource) {
38-
LOGGER.warn("Skipping replacing Pod {}.{}",
40+
LOGGER.warn("Skipping replacing {} {}.{}",
41+
resource.getKind(),
3942
resource.getMetadata().getNamespace(),
4043
resource.getMetadata().getName());
4144
return resource;
4245
}
4346

4447
@Override
4548
public void delete(T context, HasMetadata resource) {
46-
LOGGER.debug("Skipping deleting Pod {}.{}",
49+
LOGGER.debug("Skipping deleting {} {}.{}",
50+
resource.getKind(),
4751
resource.getMetadata().getNamespace(),
4852
resource.getMetadata().getName());
4953
}
5054

5155
@Override
5256
public void deleteWithOrphans(T context, HasMetadata resource) {
53-
LOGGER.debug("Skipping deleting Pod {}.{}",
57+
LOGGER.debug("Skipping deleting {} {}.{}",
58+
resource.getKind(),
5459
resource.getMetadata().getNamespace(),
5560
resource.getMetadata().getName());
5661
}

stackgres-k8s/src/operator/src/main/java/io/stackgres/operator/conciliation/cluster/ClusterConciliator.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
import io.fabric8.kubernetes.api.model.HasMetadata;
1414
import io.fabric8.kubernetes.api.model.ObjectMeta;
15+
import io.fabric8.kubernetes.api.model.OwnerReference;
16+
import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
1517
import io.fabric8.kubernetes.api.model.Pod;
1618
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
1719
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -35,6 +37,7 @@
3537
import io.stackgres.operator.conciliation.DeployedResource;
3638
import io.stackgres.operator.conciliation.DeployedResourcesCache;
3739
import io.stackgres.operator.conciliation.RequiredResourceGenerator;
40+
import io.stackgres.operatorframework.resource.ResourceUtil;
3841
import jakarta.enterprise.context.ApplicationScoped;
3942
import jakarta.inject.Inject;
4043

@@ -111,6 +114,9 @@ protected boolean forceChange(HasMetadata requiredResource, StackGresCluster con
111114
config.getMetadata().getNamespace(),
112115
config.getMetadata().getName());
113116
}
117+
if (noPrimaryPod) {
118+
return true;
119+
}
114120
final boolean anyPodWithWrongOrMissingRole;
115121
if (!isPatroniOnKubernetes) {
116122
anyPodWithWrongOrMissingRole = deployedResourcesCache
@@ -126,6 +132,9 @@ protected boolean forceChange(HasMetadata requiredResource, StackGresCluster con
126132
config.getMetadata().getNamespace(),
127133
config.getMetadata().getName());
128134
}
135+
if (anyPodWithWrongOrMissingRole) {
136+
return true;
137+
}
129138
final boolean anyPodCanRestart;
130139
if (ClusterRolloutUtil.isRolloutAllowed(config)) {
131140
anyPodCanRestart = Optional.of(config)
@@ -142,6 +151,9 @@ protected boolean forceChange(HasMetadata requiredResource, StackGresCluster con
142151
config.getMetadata().getNamespace(),
143152
config.getMetadata().getName());
144153
}
154+
if (anyPodCanRestart) {
155+
return true;
156+
}
145157
final boolean podsCountMismatch = config.getSpec().getInstances()
146158
!= deployedResourcesCache
147159
.stream()
@@ -154,7 +166,24 @@ protected boolean forceChange(HasMetadata requiredResource, StackGresCluster con
154166
config.getMetadata().getNamespace(),
155167
config.getMetadata().getName());
156168
}
157-
return noPrimaryPod || anyPodWithWrongOrMissingRole || anyPodCanRestart || podsCountMismatch;
169+
if (podsCountMismatch) {
170+
return true;
171+
}
172+
final OwnerReference clusterOwnerReference = ResourceUtil.getOwnerReference(config);
173+
final boolean anyPodOrPvcWithMissingOwner = deployedResourcesCache
174+
.stream()
175+
.map(DeployedResource::foundDeployed)
176+
.anyMatch(foundDeployedResource -> isPodOrPvcWithMissingOwner(
177+
foundDeployedResource, clusterOwnerReference));
178+
if (anyPodOrPvcWithMissingOwner && LOGGER.isDebugEnabled()) {
179+
LOGGER.debug("Will force StatefulSet reconciliation since a pod or pvc is"
180+
+ " missing owner reference for SGCluster {}.{}",
181+
config.getMetadata().getNamespace(),
182+
config.getMetadata().getName());
183+
}
184+
if (anyPodOrPvcWithMissingOwner) {
185+
return true;
186+
}
158187
}
159188
return false;
160189
}
@@ -191,4 +220,18 @@ private boolean isPodWithWrongOrMissingRole(
191220
.isPresent();
192221
}
193222

223+
private boolean isPodOrPvcWithMissingOwner(
224+
HasMetadata foundDeployedResource,
225+
OwnerReference clusterOwnerReference) {
226+
return (foundDeployedResource instanceof Pod
227+
|| foundDeployedResource instanceof PersistentVolumeClaim)
228+
&& !Optional.of(foundDeployedResource.getMetadata())
229+
.map(ObjectMeta::getOwnerReferences)
230+
.stream()
231+
.flatMap(List::stream)
232+
.anyMatch(ownerReference -> Objects.equals(
233+
clusterOwnerReference,
234+
ownerReference));
235+
}
236+
194237
}

stackgres-k8s/src/operator/src/main/java/io/stackgres/operator/conciliation/cluster/ClusterDeployedResourceScanner.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.fabric8.kubernetes.api.model.Endpoints;
1515
import io.fabric8.kubernetes.api.model.HasMetadata;
1616
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
17+
import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
1718
import io.fabric8.kubernetes.api.model.Pod;
1819
import io.fabric8.kubernetes.api.model.Secret;
1920
import io.fabric8.kubernetes.api.model.Service;
@@ -144,6 +145,7 @@ protected KubernetesClient getClient() {
144145
Map.entry(Endpoints.class, KubernetesClient::endpoints),
145146
Map.entry(Service.class, KubernetesClient::services),
146147
Map.entry(Pod.class, client -> client.pods()),
148+
Map.entry(PersistentVolumeClaim.class, client -> client.persistentVolumeClaims()),
147149
Map.entry(Job.class, client -> client.batch().v1().jobs()),
148150
Map.entry(CronJob.class, client -> client.batch().v1().cronjobs()),
149151
Map.entry(StatefulSet.class, client -> client.apps().statefulSets()),

stackgres-k8s/src/operator/src/main/java/io/stackgres/operator/conciliation/cluster/ClusterPodReconciliationHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
package io.stackgres.operator.conciliation.cluster;
77

88
import io.stackgres.common.crd.sgcluster.StackGresCluster;
9-
import io.stackgres.operator.conciliation.IgnorePodReconciliationHandler;
9+
import io.stackgres.operator.conciliation.IgnoreReconciliationHandler;
1010
import io.stackgres.operator.conciliation.ReconciliationScope;
1111
import jakarta.enterprise.context.ApplicationScoped;
1212

1313
@ReconciliationScope(value = StackGresCluster.class, kind = "Pod")
1414
@ApplicationScoped
1515
public class ClusterPodReconciliationHandler
16-
extends IgnorePodReconciliationHandler<StackGresCluster> {
16+
extends IgnoreReconciliationHandler<StackGresCluster> {
1717

1818
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright (C) 2019 OnGres, Inc.
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
package io.stackgres.operator.conciliation.cluster;
7+
8+
import io.stackgres.common.crd.sgcluster.StackGresCluster;
9+
import io.stackgres.operator.conciliation.IgnoreReconciliationHandler;
10+
import io.stackgres.operator.conciliation.ReconciliationScope;
11+
import jakarta.enterprise.context.ApplicationScoped;
12+
13+
@ReconciliationScope(value = StackGresCluster.class, kind = "PersistentVolumeClaim")
14+
@ApplicationScoped
15+
public class ClusterPvcReconciliationHandler
16+
extends IgnoreReconciliationHandler<StackGresCluster> {
17+
}

stackgres-k8s/src/operator/src/main/java/io/stackgres/operator/conciliation/factory/cluster/ClusterStatefulSet.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ public Stream<HasMetadata> generateResource(StackGresClusterContext context) {
178178
.build())
179179
.withServiceName(name)
180180
.withTemplate(podTemplateSpec.getSpec())
181+
.withNewPersistentVolumeClaimRetentionPolicy()
182+
.withWhenDeleted("Delete")
183+
.withWhenScaled("Retain")
184+
.endPersistentVolumeClaimRetentionPolicy()
181185
.withVolumeClaimTemplates(
182186
new PersistentVolumeClaimBuilder()
183187
.withNewMetadata()

0 commit comments

Comments
 (0)