Skip to content

Commit d186794

Browse files
committed
OSPRH-25976: prevent openstack operator from forcing Galera Secret
Remove the webhook rule that forces the Galera template to be copied from the top level openstack control plane secret field when blank, instead allowing the Galera level secret to be blank. This allows the Galera operator to use newer "automatic root password generation" logic delivered as part of OSPRH-14916. Workflows include deployment of an openstack control plane with no galera secret (blank or omitted) which will fully auto-generate a mariadb root password for the new install, or modifying an existing deployment's secret to be blank, which will generate a mariadb root password and update the existing galera DB to use the new root pw. References: OSPRH-25976
1 parent 7fde865 commit d186794

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)