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

Associate VolumeSnapshots with SnapshotSchedules via OwnerReferences #697

Merged
merged 4 commits into from
Jan 28, 2025
Merged
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
7 changes: 5 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func main() {
var probeAddr string
var secureMetrics bool
var enableHTTP2 bool
var enableOwnerReferences bool
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
Expand All @@ -75,6 +76,7 @@ func main() {
"If set the metrics endpoint is served securely")
flag.BoolVar(&enableHTTP2, "enable-http2", false,
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
flag.BoolVar(&enableOwnerReferences, "enable-owner-references", false, "Enable owner references for VolumeSnapshots.")
opts := zap.Options{
Development: true,
TimeEncoder: zapcore.RFC3339NanoTimeEncoder,
Expand Down Expand Up @@ -127,8 +129,9 @@ func main() {
}

if err = (&controller.SnapshotScheduleReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
EnableOwnerReferences: enableOwnerReferences,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "SnapshotSchedule")
os.Exit(1)
Expand Down
3 changes: 3 additions & 0 deletions helm/snapscheduler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ case, the defaults, shown below, should be sufficient.
- `manageCRDs`: `true`
- Whether the chart should automatically install, upgrade, or remove the
SnapshotSchedule CRD
- `enableOwnerReferences`: `false`
- If set to `true`, owner references will be added to the VolumeSnapshot
objects created by the operator.
- `rbacProxy.image.repository`: `quay.io/brancz/kube-rbac-proxy`
- Specifies the container image used for the RBAC proxy
- `rbacProxy.image.tag`: (see values file for default tag)
Expand Down
3 changes: 3 additions & 0 deletions helm/snapscheduler/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ spec:
- --health-probe-bind-address=:8081
- --metrics-bind-address=127.0.0.1:8080
- --leader-elect
{{- if .Values.enableOwnerReferences }}
- --enable-owner-references
{{- end }}
Comment on lines +56 to +58
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve currently set this up to pass values via command arguments, but I’m thinking of refactoring this to use a ConfigMap instead.
The main reason is that ConfigMaps would allow more flexibility in managing configurations, especially when changes are needed dynamically.

I’d appreciate your feedback on whether this change would align better with best practices or if the current implementation is sufficient.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to keep it as-is. Since we don't have a CM currently, I'd rather not add one.

command:
- /manager
image: {{ include "snapscheduler.image" . }}
Expand Down
2 changes: 2 additions & 0 deletions helm/snapscheduler/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""

enableOwnerReferences: false

rbacProxy:
image:
repository: quay.io/brancz/kube-rbac-proxy
Expand Down
34 changes: 24 additions & 10 deletions internal/controller/snapshotschedule_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
// SnapshotScheduleReconciler reconciles a SnapshotSchedule object
type SnapshotScheduleReconciler struct {
client.Client
Scheme *runtime.Scheme
Scheme *runtime.Scheme
EnableOwnerReferences bool
}

//nolint:lll
Expand All @@ -82,7 +83,7 @@
return ctrl.Result{}, err
}

result, err := doReconcile(ctx, instance, reqLogger, r.Client)
result, err := doReconcile(ctx, instance, reqLogger, r.Client, r.EnableOwnerReferences)

Check warning on line 86 in internal/controller/snapshotschedule_controller.go

View check run for this annotation

Codecov / codecov/patch

internal/controller/snapshotschedule_controller.go#L86

Added line #L86 was not covered by tests

// Update result in CR
if err != nil {
Expand Down Expand Up @@ -117,7 +118,7 @@
}

func doReconcile(ctx context.Context, schedule *snapschedulerv1.SnapshotSchedule,
logger logr.Logger, c client.Client) (ctrl.Result, error) {
logger logr.Logger, c client.Client, enableOwnerReferences bool) (ctrl.Result, error) {

Check warning on line 121 in internal/controller/snapshotschedule_controller.go

View check run for this annotation

Codecov / codecov/patch

internal/controller/snapshotschedule_controller.go#L121

Added line #L121 was not covered by tests
// If necessary, initialize time of next snap based on schedule
if schedule.Status.NextSnapshotTime.IsZero() {
// Update nextSnapshot time based on current time and cronspec
Expand All @@ -135,7 +136,7 @@
// modifying .status will immediately cause an addl reconcile pass
// (which will cover the rest of this reconcile function). We also don't
// want to update nextSnapshot until this round is done.
return handleSnapshotting(ctx, schedule, logger, c)
return handleSnapshotting(ctx, schedule, logger, c, enableOwnerReferences)

Check warning on line 139 in internal/controller/snapshotschedule_controller.go

View check run for this annotation

Codecov / codecov/patch

internal/controller/snapshotschedule_controller.go#L139

Added line #L139 was not covered by tests
}

// We always update nextSnapshot in case the schedule changed
Expand Down Expand Up @@ -164,7 +165,7 @@
}

func handleSnapshotting(ctx context.Context, schedule *snapschedulerv1.SnapshotSchedule,
logger logr.Logger, c client.Client) (ctrl.Result, error) {
logger logr.Logger, c client.Client, enableOwnerReferences bool) (ctrl.Result, error) {

Check warning on line 168 in internal/controller/snapshotschedule_controller.go

View check run for this annotation

Codecov / codecov/patch

internal/controller/snapshotschedule_controller.go#L168

Added line #L168 was not covered by tests
pvcList, err := listPVCsMatchingSelector(ctx, logger, c, schedule.Namespace, &schedule.Spec.ClaimSelector)
if err != nil {
logger.Error(err, "unable to get matching PVCs")
Expand All @@ -187,7 +188,7 @@
labels = schedule.Spec.SnapshotTemplate.Labels
snapshotClassName = schedule.Spec.SnapshotTemplate.SnapshotClassName
}
snap := newSnapForClaim(snapName, pvc, schedule.Name, snapTime, labels, snapshotClassName)
snap := newSnapForClaim(snapName, pvc, schedule, snapTime, labels, snapshotClassName, enableOwnerReferences)

Check warning on line 191 in internal/controller/snapshotschedule_controller.go

View check run for this annotation

Codecov / codecov/patch

internal/controller/snapshotschedule_controller.go#L191

Added line #L191 was not covered by tests
if snap != nil {
logger.Info("creating a snapshot", "PVC", pvc.Name, "Snapshot", snapName)
if err = c.Create(ctx, snap); err != nil {
Expand Down Expand Up @@ -287,8 +288,8 @@
}

func newSnapForClaim(snapName string, pvc corev1.PersistentVolumeClaim,
scheduleName string, scheduleTime time.Time,
labels map[string]string, snapClass *string) *snapv1.VolumeSnapshot {
schedule *snapschedulerv1.SnapshotSchedule, scheduleTime time.Time,
labels map[string]string, snapClass *string, enableOwnerReferences bool) *snapv1.VolumeSnapshot {
numLabels := 2
if labels != nil {
numLabels += len(labels)
Expand All @@ -297,9 +298,9 @@
for k, v := range labels {
snapLabels[k] = v
}
snapLabels[ScheduleKey] = scheduleName
snapLabels[ScheduleKey] = schedule.Name
snapLabels[WhenKey] = scheduleTime.Format(timeYYYYMMDDHHMMSS)
return &snapv1.VolumeSnapshot{
snapshot := &snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: snapName,
Namespace: pvc.Namespace,
Expand All @@ -312,4 +313,17 @@
VolumeSnapshotClassName: snapClass,
},
}

if enableOwnerReferences {
snapshot.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: schedule.APIVersion,
Kind: schedule.Kind,
Name: schedule.Name,
UID: schedule.UID,
},
}
}

return snapshot
}
24 changes: 21 additions & 3 deletions internal/controller/snapshotschedule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,39 +63,57 @@ var _ = Describe("newSnapForClaim", func() {
Namespace: "mynamespace",
},
}
schedule := snapschedulerv1.SnapshotSchedule{}
schedule.Name = "mysched"
schedule.UID = "bb327b3e-2e87-4e0a-9b42-12d61c08bf97"
schedule.Kind = "SnapshotSchedule"
schedule.APIVersion = "snapscheduler.backube.io/v1"
snapname := "mysnap"
snapClass := "snapclass"
scheduleName := "mysched"
schedTime, _ := time.Parse(timeFormat, "2010-07-23T01:02:00Z")
snap := newSnapForClaim(snapname, pvc, scheduleName, schedTime, nil, &snapClass)
snap := newSnapForClaim(snapname, pvc, &schedule, schedTime, nil, &snapClass, false)

Expect(snap.Name).To(Equal(snapname))
Expect(snap.Namespace).To(Equal(pvc.Namespace))
Expect(*snap.Spec.Source.PersistentVolumeClaimName).To(Equal(pvc.Name))
Expect(snap.Spec.VolumeSnapshotClassName).NotTo(BeNil())
Expect(*snap.Spec.VolumeSnapshotClassName).To(Equal(snapClass))
Expect(snap.ObjectMeta.OwnerReferences).To(BeEmpty())
Expect(snap.Labels).To(HaveKeyWithValue(ScheduleKey, scheduleName))
})
It("allows providing addl labels", func() {
It("allows providing addl labels and checks enableOwnerReferences", func() {
pvc := corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "mypvc",
Namespace: "mynamespace",
},
}
schedule := snapschedulerv1.SnapshotSchedule{}
schedule.Name = "mysched"
schedule.UID = "bb327b3e-2e87-4e0a-9b42-12d61c08bf97"
schedule.Kind = "SnapshotSchedule"
schedule.APIVersion = "snapscheduler.backube.io/v1"
snapname := "mysnap"
scheduleName := "mysched"
schedTime, _ := time.Parse(timeFormat, "2010-07-23T01:02:00Z")
labels := make(map[string]string, 2)
labels["one"] = "two"
labels["three"] = "four"
snap := newSnapForClaim(snapname, pvc, scheduleName, schedTime, labels, nil)
snap := newSnapForClaim(snapname, pvc, &schedule, schedTime, labels, nil, true)
// Some tests depend on knowing the internals :(
Expect(snap.Spec.VolumeSnapshotClassName).To(BeNil())
Expect(snap.Labels).NotTo(BeNil())
Expect(snap.Labels).To(HaveKeyWithValue(ScheduleKey, scheduleName))
Expect(snap.Labels).To(HaveKeyWithValue(WhenKey, schedTime.Format(timeYYYYMMDDHHMMSS)))
Expect(snap.Labels).To(HaveKeyWithValue("three", "four"))
// Test for ownerReferences
Expect(snap.OwnerReferences).To(HaveLen(1))
ownerRef := snap.OwnerReferences[0]
Expect(ownerRef.APIVersion).To(Equal(schedule.APIVersion))
Expect(ownerRef.Kind).To(Equal(schedule.Kind))
Expect(ownerRef.Name).To(Equal(schedule.Name))
Expect(ownerRef.UID).To(Equal(schedule.UID))
Expect(len(snap.Labels)).To(Equal(4))
})
})
Expand Down