Skip to content
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
20 changes: 18 additions & 2 deletions api/bases/keystone.openstack.org_keystoneapis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,23 @@ spec:
description: NodeSelector to target subset of worker nodes running
this service
type: object
notificationsBus:
description: NotificationsBus configuration (username, vhost, and
cluster)
properties:
cluster:
description: Name of the cluster
minLength: 1
type: string
user:
description: User - RabbitMQ username
type: string
vhost:
description: Vhost - RabbitMQ vhost name
type: string
required:
- cluster
type: object
override:
description: Override, provides the ability to override the generated
manifest of several child resources.
Expand Down Expand Up @@ -1462,10 +1479,10 @@ spec:
e.g. to check logs
type: boolean
rabbitMqClusterName:
default: rabbitmq
description: |-
RabbitMQ instance name
Needed to request a transportURL that is created and used in Keystone
Deprecated: Use NotificationsBus.Cluster instead
type: string
region:
default: regionOne
Expand Down Expand Up @@ -1608,7 +1625,6 @@ spec:
- containerImage
- databaseInstance
- memcachedInstance
- rabbitMqClusterName
- secret
type: object
status:
Expand Down
17 changes: 11 additions & 6 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ require (
github.com/google/uuid v1.6.0
github.com/gophercloud/gophercloud/v2 v2.8.0
github.com/onsi/gomega v1.39.0
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260123105816-865d02e287a9
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251230215914-6ba873b49a35
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251230215914-6ba873b49a35
github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20251230215914-6ba873b49a35
github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251230215914-6ba873b49a35
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260124124521-834dd78eceb9
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260126081203-efc2df9207eb
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260126081203-efc2df9207eb
github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260126081203-efc2df9207eb
github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260126081203-efc2df9207eb
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
k8s.io/api v0.31.14
k8s.io/apimachinery v0.31.14
Expand All @@ -24,7 +24,6 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
Expand Down Expand Up @@ -52,6 +51,7 @@ require (
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/rabbitmq/cluster-operator/v2 v2.16.0 // indirect
github.com/spf13/pflag v1.0.7 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
Expand Down Expand Up @@ -97,4 +97,9 @@ replace k8s.io/code-generator => k8s.io/code-generator v0.31.14 //allow-merging

replace k8s.io/component-base => k8s.io/component-base v0.31.14 //allow-merging

replace sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.19.7 //allow-merging

// custom RabbitmqClusterSpecCore for OpenStackControlplane (v2.16.0_patches)
replace github.com/rabbitmq/cluster-operator/v2 => github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec //allow-merging

replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging
23 changes: 13 additions & 10 deletions api/go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
Expand Down Expand Up @@ -84,16 +85,18 @@ github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q=
github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e h1:E1OdwSpqWuDPCedyUt0GEdoAE+r5TXy7YS21yNEo+2U=
github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo=
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260123105816-865d02e287a9 h1:tD6nnTRcyUCXdVMWPHLApk12tzQlQni5eoxvQ8XdbP8=
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260123105816-865d02e287a9/go.mod h1:ZXwFlspJCdZEUjMbmaf61t5AMB4u2vMyAMMoe/vJroE=
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251230215914-6ba873b49a35 h1:pF3mJ3nwq6r4qwom+rEWZNquZpcQW/iftHlJ1KPIDsk=
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251230215914-6ba873b49a35/go.mod h1:kycZyoe7OZdW1HUghr2nI3N7wSJtNahXf6b/ypD14f4=
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251230215914-6ba873b49a35 h1:IdcI8DFvW8rXtchONSzbDmhhRp1YyO2YaBJDBXr44Gk=
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251230215914-6ba873b49a35/go.mod h1:zOX7Y05keiSppIvLabuyh42QHBMhCcoskAtxFRbwXKo=
github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20251230215914-6ba873b49a35 h1:8WZYfCt1VJHa5sJRX0UhpmoXud/fn8LHQhXsakdYXuQ=
github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20251230215914-6ba873b49a35/go.mod h1:H0aQANk8iJPRhS2Bg9n6cYb/IHF0Cks9g7+uZG04Rhk=
github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251230215914-6ba873b49a35 h1:8rQc4Fsfe6yqRU5Xjt9lWXqUqfBjRubr0utnUpUBKTE=
github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251230215914-6ba873b49a35/go.mod h1:QWzyC+tTBB2OGuYyIiLLo1oA0+I/0NUMXD+dj4Quv4M=
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260124124521-834dd78eceb9 h1:87CTyKbobvZPGOqiigSO+/PPbMnFHoYecjwBdnxxEGw=
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260124124521-834dd78eceb9/go.mod h1:ZXwFlspJCdZEUjMbmaf61t5AMB4u2vMyAMMoe/vJroE=
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260126081203-efc2df9207eb h1:S7tnYO/E1f1KQfcp7N5bam8+ax/ExDTOhZ1WqG4Bfu0=
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260126081203-efc2df9207eb/go.mod h1:ndqfy1KbVorHH6+zlUFPIrCRhMSxO3ImYJUGaooE0x0=
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260126081203-efc2df9207eb h1:E59YGRP8XWq8vi6AUUxyYyBD1ahzcr3RKDkZmxpi+qs=
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260126081203-efc2df9207eb/go.mod h1:zOX7Y05keiSppIvLabuyh42QHBMhCcoskAtxFRbwXKo=
github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260126081203-efc2df9207eb h1:0kP9V1pKfRno6ss7qAy3GcfVK29CobWym6WA7AYA7wY=
github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260126081203-efc2df9207eb/go.mod h1:jofj+VqDszxLCZSBYo794KGkCjMo01xzhQ/gffYzf3I=
github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260126081203-efc2df9207eb h1:Fh9yjyogiR9P4oV3a2pSlSUyYzfbWbvlU6RFIjZoxsg=
github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260126081203-efc2df9207eb/go.mod h1:sqKTKvYhSzu4Opnjx/J+zzetXKRqYrhxsfvrST/NjoU=
github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec h1:saovr368HPAKHN0aRPh8h8n9s9dn3d8Frmfua0UYRlc=
github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec/go.mod h1:Nh2NEePLjovUQof2krTAg4JaAoLacqtPTZQXK6izNfg=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
11 changes: 8 additions & 3 deletions api/v1beta1/keystoneapi_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package v1beta1
import (
"fmt"

rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1"
topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1"
condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
"github.com/openstack-k8s-operators/lib-common/modules/common/endpoint"
Expand Down Expand Up @@ -183,11 +184,11 @@ type KeystoneAPISpecCore struct {
// Override, provides the ability to override the generated manifest of several child resources.
Override APIOverrideSpec `json:"override,omitempty"`

// +kubebuilder:validation:Required
// +kubebuilder:default=rabbitmq
// +kubebuilder:validation:Optional
// RabbitMQ instance name
// Needed to request a transportURL that is created and used in Keystone
RabbitMqClusterName string `json:"rabbitMqClusterName"`
// Deprecated: Use NotificationsBus.Cluster instead
RabbitMqClusterName string `json:"rabbitMqClusterName,omitempty"`

// +kubebuilder:validation:Optional
// +operator-sdk:csv:customresourcedefinitions:type=spec
Expand Down Expand Up @@ -219,6 +220,10 @@ type KeystoneAPISpecCore struct {
// +kubebuilder:default=false
// ExternalKeystoneAPI - Enable use of external Keystone API endpoints instead of deploying a local Keystone API
ExternalKeystoneAPI bool `json:"externalKeystoneAPI"`

// +kubebuilder:validation:Optional
// NotificationsBus configuration (username, vhost, and cluster)
NotificationsBus *rabbitmqv1.RabbitMqConfig `json:"notificationsBus,omitempty"`
}

// APIOverrideSpec to override the generated manifest of several child resources.
Expand Down
102 changes: 86 additions & 16 deletions api/v1beta1/keystoneapi_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"net/url"

"github.com/openstack-k8s-operators/lib-common/modules/common/service"
common_webhook "github.com/openstack-k8s-operators/lib-common/modules/common/webhook"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
Expand Down Expand Up @@ -77,6 +78,10 @@ func (spec *KeystoneAPISpecCore) Default() {
if spec.APITimeout == 0 {
spec.APITimeout = keystoneAPIDefaults.APITimeout
}

// NotificationsBus.Cluster is not defaulted - it must be explicitly set if NotificationsBus is configured
// Migration from deprecated fields is handled by openstack-operator
// This ensures users make a conscious choice about which cluster to use for notifications
}

var _ webhook.Validator = &KeystoneAPI{}
Expand All @@ -85,29 +90,36 @@ var _ webhook.Validator = &KeystoneAPI{}
func (r *KeystoneAPI) ValidateCreate() (admission.Warnings, error) {
keystoneapilog.Info("validate create", "name", r.Name)

var allWarns []string
allErrs := field.ErrorList{}
basePath := field.NewPath("spec")

if err := r.Spec.ValidateCreate(basePath, r.Namespace); err != nil {
allErrs = append(allErrs, err...)
}
warns, errs := r.Spec.ValidateCreate(basePath, r.Namespace)
allWarns = append(allWarns, warns...)
allErrs = append(allErrs, errs...)

if len(allErrs) != 0 {
return nil, apierrors.NewInvalid(GroupVersion.WithKind("KeystoneAPI").GroupKind(), r.Name, allErrs)
return allWarns, apierrors.NewInvalid(GroupVersion.WithKind("KeystoneAPI").GroupKind(), r.Name, allErrs)
}

return nil, nil
return allWarns, nil
}

// ValidateCreate - Exported function wrapping non-exported validate functions,
// this function can be called externally to validate an KeystoneAPI spec.
func (spec *KeystoneAPISpec) ValidateCreate(basePath *field.Path, namespace string) field.ErrorList {
func (spec *KeystoneAPISpec) ValidateCreate(basePath *field.Path, namespace string) ([]string, field.ErrorList) {
return spec.KeystoneAPISpecCore.ValidateCreate(basePath, namespace)
}

// ValidateCreate validates the KeystoneAPISpecCore spec during creation
func (spec *KeystoneAPISpecCore) ValidateCreate(basePath *field.Path, namespace string) field.ErrorList {
func (spec *KeystoneAPISpecCore) ValidateCreate(basePath *field.Path, namespace string) ([]string, field.ErrorList) {
var allErrs field.ErrorList
var allWarns []string

// Validate deprecated fields using reflection-based validation
warnings, errs := spec.validateDeprecatedFieldsCreate(basePath)
allWarns = append(allWarns, warnings...)
allErrs = append(allErrs, errs...)

// validate the service override key is valid
allErrs = append(allErrs, service.ValidateRoutedOverrides(basePath.Child("override").Child("service"), spec.Override.Service)...)
Expand All @@ -119,7 +131,7 @@ func (spec *KeystoneAPISpecCore) ValidateCreate(basePath *field.Path, namespace
// Validate external Keystone API configuration
allErrs = append(allErrs, spec.ValidateExternalKeystoneAPI(basePath)...)

return allErrs
return allWarns, allErrs
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
Expand All @@ -131,29 +143,36 @@ func (r *KeystoneAPI) ValidateUpdate(old runtime.Object) (admission.Warnings, er
return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert existing object"))
}

var allWarns []string
allErrs := field.ErrorList{}
basePath := field.NewPath("spec")

if err := r.Spec.ValidateUpdate(oldKeystoneAPI.Spec, basePath, r.Namespace); err != nil {
allErrs = append(allErrs, err...)
}
warns, errs := r.Spec.ValidateUpdate(oldKeystoneAPI.Spec, basePath, r.Namespace)
allWarns = append(allWarns, warns...)
allErrs = append(allErrs, errs...)

if len(allErrs) != 0 {
return nil, apierrors.NewInvalid(GroupVersion.WithKind("KeystoneAPI").GroupKind(), r.Name, allErrs)
return allWarns, apierrors.NewInvalid(GroupVersion.WithKind("KeystoneAPI").GroupKind(), r.Name, allErrs)
}

return nil, nil
return allWarns, nil
}

// ValidateUpdate - Exported function wrapping non-exported validate functions,
// this function can be called externally to validate an ironic spec.
func (spec *KeystoneAPISpec) ValidateUpdate(old KeystoneAPISpec, basePath *field.Path, namespace string) field.ErrorList {
func (spec *KeystoneAPISpec) ValidateUpdate(old KeystoneAPISpec, basePath *field.Path, namespace string) ([]string, field.ErrorList) {
return spec.KeystoneAPISpecCore.ValidateUpdate(old.KeystoneAPISpecCore, basePath, namespace)
}

// ValidateUpdate validates the KeystoneAPISpecCore spec during update
func (spec *KeystoneAPISpecCore) ValidateUpdate(_ KeystoneAPISpecCore, basePath *field.Path, namespace string) field.ErrorList {
func (spec *KeystoneAPISpecCore) ValidateUpdate(old KeystoneAPISpecCore, basePath *field.Path, namespace string) ([]string, field.ErrorList) {
var allErrs field.ErrorList
var allWarns []string

// Validate deprecated fields using centralized validation
warnings, errs := spec.validateDeprecatedFieldsUpdate(old, basePath)
allWarns = append(allWarns, warnings...)
allErrs = append(allErrs, errs...)

// validate the service override key is valid
allErrs = append(allErrs, service.ValidateRoutedOverrides(basePath.Child("override").Child("service"), spec.Override.Service)...)
Expand All @@ -165,7 +184,7 @@ func (spec *KeystoneAPISpecCore) ValidateUpdate(_ KeystoneAPISpecCore, basePath
// Validate external Keystone API configuration
allErrs = append(allErrs, spec.ValidateExternalKeystoneAPI(basePath)...)

return allErrs
return allWarns, allErrs
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
Expand Down Expand Up @@ -269,6 +288,57 @@ func (spec *KeystoneAPISpecCore) ValidateExternalKeystoneAPI(basePath *field.Pat
return allErrs
}

// getDeprecatedFields returns the centralized list of deprecated fields for KeystoneAPISpecCore
func (spec *KeystoneAPISpecCore) getDeprecatedFields(old *KeystoneAPISpecCore) []common_webhook.DeprecatedFieldUpdate {
// Handle NewValue pointer - NotificationsBus can be nil
var newValue *string
if spec.NotificationsBus != nil {
newValue = &spec.NotificationsBus.Cluster
}

deprecatedFields := []common_webhook.DeprecatedFieldUpdate{
{
DeprecatedFieldName: "rabbitMqClusterName",
NewFieldPath: []string{"notificationsBus", "cluster"},
NewDeprecatedValue: &spec.RabbitMqClusterName,
NewValue: newValue,
},
}

// If old spec is provided (UPDATE operation), add old values
if old != nil {
deprecatedFields[0].OldDeprecatedValue = &old.RabbitMqClusterName
}

return deprecatedFields
}

// validateDeprecatedFieldsCreate validates deprecated fields during CREATE operations
func (spec *KeystoneAPISpecCore) validateDeprecatedFieldsCreate(basePath *field.Path) ([]string, field.ErrorList) {
// Get deprecated fields list (without old values for CREATE)
deprecatedFieldsUpdate := spec.getDeprecatedFields(nil)

// Convert to DeprecatedField list for CREATE validation
deprecatedFields := make([]common_webhook.DeprecatedField, len(deprecatedFieldsUpdate))
for i, df := range deprecatedFieldsUpdate {
deprecatedFields[i] = common_webhook.DeprecatedField{
DeprecatedFieldName: df.DeprecatedFieldName,
NewFieldPath: df.NewFieldPath,
DeprecatedValue: df.NewDeprecatedValue,
NewValue: df.NewValue,
}
}

return common_webhook.ValidateDeprecatedFieldsCreate(deprecatedFields, basePath), nil
}

// validateDeprecatedFieldsUpdate validates deprecated fields during UPDATE operations
func (spec *KeystoneAPISpecCore) validateDeprecatedFieldsUpdate(old KeystoneAPISpecCore, basePath *field.Path) ([]string, field.ErrorList) {
// Get deprecated fields list with old values
deprecatedFields := spec.getDeprecatedFields(&old)
return common_webhook.ValidateDeprecatedFieldsUpdate(deprecatedFields, basePath)
}

// SetDefaultRouteAnnotations sets HAProxy timeout values of the route
func (spec *KeystoneAPISpecCore) SetDefaultRouteAnnotations(annotations map[string]string) {
const haProxyAnno = "haproxy.router.openshift.io/timeout"
Expand Down
6 changes: 6 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading