Skip to content

Commit 20cc36d

Browse files
committed
Add functionality for operator to take a volume snapshot of the pgdata volume after a every backup. Manage snapshots so that only one ReadyToUse snapshot is kept. Add/adjust tests for volume snapshots feature. Add volume snapshot crds directory to envtest environment setup.
1 parent a70b2b1 commit 20cc36d

23 files changed

+900
-9
lines changed

Makefile

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ PGMONITOR_DIR ?= hack/tools/pgmonitor
99
PGMONITOR_VERSION ?= v4.11.0
1010
QUERIES_CONFIG_DIR ?= hack/tools/queries
1111

12+
EXTERNAL_SNAPSHOTTER_DIR ?= hack/tools/external-snapshotter
13+
EXTERNAL_SNAPSHOTTER_VERSION ?= v8.0.1
14+
1215
# Buildah's "build" used to be "bud". Use the alias to be compatible for a while.
1316
BUILDAH_BUILD ?= buildah bud
1417

@@ -52,6 +55,12 @@ get-pgmonitor:
5255
cp -r '$(PGMONITOR_DIR)/postgres_exporter/common/.' '${QUERIES_CONFIG_DIR}'
5356
cp '$(PGMONITOR_DIR)/postgres_exporter/linux/queries_backrest.yml' '${QUERIES_CONFIG_DIR}'
5457

