Skip to content
Open
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
3 changes: 3 additions & 0 deletions pkg/controller/perconaservermongodbrestore/physical.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ func (r *ReconcilePerconaServerMongoDBRestore) updateStatefulSetForPhysicalResto
cluster.Spec.ImagePullPolicy,
cmd,
)
if cluster.CompareVersion("1.23.0") >= 0 && cluster.Spec.InitContainerSecurityContext != nil {
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

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

The version gate for applying InitContainerSecurityContext to pbm-init is >= 1.23.0, but the same field is already applied to other init containers for >= 1.14.0 (see pkg/psmdb/init.go where init.SecurityContext = cr.Spec.InitContainerSecurityContext is guarded by CompareVersion("1.14.0")). With the current 1.23.0 threshold, clusters running physical restore with spec.crVersion between 1.14.0 and 1.22.x can still miss the init container security context and continue to fail PSA restricted admission. Consider lowering/removing this version guard to match the existing init-container behavior so the setting is applied consistently across supported CR versions.

Suggested change
if cluster.CompareVersion("1.23.0") >= 0 && cluster.Spec.InitContainerSecurityContext != nil {
if cluster.CompareVersion("1.14.0") >= 0 && cluster.Spec.InitContainerSecurityContext != nil {

Copilot uses AI. Check for mistakes.
pbmInit.SecurityContext = cluster.Spec.InitContainerSecurityContext
}
sts.Spec.Template.Spec.InitContainers = append(sts.Spec.Template.Spec.InitContainers, pbmInit)

pbmIdx := -1
Expand Down
230 changes: 139 additions & 91 deletions pkg/controller/perconaservermongodbrestore/physical_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package perconaservermongodbrestore

import (
"context"
"slices"
"testing"

Expand All @@ -17,107 +16,156 @@ import (
)

func TestUpdateStatefulSetForPhysicalRestore(t *testing.T) {
ctx := context.Background()
nonRoot := true
allowPrivEsc := false
initSC := &corev1.SecurityContext{
RunAsNonRoot: &nonRoot,
AllowPrivilegeEscalation: &allowPrivEsc,
}

cluster := &psmdbv1.PerconaServerMongoDB{
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster",
Namespace: "default",
tests := []struct {
name string
crVersion string
clusterInitSC *corev1.SecurityContext
wantPbmInitSC *corev1.SecurityContext
}{
{
name: "latest_version_with_InitContainerSecurityContext",
crVersion: version.Version(),
clusterInitSC: initSC,
wantPbmInitSC: initSC,
},
Spec: psmdbv1.PerconaServerMongoDBSpec{
CRVersion: version.Version(),
Backup: psmdbv1.BackupSpec{
Image: "percona/percona-backup-mongodb:latest",
VolumeMounts: []corev1.VolumeMount{
{
Name: "extra-volume",
MountPath: "/extra",
},
},
},
ImagePullPolicy: corev1.PullIfNotPresent,
Secrets: &psmdbv1.SecretsSpec{
Users: "users-secret",
SSL: "ssl-secret",
},
{
name: "latest_version_without_InitContainerSecurityContext",
crVersion: version.Version(),
clusterInitSC: nil,
wantPbmInitSC: nil,
},
{
name: "1_22_with_InitContainerSecurityContext_ignored",
crVersion: "1.22.0",
clusterInitSC: initSC,
wantPbmInitSC: nil,
},
{
name: "1_22_without_InitContainerSecurityContext",
crVersion: "1.22.0",
clusterInitSC: nil,
wantPbmInitSC: nil,
Comment on lines +44 to +54
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

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

These test cases encode that InitContainerSecurityContext should be ignored for CRVersion 1.22.0. That expectation looks inconsistent with existing behavior where init containers already apply spec.initContainerSecurityContext for versions >= 1.14.0 (see pkg/psmdb/init.go). If the restore path should mirror that behavior (and avoid PSA restricted failures for 1.14–1.22 clusters), update the test matrix/expected values accordingly.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

No, because:

updateStatefulSetForPhysicalRestore() calls EntrypointInitContainer() to create the pbm-init container but never assigns SecurityContext after creation - unlike the other caller, InitContainers() in init.go, which does apply it.

currently but we're going to be enabling that from v1.23.0

},
}

sts := &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-rs0",
Namespace: "default",
},
Spec: appsv1.StatefulSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "my-cluster"},
},
Template: corev1.PodTemplateSpec{
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := t.Context()

cluster := &psmdbv1.PerconaServerMongoDB{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app": "my-cluster"},
Name: "my-cluster",
Namespace: "default",
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "mongod",
Image: "percona/percona-server-mongodb:latest",
},
{
Name: naming.ContainerBackupAgent,
Image: "percona/percona-backup-agent:latest",
Spec: psmdbv1.PerconaServerMongoDBSpec{
CRVersion: tt.crVersion,
Backup: psmdbv1.BackupSpec{
Image: "percona/percona-backup-mongodb:latest",
VolumeMounts: []corev1.VolumeMount{
{
Name: "extra-volume",
MountPath: "/extra",
},
},
},
ImagePullPolicy: corev1.PullIfNotPresent,
Secrets: &psmdbv1.SecretsSpec{
Users: "users-secret",
SSL: "ssl-secret",
},
InitContainerSecurityContext: tt.clusterInitSC,
},
},
},
}

secretTLS := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: cluster.Spec.Secrets.SSL,
Namespace: cluster.Namespace,
},
Data: map[string][]byte{
"ca.crt": {},
"tls.crt": {},
"tls.key": {},
},
}

