diff --git a/api/bases/core.openstack.org_openstackcontrolplanes.yaml b/api/bases/core.openstack.org_openstackcontrolplanes.yaml index 4d78d2fef..c43a189cb 100644 --- a/api/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/api/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -40,6 +40,49 @@ spec: type: object spec: properties: + applicationCredential: + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + default: 365 + minimum: 2 + type: integer + gracePeriodDays: + default: 182 + minimum: 1 + type: integer + roles: + default: + - service + items: + type: string + minItems: 1 + type: array + unrestricted: + default: false + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: self.gracePeriodDays < self.expirationDays barbican: properties: apiOverride: @@ -166,6 +209,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -674,6 +758,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -1703,6 +1828,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -3581,6 +3747,47 @@ spec: type: object type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -4509,6 +4716,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' cnfAPIOverride: properties: route: @@ -6444,6 +6692,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -8096,6 +8385,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -9163,6 +9493,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -9993,6 +10364,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' cellOverride: additionalProperties: properties: @@ -11061,6 +11473,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -12543,6 +12996,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -13618,6 +14112,47 @@ spec: type: string swift: properties: + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -14265,6 +14800,88 @@ spec: type: string type: object type: object + applicationCredentialAodh: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' + applicationCredentialCeilometer: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' cloudKittyApiOverride: properties: route: @@ -16165,6 +16782,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean diff --git a/api/core/v1beta1/openstackcontrolplane_types.go b/api/core/v1beta1/openstackcontrolplane_types.go index e74e8d50a..0efd9ab2b 100644 --- a/api/core/v1beta1/openstackcontrolplane_types.go +++ b/api/core/v1beta1/openstackcontrolplane_types.go @@ -225,6 +225,14 @@ type OpenStackControlPlaneSpec struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // Watcher - Parameters related to the Watcher service Watcher WatcherSection `json:"watcher,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // ApplicationCredential - Global configuration for ApplicationCredentials. + // Both this global section AND the per-service applicationCredential section + // must be enabled for a service to use ApplicationCredentials. + // If omitted, defaults to enabled=false with standard expiration/grace periods. + ApplicationCredential ApplicationCredentialSection `json:"applicationCredential,omitempty"` } // TLSSection defines the desired state of TLS configuration @@ -419,6 +427,13 @@ type PlacementSection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // APIOverride, provides the ability to override the generated manifest of several child resources. APIOverride Override `json:"apiOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // GlanceSection defines the desired state of Glance service @@ -445,6 +460,13 @@ type GlanceSection struct { // Convenient to avoid podname (and thus hostname) collision between different deployments. // Useful for CI jobs as well as preproduction and production environments that use the same storage backend, etc. UniquePodNames bool `json:"uniquePodNames"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // CinderSection defines the desired state of Cinder service @@ -471,6 +493,13 @@ type CinderSection struct { // Convenient to avoid podname (and thus hostname) collision between different deployments. // Useful for CI jobs as well as preproduction and production environments that use the same storage backend, etc. UniquePodNames bool `json:"uniquePodNames"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // GaleraSection defines the desired state of Galera services @@ -564,6 +593,13 @@ type NeutronSection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // APIOverride, provides the ability to override the generated manifest of several child resources. APIOverride Override `json:"apiOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // NovaSection defines the desired state of Nova services @@ -590,6 +626,13 @@ type NovaSection struct { // for a nova cell. cell0 never have compute nodes and therefore it won't have a noVNCProxy deployed. // Providing an override for cell0 noVNCProxy does not have an effect. CellOverride map[string]NovaCellOverrideSpec `json:"cellOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // NovaCellOverrideSpec to override the generated manifest of several child resources. @@ -620,6 +663,13 @@ type HeatSection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // CnfAPIOverride, provides the ability to override the generated manifest of several child resources. CnfAPIOverride Override `json:"cnfAPIOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // IronicSection defines the desired state of Ironic services @@ -644,6 +694,13 @@ type IronicSection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // InspectorOverride, provides the ability to override the generated manifest of several child resources. InspectorOverride Override `json:"inspectorOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // ManilaSection defines the desired state of Manila service @@ -663,6 +720,13 @@ type ManilaSection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // APIOverride, provides the ability to override the generated manifest of several child resources. APIOverride Override `json:"apiOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // HorizonSection defines the desired state of Horizon services @@ -716,6 +780,20 @@ type TelemetrySection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // AlertmanagerOverride, provides the ability to override the generated manifest of several child resources. AlertmanagerOverride Override `json:"alertmanagerOverride,omitempty"` + + // ApplicationCredentialCeilometer allows service-specific overrides of the global AC configuration for Ceilometer. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredentialCeilometer *ServiceAppCredSection `json:"applicationCredentialCeilometer"` + + // ApplicationCredentialAodh allows service-specific overrides of the global AC configuration for Aodh. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredentialAodh *ServiceAppCredSection `json:"applicationCredentialAodh"` } // SwiftSection defines the desired state of Swift service @@ -735,6 +813,13 @@ type SwiftSection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // ProxyOverride, provides the ability to override the generated manifest of several child resources. ProxyOverride Override `json:"proxyOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // OctaviaSection defines the desired state of the Octavia service @@ -754,6 +839,13 @@ type OctaviaSection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // APIOverride, provides the ability to override the generated manifest of several child resources. APIOverride Override `json:"apiOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // DesignateSection defines the desired state of the Designate service @@ -773,6 +865,13 @@ type DesignateSection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // APIOverride, provides the ability to override the generated manifest of several child resources. APIOverride Override `json:"apiOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // BarbicanSection defines the desired state of Barbican service @@ -792,6 +891,13 @@ type BarbicanSection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // APIOverride, provides the ability to override the generated manifest of several child resources. APIOverride Override `json:"apiOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` } // RedisSection defines the desired state of the Redis service @@ -833,6 +939,94 @@ type WatcherSection struct { // +operator-sdk:csv:customresourcedefinitions:type=spec // APIOverride, provides the ability to override the generated manifest of several child resources. APIOverride Override `json:"apiOverride,omitempty"` + + // ApplicationCredential allows service-specific overrides of the global AC configuration. + // +operator-sdk:csv:customresourcedefinitions:type=spec + // +kubebuilder:validation:Optional + // +nullable + // +kubebuilder:default={enabled:false} + ApplicationCredential *ServiceAppCredSection `json:"applicationCredential"` +} + +// +kubebuilder:validation:XValidation:rule="self.gracePeriodDays < self.expirationDays",message="gracePeriodDays must be smaller than expirationDays" +// ApplicationCredentialSection defines the desired configuration for ApplicationCredentials +type ApplicationCredentialSection struct { + // Enabled indicates whether an ApplicationCredential should be created + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + Enabled bool `json:"enabled"` + + // ExpirationDays sets the lifetime in days for the AC + // +kubebuilder:validation:Optional + // +kubebuilder:default=365 + // +kubebuilder:validation:Minimum=2 + ExpirationDays *int `json:"expirationDays"` + + // GracePeriodDays sets how many days before expiration the AC should be rotated + // +kubebuilder:validation:Optional + // +kubebuilder:default=182 + // +kubebuilder:validation:Minimum=1 + GracePeriodDays *int `json:"gracePeriodDays"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={"service"} + // +kubebuilder:validation:MinItems=1 + // Roles to assign to the ApplicationCredential + Roles []string `json:"roles"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + // Whether the AC should be unrestricted + Unrestricted *bool `json:"unrestricted"` + + // AccessRules lets supply a custom list of rules + // If unset, no accessRules field is emitted + // +kubebuilder:validation:Optional + // +listType=atomic + AccessRules []ACRule `json:"accessRules,omitempty"` +} + +// +kubebuilder:validation:XValidation:rule="!(has(self.expirationDays) && has(self.gracePeriodDays)) || self.gracePeriodDays < self.expirationDays",message="gracePeriodDays must be smaller than expirationDays" +// ServiceAppCredSection allows service-specific overrides of the global AC configuration +type ServiceAppCredSection struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + Enabled bool `json:"enabled"` + + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Minimum=2 + ExpirationDays *int `json:"expirationDays,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Minimum=1 + GracePeriodDays *int `json:"gracePeriodDays,omitempty"` + + // +kubebuilder:validation:Optional + // Roles to assign to the ApplicationCredential + Roles []string `json:"roles,omitempty"` + + // +kubebuilder:validation:Optional + // Whether the AC should be unrestricted + Unrestricted *bool `json:"unrestricted,omitempty"` + + // AccessRules lets the service override either the global rules + // +kubebuilder:validation:Optional + // +listType=atomic + AccessRules []ACRule `json:"accessRules,omitempty"` +} + +// ACRule describes a single access rule for an ApplicationCredential +// +k8s:openapi-gen=true +type ACRule struct { + // Service is the name of the service to target (e.g. "identity"). + // +kubebuilder:validation:Required + Service string `json:"service"` + // Path is the HTTP path (e.g. "/v3/auth/tokens"). + // +kubebuilder:validation:Required + Path string `json:"path"` + // Method is the HTTP method to allow (e.g. "POST"). + // +kubebuilder:validation:Required + Method string `json:"method"` } // OpenStackControlPlaneStatus defines the observed state of OpenStackControlPlane diff --git a/api/core/v1beta1/openstackcontrolplane_webhook.go b/api/core/v1beta1/openstackcontrolplane_webhook.go index 66208c5d9..20af0c67e 100644 --- a/api/core/v1beta1/openstackcontrolplane_webhook.go +++ b/api/core/v1beta1/openstackcontrolplane_webhook.go @@ -1081,6 +1081,12 @@ func (r *OpenStackControlPlane) DefaultServices() { } } + // Initialize ApplicationCredential (watcher specific) field to avoid null value + // This ensures consistent behavior with other services. + if r.Spec.Watcher.ApplicationCredential == nil { + r.Spec.Watcher.ApplicationCredential = &ServiceAppCredSection{Enabled: false} + } + } // DefaultLabel - adding default label to the OpenStackControlPlane @@ -1147,7 +1153,7 @@ func (r *OpenStackControlPlane) ValidateNotificationsBusInstance(basePath *field // NotificationsBusInstance is set and must be equal to an existing // deployed rabbitmq instance, otherwise we should fail because it // does not represent a valid string - for k := range(*r.Spec.Rabbitmq.Templates) { + for k := range *r.Spec.Rabbitmq.Templates { if *r.Spec.NotificationsBusInstance == k { return nil } diff --git a/api/core/v1beta1/zz_generated.deepcopy.go b/api/core/v1beta1/zz_generated.deepcopy.go index 2af599dd5..dc34ad7c2 100644 --- a/api/core/v1beta1/zz_generated.deepcopy.go +++ b/api/core/v1beta1/zz_generated.deepcopy.go @@ -51,6 +51,61 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ACRule) DeepCopyInto(out *ACRule) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACRule. +func (in *ACRule) DeepCopy() *ACRule { + if in == nil { + return nil + } + out := new(ACRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationCredentialSection) DeepCopyInto(out *ApplicationCredentialSection) { + *out = *in + if in.ExpirationDays != nil { + in, out := &in.ExpirationDays, &out.ExpirationDays + *out = new(int) + **out = **in + } + if in.GracePeriodDays != nil { + in, out := &in.GracePeriodDays, &out.GracePeriodDays + *out = new(int) + **out = **in + } + if in.Roles != nil { + in, out := &in.Roles, &out.Roles + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Unrestricted != nil { + in, out := &in.Unrestricted, &out.Unrestricted + *out = new(bool) + **out = **in + } + if in.AccessRules != nil { + in, out := &in.AccessRules, &out.AccessRules + *out = make([]ACRule, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationCredentialSection. +func (in *ApplicationCredentialSection) DeepCopy() *ApplicationCredentialSection { + if in == nil { + return nil + } + out := new(ApplicationCredentialSection) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BarbicanSection) DeepCopyInto(out *BarbicanSection) { *out = *in @@ -60,6 +115,11 @@ func (in *BarbicanSection) DeepCopyInto(out *BarbicanSection) { (*in).DeepCopyInto(*out) } in.APIOverride.DeepCopyInto(&out.APIOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanSection. @@ -153,6 +213,11 @@ func (in *CinderSection) DeepCopyInto(out *CinderSection) { (*in).DeepCopyInto(*out) } in.APIOverride.DeepCopyInto(&out.APIOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CinderSection. @@ -811,6 +876,11 @@ func (in *DesignateSection) DeepCopyInto(out *DesignateSection) { (*in).DeepCopyInto(*out) } in.APIOverride.DeepCopyInto(&out.APIOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DesignateSection. @@ -864,6 +934,11 @@ func (in *GlanceSection) DeepCopyInto(out *GlanceSection) { (*out)[key] = *val.DeepCopy() } } + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlanceSection. @@ -886,6 +961,11 @@ func (in *HeatSection) DeepCopyInto(out *HeatSection) { } in.APIOverride.DeepCopyInto(&out.APIOverride) in.CnfAPIOverride.DeepCopyInto(&out.CnfAPIOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeatSection. @@ -929,6 +1009,11 @@ func (in *IronicSection) DeepCopyInto(out *IronicSection) { } in.APIOverride.DeepCopyInto(&out.APIOverride) in.InspectorOverride.DeepCopyInto(&out.InspectorOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IronicSection. @@ -971,6 +1056,11 @@ func (in *ManilaSection) DeepCopyInto(out *ManilaSection) { (*in).DeepCopyInto(*out) } in.APIOverride.DeepCopyInto(&out.APIOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManilaSection. @@ -1018,6 +1108,11 @@ func (in *NeutronSection) DeepCopyInto(out *NeutronSection) { (*in).DeepCopyInto(*out) } in.APIOverride.DeepCopyInto(&out.APIOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NeutronSection. @@ -1062,6 +1157,11 @@ func (in *NovaSection) DeepCopyInto(out *NovaSection) { (*out)[key] = *val.DeepCopy() } } + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaSection. @@ -1083,6 +1183,11 @@ func (in *OctaviaSection) DeepCopyInto(out *OctaviaSection) { (*in).DeepCopyInto(*out) } in.APIOverride.DeepCopyInto(&out.APIOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OctaviaSection. @@ -1221,6 +1326,7 @@ func (in *OpenStackControlPlaneSpec) DeepCopyInto(out *OpenStackControlPlaneSpec **out = **in } in.Watcher.DeepCopyInto(&out.Watcher) + in.ApplicationCredential.DeepCopyInto(&out.ApplicationCredential) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackControlPlaneSpec. @@ -1530,6 +1636,11 @@ func (in *PlacementSection) DeepCopyInto(out *PlacementSection) { (*in).DeepCopyInto(*out) } in.APIOverride.DeepCopyInto(&out.APIOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementSection. @@ -1594,6 +1705,46 @@ func (in *RedisSection) DeepCopy() *RedisSection { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAppCredSection) DeepCopyInto(out *ServiceAppCredSection) { + *out = *in + if in.ExpirationDays != nil { + in, out := &in.ExpirationDays, &out.ExpirationDays + *out = new(int) + **out = **in + } + if in.GracePeriodDays != nil { + in, out := &in.GracePeriodDays, &out.GracePeriodDays + *out = new(int) + **out = **in + } + if in.Roles != nil { + in, out := &in.Roles, &out.Roles + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Unrestricted != nil { + in, out := &in.Unrestricted, &out.Unrestricted + *out = new(bool) + **out = **in + } + if in.AccessRules != nil { + in, out := &in.AccessRules, &out.AccessRules + *out = make([]ACRule, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAppCredSection. +func (in *ServiceAppCredSection) DeepCopy() *ServiceAppCredSection { + if in == nil { + return nil + } + out := new(ServiceAppCredSection) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceDefaults) DeepCopyInto(out *ServiceDefaults) { *out = *in @@ -1623,6 +1774,11 @@ func (in *SwiftSection) DeepCopyInto(out *SwiftSection) { (*in).DeepCopyInto(*out) } in.ProxyOverride.DeepCopyInto(&out.ProxyOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SwiftSection. @@ -1750,6 +1906,16 @@ func (in *TelemetrySection) DeepCopyInto(out *TelemetrySection) { in.CloudKittyAPIOverride.DeepCopyInto(&out.CloudKittyAPIOverride) in.PrometheusOverride.DeepCopyInto(&out.PrometheusOverride) in.AlertmanagerOverride.DeepCopyInto(&out.AlertmanagerOverride) + if in.ApplicationCredentialCeilometer != nil { + in, out := &in.ApplicationCredentialCeilometer, &out.ApplicationCredentialCeilometer + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } + if in.ApplicationCredentialAodh != nil { + in, out := &in.ApplicationCredentialAodh, &out.ApplicationCredentialAodh + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TelemetrySection. @@ -1771,6 +1937,11 @@ func (in *WatcherSection) DeepCopyInto(out *WatcherSection) { (*in).DeepCopyInto(*out) } in.APIOverride.DeepCopyInto(&out.APIOverride) + if in.ApplicationCredential != nil { + in, out := &in.ApplicationCredential, &out.ApplicationCredential + *out = new(ServiceAppCredSection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherSection. diff --git a/api/go.mod b/api/go.mod index 5a65a5f30..2a616c9ac 100644 --- a/api/go.mod +++ b/api/go.mod @@ -1,4 +1,4 @@ -module github.com/openstack-k8s-operators/openstack-operator/api +module github.com/openstack-k8s-operators/openstack-operator/apis go 1.24.4 @@ -42,6 +42,8 @@ require ( sigs.k8s.io/controller-runtime v0.19.7 ) +require github.com/openstack-k8s-operators/openstack-operator/api v0.0.0-20251202072739-b102924657dd + require ( github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -81,7 +83,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openshift/api v3.9.0+incompatible // indirect github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251103072528-9eb684fef4ef // indirect - github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251122131503-b76943960b6c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -144,3 +145,24 @@ replace k8s.io/code-generator => k8s.io/code-generator v0.31.13 //allow-merging replace k8s.io/component-base => k8s.io/component-base v0.31.13 //allow-merging replace github.com/cert-manager/cmctl/v2 => github.com/cert-manager/cmctl/v2 v2.1.2-0.20241127223932-88edb96860cf //allow-merging + +// appcred related changes +replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/Deydra71/keystone-operator/api v0.0.0-20251201081347-2823ce61c025 + +replace github.com/openstack-k8s-operators/barbican-operator/api => github.com/Deydra71/barbican-operator/api v0.0.0-20251125072812-68cb2ae77c6a + +replace github.com/openstack-k8s-operators/cinder-operator/api => github.com/Deydra71/cinder-operator/api v0.0.0-20251125074431-5ef9b351fb12 + +replace github.com/openstack-k8s-operators/glance-operator/api => github.com/Deydra71/glance-operator/api v0.0.0-20251125073306-470bb653b540 + +replace github.com/openstack-k8s-operators/swift-operator/api => github.com/Deydra71/swift-operator/api v0.0.0-20251125071516-6e972449c1da + +replace github.com/openstack-k8s-operators/manila-operator/api => github.com/Deydra71/manila-operator/api v0.0.0-20251125080051-8dbb26682df9 + +replace github.com/openstack-k8s-operators/neutron-operator/api => github.com/Deydra71/neutron-operator/api v0.0.0-20251125074539-3e6b09e24496 + +replace github.com/openstack-k8s-operators/placement-operator/api => github.com/Deydra71/placement-operator/api v0.0.0-20251125080720-2e641c2dd826 + +replace github.com/openstack-k8s-operators/designate-operator/api => github.com/Deydra71/designate-operator/api v0.0.0-20251201130300-ee35802d97e1 + +replace github.com/openstack-k8s-operators/octavia-operator/api => github.com/Deydra71/octavia-operator/api v0.0.0-20251125074921-aafe26094259 diff --git a/api/go.sum b/api/go.sum index a37d06844..bbba26db6 100644 --- a/api/go.sum +++ b/api/go.sum @@ -1,3 +1,23 @@ +github.com/Deydra71/barbican-operator/api v0.0.0-20251125072812-68cb2ae77c6a h1:09ZzxiVc12vFLj0lw3S5MwzhvZPXmCssjr1PQX5+xuo= +github.com/Deydra71/barbican-operator/api v0.0.0-20251125072812-68cb2ae77c6a/go.mod h1:SS+6KFheIRSkkjAYeMTEirWm/xHrZUf1J9XBIft/bKM= +github.com/Deydra71/cinder-operator/api v0.0.0-20251125074431-5ef9b351fb12 h1:yNwhOAY5lnMzTtveBfMAJfvXgQyPcV4WO3YbTy+Fspw= +github.com/Deydra71/cinder-operator/api v0.0.0-20251125074431-5ef9b351fb12/go.mod h1:swwsxHN0P0c/PL58uqJAZXvdqWxPz9LFB8OG5l4CSso= +github.com/Deydra71/designate-operator/api v0.0.0-20251201130300-ee35802d97e1 h1:LQTB9g6qSl0tK0TtfuGZ2LzE+hVDlYLCezTWdvkY70I= +github.com/Deydra71/designate-operator/api v0.0.0-20251201130300-ee35802d97e1/go.mod h1:CFre3O8XfJT1qmr9l5PgQlMAXIaDozWUUSPZFiPRPGE= +github.com/Deydra71/glance-operator/api v0.0.0-20251125073306-470bb653b540 h1:ZHrqOHYBiOwSk604AUjL+fUGEgBWUTAKoiMEjFMN5cc= +github.com/Deydra71/glance-operator/api v0.0.0-20251125073306-470bb653b540/go.mod h1:cs0Ws8MzFBNcrXs8WrpmVRafeEoLNnTx8fjSBSrnR1Q= +github.com/Deydra71/keystone-operator/api v0.0.0-20251201081347-2823ce61c025 h1:9tVdplONUg5JpgAczs7wV4Swkc0mqAueWVT58cin9ZY= +github.com/Deydra71/keystone-operator/api v0.0.0-20251201081347-2823ce61c025/go.mod h1:b98Jl8eyUw8V07l9YiuQnoMlnWC748oV8IhXH15NCC4= +github.com/Deydra71/manila-operator/api v0.0.0-20251125080051-8dbb26682df9 h1:MHXveFFkhRzXi8+hZjh5zLVlV+NocVsym3NYhr+LEUw= +github.com/Deydra71/manila-operator/api v0.0.0-20251125080051-8dbb26682df9/go.mod h1:BWHGjAf4EJC9WuGYchWAvrrlV6TrBnanPemvRCQ8xMc= +github.com/Deydra71/neutron-operator/api v0.0.0-20251125074539-3e6b09e24496 h1:+TrgyM8QgBk8sYfRj8CS6TPFMnQ8f4O7qfv/NCNy+QA= +github.com/Deydra71/neutron-operator/api v0.0.0-20251125074539-3e6b09e24496/go.mod h1:RffdzUML/sicsT3EjAm9P9xecBwHeZV3BpWhh/Ln8Lo= +github.com/Deydra71/octavia-operator/api v0.0.0-20251125074921-aafe26094259 h1:/fcHRESvUJrmwlgIhokWNo23GJR3BTmEWqse4agxoLg= +github.com/Deydra71/octavia-operator/api v0.0.0-20251125074921-aafe26094259/go.mod h1:pNm0SXf3kvVFf3aZOZoZ8PsawDI0DaPIrCTwzdJWD2I= +github.com/Deydra71/placement-operator/api v0.0.0-20251125080720-2e641c2dd826 h1:o7+MbG7PDseypW5VsAVz14x4VHtsNkX9eLCc1k36ac4= +github.com/Deydra71/placement-operator/api v0.0.0-20251125080720-2e641c2dd826/go.mod h1:Hk9mKb836A09vSIsMVdh2O0ch0H65xLz2IZWd8x9g8o= +github.com/Deydra71/swift-operator/api v0.0.0-20251125071516-6e972449c1da h1:P+9gHdFTR7h+A3dYpARawXktiWSOngwqAfC42l3hcHk= +github.com/Deydra71/swift-operator/api v0.0.0-20251125071516-6e972449c1da/go.mod h1:iMSZnbR9M3AfAIwInL4umy+y59Aj4Lx0b1+TPB4M2Is= 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= @@ -114,14 +134,6 @@ github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= 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/barbican-operator/api v0.6.1-0.20251125115107-f489fa5ceb3c h1:cQRQbyBmYfS4Ubj/0hwj5IVs0AAZqMyhqA1i50r4oTI= -github.com/openstack-k8s-operators/barbican-operator/api v0.6.1-0.20251125115107-f489fa5ceb3c/go.mod h1:HURjuNEy1OrE7bn2snCYMzk148bHaD7u7JleEu9h5ws= -github.com/openstack-k8s-operators/cinder-operator/api v0.6.1-0.20251125143015-0548029d9df0 h1:BfTkwCr4sP5PlsQu65CHO8ZTIDNK7BfB0UWbFTRh730= -github.com/openstack-k8s-operators/cinder-operator/api v0.6.1-0.20251125143015-0548029d9df0/go.mod h1:IrtEtP+mjH2pHLQvk9ZzKebtjMW50HKG0IhooZgmmDw= -github.com/openstack-k8s-operators/designate-operator/api v0.6.1-0.20251125143014-f2b7132f2963 h1:oMTI+YjFMxX2b2Ac9J/YSdpkB1egfSdFDype9sD2y2E= -github.com/openstack-k8s-operators/designate-operator/api v0.6.1-0.20251125143014-f2b7132f2963/go.mod h1:qzxe5RJalpItYbD3+pDIGMrPer/P5M50LQQyuusU7lI= -github.com/openstack-k8s-operators/glance-operator/api v0.6.1-0.20251128173340-057b4476099f h1:WH/zNMwv/gH1oOBitGF3asIsFQgpoZNVJg9RU3TAsZY= -github.com/openstack-k8s-operators/glance-operator/api v0.6.1-0.20251128173340-057b4476099f/go.mod h1:e/7kDMADuTn7S+oVWsbaB6gr0snZ4azV2oykPhZozx8= github.com/openstack-k8s-operators/heat-operator/api v0.6.1-0.20251125115646-26b110b9f3e7 h1:dEx1vGdyq071vl0q0D74NxFLf67T5UJmoCslmlN9DLY= github.com/openstack-k8s-operators/heat-operator/api v0.6.1-0.20251125115646-26b110b9f3e7/go.mod h1:ABIb9YiV6nmcGC4h49X8eJIeqmnG/6j77eIiYQGWrUc= github.com/openstack-k8s-operators/horizon-operator/api v0.6.1-0.20251125145341-8bc80a35f9c5 h1:VxWuwuIbNmexcpL2JpLaWAj1aFR0v1BfehIKpbCwIfY= @@ -130,8 +142,6 @@ github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251124130651-1 github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251124130651-1ff40691b66d/go.mod h1:U6fKKmnazlF/il/jP5DQdKzkh0QX3Z95Pau46KoeTMo= github.com/openstack-k8s-operators/ironic-operator/api v0.6.1-0.20251125165311-d458faac6271 h1:ExfEekNT07SmCYx0S8daR8ZyWKTxj9jgV3LKNlUvYvw= github.com/openstack-k8s-operators/ironic-operator/api v0.6.1-0.20251125165311-d458faac6271/go.mod h1:383ooOUA0DHtaeR9etcM7oKiTz5s8QMzrAZRunzZUrY= -github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251128160419-8b3a77972a77 h1:XzVPjfzxDJwgW8sNGv9K577Ui2mb6Mp3sDItuDmTv9E= -github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251128160419-8b3a77972a77/go.mod h1:b98Jl8eyUw8V07l9YiuQnoMlnWC748oV8IhXH15NCC4= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c h1:wM8qXCB5mQwSosCvtaydzuXitWVVKBHTzH0A2znQ+Jg= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c/go.mod h1:+Me0raWPPdz8gRi9D4z1khmvUgS9vIKAVC8ckg1yJZU= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251103072528-9eb684fef4ef h1:Ql4G7sRHpqWFGwXypN7MorDGUWv4jz5n34ayzVt3R9E= @@ -140,26 +150,18 @@ github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.202511221 github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20251122131503-b76943960b6c/go.mod h1:fy1lvz3uuzzh01DKKdgroXvmJgMpJBsvl2r9eTtAll0= github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251122131503-b76943960b6c h1:YdTv3RXKfFg2QHXtLJSnKaPruslyp1Fd+ArcsxLcy6k= github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251122131503-b76943960b6c/go.mod h1:lgYyrXEYA2BPsq4Kg6dqa+QsHgOjMPyOsEYrvyYW3jk= -github.com/openstack-k8s-operators/manila-operator/api v0.6.1-0.20251128162048-2454ee694c60 h1:MzY54K+s30dSTw6pLB+hu1hVo1NB2lo9XbbTifk0Lxc= -github.com/openstack-k8s-operators/manila-operator/api v0.6.1-0.20251128162048-2454ee694c60/go.mod h1:GkUShNT3xXH7SWfgZfWeDTcKm2D4JL0Tv/qpBvZtV4w= github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20251125174936-3989cc50de31 h1:pcxorrSN8YlCad3RgJWfMOdhlYtiT6ztYs+oTIYsxxc= github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20251125174936-3989cc50de31/go.mod h1:rSNA5Yd+Xt6eJ6KGgXeWjSEeU9VcaPdt6B9IenjeDqY= -github.com/openstack-k8s-operators/neutron-operator/api v0.6.1-0.20251125150830-633e42336356 h1:YPD0uZZ3fbEfbo/HOGi/5AK+BrFC3bErg0OBJyZyRUg= -github.com/openstack-k8s-operators/neutron-operator/api v0.6.1-0.20251125150830-633e42336356/go.mod h1:CNJF0ekxqVqrEMLGL/hV489p0RrVoB3alMhjE9cxv1o= github.com/openstack-k8s-operators/nova-operator/api v0.6.1-0.20251127143706-407c63ad016a h1:lYmks3cHkbmBX1+I9a8gnajkfSddLAvNqakxx8GV3AA= github.com/openstack-k8s-operators/nova-operator/api v0.6.1-0.20251127143706-407c63ad016a/go.mod h1:4Bp2ias9AUXvPBOSOlEkuuegDkAcJEYB9K1UtmX4q8c= -github.com/openstack-k8s-operators/octavia-operator/api v0.6.1-0.20251127161151-38d49bbc1c5d h1:MrDtVqdSNQpl+3ttzzbvDx/83ZQJ5ArUGcy+XJQQcic= -github.com/openstack-k8s-operators/octavia-operator/api v0.6.1-0.20251127161151-38d49bbc1c5d/go.mod h1:rI8CZdEfQB+QRBR3uFmA2i/D9KxTNTTZWFH3MhpID5A= github.com/openstack-k8s-operators/openstack-baremetal-operator/api v0.6.1-0.20251126095155-fe40bf12d8c6 h1:J/DqrMqnZEBUkBQc+GUVNIklI+nBSfo6z5Pf9sbxl5Y= github.com/openstack-k8s-operators/openstack-baremetal-operator/api v0.6.1-0.20251126095155-fe40bf12d8c6/go.mod h1:SUcjA0LDLte1F4bEmtfWfCEQRwwaACk9NCMvWNC0o0Y= +github.com/openstack-k8s-operators/openstack-operator/api v0.0.0-20251202072739-b102924657dd h1:iBdbG7lUfBazkEKooKui/Bepmir2itS0TjahSnp1hwU= +github.com/openstack-k8s-operators/openstack-operator/api v0.0.0-20251202072739-b102924657dd/go.mod h1:SUyEHPvF8sV9lAw/e9JH/5QVePfimPWYw66wF0GwPuI= github.com/openstack-k8s-operators/ovn-operator/api v0.6.1-0.20251127135801-f3d54911d811 h1:H1b2RlE9EsemU/dbtV96xIXxmGBLS2UcBtdSS0bYucw= github.com/openstack-k8s-operators/ovn-operator/api v0.6.1-0.20251127135801-f3d54911d811/go.mod h1:LTrCp/cf4HFozb0ZhblhQKO0jUmmBnvD8zFocOsasAM= -github.com/openstack-k8s-operators/placement-operator/api v0.6.1-0.20251125174406-42e6ab0985af h1:eX8vTK5MZIVNLoUtSZ64VkuNg4p9BuwWm4de8HAgi40= -github.com/openstack-k8s-operators/placement-operator/api v0.6.1-0.20251125174406-42e6ab0985af/go.mod h1:zvM01kqm1Axt1EJLzL5Cn4FzMTeuWdTy+77cRF3MR7M= 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/openstack-k8s-operators/swift-operator/api v0.6.1-0.20251125135122-5157cb4f1cc5 h1:AEvZZoCKgKzlEbkvGa6EtHD4JhrkT0DdTz4otvwlfl0= -github.com/openstack-k8s-operators/swift-operator/api v0.6.1-0.20251125135122-5157cb4f1cc5/go.mod h1:kHJTmMo++FSs+U/1r7kB3lfzxXOIUklfNp6n2tk0y6E= github.com/openstack-k8s-operators/telemetry-operator/api v0.6.1-0.20251124182638-bf35154a77d3 h1:rLz1nz/4P9K5uRPyT3IPjUHTF2Hk0RfUX6rYCayvb1Q= github.com/openstack-k8s-operators/telemetry-operator/api v0.6.1-0.20251124182638-bf35154a77d3/go.mod h1:JZi2/dRupf7GVitj8JY3qI34WC4MZKf7zs6Rq9W7yBQ= github.com/openstack-k8s-operators/watcher-operator/api v0.6.1-0.20251127070224-35dcc7e8c5b2 h1:wIMJJtAjepOd6BA3UkkeCeKG/7/JB2QEoHzPPwunLnU= diff --git a/bindata/crds/crds.yaml b/bindata/crds/crds.yaml index c072f1e9b..21a65d92e 100644 --- a/bindata/crds/crds.yaml +++ b/bindata/crds/crds.yaml @@ -206,6 +206,49 @@ spec: type: object spec: properties: + applicationCredential: + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + default: 365 + minimum: 2 + type: integer + gracePeriodDays: + default: 182 + minimum: 1 + type: integer + roles: + default: + - service + items: + type: string + minItems: 1 + type: array + unrestricted: + default: false + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: self.gracePeriodDays < self.expirationDays barbican: properties: apiOverride: @@ -332,6 +375,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -840,6 +924,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -1869,6 +1994,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -3747,6 +3913,47 @@ spec: type: object type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -4675,6 +4882,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' cnfAPIOverride: properties: route: @@ -6610,6 +6858,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -8262,6 +8551,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -9329,6 +9659,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -10159,6 +10530,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' cellOverride: additionalProperties: properties: @@ -11227,6 +11639,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -12709,6 +13162,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -13784,6 +14278,47 @@ spec: type: string swift: properties: + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -14431,6 +14966,88 @@ spec: type: string type: object type: object + applicationCredentialAodh: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' + applicationCredentialCeilometer: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' cloudKittyApiOverride: properties: route: @@ -16331,6 +16948,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean diff --git a/bindata/crds/keystone.openstack.org_keystoneapplicationcredentials.yaml b/bindata/crds/keystone.openstack.org_keystoneapplicationcredentials.yaml new file mode 100644 index 000000000..850f29743 --- /dev/null +++ b/bindata/crds/keystone.openstack.org_keystoneapplicationcredentials.yaml @@ -0,0 +1,214 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + creationTimestamp: null + name: keystoneapplicationcredentials.keystone.openstack.org +spec: + group: keystone.openstack.org + names: + kind: KeystoneApplicationCredential + listKind: KeystoneApplicationCredentialList + plural: keystoneapplicationcredentials + shortNames: + - appcred + singular: keystoneapplicationcredential + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Keystone ApplicationCredential ID + jsonPath: .status.acID + name: ACID + type: string + - description: Secret holding ApplicationCredential secret + jsonPath: .status.secretName + name: SecretName + type: string + - description: Last rotation time + format: date-time + jsonPath: .status.lastRotated + name: LastRotated + type: string + - description: When rotation becomes eligible + format: date-time + jsonPath: .status.rotationEligibleAt + name: RotationEligible + type: string + - description: Status + jsonPath: .status.conditions[0].status + name: Status + type: string + - description: Message + jsonPath: .status.conditions[0].message + name: Message + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: KeystoneApplicationCredential is the Schema for the applicationcredentials + API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: KeystoneApplicationCredentialSpec defines what the user can + set + properties: + accessRules: + description: AccessRules defines which services the ApplicationCredential + is permitted to access + items: + description: ACRule defines a additional access rule for an ApplicationCredential + properties: + method: + description: Method is the HTTP verb to allow (defaults to all + if empty) + type: string + path: + description: Path is the API path to allow + type: string + service: + description: Service is the OpenStack service type + type: string + type: object + type: array + expirationDays: + default: 365 + description: ExpirationDays sets the lifetime in days for the ApplicationCredential + minimum: 2 + type: integer + gracePeriodDays: + default: 182 + description: GracePeriodDays sets how many days before expiration + the ApplicationCredential should be rotated + minimum: 1 + type: integer + passwordSelector: + description: PasswordSelector for extracting the service password + type: string + roles: + description: Roles to assign to the ApplicationCredential + items: + type: string + minItems: 1 + type: array + secret: + description: Secret containing service user password + type: string + unrestricted: + default: false + description: Unrestricted indicates whether the ApplicationCredential + may be used to create or destroy other credentials or trusts + type: boolean + userName: + description: UserName - the Keystone user under which this ApplicationCredential + is created + type: string + required: + - passwordSelector + - roles + - secret + - userName + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: self.gracePeriodDays < self.expirationDays + status: + description: KeystoneApplicationCredentialStatus defines the observed + state + properties: + acID: + description: ACID - the ID in Keystone for this ApplicationCredential + type: string + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + createdAt: + description: CreatedAt - timestap of creation + format: date-time + type: string + expiresAt: + description: ExpiresAt - time of validity expiration + format: date-time + type: string + lastRotated: + description: LastRotated - timestamp when credentials were last rotated + format: date-time + type: string + rotationEligibleAt: + description: |- + RotationEligibleAt indicates when rotation becomes eligible (start of grace period window). + Computed as ExpiresAt - GracePeriodDays. The AC can be rotated after this timestamp. + format: date-time + type: string + secretName: + description: SecretName - name of the k8s Secret storing the ApplicationCredential + secret + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bindata/rbac/keystone-operator-rbac.yaml b/bindata/rbac/keystone-operator-rbac.yaml index e0acef4bb..4e4d3d68b 100644 --- a/bindata/rbac/keystone-operator-rbac.yaml +++ b/bindata/rbac/keystone-operator-rbac.yaml @@ -81,6 +81,24 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - secrets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update - apiGroups: - "" resources: @@ -129,6 +147,7 @@ rules: - keystone.openstack.org resources: - keystoneapis + - keystoneapplicationcredentials - keystoneendpoints - keystoneservices verbs: @@ -143,6 +162,7 @@ rules: - keystone.openstack.org resources: - keystoneapis/finalizers + - keystoneapplicationcredentials/finalizers - keystoneendpoints/finalizers - keystoneservices/finalizers verbs: @@ -152,6 +172,7 @@ rules: - keystone.openstack.org resources: - keystoneapis/status + - keystoneapplicationcredentials/status - keystoneendpoints/status - keystoneservices/status verbs: diff --git a/bindata/rbac/rbac.yaml b/bindata/rbac/rbac.yaml index 7aed4290f..a006df8fa 100644 --- a/bindata/rbac/rbac.yaml +++ b/bindata/rbac/rbac.yaml @@ -413,6 +413,7 @@ rules: - keystone.openstack.org resources: - keystoneapis + - keystoneapplicationcredentials verbs: - create - delete @@ -421,6 +422,14 @@ rules: - patch - update - watch +- apiGroups: + - keystone.openstack.org + resources: + - keystoneapplicationcredentials/status + verbs: + - get + - patch + - update - apiGroups: - machineconfiguration.openshift.io resources: diff --git a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml index 4d78d2fef..c43a189cb 100644 --- a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -40,6 +40,49 @@ spec: type: object spec: properties: + applicationCredential: + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + default: 365 + minimum: 2 + type: integer + gracePeriodDays: + default: 182 + minimum: 1 + type: integer + roles: + default: + - service + items: + type: string + minItems: 1 + type: array + unrestricted: + default: false + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: self.gracePeriodDays < self.expirationDays barbican: properties: apiOverride: @@ -166,6 +209,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -674,6 +758,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -1703,6 +1828,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -3581,6 +3747,47 @@ spec: type: object type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -4509,6 +4716,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' cnfAPIOverride: properties: route: @@ -6444,6 +6692,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -8096,6 +8385,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -9163,6 +9493,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -9993,6 +10364,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' cellOverride: additionalProperties: properties: @@ -11061,6 +11473,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean @@ -12543,6 +12996,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -13618,6 +14112,47 @@ spec: type: string swift: properties: + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: true type: boolean @@ -14265,6 +14800,88 @@ spec: type: string type: object type: object + applicationCredentialAodh: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' + applicationCredentialCeilometer: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' cloudKittyApiOverride: properties: route: @@ -16165,6 +16782,47 @@ spec: type: string type: object type: object + applicationCredential: + default: + enabled: false + nullable: true + properties: + accessRules: + items: + properties: + method: + type: string + path: + type: string + service: + type: string + required: + - method + - path + - service + type: object + type: array + x-kubernetes-list-type: atomic + enabled: + default: false + type: boolean + expirationDays: + minimum: 2 + type: integer + gracePeriodDays: + minimum: 1 + type: integer + roles: + items: + type: string + type: array + unrestricted: + type: boolean + type: object + x-kubernetes-validations: + - message: gracePeriodDays must be smaller than expirationDays + rule: '!(has(self.expirationDays) && has(self.gracePeriodDays)) + || self.gracePeriodDays < self.expirationDays' enabled: default: false type: boolean diff --git a/config/manifests/bases/openstack-operator.clusterserviceversion.yaml b/config/manifests/bases/openstack-operator.clusterserviceversion.yaml index e2a3e6bea..284985c89 100644 --- a/config/manifests/bases/openstack-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/openstack-operator.clusterserviceversion.yaml @@ -38,6 +38,9 @@ spec: kind: OpenStackControlPlane name: openstackcontrolplanes.core.openstack.org specDescriptors: + - description: ApplicationCredential - Parameters related to the ApplicationCredential + displayName: Application Credential + path: applicationCredential - description: Barbican - Parameters related to the Barbican service displayName: Barbican path: barbican @@ -48,6 +51,10 @@ spec: - description: TLS - overrides tls parameters for public endpoint displayName: TLS path: barbican.apiOverride.tls + - description: ApplicationCredential allows service-specific overrides of the + global AC configuration. + displayName: Application Credential + path: barbican.applicationCredential - description: Enabled - Whether Barbican service should be deployed and managed displayName: Enabled path: barbican.enabled @@ -66,6 +73,10 @@ spec: - description: TLS - overrides tls parameters for public endpoint displayName: TLS path: cinder.apiOverride.tls + - description: ApplicationCredential allows service-specific overrides of the + global AC configuration. + displayName: Application Credential + path: cinder.applicationCredential - description: Enabled - Whether Cinder service should be deployed and managed displayName: Enabled path: cinder.enabled @@ -131,6 +142,10 @@ spec: - description: TLS - overrides tls parameters for public endpoint displayName: TLS path: glance.apiOverrides.tls + - description: ApplicationCredential allows service-specific overrides of the + global AC configuration. + displayName: Application Credential + path: glance.applicationCredential - description: Enabled - Whether Glance service should be deployed and managed displayName: Enabled path: glance.enabled @@ -264,6 +279,10 @@ spec: - description: TLS - overrides tls parameters for public endpoint displayName: TLS path: neutron.apiOverride.tls + - description: ApplicationCredential allows service-specific overrides of the + global AC configuration. + displayName: Application Credential + path: neutron.applicationCredential - description: Enabled - Whether Neutron service should be deployed and managed displayName: Enabled path: neutron.enabled @@ -286,6 +305,10 @@ spec: - description: TLS - overrides tls parameters for public endpoint displayName: TLS path: nova.apiOverride.tls + - description: ApplicationCredential allows service-specific overrides of the + global AC configuration. + displayName: Application Credential + path: nova.applicationCredential - description: |- CellOverride, provides the ability to override the generated manifest of several child resources for a nova cell. cell0 never have compute nodes and therefore it won't have a noVNCProxy deployed. @@ -364,6 +387,10 @@ spec: - description: TLS - overrides tls parameters for public endpoint displayName: TLS path: placement.apiOverride.tls + - description: ApplicationCredential allows service-specific overrides of the + global AC configuration. + displayName: Application Credential + path: placement.applicationCredential - description: Enabled - Whether Placement service should be deployed and managed displayName: Enabled path: placement.enabled @@ -404,6 +431,10 @@ spec: - description: Swift - Parameters related to the Swift service displayName: Swift path: swift + - description: ApplicationCredential allows service-specific overrides of the + global AC configuration. + displayName: Application Credential + path: swift.applicationCredential - description: Enabled - Whether Swift service should be deployed and managed displayName: Enabled path: swift.enabled @@ -436,6 +467,10 @@ spec: - description: TLS - overrides tls parameters for public endpoint displayName: TLS path: telemetry.aodhApiOverride.tls + - description: ApplicationCredential allows service-specific overrides of the + global AC configuration. + displayName: Application Credential + path: telemetry.applicationCredential - description: Enabled - Whether OpenStack Telemetry services should be deployed and managed displayName: Enabled diff --git a/config/operator/manager_operator_images.yaml b/config/operator/manager_operator_images.yaml index 297e6edf6..60d080eb8 100644 --- a/config/operator/manager_operator_images.yaml +++ b/config/operator/manager_operator_images.yaml @@ -14,13 +14,13 @@ spec: - name: operator env: - name: RELATED_IMAGE_BARBICAN_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/barbican-operator@sha256:f6059a0fbf031d34dcf086d14ce8c0546caeaee23c5780e90b5037c5feee9fea + value: quay.io/rh-ee-vfisarov/barbican-operator@sha256:73cffae9d124feff122f50487d65e4d15620639a2e140563eb14e9fd2cbe60a1 - name: RELATED_IMAGE_CINDER_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/cinder-operator@sha256:1d60701214b39cdb0fa70bbe5710f9b131139a9f4b482c2db4058a04daefb801 + value: quay.io/rh-ee-vfisarov/cinder-operator@sha256:642d632298f7a740d6931f8c824d59a40f92caa655d3748f56d87a79b4281a2d - name: RELATED_IMAGE_DESIGNATE_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/designate-operator@sha256:9f68d7bc8c6bce38f46dee8a8272d5365c49fe7b32b2af52e8ac884e212f3a85 + value: quay.io/rh-ee-vfisarov/designate-operator@sha256:1cc3853c58180d265515323c643b1d75179e0e0cb2e92421151f3dc417c7e9bf - name: RELATED_IMAGE_GLANCE_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/glance-operator@sha256:abdb733b01e92ac17f565762f30f1d075b44c16421bd06e557f6bb3c319e1809 + value: quay.io/rh-ee-vfisarov/glance-operator@sha256:94a359fdaf34b2b3e63534d3eb9aa0868efaff836287f90cd2dc8703fd5d2fe6 - name: RELATED_IMAGE_HEAT_OPERATOR_MANAGER_IMAGE_URL value: quay.io/openstack-k8s-operators/heat-operator@sha256:c4abfc148600dfa85915f3dc911d988ea2335f26cb6b8d749fe79bfe53e5e429 - name: RELATED_IMAGE_HORIZON_OPERATOR_MANAGER_IMAGE_URL @@ -30,30 +30,16 @@ spec: - name: RELATED_IMAGE_IRONIC_OPERATOR_MANAGER_IMAGE_URL value: quay.io/openstack-k8s-operators/ironic-operator@sha256:0f523b7e2fa9e86fef986acf07d0c42d5658c475d565f11eaea926ebffcb6530 - name: RELATED_IMAGE_KEYSTONE_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/keystone-operator@sha256:72ad6517987f674af0d0ae092cbb874aeae909c8b8b60188099c311762ebc8f7 + value: quay.io/rh-ee-vfisarov/keystone-operator@sha256:50aea11546f5bddf88560e24d4f4116d5c820493c69de76c35eb5393186ad699 - name: RELATED_IMAGE_MANILA_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/manila-operator@sha256:2e59cfbeefc3aff0bb0a6ae9ce2235129f5173c98dd5ee8dac229ad4895faea9 + value: quay.io/rh-ee-vfisarov/manila-operator@sha256:db0e1f1679e26ba1f6c70d14eeeb68686f7a19488078c2a9f44ea5a36bfc9a89 - name: RELATED_IMAGE_MARIADB_OPERATOR_MANAGER_IMAGE_URL value: quay.io/openstack-k8s-operators/mariadb-operator@sha256:600ca007e493d3af0fcc2ebac92e8da5efd2afe812b62d7d3d4dd0115bdf05d7 - name: RELATED_IMAGE_NEUTRON_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/neutron-operator@sha256:0b3fb69f35c151895d3dffd514974a9f9fe1c77c3bca69b78b81efb183cf4557 + value: quay.io/rh-ee-vfisarov/neutron-operator@sha256:d54ab96f2e9b37130d9ca7544e3bf724f3a939068680335e75639b2df598a24f - name: RELATED_IMAGE_NOVA_OPERATOR_MANAGER_IMAGE_URL value: quay.io/openstack-k8s-operators/nova-operator@sha256:779f0cee6024d0fb8f259b036fe790e62aa5a3b0431ea9bf15a6e7d02e2e5670 - name: RELATED_IMAGE_OCTAVIA_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/octavia-operator@sha256:d9a3694865a7d54ee96397add18c3898886e98d079aa20876a0f4de1fa7a7168 + value: quay.io/rh-ee-vfisarov/octavia-operator@sha256:37573e2dc2db379324bad8b38ce8c7b37d6ab221eee64521998a7747c9cc3003 - name: RELATED_IMAGE_OPENSTACK_BAREMETAL_OPERATOR_MANAGER_IMAGE_URL value: quay.io/openstack-k8s-operators/openstack-baremetal-operator@sha256:14cfad6ea2e7f7ecc4cb2aafceb9c61514b3d04b66668832d1e4ac3b19f1ab81 - - name: RELATED_IMAGE_OVN_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/ovn-operator@sha256:635a4aef9d6f0b799e8ec91333dbb312160c001d05b3c63f614c124e0b67cb59 - - name: RELATED_IMAGE_PLACEMENT_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/placement-operator@sha256:d29650b006da97eb9178fcc58f2eb9fead8c2b414fac18f86a3c3a1507488c4f - - name: RELATED_IMAGE_RABBITMQ_CLUSTER_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/rabbitmq-cluster-operator@sha256:893e66303c1b0bc1d00a299a3f0380bad55c8dc813c8a1c6a4aab379f5aa12a2 - - name: RELATED_IMAGE_SWIFT_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/swift-operator@sha256:2a3d21728a8bfb4e64617e63e61e2d1cb70a383ea3e8f846e0c3c3c02d2b0a9d - - name: RELATED_IMAGE_TELEMETRY_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/telemetry-operator@sha256:7d66757c0af67104f0389e851a7cc0daa44443ad202d157417bd86bbb57cc385 - - name: RELATED_IMAGE_TEST_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/test-operator@sha256:101b3e007d8c9f2e183262d7712f986ad51256448099069bc14f1ea5f997ab94 - - name: RELATED_IMAGE_WATCHER_OPERATOR_MANAGER_IMAGE_URL - value: quay.io/openstack-k8s-operators/watcher-operator@sha256:9aa8c03633e4b934c57868c1660acf47e7d386ac86bcb344df262c9ad76b8621 diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 574879be7..f16b520df 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -364,6 +364,7 @@ rules: - keystone.openstack.org resources: - keystoneapis + - keystoneapplicationcredentials verbs: - create - delete @@ -372,6 +373,14 @@ rules: - patch - update - watch +- apiGroups: + - keystone.openstack.org + resources: + - keystoneapplicationcredentials/status + verbs: + - get + - patch + - update - apiGroups: - machineconfiguration.openshift.io resources: diff --git a/config/samples/applicationcredentials/kustomization.yaml b/config/samples/applicationcredentials/kustomization.yaml new file mode 100644 index 000000000..c7cbe3ecf --- /dev/null +++ b/config/samples/applicationcredentials/kustomization.yaml @@ -0,0 +1,14 @@ +resources: +- ../base/openstackcontrolplane + +patches: +- target: + kind: OpenStackControlPlane + name: .* + patch: |- + - op: replace + path: /metadata/name + value: openstack +- target: + kind: OpenStackControlPlane + path: patch.yaml diff --git a/config/samples/applicationcredentials/patch.yaml b/config/samples/applicationcredentials/patch.yaml new file mode 100644 index 000000000..566cac6f4 --- /dev/null +++ b/config/samples/applicationcredentials/patch.yaml @@ -0,0 +1,33 @@ +apiVersion: core.openstack.org/v1beta1 +kind: OpenStackControlPlane +metadata: + name: openstack +spec: + applicationCredential: + enabled: true + expirationDays: 200 + gracePeriodDays: 90 + roles: + - service + unrestricted: false + barbican: + applicationCredential: + enabled: true + glance: + applicationCredential: + enabled: true + swift: + applicationCredential: + enabled: true + cinder: + applicationCredential: + enabled: true + expirationDays: 10 + gracePeriodDays: 5 + roles: + - admin + - service + unrestricted: true + manila: + applicationCredential: + enabled: true diff --git a/go.mod b/go.mod index 417e6fcd7..1ddf912ea 100644 --- a/go.mod +++ b/go.mod @@ -21,23 +21,23 @@ require ( github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251124130651-1ff40691b66d github.com/openstack-k8s-operators/ironic-operator/api v0.6.1-0.20251125165311-d458faac6271 github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251128160419-8b3a77972a77 - github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20251122131503-b76943960b6c - github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.6.1-0.20251122131503-b76943960b6c + github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20251021145236-2b84ec9fd9bb + github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.6.1-0.20251021145236-2b84ec9fd9bb github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20251122131503-b76943960b6c - github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251122131503-b76943960b6c + github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251103072528-9eb684fef4ef github.com/openstack-k8s-operators/manila-operator/api v0.6.1-0.20251128162048-2454ee694c60 github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20251125174936-3989cc50de31 github.com/openstack-k8s-operators/neutron-operator/api v0.6.1-0.20251125150830-633e42336356 github.com/openstack-k8s-operators/nova-operator/api v0.6.1-0.20251127143706-407c63ad016a github.com/openstack-k8s-operators/octavia-operator/api v0.6.1-0.20251127161151-38d49bbc1c5d github.com/openstack-k8s-operators/openstack-baremetal-operator/api v0.6.1-0.20251126095155-fe40bf12d8c6 - github.com/openstack-k8s-operators/openstack-operator/api v0.0.0-00010101000000-000000000000 + github.com/openstack-k8s-operators/openstack-operator/api v0.0.0-20251202072739-b102924657dd github.com/openstack-k8s-operators/ovn-operator/api v0.6.1-0.20251127135801-f3d54911d811 github.com/openstack-k8s-operators/placement-operator/api v0.6.1-0.20251125174406-42e6ab0985af github.com/openstack-k8s-operators/swift-operator/api v0.6.1-0.20251125135122-5157cb4f1cc5 github.com/openstack-k8s-operators/telemetry-operator/api v0.6.1-0.20251124182638-bf35154a77d3 - github.com/openstack-k8s-operators/test-operator/api v0.6.1-0.20251127114833-80f76294e428 + github.com/openstack-k8s-operators/test-operator/api v0.6.1-0.20251015105850-737534bb4fcf github.com/openstack-k8s-operators/watcher-operator/api v0.6.1-0.20251127070224-35dcc7e8c5b2 github.com/pkg/errors v0.9.1 github.com/rabbitmq/cluster-operator/v2 v2.16.0 @@ -181,3 +181,24 @@ replace k8s.io/code-generator => k8s.io/code-generator v0.31.13 //allow-merging replace k8s.io/component-base => k8s.io/component-base v0.31.13 //allow-merging replace github.com/cert-manager/cmctl/v2 => github.com/cert-manager/cmctl/v2 v2.1.2-0.20241127223932-88edb96860cf //allow-merging + +// appcred related changes +replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/Deydra71/keystone-operator/api v0.0.0-20251201081347-2823ce61c025 + +replace github.com/openstack-k8s-operators/barbican-operator/api => github.com/Deydra71/barbican-operator/api v0.0.0-20251125072812-68cb2ae77c6a + +replace github.com/openstack-k8s-operators/cinder-operator/api => github.com/Deydra71/cinder-operator/api v0.0.0-20251125074431-5ef9b351fb12 + +replace github.com/openstack-k8s-operators/glance-operator/api => github.com/Deydra71/glance-operator/api v0.0.0-20251125073306-470bb653b540 + +replace github.com/openstack-k8s-operators/swift-operator/api => github.com/Deydra71/swift-operator/api v0.0.0-20251125071516-6e972449c1da + +replace github.com/openstack-k8s-operators/manila-operator/api => github.com/Deydra71/manila-operator/api v0.0.0-20251125080051-8dbb26682df9 + +replace github.com/openstack-k8s-operators/neutron-operator/api => github.com/Deydra71/neutron-operator/api v0.0.0-20251125074539-3e6b09e24496 + +replace github.com/openstack-k8s-operators/placement-operator/api => github.com/Deydra71/placement-operator/api v0.0.0-20251125080720-2e641c2dd826 + +replace github.com/openstack-k8s-operators/designate-operator/api => github.com/Deydra71/designate-operator/api v0.0.0-20251201130300-ee35802d97e1 + +replace github.com/openstack-k8s-operators/octavia-operator/api => github.com/Deydra71/octavia-operator/api v0.0.0-20251125074921-aafe26094259 diff --git a/go.sum b/go.sum index 73dbd1851..3faaba5ed 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,23 @@ +github.com/Deydra71/barbican-operator/api v0.0.0-20251125072812-68cb2ae77c6a h1:09ZzxiVc12vFLj0lw3S5MwzhvZPXmCssjr1PQX5+xuo= +github.com/Deydra71/barbican-operator/api v0.0.0-20251125072812-68cb2ae77c6a/go.mod h1:SS+6KFheIRSkkjAYeMTEirWm/xHrZUf1J9XBIft/bKM= +github.com/Deydra71/cinder-operator/api v0.0.0-20251125074431-5ef9b351fb12 h1:yNwhOAY5lnMzTtveBfMAJfvXgQyPcV4WO3YbTy+Fspw= +github.com/Deydra71/cinder-operator/api v0.0.0-20251125074431-5ef9b351fb12/go.mod h1:swwsxHN0P0c/PL58uqJAZXvdqWxPz9LFB8OG5l4CSso= +github.com/Deydra71/designate-operator/api v0.0.0-20251201130300-ee35802d97e1 h1:LQTB9g6qSl0tK0TtfuGZ2LzE+hVDlYLCezTWdvkY70I= +github.com/Deydra71/designate-operator/api v0.0.0-20251201130300-ee35802d97e1/go.mod h1:CFre3O8XfJT1qmr9l5PgQlMAXIaDozWUUSPZFiPRPGE= +github.com/Deydra71/glance-operator/api v0.0.0-20251125073306-470bb653b540 h1:ZHrqOHYBiOwSk604AUjL+fUGEgBWUTAKoiMEjFMN5cc= +github.com/Deydra71/glance-operator/api v0.0.0-20251125073306-470bb653b540/go.mod h1:cs0Ws8MzFBNcrXs8WrpmVRafeEoLNnTx8fjSBSrnR1Q= +github.com/Deydra71/keystone-operator/api v0.0.0-20251201081347-2823ce61c025 h1:9tVdplONUg5JpgAczs7wV4Swkc0mqAueWVT58cin9ZY= +github.com/Deydra71/keystone-operator/api v0.0.0-20251201081347-2823ce61c025/go.mod h1:b98Jl8eyUw8V07l9YiuQnoMlnWC748oV8IhXH15NCC4= +github.com/Deydra71/manila-operator/api v0.0.0-20251125080051-8dbb26682df9 h1:MHXveFFkhRzXi8+hZjh5zLVlV+NocVsym3NYhr+LEUw= +github.com/Deydra71/manila-operator/api v0.0.0-20251125080051-8dbb26682df9/go.mod h1:BWHGjAf4EJC9WuGYchWAvrrlV6TrBnanPemvRCQ8xMc= +github.com/Deydra71/neutron-operator/api v0.0.0-20251125074539-3e6b09e24496 h1:+TrgyM8QgBk8sYfRj8CS6TPFMnQ8f4O7qfv/NCNy+QA= +github.com/Deydra71/neutron-operator/api v0.0.0-20251125074539-3e6b09e24496/go.mod h1:RffdzUML/sicsT3EjAm9P9xecBwHeZV3BpWhh/Ln8Lo= +github.com/Deydra71/octavia-operator/api v0.0.0-20251125074921-aafe26094259 h1:/fcHRESvUJrmwlgIhokWNo23GJR3BTmEWqse4agxoLg= +github.com/Deydra71/octavia-operator/api v0.0.0-20251125074921-aafe26094259/go.mod h1:pNm0SXf3kvVFf3aZOZoZ8PsawDI0DaPIrCTwzdJWD2I= +github.com/Deydra71/placement-operator/api v0.0.0-20251125080720-2e641c2dd826 h1:o7+MbG7PDseypW5VsAVz14x4VHtsNkX9eLCc1k36ac4= +github.com/Deydra71/placement-operator/api v0.0.0-20251125080720-2e641c2dd826/go.mod h1:Hk9mKb836A09vSIsMVdh2O0ch0H65xLz2IZWd8x9g8o= +github.com/Deydra71/swift-operator/api v0.0.0-20251125071516-6e972449c1da h1:P+9gHdFTR7h+A3dYpARawXktiWSOngwqAfC42l3hcHk= +github.com/Deydra71/swift-operator/api v0.0.0-20251125071516-6e972449c1da/go.mod h1:iMSZnbR9M3AfAIwInL4umy+y59Aj4Lx0b1+TPB4M2Is= 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/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= @@ -119,7 +139,6 @@ github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUt github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/metal3-io/baremetal-operator/apis v0.9.3 h1:4y2jg8r3JSaHYjPkf4KSrJSTxqAkv1ulkEtx5dJ0mGI= -github.com/metal3-io/baremetal-operator/apis v0.9.3/go.mod h1:IPQkSeeggkANzF7jyTK3UDuPY+MubE8JbID5v4RVrT4= github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.5.1 h1:X0+MWsJ+Gj/TAkmhGybvesvxk6zQKu3NQXzvC6l0iJs= github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.5.1/go.mod h1:399nvdaqoU9rTI25UdFw2EWcVjmJPpeZPIhfDAIx/XU= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= @@ -138,62 +157,30 @@ github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= 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/barbican-operator/api v0.6.1-0.20251125115107-f489fa5ceb3c h1:cQRQbyBmYfS4Ubj/0hwj5IVs0AAZqMyhqA1i50r4oTI= -github.com/openstack-k8s-operators/barbican-operator/api v0.6.1-0.20251125115107-f489fa5ceb3c/go.mod h1:HURjuNEy1OrE7bn2snCYMzk148bHaD7u7JleEu9h5ws= -github.com/openstack-k8s-operators/cinder-operator/api v0.6.1-0.20251125143015-0548029d9df0 h1:BfTkwCr4sP5PlsQu65CHO8ZTIDNK7BfB0UWbFTRh730= -github.com/openstack-k8s-operators/cinder-operator/api v0.6.1-0.20251125143015-0548029d9df0/go.mod h1:IrtEtP+mjH2pHLQvk9ZzKebtjMW50HKG0IhooZgmmDw= -github.com/openstack-k8s-operators/designate-operator/api v0.6.1-0.20251125143014-f2b7132f2963 h1:oMTI+YjFMxX2b2Ac9J/YSdpkB1egfSdFDype9sD2y2E= -github.com/openstack-k8s-operators/designate-operator/api v0.6.1-0.20251125143014-f2b7132f2963/go.mod h1:qzxe5RJalpItYbD3+pDIGMrPer/P5M50LQQyuusU7lI= -github.com/openstack-k8s-operators/glance-operator/api v0.6.1-0.20251128173340-057b4476099f h1:WH/zNMwv/gH1oOBitGF3asIsFQgpoZNVJg9RU3TAsZY= -github.com/openstack-k8s-operators/glance-operator/api v0.6.1-0.20251128173340-057b4476099f/go.mod h1:e/7kDMADuTn7S+oVWsbaB6gr0snZ4azV2oykPhZozx8= github.com/openstack-k8s-operators/heat-operator/api v0.6.1-0.20251125115646-26b110b9f3e7 h1:dEx1vGdyq071vl0q0D74NxFLf67T5UJmoCslmlN9DLY= -github.com/openstack-k8s-operators/heat-operator/api v0.6.1-0.20251125115646-26b110b9f3e7/go.mod h1:ABIb9YiV6nmcGC4h49X8eJIeqmnG/6j77eIiYQGWrUc= github.com/openstack-k8s-operators/horizon-operator/api v0.6.1-0.20251125145341-8bc80a35f9c5 h1:VxWuwuIbNmexcpL2JpLaWAj1aFR0v1BfehIKpbCwIfY= -github.com/openstack-k8s-operators/horizon-operator/api v0.6.1-0.20251125145341-8bc80a35f9c5/go.mod h1:NHffNXXDFrfJx/+htPjd8WvDRMPqddRJThBqujwlFc8= github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251124130651-1ff40691b66d h1:5513vDczN+/Sc/vNIVus+M/Li61oP5/sQzSiPRCmUSA= -github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251124130651-1ff40691b66d/go.mod h1:U6fKKmnazlF/il/jP5DQdKzkh0QX3Z95Pau46KoeTMo= github.com/openstack-k8s-operators/ironic-operator/api v0.6.1-0.20251125165311-d458faac6271 h1:ExfEekNT07SmCYx0S8daR8ZyWKTxj9jgV3LKNlUvYvw= -github.com/openstack-k8s-operators/ironic-operator/api v0.6.1-0.20251125165311-d458faac6271/go.mod h1:383ooOUA0DHtaeR9etcM7oKiTz5s8QMzrAZRunzZUrY= -github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251128160419-8b3a77972a77 h1:XzVPjfzxDJwgW8sNGv9K577Ui2mb6Mp3sDItuDmTv9E= -github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251128160419-8b3a77972a77/go.mod h1:b98Jl8eyUw8V07l9YiuQnoMlnWC748oV8IhXH15NCC4= -github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20251122131503-b76943960b6c h1:RrncYRidCdu8qP6njKRhteaWlY0y6tWhF9qWRhdRdkE= -github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20251122131503-b76943960b6c/go.mod h1:tXxVkkk8HlATwTmDA5RTP3b+c8apfuMM15mZ2wW5iNs= -github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.6.1-0.20251122131503-b76943960b6c h1:cPnMoKY5QkPF3ynahGs2+epjYVBb59vqt+uO6RHGFHE= -github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.6.1-0.20251122131503-b76943960b6c/go.mod h1:3zDlaWh4PKwFAhYM6zcKe+bAnCggnSB94v4unP4snUM= +github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20251021145236-2b84ec9fd9bb h1:pFa+ZX8xI1AMxjx7VWvNF96qqzbPL0eNg9wjKLldgV4= +github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20251021145236-2b84ec9fd9bb/go.mod h1:tXxVkkk8HlATwTmDA5RTP3b+c8apfuMM15mZ2wW5iNs= +github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.6.1-0.20251021145236-2b84ec9fd9bb h1:GIpUwDNfOLX8VJNB/aSiXp7rvYlxJazfLRnqvUcNem8= +github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.6.1-0.20251021145236-2b84ec9fd9bb/go.mod h1:9u3/TiMn/d8+ZWvRXZKP/vdvZer4Fk655n1PQZjZ904= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c h1:wM8qXCB5mQwSosCvtaydzuXitWVVKBHTzH0A2znQ+Jg= -github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c/go.mod h1:+Me0raWPPdz8gRi9D4z1khmvUgS9vIKAVC8ckg1yJZU= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251103072528-9eb684fef4ef h1:Ql4G7sRHpqWFGwXypN7MorDGUWv4jz5n34ayzVt3R9E= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251103072528-9eb684fef4ef/go.mod h1:yf13jWb60XV26eA7A8o86ZCXNWBLNK9dPkTSWFaTPCw= github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20251122131503-b76943960b6c h1:dVIaDL5BeIdJjERGaN/XlcvZVplfkzh0uUfiVUHj/6Q= -github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20251122131503-b76943960b6c/go.mod h1:fy1lvz3uuzzh01DKKdgroXvmJgMpJBsvl2r9eTtAll0= -github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251122131503-b76943960b6c h1:YdTv3RXKfFg2QHXtLJSnKaPruslyp1Fd+ArcsxLcy6k= -github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251122131503-b76943960b6c/go.mod h1:lgYyrXEYA2BPsq4Kg6dqa+QsHgOjMPyOsEYrvyYW3jk= -github.com/openstack-k8s-operators/manila-operator/api v0.6.1-0.20251128162048-2454ee694c60 h1:MzY54K+s30dSTw6pLB+hu1hVo1NB2lo9XbbTifk0Lxc= -github.com/openstack-k8s-operators/manila-operator/api v0.6.1-0.20251128162048-2454ee694c60/go.mod h1:GkUShNT3xXH7SWfgZfWeDTcKm2D4JL0Tv/qpBvZtV4w= +github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251103072528-9eb684fef4ef h1:U9cgXJs/GuO6/0bRn6oaS7ovDrabyGPZpmZyAWksUuQ= +github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251103072528-9eb684fef4ef/go.mod h1:lgYyrXEYA2BPsq4Kg6dqa+QsHgOjMPyOsEYrvyYW3jk= github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20251125174936-3989cc50de31 h1:pcxorrSN8YlCad3RgJWfMOdhlYtiT6ztYs+oTIYsxxc= -github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20251125174936-3989cc50de31/go.mod h1:rSNA5Yd+Xt6eJ6KGgXeWjSEeU9VcaPdt6B9IenjeDqY= -github.com/openstack-k8s-operators/neutron-operator/api v0.6.1-0.20251125150830-633e42336356 h1:YPD0uZZ3fbEfbo/HOGi/5AK+BrFC3bErg0OBJyZyRUg= -github.com/openstack-k8s-operators/neutron-operator/api v0.6.1-0.20251125150830-633e42336356/go.mod h1:CNJF0ekxqVqrEMLGL/hV489p0RrVoB3alMhjE9cxv1o= github.com/openstack-k8s-operators/nova-operator/api v0.6.1-0.20251127143706-407c63ad016a h1:lYmks3cHkbmBX1+I9a8gnajkfSddLAvNqakxx8GV3AA= -github.com/openstack-k8s-operators/nova-operator/api v0.6.1-0.20251127143706-407c63ad016a/go.mod h1:4Bp2ias9AUXvPBOSOlEkuuegDkAcJEYB9K1UtmX4q8c= -github.com/openstack-k8s-operators/octavia-operator/api v0.6.1-0.20251127161151-38d49bbc1c5d h1:MrDtVqdSNQpl+3ttzzbvDx/83ZQJ5ArUGcy+XJQQcic= -github.com/openstack-k8s-operators/octavia-operator/api v0.6.1-0.20251127161151-38d49bbc1c5d/go.mod h1:rI8CZdEfQB+QRBR3uFmA2i/D9KxTNTTZWFH3MhpID5A= github.com/openstack-k8s-operators/openstack-baremetal-operator/api v0.6.1-0.20251126095155-fe40bf12d8c6 h1:J/DqrMqnZEBUkBQc+GUVNIklI+nBSfo6z5Pf9sbxl5Y= -github.com/openstack-k8s-operators/openstack-baremetal-operator/api v0.6.1-0.20251126095155-fe40bf12d8c6/go.mod h1:SUcjA0LDLte1F4bEmtfWfCEQRwwaACk9NCMvWNC0o0Y= github.com/openstack-k8s-operators/ovn-operator/api v0.6.1-0.20251127135801-f3d54911d811 h1:H1b2RlE9EsemU/dbtV96xIXxmGBLS2UcBtdSS0bYucw= -github.com/openstack-k8s-operators/ovn-operator/api v0.6.1-0.20251127135801-f3d54911d811/go.mod h1:LTrCp/cf4HFozb0ZhblhQKO0jUmmBnvD8zFocOsasAM= -github.com/openstack-k8s-operators/placement-operator/api v0.6.1-0.20251125174406-42e6ab0985af h1:eX8vTK5MZIVNLoUtSZ64VkuNg4p9BuwWm4de8HAgi40= -github.com/openstack-k8s-operators/placement-operator/api v0.6.1-0.20251125174406-42e6ab0985af/go.mod h1:zvM01kqm1Axt1EJLzL5Cn4FzMTeuWdTy+77cRF3MR7M= 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/openstack-k8s-operators/swift-operator/api v0.6.1-0.20251125135122-5157cb4f1cc5 h1:AEvZZoCKgKzlEbkvGa6EtHD4JhrkT0DdTz4otvwlfl0= -github.com/openstack-k8s-operators/swift-operator/api v0.6.1-0.20251125135122-5157cb4f1cc5/go.mod h1:kHJTmMo++FSs+U/1r7kB3lfzxXOIUklfNp6n2tk0y6E= github.com/openstack-k8s-operators/telemetry-operator/api v0.6.1-0.20251124182638-bf35154a77d3 h1:rLz1nz/4P9K5uRPyT3IPjUHTF2Hk0RfUX6rYCayvb1Q= -github.com/openstack-k8s-operators/telemetry-operator/api v0.6.1-0.20251124182638-bf35154a77d3/go.mod h1:JZi2/dRupf7GVitj8JY3qI34WC4MZKf7zs6Rq9W7yBQ= -github.com/openstack-k8s-operators/test-operator/api v0.6.1-0.20251127114833-80f76294e428 h1:sn9fzHbhB4Lzav+mEP5FwgrHblffYaOxGvDlJuFwVto= -github.com/openstack-k8s-operators/test-operator/api v0.6.1-0.20251127114833-80f76294e428/go.mod h1:/XsSVYn6GB4Qrr1RUtxuq6o+t61Ch/mlTmHIytrT4MU= +github.com/openstack-k8s-operators/test-operator/api v0.6.1-0.20251015105850-737534bb4fcf h1:+6NbDmvZkgQOO/0ZvSMo8EaOqFJPyfkz3uyU7imoj+k= +github.com/openstack-k8s-operators/test-operator/api v0.6.1-0.20251015105850-737534bb4fcf/go.mod h1:d9GodfMDTQYcEwdQ6CL5DYVQcHeGHIPciBI/BJ8h2Lo= github.com/openstack-k8s-operators/watcher-operator/api v0.6.1-0.20251127070224-35dcc7e8c5b2 h1:wIMJJtAjepOd6BA3UkkeCeKG/7/JB2QEoHzPPwunLnU= -github.com/openstack-k8s-operators/watcher-operator/api v0.6.1-0.20251127070224-35dcc7e8c5b2/go.mod h1:ApBjfXy0V5Qtc9n0+tUi/lj7fGUzLbJ6PbZEwEwnC74= 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= @@ -220,7 +207,6 @@ github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/hack/export_operator_related_images.sh b/hack/export_operator_related_images.sh index 75ea71882..1d9aa2632 100644 --- a/hack/export_operator_related_images.sh +++ b/hack/export_operator_related_images.sh @@ -1,24 +1,17 @@ # NOTE: this file is automatically generated by hack/sync-bindata.sh! -export RELATED_IMAGE_BARBICAN_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/barbican-operator@sha256:f6059a0fbf031d34dcf086d14ce8c0546caeaee23c5780e90b5037c5feee9fea -export RELATED_IMAGE_CINDER_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/cinder-operator@sha256:1d60701214b39cdb0fa70bbe5710f9b131139a9f4b482c2db4058a04daefb801 -export RELATED_IMAGE_DESIGNATE_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/designate-operator@sha256:9f68d7bc8c6bce38f46dee8a8272d5365c49fe7b32b2af52e8ac884e212f3a85 -export RELATED_IMAGE_GLANCE_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/glance-operator@sha256:abdb733b01e92ac17f565762f30f1d075b44c16421bd06e557f6bb3c319e1809 +export RELATED_IMAGE_BARBICAN_OPERATOR_MANAGER_IMAGE_URL=quay.io/rh-ee-vfisarov/barbican-operator@sha256:73cffae9d124feff122f50487d65e4d15620639a2e140563eb14e9fd2cbe60a1 +export RELATED_IMAGE_CINDER_OPERATOR_MANAGER_IMAGE_URL=quay.io/rh-ee-vfisarov/cinder-operator@sha256:642d632298f7a740d6931f8c824d59a40f92caa655d3748f56d87a79b4281a2d +export RELATED_IMAGE_DESIGNATE_OPERATOR_MANAGER_IMAGE_URL=quay.io/rh-ee-vfisarov/designate-operator@sha256:1cc3853c58180d265515323c643b1d75179e0e0cb2e92421151f3dc417c7e9bf +export RELATED_IMAGE_GLANCE_OPERATOR_MANAGER_IMAGE_URL=quay.io/rh-ee-vfisarov/glance-operator@sha256:94a359fdaf34b2b3e63534d3eb9aa0868efaff836287f90cd2dc8703fd5d2fe6 export RELATED_IMAGE_HEAT_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/heat-operator@sha256:c4abfc148600dfa85915f3dc911d988ea2335f26cb6b8d749fe79bfe53e5e429 export RELATED_IMAGE_HORIZON_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/horizon-operator@sha256:9e847f4dbdea19ab997f32a02b3680a9bd966f9c705911645c3866a19fda9ea5 export RELATED_IMAGE_INFRA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/infra-operator@sha256:09a6d0613ee2d3c1c809fc36c22678458ac271e0da87c970aec0a5339f5423f7 export RELATED_IMAGE_IRONIC_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/ironic-operator@sha256:0f523b7e2fa9e86fef986acf07d0c42d5658c475d565f11eaea926ebffcb6530 -export RELATED_IMAGE_KEYSTONE_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/keystone-operator@sha256:72ad6517987f674af0d0ae092cbb874aeae909c8b8b60188099c311762ebc8f7 -export RELATED_IMAGE_MANILA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/manila-operator@sha256:2e59cfbeefc3aff0bb0a6ae9ce2235129f5173c98dd5ee8dac229ad4895faea9 +export RELATED_IMAGE_KEYSTONE_OPERATOR_MANAGER_IMAGE_URL=quay.io/rh-ee-vfisarov/keystone-operator@sha256:50aea11546f5bddf88560e24d4f4116d5c820493c69de76c35eb5393186ad699 +export RELATED_IMAGE_MANILA_OPERATOR_MANAGER_IMAGE_URL=quay.io/rh-ee-vfisarov/manila-operator@sha256:db0e1f1679e26ba1f6c70d14eeeb68686f7a19488078c2a9f44ea5a36bfc9a89 export RELATED_IMAGE_MARIADB_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/mariadb-operator@sha256:600ca007e493d3af0fcc2ebac92e8da5efd2afe812b62d7d3d4dd0115bdf05d7 -export RELATED_IMAGE_NEUTRON_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/neutron-operator@sha256:0b3fb69f35c151895d3dffd514974a9f9fe1c77c3bca69b78b81efb183cf4557 +export RELATED_IMAGE_NEUTRON_OPERATOR_MANAGER_IMAGE_URL=quay.io/rh-ee-vfisarov/neutron-operator@sha256:d54ab96f2e9b37130d9ca7544e3bf724f3a939068680335e75639b2df598a24f export RELATED_IMAGE_NOVA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/nova-operator@sha256:779f0cee6024d0fb8f259b036fe790e62aa5a3b0431ea9bf15a6e7d02e2e5670 -export RELATED_IMAGE_OCTAVIA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/octavia-operator@sha256:d9a3694865a7d54ee96397add18c3898886e98d079aa20876a0f4de1fa7a7168 +export RELATED_IMAGE_OCTAVIA_OPERATOR_MANAGER_IMAGE_URL=quay.io/rh-ee-vfisarov/octavia-operator@sha256:37573e2dc2db379324bad8b38ce8c7b37d6ab221eee64521998a7747c9cc3003 export RELATED_IMAGE_OPENSTACK_BAREMETAL_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/openstack-baremetal-operator@sha256:14cfad6ea2e7f7ecc4cb2aafceb9c61514b3d04b66668832d1e4ac3b19f1ab81 -export RELATED_IMAGE_OVN_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/ovn-operator@sha256:635a4aef9d6f0b799e8ec91333dbb312160c001d05b3c63f614c124e0b67cb59 -export RELATED_IMAGE_PLACEMENT_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/placement-operator@sha256:d29650b006da97eb9178fcc58f2eb9fead8c2b414fac18f86a3c3a1507488c4f -export RELATED_IMAGE_RABBITMQ_CLUSTER_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/rabbitmq-cluster-operator@sha256:893e66303c1b0bc1d00a299a3f0380bad55c8dc813c8a1c6a4aab379f5aa12a2 -export RELATED_IMAGE_SWIFT_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/swift-operator@sha256:2a3d21728a8bfb4e64617e63e61e2d1cb70a383ea3e8f846e0c3c3c02d2b0a9d -export RELATED_IMAGE_TELEMETRY_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/telemetry-operator@sha256:7d66757c0af67104f0389e851a7cc0daa44443ad202d157417bd86bbb57cc385 -export RELATED_IMAGE_TEST_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/test-operator@sha256:101b3e007d8c9f2e183262d7712f986ad51256448099069bc14f1ea5f997ab94 -export RELATED_IMAGE_WATCHER_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/watcher-operator@sha256:9aa8c03633e4b934c57868c1660acf47e7d386ac86bcb344df262c9ad76b8621 diff --git a/internal/controller/core/openstackcontrolplane_controller.go b/internal/controller/core/openstackcontrolplane_controller.go index 909316acb..30fbcbc2d 100644 --- a/internal/controller/core/openstackcontrolplane_controller.go +++ b/internal/controller/core/openstackcontrolplane_controller.go @@ -92,6 +92,8 @@ func (r *OpenStackControlPlaneReconciler) GetLogger(ctx context.Context) logr.Lo // +kubebuilder:rbac:groups=client.openstack.org,resources=openstackclients,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=horizon.openstack.org,resources=horizons,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneapis,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneapplicationcredentials,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneapplicationcredentials/status,verbs=get;patch;update // +kubebuilder:rbac:groups=placement.openstack.org,resources=placementapis,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=glance.openstack.org,resources=glances,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=heat.openstack.org,resources=heats,verbs=get;list;watch;create;update;patch;delete @@ -597,6 +599,13 @@ func (r *OpenStackControlPlaneReconciler) reconcileNormal(ctx context.Context, i instance.Status.Conditions.Remove(corev1beta1.OpenStackControlPlaneCertCleanupReadyCondition) + ctrlResult, err = openstack.ReconcileApplicationCredentials(ctx, instance, version, helper) + if err != nil { + return ctrl.Result{}, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + return ctrl.Result{}, nil } @@ -705,6 +714,7 @@ func (r *OpenStackControlPlaneReconciler) SetupWithManager( Owns(&mariadbv1.Galera{}). Owns(&memcachedv1.Memcached{}). Owns(&keystonev1.KeystoneAPI{}). + Owns(&keystonev1.KeystoneApplicationCredential{}). Owns(&placementv1.PlacementAPI{}). Owns(&glancev1.Glance{}). Owns(&cinderv1.Cinder{}). diff --git a/internal/openstack/applicationcredential.go b/internal/openstack/applicationcredential.go new file mode 100644 index 000000000..0f02355d1 --- /dev/null +++ b/internal/openstack/applicationcredential.go @@ -0,0 +1,425 @@ +package openstack + +import ( + "context" + "fmt" + + keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common/helper" + corev1beta1 "github.com/openstack-k8s-operators/openstack-operator/api/core/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +// ServiceUserConfig defines a single user for a service that needs an ApplicationCredential +type ServiceUserConfig struct { + UserName string + PasswordSelector string + Suffix string // For services that have multiple keystone service users (ironic) +} + +// servicesWithMultipleUsers defines services that have multiple Keystone users +// The function returns nil if the service is not enabled or template is not initialized +var servicesWithMultipleUsers = map[string]func(*corev1beta1.OpenStackControlPlane) []ServiceUserConfig{ + "heat": func(instance *corev1beta1.OpenStackControlPlane) []ServiceUserConfig { + if !instance.Spec.Heat.Enabled || instance.Spec.Heat.Template == nil { + return nil + } + // Note: heat_stack_domain_admin is excluded from app cred support + // as it requires special handling (domain-scoped auth without project) + return []ServiceUserConfig{ + { + UserName: instance.Spec.Heat.Template.ServiceUser, + PasswordSelector: instance.Spec.Heat.Template.PasswordSelectors.Service, + Suffix: "", // Main service, no suffix -> "ac-heat" + }, + } + }, + "ironic": func(instance *corev1beta1.OpenStackControlPlane) []ServiceUserConfig { + if !instance.Spec.Ironic.Enabled || instance.Spec.Ironic.Template == nil { + return nil + } + return []ServiceUserConfig{ + { + UserName: instance.Spec.Ironic.Template.ServiceUser, + PasswordSelector: instance.Spec.Ironic.Template.PasswordSelectors.Service, + Suffix: "", // Main service, no suffix -> "ac-ironic" + }, + { + UserName: instance.Spec.Ironic.Template.IronicInspector.ServiceUser, + PasswordSelector: instance.Spec.Ironic.Template.IronicInspector.PasswordSelectors.Service, + Suffix: "inspector", // -> "ac-ironic-inspector" + }, + } + }, +} + +// mergeAppCred returns a new ApplicationCredentialSection by overlaying +// service-specific values on top of the global defaults. +func mergeAppCred( + global corev1beta1.ApplicationCredentialSection, + svc *corev1beta1.ServiceAppCredSection, +) corev1beta1.ApplicationCredentialSection { + out := global + if svc != nil { + out.Enabled = svc.Enabled + + // only override expiry/grace if specified + if svc.ExpirationDays != nil { + out.ExpirationDays = svc.ExpirationDays + } + if svc.GracePeriodDays != nil { + out.GracePeriodDays = svc.GracePeriodDays + } + + // only override Roles if user set them + if len(svc.Roles) > 0 { + out.Roles = svc.Roles + } + // only override Unrestricted if user set it + if svc.Unrestricted != nil { + out.Unrestricted = svc.Unrestricted + } + // only override AccessRules if user set them + if len(svc.AccessRules) > 0 { + out.AccessRules = svc.AccessRules + } + } + + return out +} + +// ReconcileApplicationCredentials ensures an AC CR per enabled service, +// propagating its secret name, passwordSelector, and serviceUser fields. +func ReconcileApplicationCredentials( + ctx context.Context, + instance *corev1beta1.OpenStackControlPlane, + _ *corev1beta1.OpenStackVersion, + helper *helper.Helper, +) (ctrl.Result, error) { + log := GetLogger(ctx) + + // If global disabled, delete all ACs: + if !instance.Spec.ApplicationCredential.Enabled { + log.Info("Global AC disabled; deleting per-service AC CRs") + for _, svc := range []string{"glance", "nova", "swift", "ceilometer", "barbican", "cinder", "placement", "neutron", "ironic", "heat", "octavia", "manila", "designate", "watcher", "aodh"} { + if res, err := deleteServiceACs(ctx, helper, instance, svc); err != nil { + return res, err + } + } + return ctrl.Result{}, nil + } + + // Type definition for service AC configuration + type svcAC struct { + Key string + Enabled bool + ACSection *corev1beta1.ServiceAppCredSection + } + + // Collect each service's enabled flag and AC section + svcs := []svcAC{ + {"glance", instance.Spec.Glance.Enabled, instance.Spec.Glance.ApplicationCredential}, + {"nova", instance.Spec.Nova.Enabled, instance.Spec.Nova.ApplicationCredential}, + {"swift", instance.Spec.Swift.Enabled, instance.Spec.Swift.ApplicationCredential}, + {"ceilometer", instance.Spec.Telemetry.Enabled, instance.Spec.Telemetry.ApplicationCredentialCeilometer}, + {"barbican", instance.Spec.Barbican.Enabled, instance.Spec.Barbican.ApplicationCredential}, + {"cinder", instance.Spec.Cinder.Enabled, instance.Spec.Cinder.ApplicationCredential}, + {"placement", instance.Spec.Placement.Enabled, instance.Spec.Placement.ApplicationCredential}, + {"neutron", instance.Spec.Neutron.Enabled, instance.Spec.Neutron.ApplicationCredential}, + {"ironic", instance.Spec.Ironic.Enabled, instance.Spec.Ironic.ApplicationCredential}, + {"heat", instance.Spec.Heat.Enabled, instance.Spec.Heat.ApplicationCredential}, + {"octavia", instance.Spec.Octavia.Enabled, instance.Spec.Octavia.ApplicationCredential}, + {"manila", instance.Spec.Manila.Enabled, instance.Spec.Manila.ApplicationCredential}, + {"designate", instance.Spec.Designate.Enabled, instance.Spec.Designate.ApplicationCredential}, + {"watcher", instance.Spec.Watcher.Enabled, instance.Spec.Watcher.ApplicationCredential}, + {"aodh", instance.Spec.Telemetry.Enabled, instance.Spec.Telemetry.ApplicationCredentialAodh}, + } + global := instance.Spec.ApplicationCredential + + // Helper functions to safely access Watcher's pointer fields + getWatcherSecret := func() string { + if instance.Spec.Watcher.Template != nil && instance.Spec.Watcher.Template.Secret != nil { + return *instance.Spec.Watcher.Template.Secret + } + return "" + } + getWatcherServiceUser := func() string { + if instance.Spec.Watcher.Template != nil && instance.Spec.Watcher.Template.ServiceUser != nil { + return *instance.Spec.Watcher.Template.ServiceUser + } + return "" + } + getWatcherPasswordSelector := func() string { + if instance.Spec.Watcher.Template != nil && + instance.Spec.Watcher.Template.PasswordSelectors.Service != nil { + return *instance.Spec.Watcher.Template.PasswordSelectors.Service + } + return "" + } + + // getServiceInfo retrieves service info from Template + // When service is enabled, webhook ensures Template is initialized with defaults + // This function is only called after verifying service is enabled + getServiceInfo := func(key string) struct { + SecretName string + PasswordSelector string + ServiceUser string + } { + var secretName, passwordSelector, serviceUser string + + switch key { + case "glance": + secretName = instance.Spec.Glance.Template.Secret + passwordSelector = instance.Spec.Glance.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Glance.Template.ServiceUser + case "nova": + secretName = instance.Spec.Nova.Template.Secret + passwordSelector = instance.Spec.Nova.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Nova.Template.ServiceUser + case "swift": + secretName = instance.Spec.Swift.Template.SwiftProxy.Secret + passwordSelector = instance.Spec.Swift.Template.SwiftProxy.PasswordSelectors.Service + serviceUser = instance.Spec.Swift.Template.SwiftProxy.ServiceUser + case "ceilometer": + secretName = instance.Spec.Telemetry.Template.Ceilometer.Secret + passwordSelector = instance.Spec.Telemetry.Template.Ceilometer.PasswordSelectors.CeilometerService + serviceUser = instance.Spec.Telemetry.Template.Ceilometer.ServiceUser + case "barbican": + secretName = instance.Spec.Barbican.Template.Secret + passwordSelector = instance.Spec.Barbican.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Barbican.Template.ServiceUser + case "cinder": + secretName = instance.Spec.Cinder.Template.Secret + passwordSelector = instance.Spec.Cinder.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Cinder.Template.ServiceUser + case "placement": + secretName = instance.Spec.Placement.Template.Secret + passwordSelector = instance.Spec.Placement.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Placement.Template.ServiceUser + case "neutron": + secretName = instance.Spec.Neutron.Template.Secret + passwordSelector = instance.Spec.Neutron.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Neutron.Template.ServiceUser + case "ironic": + secretName = instance.Spec.Ironic.Template.Secret + passwordSelector = instance.Spec.Ironic.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Ironic.Template.ServiceUser + case "heat": + secretName = instance.Spec.Heat.Template.Secret + passwordSelector = instance.Spec.Heat.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Heat.Template.ServiceUser + case "octavia": + secretName = instance.Spec.Octavia.Template.Secret + passwordSelector = instance.Spec.Octavia.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Octavia.Template.ServiceUser + case "manila": + secretName = instance.Spec.Manila.Template.Secret + passwordSelector = instance.Spec.Manila.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Manila.Template.ServiceUser + case "designate": + secretName = instance.Spec.Designate.Template.Secret + passwordSelector = instance.Spec.Designate.Template.PasswordSelectors.Service + serviceUser = instance.Spec.Designate.Template.ServiceUser + case "watcher": + secretName = getWatcherSecret() + passwordSelector = getWatcherPasswordSelector() + serviceUser = getWatcherServiceUser() + case "aodh": + secretName = instance.Spec.Telemetry.Template.Autoscaling.Aodh.Secret + passwordSelector = instance.Spec.Telemetry.Template.Autoscaling.Aodh.PasswordSelectors.AodhService + serviceUser = instance.Spec.Telemetry.Template.Autoscaling.Aodh.ServiceUser + default: + return struct { + SecretName string + PasswordSelector string + ServiceUser string + }{} + } + + // If service-specific Secret is empty, use top-level Secret + if secretName == "" { + secretName = instance.Spec.Secret + } + + return struct { + SecretName string + PasswordSelector string + ServiceUser string + }{secretName, passwordSelector, serviceUser} + } + + // Loop, CreateOrPatch or delete each AC CR: + for _, svc := range svcs { + // Check if service is enabled + if !svc.Enabled { + // Delete all possible AC CRs for this service + if res, err := deleteServiceACs(ctx, helper, instance, svc.Key); err != nil { + return res, err + } + continue + } + + // Merge flags - only check AC enabled after verifying service is enabled + effective := mergeAppCred(global, svc.ACSection) + if !effective.Enabled { + // Delete all possible AC CRs for this service + if res, err := deleteServiceACs(ctx, helper, instance, svc.Key); err != nil { + return res, err + } + continue + } + + // Check if this service has multiple users + if userConfigsFn, hasMultiple := servicesWithMultipleUsers[svc.Key]; hasMultiple { + userConfigs := userConfigsFn(instance) + if userConfigs == nil || len(userConfigs) == 0 { + // Service enabled but template not ready yet + log.Info("Skipping ApplicationCredential creation: Template not initialized", "service", svc.Key) + continue + } + + // Get base service info (secretName) + svcInfo := getServiceInfo(svc.Key) + if svcInfo.SecretName == "" { + log.Info("Skipping ApplicationCredential creation: Secret name empty", "service", svc.Key) + continue + } + + // Create AC CR for each user + for _, userCfg := range userConfigs { + acName := fmt.Sprintf("ac-%s", svc.Key) + if userCfg.Suffix != "" { + acName = fmt.Sprintf("ac-%s-%s", svc.Key, userCfg.Suffix) + } + + if err := reconcileApplicationCredential( + ctx, + helper, + instance, + acName, + userCfg.UserName, + svcInfo.SecretName, + userCfg.PasswordSelector, + effective, + ); err != nil { + return ctrl.Result{}, err + } + } + } else { + // Single user service + svcInfo := getServiceInfo(svc.Key) + if svcInfo.SecretName == "" || svcInfo.PasswordSelector == "" { + log.Info("Skipping ApplicationCredential creation: Template fields empty", "service", svc.Key) + if res, err := deleteServiceACs(ctx, helper, instance, svc.Key); err != nil { + return res, err + } + continue + } + + acName := fmt.Sprintf("ac-%s", svc.Key) + if err := reconcileApplicationCredential( + ctx, + helper, + instance, + acName, + svcInfo.ServiceUser, + svcInfo.SecretName, + svcInfo.PasswordSelector, + effective, + ); err != nil { + return ctrl.Result{}, err + } + } + } + + return ctrl.Result{}, nil +} + +// reconcileApplicationCredential creates or updates a single ApplicationCredential CR +func reconcileApplicationCredential( + ctx context.Context, + helper *helper.Helper, + instance *corev1beta1.OpenStackControlPlane, + acName string, + userName string, + secretName string, + passwordSelector string, + effective corev1beta1.ApplicationCredentialSection, +) error { + log := GetLogger(ctx) + + acObj := &keystonev1.KeystoneApplicationCredential{ + ObjectMeta: metav1.ObjectMeta{ + Name: acName, + Namespace: instance.Namespace, + }, + } + + op, err := controllerutil.CreateOrPatch(ctx, helper.GetClient(), acObj, func() error { + acObj.Spec.UserName = userName + acObj.Spec.ExpirationDays = *effective.ExpirationDays + acObj.Spec.GracePeriodDays = *effective.GracePeriodDays + acObj.Spec.Secret = secretName + acObj.Spec.PasswordSelector = passwordSelector + acObj.Spec.Roles = effective.Roles + acObj.Spec.Unrestricted = *effective.Unrestricted + + if len(effective.AccessRules) > 0 { + kr := make([]keystonev1.ACRule, 0, len(effective.AccessRules)) + for _, r := range effective.AccessRules { + kr = append(kr, keystonev1.ACRule{ + Service: r.Service, + Path: r.Path, + Method: r.Method, + }) + } + acObj.Spec.AccessRules = kr + } + + return controllerutil.SetControllerReference( + helper.GetBeforeObject(), acObj, helper.GetScheme(), + ) + }) + if err != nil { + return err + } + if op != controllerutil.OperationResultNone { + log.Info("Reconciled ApplicationCredential", "name", acName, "user", userName, "operation", op) + } + return nil +} + +// deleteServiceACs deletes all AC CRs for a service (handles both single and multi-user) +func deleteServiceACs( + ctx context.Context, + helper *helper.Helper, + instance *corev1beta1.OpenStackControlPlane, + serviceKey string, +) (ctrl.Result, error) { + // List of possible AC CR names for this service + possibleNames := []string{ + fmt.Sprintf("ac-%s", serviceKey), + } + + // Add additional names for multi-user services + if serviceKey == "ironic" { + possibleNames = append(possibleNames, "ac-ironic-inspector") + } + + // Try to delete each possible CR + for _, acName := range possibleNames { + acObj := &keystonev1.KeystoneApplicationCredential{ + ObjectMeta: metav1.ObjectMeta{ + Name: acName, + Namespace: instance.Namespace, + }, + } + if res, err := EnsureDeleted(ctx, helper, acObj); err != nil { + return res, err + } + } + + return ctrl.Result{}, nil +} diff --git a/test/functional/ctlplane/openstackoperator_controller_test.go b/test/functional/ctlplane/openstackoperator_controller_test.go index 58318df1f..d7f738c5a 100644 --- a/test/functional/ctlplane/openstackoperator_controller_test.go +++ b/test/functional/ctlplane/openstackoperator_controller_test.go @@ -4063,7 +4063,233 @@ var _ = Describe("OpenStackOperator controller nova cell deletion", func() { g.Expect(k8s_errors.IsNotFound(err)).To(BeTrue()) }, timeout, interval).Should(Succeed()) }) + }) + }) +}) + +var _ = Describe("Application Credentials configuration in control plane", func() { + When("global application credentials are enabled", func() { + BeforeEach(func() { + spec := GetDefaultOpenStackControlPlaneSpec() + spec["applicationCredential"] = map[string]interface{}{ + "enabled": true, + "expirationDays": 365, + "gracePeriodDays": 182, + "roles": []string{"service"}, + "unrestricted": false, + } + spec["cinder"] = map[string]interface{}{ + "enabled": true, + "applicationCredential": map[string]interface{}{ + "enabled": true, + "expirationDays": 100, + "gracePeriodDays": 50, + "roles": []string{"custom", "role"}, + "unrestricted": true, + }, + } + + DeferCleanup(th.DeleteInstance, + CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec), + ) + }) + + It("should fill defaults correctly", func() { + Eventually(func(g Gomega) { + cp := GetOpenStackControlPlane(names.OpenStackControlplaneName) + g.Expect(cp.Spec.ApplicationCredential.Enabled).To(BeTrue()) + g.Expect(*cp.Spec.ApplicationCredential.ExpirationDays).To(Equal(365)) + g.Expect(*cp.Spec.ApplicationCredential.GracePeriodDays).To(Equal(182)) + g.Expect(cp.Spec.ApplicationCredential.Roles).To(ConsistOf("service")) + g.Expect(*cp.Spec.ApplicationCredential.Unrestricted).To(BeFalse()) + + ac := cp.Spec.Cinder.ApplicationCredential + g.Expect(ac).NotTo(BeNil()) + g.Expect(*ac.ExpirationDays).To(Equal(100)) + g.Expect(*ac.GracePeriodDays).To(Equal(50)) + g.Expect(ac.Roles).To(ConsistOf("custom", "role")) + g.Expect(*ac.Unrestricted).To(BeTrue()) + }, timeout, interval).Should(Succeed()) + }) + + It("should configure ApplicationCredential with service-specific overrides and global defaults", func() { + cp := GetOpenStackControlPlane(names.OpenStackControlplaneName) + + // Verify global AC configuration + global := cp.Spec.ApplicationCredential + Expect(global.Enabled).To(BeTrue()) + Expect(*global.ExpirationDays).To(Equal(365)) + Expect(*global.GracePeriodDays).To(Equal(182)) + Expect(global.Roles).To(ConsistOf("service")) + Expect(*global.Unrestricted).To(BeFalse()) + + // Verify Cinder has service-specific overrides + Expect(cp.Spec.Cinder.Enabled).To(BeTrue()) + Expect(cp.Spec.Cinder.ApplicationCredential).NotTo(BeNil()) + Expect(cp.Spec.Cinder.ApplicationCredential.Enabled).To(BeTrue()) + cinderAC := cp.Spec.Cinder.ApplicationCredential + Expect(*cinderAC.ExpirationDays).To(Equal(100)) + Expect(*cinderAC.GracePeriodDays).To(Equal(50)) + Expect(cinderAC.Roles).To(ConsistOf("custom", "role")) + Expect(*cinderAC.Unrestricted).To(BeTrue()) + + // Verify Glance and Manila inherit global defaults (no service-specific AC overrides) + // The service specific values are nil/empty, they inherit the global defaults with mergeAppCred function + Expect(cp.Spec.Glance.Enabled).To(BeTrue()) + Expect(cp.Spec.Manila.Enabled).To(BeTrue()) + Expect(cp.Spec.Manila.Template).NotTo(BeNil()) + + if cp.Spec.Glance.ApplicationCredential != nil { + glanceAC := cp.Spec.Glance.ApplicationCredential + Expect(glanceAC.ExpirationDays).To(BeNil()) + Expect(glanceAC.GracePeriodDays).To(BeNil()) + Expect(glanceAC.Roles).To(BeEmpty()) + Expect(glanceAC.Unrestricted).To(BeNil()) + } + + if cp.Spec.Manila.ApplicationCredential != nil { + manilaAC := cp.Spec.Manila.ApplicationCredential + Expect(manilaAC.ExpirationDays).To(BeNil()) + Expect(manilaAC.GracePeriodDays).To(BeNil()) + Expect(manilaAC.Roles).To(BeEmpty()) + Expect(manilaAC.Unrestricted).To(BeNil()) + } + }) + }) + + When("global application credentials are disabled", func() { + BeforeEach(func() { + spec := GetDefaultOpenStackControlPlaneSpec() + spec["applicationCredential"] = map[string]interface{}{"enabled": false} + spec["cinder"] = map[string]interface{}{ + "enabled": true, + "applicationCredential": map[string]interface{}{ + "enabled": true, + }, + } + spec["glance"] = map[string]interface{}{ + "enabled": true, + } + + DeferCleanup(th.DeleteInstance, + CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec), + ) + }) + + It("should have global AC disabled in spec", func() { + cp := GetOpenStackControlPlane(names.OpenStackControlplaneName) + Expect(cp.Spec.ApplicationCredential.Enabled).To(BeFalse()) + }) + }) + + When("service-specific application credentials are disabled", func() { + BeforeEach(func() { + spec := GetDefaultOpenStackControlPlaneSpec() + spec["applicationCredential"] = map[string]interface{}{"enabled": true} + spec["glance"] = map[string]interface{}{ + "enabled": true, + "applicationCredential": map[string]interface{}{ + "enabled": false, + }, + } + spec["cinder"] = map[string]interface{}{ + "enabled": true, + "applicationCredential": map[string]interface{}{ + "enabled": true, + }, + } + + DeferCleanup(th.DeleteInstance, + CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec), + ) + }) + + It("should have service-specific AC disabled in spec", func() { + cp := GetOpenStackControlPlane(names.OpenStackControlplaneName) + + // Glance is disabled + Expect(cp.Spec.Glance.Enabled).To(BeTrue()) + Expect(cp.Spec.Glance.ApplicationCredential).NotTo(BeNil()) + Expect(cp.Spec.Glance.ApplicationCredential.Enabled).To(BeFalse()) + + // Cidner is enabled + Expect(cp.Spec.Cinder.Enabled).To(BeTrue()) + Expect(cp.Spec.Cinder.ApplicationCredential).NotTo(BeNil()) + Expect(cp.Spec.Cinder.ApplicationCredential.Enabled).To(BeTrue()) + }) + }) + + When("Heat service with application credentials enabled", func() { + BeforeEach(func() { + spec := GetDefaultOpenStackControlPlaneSpec() + spec["applicationCredential"] = map[string]interface{}{ + "enabled": true, + "expirationDays": 365, + "gracePeriodDays": 182, + } + spec["heat"] = map[string]interface{}{ + "enabled": true, + "template": map[string]interface{}{ + "databaseInstance": "openstack", + "secret": "osp-secret", + "apiTimeout": 60, + }, + "applicationCredential": map[string]interface{}{ + "enabled": true, + }, + } + + DeferCleanup(th.DeleteInstance, + CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec), + ) + }) + + It("should configure ApplicationCredential in spec for Heat service", func() { + // Verify the spec is configured correctly for Heat AC + cp := GetOpenStackControlPlane(names.OpenStackControlplaneName) + Expect(cp.Spec.Heat.Enabled).To(BeTrue()) + Expect(cp.Spec.Heat.ApplicationCredential).NotTo(BeNil()) + Expect(cp.Spec.Heat.ApplicationCredential.Enabled).To(BeTrue()) + Expect(cp.Spec.Heat.Template).NotTo(BeNil()) + }) + }) + + When("Ironic service with application credentials enabled", func() { + BeforeEach(func() { + spec := GetDefaultOpenStackControlPlaneSpec() + spec["applicationCredential"] = map[string]interface{}{ + "enabled": true, + "expirationDays": 365, + "gracePeriodDays": 182, + } + spec["ironic"] = map[string]interface{}{ + "enabled": true, + "template": map[string]interface{}{ + "databaseInstance": "openstack", + "secret": "osp-secret", + "ironicConductors": []map[string]interface{}{ + { + "replicas": 1, + }, + }, + }, + "applicationCredential": map[string]interface{}{ + "enabled": true, + }, + } + + DeferCleanup(th.DeleteInstance, + CreateOpenStackControlPlane(names.OpenStackControlplaneName, spec), + ) + }) + It("should configure ApplicationCredential in spec for Ironic service", func() { + // Verify the spec is configured correctly for Ironic AC + cp := GetOpenStackControlPlane(names.OpenStackControlplaneName) + Expect(cp.Spec.Ironic.Enabled).To(BeTrue()) + Expect(cp.Spec.Ironic.ApplicationCredential).NotTo(BeNil()) + Expect(cp.Spec.Ironic.ApplicationCredential.Enabled).To(BeTrue()) + Expect(cp.Spec.Ironic.Template).NotTo(BeNil()) }) }) }) diff --git a/test/kuttl/common/osp_check_appcred_id.sh b/test/kuttl/common/osp_check_appcred_id.sh new file mode 100755 index 000000000..455dd9d5e --- /dev/null +++ b/test/kuttl/common/osp_check_appcred_id.sh @@ -0,0 +1,249 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Script to verify Application Credential IDs and AC secrets are correctly configured in OpenStack service pods +# Usage: osp_check_appcred_id.sh + +if [[ $# -lt 2 ]]; then + echo "Usage: $0 " >&2 + echo " SERVICE: barbican, glance, swift, cinder, manila (or 'all' for supported services)" >&2 + exit 1 +fi + +NAMESPACE="$1" +REQUESTED_SERVICE="$2" +DEBUG=${DEBUG:-0} + +declare -a FAILED_CHECKS=() + +# Service definitions: service_name -> "config_path|resource_type/resource_name:container,resource_type/resource_name:container" +# resource_type can be: deploy (deployment) or sts (statefulset) +declare -A SERVICES=( + [barbican]="/etc/barbican/barbican.conf.d/00-default.conf|deploy/barbican-api:barbican-api" + [cinder]="/etc/cinder/cinder.conf.d/00-default.conf|sts/cinder-api:cinder-api,sts/cinder-scheduler:cinder-scheduler" + [glance]="/etc/glance/glance-api.conf.d/00-default.conf|sts/glance-default-external-api:glance-api,sts/glance-default-internal-api:glance-api" + [swift]="/etc/swift/proxy-server.conf.d/00-proxy-server.conf|deploy/swift-proxy:proxy-server" + [manila]="/etc/manila/manila.conf.d/00-config.conf|sts/manila-api:manila-api,sts/manila-scheduler:manila-scheduler" + # TODO: Add remaining services when they support application credentials in their operators: + # [nova]="/etc/nova/nova.conf|sts/nova-api:nova-api,sts/nova-cell0-conductor:nova-conductor,sts/nova-cell1-conductor:nova-conductor,sts/nova-metadata:nova-metadata,sts/nova-scheduler:nova-scheduler" + [neutron]="/etc/neutron/neutron.conf.d/01-default.conf|deploy/neutron:neutron-server" + [placement]="/etc/placement/placement.conf|deploy/placement:placement-api" + # [ceilometer]="/etc/ceilometer/ceilometer.conf|sts/ceilometer:ceilometer-central" + # [aodh]="/etc/aodh/aodh.conf|sts/aodh-api:aodh-api" + # [heat]="/etc/heat/heat.conf|sts/heat-api:heat-api" + # [ironic]="/etc/ironic/ironic.conf|sts/ironic-api:ironic-api" + [octavia]="/etc/octavia/octavia.conf|sts/octavia-api:octavia-api" + # [designate]="/etc/designate/designate.conf|sts/designate-api:designate-api" + # [watcher]="/etc/watcher/watcher.conf|sts/watcher-api:watcher-api" +) + +RESOURCE_TYPE="keystoneapplicationcredential" + +debug() { + [[ $DEBUG -ge 1 ]] && echo "[DEBUG] $*" >&2 +} + +error() { + echo "[ERROR] $*" >&2 +} + +# Add a failed check to the global list +add_failed_check() { + local service="$1" resource_spec="$2" container="$3" reason="$4" + FAILED_CHECKS+=("$service: $resource_spec/$container - $reason") +} + +# Convert resource type shorthand to full name +get_resource_type() { + case "$1" in + deploy) echo "deployment" ;; + sts) echo "statefulset" ;; + *) echo "$1" ;; + esac +} + +# Extract application credential field from config file in pod +get_app_cred_field_from_pod() { + local resource_spec="$1" container="$2" config_path="$3" field_name="$4" + local output + + debug "Executing: oc exec -n $NAMESPACE $resource_spec -c $container -- sh -c \"grep '^[[:space:]]*${field_name}[[:space:]]*=' '$config_path' | sed 's/^[^=]*=[[:space:]]*//' | sed 's/[[:space:]]*$//' | head -1\"" + + if output=$(oc exec -n "$NAMESPACE" "$resource_spec" -c "$container" -- \ + sh -c "grep '^[[:space:]]*${field_name}[[:space:]]*=' '$config_path' | sed 's/^[^=]*=[[:space:]]*//' | sed 's/[[:space:]]*$//' | head -1" 2>/dev/null); then + debug "Successfully extracted $field_name from $resource_spec/$container: $output" + echo "$output" + return 0 + fi + + error "Failed to extract $field_name from $resource_spec/$container" + return 1 +} + +# Check a single service +check_service() { + local service="$1" + local service_def="${SERVICES[$service]}" + local config_path="${service_def%%|*}" + local targets="${service_def##*|}" + + echo "Checking service: $service" + + # Get expected Application Credential data from Kubernetes resources + local cr_name="ac-$service" + local expected_id expected_secret + + # Get AC ID from the KeystoneApplicationCredential status + if ! expected_id=$(oc get "$RESOURCE_TYPE" "$cr_name" -n "$NAMESPACE" -o jsonpath='{.status.acID}' 2>/dev/null); then + error "Failed to get Application Credential ID from $RESOURCE_TYPE/$cr_name" + add_failed_check "$service" "$cr_name" "N/A" "Failed to get Application Credential ID from Kubernetes resource" + return 1 + fi + + if [[ -z "$expected_id" ]]; then + error "$RESOURCE_TYPE/$cr_name has empty .status.acID" + add_failed_check "$service" "$cr_name" "N/A" "Empty .status.acID in Kubernetes resource" + return 1 + fi + + # Get AC Secret from the associated secret (base64 decoded) + local secret_name + if ! secret_name=$(oc get "$RESOURCE_TYPE" "$cr_name" -n "$NAMESPACE" -o jsonpath='{.status.secretName}' 2>/dev/null); then + error "Failed to get secret name from $RESOURCE_TYPE/$cr_name" + add_failed_check "$service" "$cr_name" "N/A" "Failed to get secret name from Kubernetes resource" + return 1 + fi + + if [[ -z "$secret_name" ]]; then + error "$RESOURCE_TYPE/$cr_name has empty .status.secretName" + add_failed_check "$service" "$cr_name" "N/A" "Empty .status.secretName in Kubernetes resource" + return 1 + fi + + # Get and decode the AC_SECRET from the Kubernetes secret + if ! expected_secret=$(oc get secret "$secret_name" -n "$NAMESPACE" -o jsonpath='{.data.AC_SECRET}' 2>/dev/null | base64 -d); then + error "Failed to get AC_SECRET from secret/$secret_name" + add_failed_check "$service" "$secret_name" "N/A" "Failed to get AC_SECRET from Kubernetes secret" + return 1 + fi + + if [[ -z "$expected_secret" ]]; then + error "secret/$secret_name has empty AC_SECRET" + add_failed_check "$service" "$secret_name" "N/A" "Empty AC_SECRET in Kubernetes secret" + return 1 + fi + + echo " Expected ID: $expected_id" + echo " Expected Secret: ${expected_secret:0:20}..." + + local failed=0 + + # Check each resource/container pair + IFS=',' read -ra TARGET_LIST <<< "$targets" + for target in "${TARGET_LIST[@]}"; do + local resource_spec="${target%%:*}" # e.g., "deploy/swift-proxy" + local container="${target##*:}" # e.g., "proxy-server" + + # Parse resource type and name + local resource_type_short="${resource_spec%%/*}" # e.g., "deploy" + local resource_name="${resource_spec##*/}" # e.g., "swift-proxy" + local resource_type_full=$(get_resource_type "$resource_type_short") + + # Skip if resource doesn't exist + if ! oc get "$resource_type_full" "$resource_name" -n "$NAMESPACE" >/dev/null 2>&1; then + echo " Skipping $resource_type_full/$resource_name (not found)" + continue + fi + + # Check Application Credential ID + local actual_id + if ! actual_id=$(get_app_cred_field_from_pod "$resource_type_full/$resource_name" "$container" "$config_path" "application_credential_id"); then + add_failed_check "$service" "$resource_spec" "$container" "Failed to extract application_credential_id from pod" + failed=1 + elif [[ -z "$actual_id" ]]; then + error " $resource_spec/$container: application_credential_id not found in $config_path" + add_failed_check "$service" "$resource_spec" "$container" "application_credential_id not found in config file" + failed=1 + elif [[ "$actual_id" != "$expected_id" ]]; then + echo " $resource_spec/$container: Found ID: $actual_id" + error " $resource_spec/$container: ID mismatch (got: $actual_id, expected: $expected_id)" + add_failed_check "$service" "$resource_spec" "$container" "ID mismatch (got: $actual_id, expected: $expected_id)" + failed=1 + else + echo " $resource_spec/$container: Found ID: $actual_id" + echo " $resource_spec/$container: ID matches" + fi + + # Check Application Credential Secret + local actual_secret + if ! actual_secret=$(get_app_cred_field_from_pod "$resource_type_full/$resource_name" "$container" "$config_path" "application_credential_secret"); then + add_failed_check "$service" "$resource_spec" "$container" "Failed to extract application_credential_secret from pod" + failed=1 + elif [[ -z "$actual_secret" ]]; then + error " $resource_spec/$container: application_credential_secret not found in $config_path" + add_failed_check "$service" "$resource_spec" "$container" "application_credential_secret not found in config file" + failed=1 + elif [[ "$actual_secret" != "$expected_secret" ]]; then + echo " $resource_spec/$container: Found Secret: ${actual_secret:0:20}..." + error " $resource_spec/$container: Secret mismatch (got: ${actual_secret:0:20}..., expected: ${expected_secret:0:20}...)" + add_failed_check "$service" "$resource_spec" "$container" "Secret mismatch" + failed=1 + else + echo " $resource_spec/$container: Found Secret: ${actual_secret:0:20}..." + echo " $resource_spec/$container: Secret matches" + fi + done + + return $failed +} + +# Print summary of failed checks +print_failed_checks_summary() { + if [[ ${#FAILED_CHECKS[@]} -eq 0 ]]; then + return + fi + + echo + echo "FAILED APPLICATION CREDENTIAL CHECKS:" + for failure in "${FAILED_CHECKS[@]}"; do + echo " $failure" + done +} + +# Main execution +main() { + local services_to_check=() + + if [[ "$REQUESTED_SERVICE" == "all" ]]; then + services_to_check=("${!SERVICES[@]}") + else + if [[ -z "${SERVICES[$REQUESTED_SERVICE]:-}" ]]; then + error "Service '$REQUESTED_SERVICE' is not supported" + echo "Supported services: ${!SERVICES[*]}" >&2 + exit 1 + fi + services_to_check=("$REQUESTED_SERVICE") + fi + + local overall_failed=0 + + for service in "${services_to_check[@]}"; do + if ! check_service "$service"; then + overall_failed=1 + fi + echo + done + + # Print summary of failures + print_failed_checks_summary + + if [[ $overall_failed -eq 0 ]]; then + echo "All Application Credential checks passed" + else + echo "Some Application Credential checks failed" + fi + + exit $overall_failed +} + +main diff --git a/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/01-assert-deploy-openstack.yaml b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/01-assert-deploy-openstack.yaml new file mode 120000 index 000000000..762a8cf31 --- /dev/null +++ b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/01-assert-deploy-openstack.yaml @@ -0,0 +1 @@ +../../common/assert-sample-deployment.yaml \ No newline at end of file diff --git a/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/01-deploy-openstack.yaml b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/01-deploy-openstack.yaml new file mode 100644 index 000000000..6c9d0887d --- /dev/null +++ b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/01-deploy-openstack.yaml @@ -0,0 +1,5 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + oc kustomize ../../../../config/samples/base/openstackcontrolplane | oc apply -n $NAMESPACE -f - diff --git a/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/02-assert-appcred-crs.yaml b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/02-assert-appcred-crs.yaml new file mode 100644 index 000000000..8e526419a --- /dev/null +++ b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/02-assert-appcred-crs.yaml @@ -0,0 +1,84 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: + - script: |- + set -euo pipefail + NS="${NAMESPACE}" + + wait_ready() { oc wait appcred/$1 -n "$NS" --for=condition=Ready --timeout=120s; } + + # ---- ac-barbican ---- + wait_ready ac-barbican + + EXP=$(oc get appcred ac-barbican -n "$NS" -o jsonpath='{.spec.expirationDays}') + [ "${EXP:-}" -eq 200 ] + + GRP=$(oc get appcred ac-barbican -n "$NS" -o jsonpath='{.spec.gracePeriodDays}') + [ "${GRP:-}" -eq 90 ] + + ROL=$(oc get appcred ac-barbican -n "$NS" -o jsonpath='{.spec.roles[*]}') + case " $ROL " in *" service "*) ;; *) exit 1;; esac + + UNR=$(oc get appcred ac-barbican -n "$NS" -o jsonpath='{.spec.unrestricted}' 2>/dev/null || echo "") + [ "${UNR:-false}" = "false" ] + + # ---- ac-cinder ---- + wait_ready ac-cinder + + EXP=$(oc get appcred ac-cinder -n "$NS" -o jsonpath='{.spec.expirationDays}') + [ "${EXP:-}" -eq 10 ] + + GRP=$(oc get appcred ac-cinder -n "$NS" -o jsonpath='{.spec.gracePeriodDays}') + [ "${GRP:-}" -eq 5 ] + + ROL=$(oc get appcred ac-cinder -n "$NS" -o jsonpath='{.spec.roles[*]}') + case " $ROL " in *" admin "*) ;; *) exit 1;; esac + case " $ROL " in *" service "*) ;; *) exit 1;; esac + + UNR=$(oc get appcred ac-cinder -n "$NS" -o jsonpath='{.spec.unrestricted}' 2>/dev/null || echo "") + [ "${UNR}" = "true" ] + + # ---- ac-swift ---- + wait_ready ac-swift + + EXP=$(oc get appcred ac-swift -n "$NS" -o jsonpath='{.spec.expirationDays}') + [ "${EXP:-}" -eq 200 ] + + GRP=$(oc get appcred ac-swift -n "$NS" -o jsonpath='{.spec.gracePeriodDays}') + [ "${GRP:-}" -eq 90 ] + + ROL=$(oc get appcred ac-swift -n "$NS" -o jsonpath='{.spec.roles[*]}') + case " $ROL " in *" service "*) ;; *) exit 1;; esac + + UNR=$(oc get appcred ac-swift -n "$NS" -o jsonpath='{.spec.unrestricted}' 2>/dev/null || echo "") + [ "${UNR:-false}" = "false" ] + + # ---- ac-glance ---- + wait_ready ac-glance + + EXP=$(oc get appcred ac-glance -n "$NS" -o jsonpath='{.spec.expirationDays}') + [ "${EXP:-}" -eq 200 ] + + GRP=$(oc get appcred ac-glance -n "$NS" -o jsonpath='{.spec.gracePeriodDays}') + [ "${GRP:-}" -eq 90 ] + + ROL=$(oc get appcred ac-glance -n "$NS" -o jsonpath='{.spec.roles[*]}') + case " $ROL " in *" service "*) ;; *) exit 1;; esac + + UNR=$(oc get appcred ac-glance -n "$NS" -o jsonpath='{.spec.unrestricted}' 2>/dev/null || echo "") + [ "${UNR:-false}" = "false" ] + + # ---- ac-manila ---- + wait_ready ac-manila + + EXP=$(oc get appcred ac-manila -n "$NS" -o jsonpath='{.spec.expirationDays}') + [ "${EXP:-}" -eq 200 ] + + GRP=$(oc get appcred ac-manila -n "$NS" -o jsonpath='{.spec.gracePeriodDays}') + [ "${GRP:-}" -eq 90 ] + + ROL=$(oc get appcred ac-manila -n "$NS" -o jsonpath='{.spec.roles[*]}') + case " $ROL " in *" service "*) ;; *) exit 1;; esac + + UNR=$(oc get appcred ac-manila -n "$NS" -o jsonpath='{.spec.unrestricted}' 2>/dev/null || echo "") + [ "${UNR:-false}" = "false" ] diff --git a/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/02-deploy-appcred-config.yaml b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/02-deploy-appcred-config.yaml new file mode 100644 index 000000000..3cb5652ca --- /dev/null +++ b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/02-deploy-appcred-config.yaml @@ -0,0 +1,5 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + oc kustomize ../../../../config/samples/applicationcredentials | oc apply -n $NAMESPACE -f - diff --git a/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/03-get-appcred-ids.yaml b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/03-get-appcred-ids.yaml new file mode 100644 index 000000000..d0491d3cb --- /dev/null +++ b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/03-get-appcred-ids.yaml @@ -0,0 +1,28 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + echo "Waiting 60 seconds for OpenStack control plane to be fully ready..." + echo "Control plane switches to ready state before service pods catch up with AC changes" + sleep 60 + + echo "Gather and save appcred IDs" + # TODO: Uncomment services here when they support application credentials in their operators + # Currently implemented: barbican, cinder, glance, swift, manila + # Future support: nova, neutron, placement, ceilometer, ironic, heat, octavia, designate, watcher, aodh + bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE barbican + bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE cinder + bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE glance + bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE swift + bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE manila + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE nova + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE neutron + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE placement + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE ceilometer + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE ironic + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE heat + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE octavia + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE designate + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE watcher + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE aodh + # bash -s < ../../common/osp_check_appcred_id.sh $NAMESPACE all diff --git a/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/07-cleanup.yaml b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/07-cleanup.yaml new file mode 100644 index 000000000..df9df9fe0 --- /dev/null +++ b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/07-cleanup.yaml @@ -0,0 +1,11 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +delete: +- apiVersion: core.openstack.org/v1beta1 + kind: OpenStackControlPlane + name: openstack +commands: +- script: | + oc delete secret --ignore-not-found=true combined-ca-bundle -n $NAMESPACE + oc delete secret -l service-cert -n $NAMESPACE + oc delete secret -l ca-cert -n $NAMESPACE diff --git a/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/07-errors-cleanup.yaml b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/07-errors-cleanup.yaml new file mode 120000 index 000000000..4d7b8362e --- /dev/null +++ b/tests/kuttl/tests/ctlplane-basic-deployment-with-appcred/07-errors-cleanup.yaml @@ -0,0 +1 @@ +../../common/errors_cleanup_openstack.yaml \ No newline at end of file