Skip to content

Commit

Permalink
Fix issue with Local provider for etcd-backup-restore distroless image (
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronfern authored and shreyas-s-rao committed Aug 9, 2023
1 parent 3577d23 commit be17a45
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 35 deletions.
4 changes: 2 additions & 2 deletions controllers/etcd/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func (r *Reconciler) delete(ctx context.Context, etcd *druidv1alpha1.Etcd) (ctrl
logger := r.logger.WithValues("etcd", kutil.Key(etcd.Namespace, etcd.Name).String(), "operation", "delete")
logger.Info("Starting deletion operation", "namespace", etcd.Namespace, "name", etcd.Name)

stsDeployer := gardenercomponent.OpDestroyAndWait(componentsts.New(r.Client, logger, componentsts.Values{Name: etcd.Name, Namespace: etcd.Namespace}))
stsDeployer := gardenercomponent.OpDestroyAndWait(componentsts.New(r.Client, logger, componentsts.Values{Name: etcd.Name, Namespace: etcd.Namespace}, r.config.FeatureGates))
if err := stsDeployer.Destroy(ctx); err != nil {
if err = r.updateEtcdErrorStatus(ctx, etcd, reconcileResult{err: err}); err != nil {
return ctrl.Result{
Expand Down Expand Up @@ -383,7 +383,7 @@ func (r *Reconciler) reconcileEtcd(ctx context.Context, logger logr.Logger, etcd

// Create an OpWaiter because after the deployment we want to wait until the StatefulSet is ready.
var (
stsDeployer = componentsts.New(r.Client, logger, statefulSetValues)
stsDeployer = componentsts.New(r.Client, logger, statefulSetValues, r.config.FeatureGates)
deployWaiter = gardenercomponent.OpWait(stsDeployer)
)

Expand Down
50 changes: 42 additions & 8 deletions controllers/etcdcopybackupstask/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ func (r *Reconciler) createJobObject(ctx context.Context, task *druidv1alpha1.Et
env := append(createEnvVarsFromStore(&sourceStore, sourceProvider, "SOURCE_", sourcePrefix), createEnvVarsFromStore(&targetStore, targetProvider, "", "")...)

// Formulate the job's volume mounts.
volumeMounts := append(createVolumeMountsFromStore(&sourceStore, sourceProvider, sourcePrefix), createVolumeMountsFromStore(&targetStore, targetProvider, targetPrefix)...)
volumeMounts := append(createVolumeMountsFromStore(&sourceStore, sourceProvider, sourcePrefix, r.Config.FeatureGates[string(features.UseEtcdWrapper)]), createVolumeMountsFromStore(&targetStore, targetProvider, targetPrefix, r.Config.FeatureGates[string(features.UseEtcdWrapper)])...)

// Formulate the job's volumes from the source store.
sourceVolumes, err := r.createVolumesFromStore(ctx, &sourceStore, task.Namespace, sourceProvider, sourcePrefix)
Expand Down Expand Up @@ -355,12 +355,39 @@ func (r *Reconciler) createJobObject(ctx context.Context, task *druidv1alpha1.Et
VolumeMounts: volumeMounts,
},
},
Volumes: volumes,
ShareProcessNamespace: pointer.Bool(true),
Volumes: volumes,
},
},
},
}

if r.Config.FeatureGates[string(features.UseEtcdWrapper)] {
if targetProvider == druidutils.Local {
// init container to change file permissions of the folders used as store to 65532 (nonroot)
// used only with local provider
job.Spec.Template.Spec.InitContainers = []corev1.Container{
{
Name: "change-backup-bucket-permissions",
Image: "alpine:3.18.2",
Command: []string{"sh", "-c", "--"},
Args: []string{fmt.Sprintf("%s%s%s%s", "chown -R 65532:65532 /home/nonroot/", *targetStore.Container, " /home/nonroot/", *sourceStore.Container)},
VolumeMounts: volumeMounts,
SecurityContext: &corev1.SecurityContext{
RunAsGroup: pointer.Int64(0),
RunAsNonRoot: pointer.Bool(false),
RunAsUser: pointer.Int64(0),
},
},
}
}
job.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{
RunAsGroup: pointer.Int64(65532),
RunAsNonRoot: pointer.Bool(true),
RunAsUser: pointer.Int64(65532),
}
}

if err := controllerutil.SetControllerReference(task, job, r.Scheme()); err != nil {
return nil, fmt.Errorf("could not set owner reference for job %v: %w", kutil.ObjectName(job), err)
}
Expand All @@ -371,7 +398,7 @@ func createJobArgs(task *druidv1alpha1.EtcdCopyBackupsTask, sourceObjStoreProvid
// Create the initial arguments for the copy-backups job.
args := []string{
"copy",
"--snapstore-temp-directory=/var/etcd/data/tmp",
"--snapstore-temp-directory=/home/nonroot/data/tmp",
}

// Formulate the job's arguments.
Expand Down Expand Up @@ -444,13 +471,20 @@ func (r *Reconciler) createVolumesFromStore(ctx context.Context, store *druidv1a
// createVolumesFromStore generates a slice of volumes for an EtcdCopyBackups job based on the given StoreSpec, namespace,
// provider, and prefix. The prefix is used to differentiate between source and target volumes.
// This function creates the necessary Volume configurations for various storage providers and returns any errors encountered.
func createVolumeMountsFromStore(store *druidv1alpha1.StoreSpec, provider, volumeMountPrefix string) (volumeMounts []corev1.VolumeMount) {
func createVolumeMountsFromStore(store *druidv1alpha1.StoreSpec, provider, volumeMountPrefix string, useEtcdWrapper bool) (volumeMounts []corev1.VolumeMount) {
switch provider {
case druidutils.Local:
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: volumeMountPrefix + "host-storage",
MountPath: *store.Container,
})
if useEtcdWrapper {
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: volumeMountPrefix + "host-storage",
MountPath: "/home/nonroot/" + *store.Container,
})
} else {
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: volumeMountPrefix + "host-storage",
MountPath: *store.Container,
})
}
case druidutils.GCS:
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: getVolumeNamePrefix(volumeMountPrefix) + "etcd-backup",
Expand Down
9 changes: 6 additions & 3 deletions controllers/etcdcopybackupstask/reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ var _ = Describe("EtcdCopyBackupsTaskController", func() {
Repository: "test-repo",
Tag: pointer.String("etcd-test-tag"),
}},
Config: &Config{
FeatureGates: make(map[string]bool),
},
}
})

