Skip to content

Commit 181f852

Browse files
Merge pull request #1851 from zzzeek/OSPRH-25976
OSPRH-25976: prevent openstack operator from forcing Galera Secret
2 parents 06a3bb4 + d186794 commit 181f852

3 files changed

Lines changed: 231 additions & 3 deletions

File tree

api/core/v1beta1/openstackcontrolplane_webhook.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -990,9 +990,8 @@ func (r *OpenStackControlPlane) DefaultServices() {
990990
if template.StorageClass == "" {
991991
template.StorageClass = r.Spec.StorageClass
992992
}
993-
if template.Secret == "" {
994-
template.Secret = r.Spec.Secret
995-
}
993+
// Don't default Secret here - it's handled conditionally in reconciliation
994+
// to support both default (osp-secret) and auto-generated (blank) passwords
996995
template.Default()
997996
// By-value copy, need to update
998997
(*r.Spec.Galera.Templates)[key] = template

api/core/v1beta1/openstackcontrolplane_webhook_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1"
1212
keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
1313
manilav1 "github.com/openstack-k8s-operators/manila-operator/api/v1beta1"
14+
mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1"
1415
neutronv1 "github.com/openstack-k8s-operators/neutron-operator/api/v1beta1"
1516
novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1"
1617
octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1"
@@ -19,6 +20,7 @@ import (
1920
watcherv1 "github.com/openstack-k8s-operators/watcher-operator/api/v1beta1"
2021
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122
"k8s.io/apimachinery/pkg/util/validation/field"
23+
"k8s.io/utils/ptr"
2224
)
2325

2426
var _ = Describe("OpenStackControlPlane Webhook", func() {
@@ -942,4 +944,69 @@ var _ = Describe("OpenStackControlPlane Webhook", func() {
942944
})
943945
})
944946
})
947+
948+
Context("Galera Secret field defaulting behavior", func() {
949+
var instance *OpenStackControlPlane
950+
951+
BeforeEach(func() {
952+
instance = &OpenStackControlPlane{
953+
ObjectMeta: metav1.ObjectMeta{
954+
Name: "test",
955+
Namespace: "test-namespace",
956+
},
957+
Spec: OpenStackControlPlaneSpec{
958+
Secret: "osp-secret",
959+
StorageClass: "local-storage",
960+
Galera: GaleraSection{
961+
Enabled: true,
962+
},
963+
},
964+
}
965+
})
966+
967+
It("should not default template Secret when omitted in webhook", func() {
968+
instance.Spec.Galera.Templates = ptr.To(map[string]mariadbv1.GaleraSpecCore{
969+
"openstack": {
970+
StorageRequest: "500M",
971+
// Secret field is omitted/empty
972+
},
973+
})
974+
975+
instance.DefaultServices()
976+
977+
template := (*instance.Spec.Galera.Templates)["openstack"]
978+
Expect(template.Secret).To(Equal(""))
979+
})
980+
981+
It("should preserve explicitly set Secret value", func() {
982+
// Create a Galera template with explicit Secret
983+
instance.Spec.Galera.Templates = ptr.To(map[string]mariadbv1.GaleraSpecCore{
984+
"openstack": {
985+
StorageRequest: "500M",
986+
Secret: "custom-secret",
987+
},
988+
})
989+
990+
instance.DefaultServices()
991+
992+
template := (*instance.Spec.Galera.Templates)["openstack"]
993+
Expect(template.Secret).To(Equal("custom-secret"))
994+
})
995+
996+
It("should preserve explicitly blank Secret for auto-generation", func() {
997+
// Create a Galera template with explicitly blank Secret
998+
instance.Spec.Galera.Templates = ptr.To(map[string]mariadbv1.GaleraSpecCore{
999+
"openstack": {
1000+
StorageRequest: "500M",
1001+
Secret: "", // Explicitly blank for auto-generation
1002+
},
1003+
})
1004+
1005+
instance.DefaultServices()
1006+
1007+
template := (*instance.Spec.Galera.Templates)["openstack"]
1008+
// Should remain blank to allow mariadb-operator auto-generation
1009+
Expect(template.Secret).To(Equal(""))
1010+
})
1011+
})
9451012
})