58+
.PHONY: get-external-snapshotter
59+
get-external-snapshotter:
60+
git -C '$(dir $(EXTERNAL_SNAPSHOTTER_DIR))' clone https://github.com/kubernetes-csi/external-snapshotter.git || git -C '$(EXTERNAL_SNAPSHOTTER_DIR)' fetch origin
61+
@git -C '$(EXTERNAL_SNAPSHOTTER_DIR)' checkout '$(EXTERNAL_SNAPSHOTTER_VERSION)'
62+
@git -C '$(EXTERNAL_SNAPSHOTTER_DIR)' config pull.ff only
63+
5564
.PHONY: clean
5665
clean: ## Clean resources
5766
clean: clean-deprecated
@@ -64,6 +73,7 @@ clean: clean-deprecated
6473
[ ! -f hack/tools/setup-envtest ] || rm hack/tools/setup-envtest
6574
[ ! -d hack/tools/envtest ] || { chmod -R u+w hack/tools/envtest && rm -r hack/tools/envtest; }
6675
[ ! -d hack/tools/pgmonitor ] || rm -rf hack/tools/pgmonitor
76+
[ ! -d hack/tools/external-snapshotter ] || rm -rf hack/tools/external-snapshotter
6777
[ ! -n "$$(ls hack/tools)" ] || rm -r hack/tools/*
6878
[ ! -d hack/.kube ] || rm -r hack/.kube
6979

@@ -113,7 +123,7 @@ undeploy: ## Undeploy the PostgreSQL Operator
113123

114124
.PHONY: deploy-dev
115125
deploy-dev: ## Deploy the PostgreSQL Operator locally
116-
deploy-dev: PGO_FEATURE_GATES ?= "TablespaceVolumes=true"
126+
deploy-dev: PGO_FEATURE_GATES ?= "TablespaceVolumes=true,VolumeSnapshots=true"
117127
deploy-dev: get-pgmonitor
118128
deploy-dev: build-postgres-operator
119129
deploy-dev: createnamespaces
@@ -190,7 +200,7 @@ check: get-pgmonitor
190200
check-envtest: ## Run check using envtest and a mock kube api
191201
check-envtest: ENVTEST_USE = $(ENVTEST) --bin-dir=$(CURDIR)/hack/tools/envtest use $(ENVTEST_K8S_VERSION)
192202
check-envtest: SHELL = bash
193-
check-envtest: get-pgmonitor tools/setup-envtest
203+
check-envtest: get-pgmonitor tools/setup-envtest get-external-snapshotter
194204
@$(ENVTEST_USE) --print=overview && echo
195205
source <($(ENVTEST_USE) --print=env) && PGO_NAMESPACE="postgres-operator" QUERIES_CONFIG_DIR="$(CURDIR)/${QUERIES_CONFIG_DIR}" \
196206
$(GO_TEST) -count=1 -cover ./...
@@ -201,7 +211,7 @@ check-envtest: get-pgmonitor tools/setup-envtest
201211
# make check-envtest-existing PGO_TEST_TIMEOUT_SCALE=1.2
202212
.PHONY: check-envtest-existing
203213
check-envtest-existing: ## Run check using envtest and an existing kube api
204-
check-envtest-existing: get-pgmonitor
214+
check-envtest-existing: get-pgmonitor get-external-snapshotter
205215
check-envtest-existing: createnamespaces
206216
kubectl apply --server-side -k ./config/dev
207217
USE_EXISTING_CLUSTER=true PGO_NAMESPACE="postgres-operator" QUERIES_CONFIG_DIR="$(CURDIR)/${QUERIES_CONFIG_DIR}" \

config/crd/bases/postgres-operator.crunchydata.com_postgresclusters.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4324,6 +4324,16 @@ spec:
43244324
required:
43254325
- repos
43264326
type: object
4327+
snapshots:
4328+
description: VolumeSnapshot configuration
4329+
properties:
4330+
volumeSnapshotClassName:
4331+
description: Name of the VolumeSnapshotClass that should be
4332+
used by VolumeSnapshots
4333+
type: string
4334+
required:
4335+
- volumeSnapshotClassName
4336+
type: object
43274337
required:
43284338
- pgbackrest
43294339
type: object

config/rbac/cluster/role.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,14 @@ rules:
171171
- list
172172
- patch
173173
- watch
174+
- apiGroups:
175+
- snapshot.storage.k8s.io
176+
resources:
177+
- volumesnapshots
178+
verbs:
179+
- create
180+
- delete
181+
- get
182+
- list
183+
- patch
184+
- watch

config/rbac/namespace/role.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,14 @@ rules:
171171
- list
172172
- patch
173173
- watch
174+
- apiGroups:
175+
- snapshot.storage.k8s.io
176+
resources:
177+
- volumesnapshots
178+
verbs:
179+
- create
180+
- delete
181+
- get
182+
- list
183+
- patch
184+
- watch

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/golang-jwt/jwt/v5 v5.2.1
1111
github.com/google/go-cmp v0.6.0
1212
github.com/google/uuid v1.6.0
13+
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.0.0
1314
github.com/onsi/ginkgo/v2 v2.17.2
1415
github.com/onsi/gomega v1.33.1
1516
github.com/pganalyze/pg_query_go/v5 v5.1.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
7676
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
7777
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
7878
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
79+
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.0.0 h1:mjQG0Vakr2h246kEDR85U8y8ZhPgT3bguTCajRa/jaw=
80+
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.0.0/go.mod h1:E3vdYxHj2C2q6qo8/Da4g7P+IcwqRZyy3gJBzYybV9Y=
7981
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
8082
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
8183
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=

internal/controller/postgrescluster/controller.go

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ import (
2929
policyv1 "k8s.io/api/policy/v1"
3030
rbacv1 "k8s.io/api/rbac/v1"
3131
"k8s.io/apimachinery/pkg/api/equality"
32+
apierrors "k8s.io/apimachinery/pkg/api/errors"
3233
"k8s.io/apimachinery/pkg/api/meta"
3334
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3435
"k8s.io/apimachinery/pkg/util/validation/field"
36+
"k8s.io/client-go/discovery"
3537
"k8s.io/client-go/tools/record"
3638
"sigs.k8s.io/controller-runtime/pkg/builder"
3739
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -60,10 +62,11 @@ const (
6062

6163
// Reconciler holds resources for the PostgresCluster reconciler
6264
type Reconciler struct {
63-
Client client.Client
64-
IsOpenShift bool
65-
Owner client.FieldOwner
66-
PodExec func(
65+
Client client.Client
66+
DiscoveryClient *discovery.DiscoveryClient
67+
IsOpenShift bool
68+
Owner client.FieldOwner
69+
PodExec func(
6770
ctx context.Context, namespace, pod, container string,
6871
stdin io.Reader, stdout, stderr io.Writer, command ...string,
6972
) error
@@ -345,6 +348,9 @@ func (r *Reconciler) Reconcile(
345348
}
346349
}
347350
}
351+
if err == nil {
352+
err = r.reconcileVolumeSnapshots(ctx, cluster, instances, clusterVolumes)
353+
}
348354
if err == nil {
349355
err = r.reconcilePGBouncer(ctx, cluster, instances, primaryCertificate, rootCA)
350356
}
@@ -447,6 +453,14 @@ func (r *Reconciler) SetupWithManager(mgr manager.Manager) error {
447453
}
448454
}
449455

456+
if r.DiscoveryClient == nil {
457+
var err error
458+
r.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(mgr.GetConfig())
459+
if err != nil {
460+
return err
461+
}
462+
}
463+
450464
return builder.ControllerManagedBy(mgr).
451465
For(&v1beta1.PostgresCluster{}).
452466
Owns(&corev1.ConfigMap{}).
@@ -467,3 +481,28 @@ func (r *Reconciler) SetupWithManager(mgr manager.Manager) error {
467481
r.controllerRefHandlerFuncs()). // watch all StatefulSets
468482
Complete(r)
469483
}
484+
485+
// GroupVersionKindExists checks to see whether a given Kind for a given
486+
// GroupVersion exists in the Kubernetes API Server.
487+
func (r *Reconciler) GroupVersionKindExists(groupVersion, kind string) (*bool, error) {
488+
if r.DiscoveryClient == nil {
489+
return initialize.Bool(false), nil
490+
}
491+
492+
resourceList, err := r.DiscoveryClient.ServerResourcesForGroupVersion(groupVersion)
493+
if err != nil {
494+
if apierrors.IsNotFound(err) {
495+
return initialize.Bool(false), nil
496+
}
497+
498+
return nil, err
499+
}
500+
501+
for _, resource := range resourceList.APIResources {
502+
if resource.Kind == kind {
503+
return initialize.Bool(true), nil
504+
}
505+
}
506+
507+
return initialize.Bool(false), nil
508+
}

0 commit comments

Comments
 (0)