Expand Down Expand Up @@ -301,7 +304,7 @@ var _ = Describe("EtcdCopyBackupsTaskController", func() {
task *druidv1alpha1.EtcdCopyBackupsTask
expected = []string{
"copy",
"--snapstore-temp-directory=/var/etcd/data/tmp",
"--snapstore-temp-directory=/home/nonroot/data/tmp",
"--storage-provider=S3",
"--store-prefix=/target",
"--store-container=target-container",
Expand Down Expand Up @@ -442,7 +445,7 @@ var _ = Describe("EtcdCopyBackupsTaskController", func() {
})

It("should create the correct volume mounts", func() {
volumeMounts := createVolumeMountsFromStore(storeSpec, provider, volumeMountPrefix)
volumeMounts := createVolumeMountsFromStore(storeSpec, provider, volumeMountPrefix, false)
Expect(volumeMounts).To(HaveLen(1))

expectedMountPath := ""
Expand Down Expand Up @@ -758,7 +761,7 @@ func matchJob(task *druidv1alpha1.EtcdCopyBackupsTask, imageVector imagevector.I
func getArgElements(task *druidv1alpha1.EtcdCopyBackupsTask, sourceProvider, targetProvider string) Elements {
elements := Elements{
"copy": Equal("copy"),
"--snapstore-temp-directory=/var/etcd/data/tmp": Equal("--snapstore-temp-directory=/var/etcd/data/tmp"),
"--snapstore-temp-directory=/home/nonroot/data/tmp": Equal("--snapstore-temp-directory=/home/nonroot/data/tmp"),
}
if targetProvider != "" {
addEqual(elements, fmt.Sprintf("%s=%s", "--storage-provider", targetProvider))
Expand Down
64 changes: 46 additions & 18 deletions pkg/component/etcd/statefulset/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,21 @@ type Interface interface {
}

type component struct {
client client.Client
logger logr.Logger
values Values
client client.Client
logger logr.Logger
values Values
featureGates map[string]bool
}

// New creates a new StatefulSet deployer instance.
func New(c client.Client, logger logr.Logger, values Values) Interface {
func New(c client.Client, logger logr.Logger, values Values, featureGates map[string]bool) Interface {
objectLogger := logger.WithValues("sts", client.ObjectKey{Name: values.Name, Namespace: values.Namespace})

return &component{
client: c,
logger: objectLogger,
values: values,
client: c,
logger: objectLogger,
values: values,
featureGates: featureGates,
}
}

Expand Down Expand Up @@ -456,7 +458,7 @@ func (c *component) createOrPatch(ctx context.Context, sts *appsv1.StatefulSet,
Ports: getBackupPorts(c.values),
Resources: getBackupResources(c.values),
Env: getBackupRestoreEnvVars(c.values),
VolumeMounts: getBackupRestoreVolumeMounts(c.values),
VolumeMounts: getBackupRestoreVolumeMounts(c),
SecurityContext: &corev1.SecurityContext{
Capabilities: &corev1.Capabilities{
Add: []corev1.Capability{
Expand Down Expand Up @@ -507,6 +509,25 @@ func (c *component) createOrPatch(ctx context.Context, sts *appsv1.StatefulSet,
},
},
}
if c.values.BackupStore != nil {
// Special container to change permissions of backup bucket folder to 65532 (nonroot)
// Only used with local provider
prov, _ := utils.StorageProviderFromInfraProvider(c.values.BackupStore.Provider)
if prov == utils.Local {
sts.Spec.Template.Spec.InitContainers = append(sts.Spec.Template.Spec.InitContainers, corev1.Container{
Name: "change-backup-bucket-permissions",
Image: "alpine:3.18.2",
Command: []string{"sh", "-c", "--"},
Args: []string{fmt.Sprintf("chown -R 65532:65532 /home/nonroot/%s", *c.values.BackupStore.Container)},
VolumeMounts: getBackupRestoreVolumeMounts(c),
SecurityContext: &corev1.SecurityContext{
RunAsGroup: pointer.Int64(0),
RunAsNonRoot: pointer.Bool(false),
RunAsUser: pointer.Int64(0),
},
})
}
}
sts.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{
RunAsGroup: pointer.Int64(65532),
RunAsNonRoot: pointer.Bool(true),
Expand Down Expand Up @@ -688,10 +709,10 @@ func getSecretVolumeMounts(clientUrlTLS, peerUrlTLS *druidv1alpha1.TLSConfig) []
return vms
}

func getBackupRestoreVolumeMounts(val Values) []corev1.VolumeMount {
func getBackupRestoreVolumeMounts(c *component) []corev1.VolumeMount {
vms := []corev1.VolumeMount{
{
Name: val.VolumeClaimTemplateName,
Name: c.values.VolumeClaimTemplateName,
MountPath: "/var/etcd/data",
},
{
Expand All @@ -700,24 +721,31 @@ func getBackupRestoreVolumeMounts(val Values) []corev1.VolumeMount {
},
}

vms = append(vms, getSecretVolumeMounts(val.ClientUrlTLS, val.PeerUrlTLS)...)
vms = append(vms, getSecretVolumeMounts(c.values.ClientUrlTLS, c.values.PeerUrlTLS)...)

if val.BackupStore == nil {
if c.values.BackupStore == nil {
return vms
}

provider, err := utils.StorageProviderFromInfraProvider(val.BackupStore.Provider)
provider, err := utils.StorageProviderFromInfraProvider(c.values.BackupStore.Provider)
if err != nil {
return vms
}

switch provider {
case utils.Local:
if val.BackupStore.Container != nil {
vms = append(vms, corev1.VolumeMount{
Name: "host-storage",
MountPath: pointer.StringDeref(val.BackupStore.Container, ""),
})
if c.values.BackupStore.Container != nil {
if c.featureGates["UseEtcdWrapper"] {
vms = append(vms, corev1.VolumeMount{
Name: "host-storage",
MountPath: "/home/nonroot/" + pointer.StringDeref(c.values.BackupStore.Container, ""),
})
} else {
vms = append(vms, corev1.VolumeMount{
Name: "host-storage",
MountPath: pointer.StringDeref(c.values.BackupStore.Container, ""),
})
}
}
case utils.GCS:
vms = append(vms, corev1.VolumeMount{
Expand Down
7 changes: 5 additions & 2 deletions pkg/component/etcd/statefulset/statefulset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,10 @@ var _ = Describe("Statefulset", func() {
false,
true,
)
stsDeployer = New(cl, logr.Discard(), values)
fg := map[string]bool{
"UseEtcdWrapper": true,
}
stsDeployer = New(cl, logr.Discard(), values, fg)

sts = &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -924,6 +927,6 @@ func checkLocalProviderVaues(etcd *druidv1alpha1.Etcd, sts *appsv1.StatefulSet,
// check volume mount
ExpectWithOffset(1, backupRestoreContainer.VolumeMounts).To(ContainElement(corev1.VolumeMount{
Name: "host-storage",
MountPath: container,
MountPath: "/home/nonroot/" + container,
}))
}
2 changes: 1 addition & 1 deletion test/integration/controllers/etcd/reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1145,7 +1145,7 @@ func validateEtcd(instance *druidv1alpha1.Etcd, s *appsv1.StatefulSet, cm *corev
}),
"host-storage": MatchFields(IgnoreExtras, Fields{
"Name": Equal("host-storage"),
"MountPath": Equal(*instance.Spec.Backup.Store.Container),
"MountPath": Equal("/home/nonroot/" + *instance.Spec.Backup.Store.Container),
}),
}),
"Env": MatchElements(testutils.EnvIterator, IgnoreExtras, Elements{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func matchJob(task *druidv1alpha1.EtcdCopyBackupsTask, imageVector imagevector.I
func getArgElements(task *druidv1alpha1.EtcdCopyBackupsTask, sourceProvider, targetProvider string) Elements {
elements := Elements{
"copy": Equal("copy"),
"--snapstore-temp-directory=/var/etcd/data/tmp": Equal("--snapstore-temp-directory=/var/etcd/data/tmp"),
"--snapstore-temp-directory=/home/nonroot/data/tmp": Equal("--snapstore-temp-directory=/home/nonroot/data/tmp"),
}
if targetProvider != "" {
addEqual(elements, fmt.Sprintf("%s=%s", "--storage-provider", targetProvider))
Expand Down

0 comments on commit be17a45

Please sign in to comment.