test/functional/ctlplane/openstackoperator_controller_test.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3083,6 +3083,168 @@ var _ = Describe("OpenStackOperator controller", func() {
30833083
// to nil does not clear template-level NotificationsBus configuration.
30843084
// Template-level takes precedence over top-level.
30853085
})
3086+
3087+
//
3088+
// Galera Secret field behavior tests
3089+
//
3090+
When("A OpenStackControlPlane with blank Galera secret is created", func() {
3091+
BeforeEach(func() {
3092+
spec := GetDefaultOpenStackControlPlaneSpec()
3093+
spec["tls"] = GetTLSPublicSpec()
3094+
3095+
// Modify galera template to have blank secret for auto-generation
3096+
galeraTemplate := spec["galera"].(map[string]interface{})
3097+
templates := galeraTemplate["templates"].(map[string]interface{})
3098+
dbTemplate := templates[names.DBName.Name].(map[string]interface{})
3099+
dbTemplate["secret"] = "" // Explicitly blank for auto-generated password
3100+
3101+
DeferCleanup(
3102+
th.DeleteInstance,
3103+
CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec),
3104+
)
3105+
})
3106+
3107+
It("should create Galera CR with blank secret allowing auto-generation", func() {
3108+
OSCtlplane := GetOpenStackControlPlane(names.OpenStackControlplaneName)
3109+
Expect(OSCtlplane.Spec.Galera.Enabled).Should(BeTrue())
3110+
3111+
Eventually(func(g Gomega) {
3112+
db := mariadb.GetGalera(names.DBName)
3113+
g.Expect(db).Should(Not(BeNil()))
3114+
// When created fresh with blank secret, it should remain blank
3115+
// (allowing mariadb-operator to auto-generate the root password)
3116+
g.Expect(db.Spec.Secret).To(Equal(""))
3117+
}, timeout, interval).Should(Succeed())
3118+
})
3119+
})
3120+
3121+
When("A OpenStackControlPlane with omitted Galera secret is created", func() {
3122+
BeforeEach(func() {
3123+
spec := GetDefaultOpenStackControlPlaneSpec()
3124+
spec["tls"] = GetTLSPublicSpec()
3125+
3126+
// Modify galera template to omit secret entirely (not set)
3127+
galeraTemplate := spec["galera"].(map[string]interface{})
3128+
templates := galeraTemplate["templates"].(map[string]interface{})
3129+
dbTemplate := templates[names.DBName.Name].(map[string]interface{})
3130+
delete(dbTemplate, "secret") // Omit the field entirely
3131+
3132+
DeferCleanup(th.DeleteInstance, CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec))
3133+
})
3134+
3135+
It("should create Galera CR with blank secret for auto-generation", func() {
3136+
Eventually(func(g Gomega) {
3137+
db := mariadb.GetGalera(names.DBName)
3138+
g.Expect(db).ShouldNot(BeNil())
3139+
// When omitted (not explicitly set), should remain blank
3140+
// allowing mariadb-operator to auto-generate the password
3141+
g.Expect(db.Spec.Secret).To(Equal(""))
3142+
}, timeout, interval).Should(Succeed())
3143+
})
3144+
})
3145+
3146+
When("A OpenStackControlPlane with explicit custom Galera secret is created", func() {
3147+
BeforeEach(func() {
3148+
spec := GetDefaultOpenStackControlPlaneSpec()
3149+
spec["tls"] = GetTLSPublicSpec()
3150+
3151+
// Modify galera template to use custom secret
3152+
galeraTemplate := spec["galera"].(map[string]interface{})
3153+
templates := galeraTemplate["templates"].(map[string]interface{})
3154+
dbTemplate := templates[names.DBName.Name].(map[string]interface{})
3155+
dbTemplate["secret"] = "custom-galera-secret"
3156+
3157+
DeferCleanup(th.DeleteInstance, CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec))
3158+
})
3159+
3160+
It("should create Galera CR with the custom secret", func() {
3161+
Eventually(func(g Gomega) {
3162+
db := mariadb.GetGalera(names.DBName)
3163+
g.Expect(db).ShouldNot(BeNil())
3164+
g.Expect(db.Spec.Secret).To(Equal("custom-galera-secret"))
3165+
}, timeout, interval).Should(Succeed())
3166+
})
3167+
})
3168+
3169+
When("Multiple Galera templates with different secret configurations are created", func() {
3170+
BeforeEach(func() {
3171+
spec := GetDefaultOpenStackControlPlaneSpec()
3172+
spec["tls"] = GetTLSPublicSpec()
3173+
3174+
// Modify galera templates to have different secret configurations
3175+
galeraTemplate := spec["galera"].(map[string]interface{})
3176+
templates := map[string]interface{}{
3177+
names.DBName.Name: map[string]interface{}{
3178+
"storageRequest": "500M",
3179+
// secret is omitted
3180+
},
3181+
names.DBCell1Name.Name: map[string]interface{}{
3182+
"storageRequest": "500M",
3183+
"secret": "cell1-secret", // Explicit secret
3184+
},
3185+
}
3186+
galeraTemplate["templates"] = templates
3187+
3188+
DeferCleanup(th.DeleteInstance, CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec))
3189+
})
3190+
3191+
It("should create each Galera CR with its respective secret configuration", func() {
3192+
// Verify main DB with blank secret
3193+
Eventually(func(g Gomega) {
3194+
db := mariadb.GetGalera(names.DBName)
3195+
g.Expect(db).ShouldNot(BeNil())
3196+
g.Expect(db.Spec.Secret).To(Equal(""))
3197+
}, timeout, interval).Should(Succeed())
3198+
3199+
// Verify cell1 DB with explicit secret
3200+
Eventually(func(g Gomega) {
3201+
db := mariadb.GetGalera(names.DBCell1Name)
3202+
g.Expect(db).ShouldNot(BeNil())
3203+
g.Expect(db.Spec.Secret).To(Equal("cell1-secret"))
3204+
}, timeout, interval).Should(Succeed())
3205+
})
3206+
})
3207+
3208+
// Test that we can change from explicit secret to auto-generated
3209+
When("An OpenStackControlPlane Galera secret starts as osp-secret", func() {
3210+
BeforeEach(func() {
3211+
spec := GetDefaultOpenStackControlPlaneSpec()
3212+
spec["tls"] = GetTLSPublicSpec()
3213+
3214+
// Start with an EXPLICIT secret (old deployment style)
3215+
galeraTemplate := spec["galera"].(map[string]interface{})
3216+
templates := galeraTemplate["templates"].(map[string]interface{})
3217+
dbTemplate := templates[names.DBName.Name].(map[string]interface{})
3218+
dbTemplate["secret"] = "osp-secret" // Explicit secret, as in pre-FR6 versions
3219+
3220+
DeferCleanup(th.DeleteInstance, CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec))
3221+
})
3222+
3223+
It("should allow changing to blank secret for auto-generation", func() {
3224+
Eventually(func(g Gomega) {
3225+
db := mariadb.GetGalera(names.DBName)
3226+
g.Expect(db).ShouldNot(BeNil())
3227+
g.Expect(db.Spec.Secret).To(Equal("osp-secret"))
3228+
}, timeout, interval).Should(Succeed())
3229+
3230+
Eventually(func(g Gomega) {
3231+
oscp := GetOpenStackControlPlane(names.OpenStackControlplaneName)
3232+
templates := *oscp.Spec.Galera.Templates
3233+
t := templates[names.DBName.Name]
3234+
t.Secret = "" // User removes secret to enable auto-gen
3235+
templates[names.DBName.Name] = t
3236+
oscp.Spec.Galera.Templates = &templates
3237+
g.Expect(k8sClient.Update(ctx, oscp)).Should(Succeed())
3238+
}, timeout, interval).Should(Succeed())
3239+
3240+
Eventually(func(g Gomega) {
3241+
db := mariadb.GetGalera(names.DBName)
3242+
g.Expect(db).ShouldNot(BeNil())
3243+
g.Expect(db.Spec.Secret).To(Equal(""))
3244+
}, timeout, interval).Should(Succeed())
3245+
})
3246+
3247+
})
30863248
})
30873249

30883250
var _ = Describe("OpenStackOperator Webhook", func() {

0 commit comments

Comments
 (0)