r := fakeReconciler(cluster, sts, secretTLS)
namespacedName := types.NamespacedName{
Name: sts.Name,
Namespace: sts.Namespace,
}
}

err := r.updateStatefulSetForPhysicalRestore(ctx, cluster, namespacedName, 27017)
assert.NoError(t, err)

updatedSTS := &appsv1.StatefulSet{}
err = r.client.Get(ctx, namespacedName, updatedSTS)
assert.NoError(t, err)

assert.Equal(t, "true", updatedSTS.Annotations[psmdbv1.AnnotationRestoreInProgress])
sts := &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-rs0",
Namespace: "default",
},
Spec: appsv1.StatefulSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "my-cluster"},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app": "my-cluster"},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "mongod",
Image: "percona/percona-server-mongodb:latest",
},
{
Name: naming.ContainerBackupAgent,
Image: "percona/percona-backup-agent:latest",
},
},
},
},
},
}

for _, c := range updatedSTS.Spec.Template.Spec.Containers {
assert.NotEqual(t, naming.ContainerBackupAgent, c.Name)
secretTLS := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: cluster.Spec.Secrets.SSL,
Namespace: cluster.Namespace,
},
Data: map[string][]byte{
"ca.crt": {},
"tls.crt": {},
"tls.key": {},
},
}

r := fakeReconciler(cluster, sts, secretTLS)
namespacedName := types.NamespacedName{
Name: sts.Name,
Namespace: sts.Namespace,
}

err := r.updateStatefulSetForPhysicalRestore(ctx, cluster, namespacedName, 27017)
assert.NoError(t, err)

updatedSTS := &appsv1.StatefulSet{}
err = r.client.Get(ctx, namespacedName, updatedSTS)
assert.NoError(t, err)

assert.Equal(t, "true", updatedSTS.Annotations[psmdbv1.AnnotationRestoreInProgress])

for _, c := range updatedSTS.Spec.Template.Spec.Containers {
assert.NotEqual(t, naming.ContainerBackupAgent, c.Name)
}

var pbmInit *corev1.Container
for i := range updatedSTS.Spec.Template.Spec.InitContainers {
if updatedSTS.Spec.Template.Spec.InitContainers[i].Name == "pbm-init" {
pbmInit = &updatedSTS.Spec.Template.Spec.InitContainers[i]
break
}
}
assert.NotNil(t, pbmInit)
assert.Equal(t, tt.wantPbmInitSC, pbmInit.SecurityContext)

assert.Equal(t, "/opt/percona/physical-restore-ps-entry.sh", updatedSTS.Spec.Template.Spec.Containers[0].Command[0])

assert.True(t,
slices.ContainsFunc(updatedSTS.Spec.Template.Spec.Containers[0].VolumeMounts, func(c corev1.VolumeMount) bool {
return c.MountPath == "/etc/pbm/"
}))

lastEnvVar := updatedSTS.Spec.Template.Spec.Containers[0].Env[len(updatedSTS.Spec.Template.Spec.Containers[0].Env)-1]
expectedURI := "mongodb://$(PBM_AGENT_MONGODB_USERNAME):$(PBM_AGENT_MONGODB_PASSWORD)@localhost:$(PBM_MONGODB_PORT)/?tls=true&tlsCertificateKeyFile=/tmp/tls.pem&tlsCAFile=/etc/mongodb-ssl/ca.crt&tlsInsecure=true"

assert.Equal(t, "PBM_MONGODB_URI", lastEnvVar.Name)
assert.Equal(t, expectedURI, lastEnvVar.Value)
})
}

assert.True(t,
slices.ContainsFunc(updatedSTS.Spec.Template.Spec.InitContainers, func(c corev1.Container) bool {
return c.Name == "pbm-init"
}))

assert.Equal(t, "/opt/percona/physical-restore-ps-entry.sh", updatedSTS.Spec.Template.Spec.Containers[0].Command[0])

assert.True(t,
slices.ContainsFunc(updatedSTS.Spec.Template.Spec.Containers[0].VolumeMounts, func(c corev1.VolumeMount) bool {
return c.MountPath == "/etc/pbm/"
}))

lastEnvVar := updatedSTS.Spec.Template.Spec.Containers[0].Env[len(updatedSTS.Spec.Template.Spec.Containers[0].Env)-1]
expectedURI := "mongodb://$(PBM_AGENT_MONGODB_USERNAME):$(PBM_AGENT_MONGODB_PASSWORD)@localhost:$(PBM_MONGODB_PORT)/?tls=true&tlsCertificateKeyFile=/tmp/tls.pem&tlsCAFile=/etc/mongodb-ssl/ca.crt&tlsInsecure=true"

assert.Equal(t, "PBM_MONGODB_URI", lastEnvVar.Name)
assert.Equal(t, expectedURI, lastEnvVar.Value)
}
Loading