From e0cf6457e671ba2a0edeb1ddaecdda2859487f4a Mon Sep 17 00:00:00 2001 From: Mauricio Harley Date: Thu, 5 Oct 2023 06:58:48 -0400 Subject: [PATCH 1/3] Create barbican keystone listener deployment --- api/v1beta1/barbican_types.go | 13 +- api/v1beta1/barbican_webhook.go | 9 +- api/v1beta1/barbicankeystonelistener_types.go | 88 +++ api/v1beta1/conditions.go | 8 + api/v1beta1/zz_generated.deepcopy.go | 137 ++++ ...enstack.org_barbicankeystonelisteners.yaml | 292 ++++++++ .../barbican.openstack.org_barbicans.yaml | 109 +++ ...njection_in_barbicankeystonelisteners.yaml | 7 + .../webhook_in_barbicankeystonelisteners.yaml | 16 + config/default/manager_default_images.yaml | 2 + config/samples/barbican_v1beta1_barbican.yaml | 10 + controllers/barbican_controller.go | 53 ++ .../barbicankeystonelistener_controller.go | 646 ++++++++++++++++++ controllers/barbicanworker_controller.go | 3 +- main.go | 10 + pkg/barbican/const.go | 7 + pkg/barbicankeystonelistener/deployment.go | 161 +++++ templates/barbican/config/00-default.conf | 6 +- .../config/barbican-keystone-listener | 7 + .../barbican-keystone-listener-config.json | 39 ++ templates/barbicankeystonelistener | 1 + 21 files changed, 1616 insertions(+), 8 deletions(-) create mode 100644 api/v1beta1/barbicankeystonelistener_types.go create mode 100644 config/crd/bases/barbican.openstack.org_barbicankeystonelisteners.yaml create mode 100644 config/crd/patches/cainjection_in_barbicankeystonelisteners.yaml create mode 100644 config/crd/patches/webhook_in_barbicankeystonelisteners.yaml create mode 100644 controllers/barbicankeystonelistener_controller.go create mode 100644 pkg/barbicankeystonelistener/deployment.go create mode 100644 templates/barbican/config/barbican-keystone-listener create mode 100644 templates/barbican/config/barbican-keystone-listener-config.json create mode 120000 templates/barbicankeystonelistener diff --git a/api/v1beta1/barbican_types.go b/api/v1beta1/barbican_types.go index e1f465d..c0aed06 100644 --- a/api/v1beta1/barbican_types.go +++ b/api/v1beta1/barbican_types.go @@ -33,6 +33,9 @@ const ( // BarbicanWorkerContainerImage is the fall-back container image for BarbicanAPI BarbicanWorkerContainerImage = "quay.io/podified-antelope-centos9/openstack-barbican-worker:current-podified" + + // BarbicanKeystoneListenerContainerImage is the fall-back container image for BarbicanAPI + BarbicanKeystoneListenerContainerImage = "quay.io/podified-antelope-centos9/openstack-barbican-keystone-listener:current-podified" ) // BarbicanSpec defines the desired state of Barbican @@ -67,6 +70,8 @@ type BarbicanSpec struct { BarbicanAPI BarbicanAPITemplate `json:"barbicanAPI"` BarbicanWorker BarbicanWorkerTemplate `json:"barbicanWorker"` + + BarbicanKeystoneListener BarbicanKeystoneListenerTemplate `json:"barbicanKeystoneListener"` } // BarbicanStatus defines the observed state of Barbican @@ -86,6 +91,9 @@ type BarbicanStatus struct { // ReadyCount of Barbican Worker instances BarbicanWorkerReadyCount int32 `json:"barbicanWorkerReadyCount,omitempty"` + // ReadyCount of Barbican KeystoneListener instances + BarbicanKeystoneListenerReadyCount int32 `json:"barbicanKeystoneListenerReadyCount,omitempty"` + // TransportURLSecret - Secret containing RabbitMQ transportURL TransportURLSecret string `json:"transportURLSecret,omitempty"` @@ -142,8 +150,9 @@ func init() { func SetupDefaults() { // Acquire environmental defaults and initialize Barbican defaults with them barbicanDefaults := BarbicanDefaults{ - APIContainerImageURL: util.GetEnvVar("BARBICAN_API_IMAGE_URL_DEFAULT", BarbicanAPIContainerImage), - WorkerContainerImageURL: util.GetEnvVar("BARBICAN_WORKER_IMAGE_URL_DEFAULT", BarbicanWorkerContainerImage), + APIContainerImageURL: util.GetEnvVar("BARBICAN_API_IMAGE_URL_DEFAULT", BarbicanAPIContainerImage), + WorkerContainerImageURL: util.GetEnvVar("BARBICAN_WORKER_IMAGE_URL_DEFAULT", BarbicanWorkerContainerImage), + KeystoneListenerContainerImageURL: util.GetEnvVar("BARBICAN_KEYSTONE_LISTENER_IMAGE_URL_DEFAULT", BarbicanKeystoneListenerContainerImage), } SetupBarbicanDefaults(barbicanDefaults) diff --git a/api/v1beta1/barbican_webhook.go b/api/v1beta1/barbican_webhook.go index 7381c9b..2f2e8e8 100644 --- a/api/v1beta1/barbican_webhook.go +++ b/api/v1beta1/barbican_webhook.go @@ -31,8 +31,9 @@ import ( // BarbicanDefaults - type BarbicanDefaults struct { - APIContainerImageURL string - WorkerContainerImageURL string + APIContainerImageURL string + WorkerContainerImageURL string + KeystoneListenerContainerImageURL string } var barbicanDefaults BarbicanDefaults @@ -73,6 +74,10 @@ func (spec *BarbicanSpec) Default() { if spec.BarbicanWorker.ContainerImage == "" { spec.BarbicanWorker.ContainerImage = barbicanDefaults.WorkerContainerImageURL } + + if spec.BarbicanKeystoneListener.ContainerImage == "" { + spec.BarbicanKeystoneListener.ContainerImage = barbicanDefaults.KeystoneListenerContainerImageURL + } } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. diff --git a/api/v1beta1/barbicankeystonelistener_types.go b/api/v1beta1/barbicankeystonelistener_types.go new file mode 100644 index 0000000..b7732aa --- /dev/null +++ b/api/v1beta1/barbicankeystonelistener_types.go @@ -0,0 +1,88 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// BarbicanKeystoneListenerTemplate defines common Spec elements for the KeystoneListener process +type BarbicanKeystoneListenerTemplate struct { + BarbicanComponentTemplate `json:",inline"` + + // TODO(dmendiza): Do we need a setting for number of keystone listener processes + // or is replica scaling good enough? +} + +// BarbicanKeystoneListenerSpec defines the desired state of BarbicanKeystoneListener +type BarbicanKeystoneListenerSpec struct { + BarbicanTemplate `json:",inline"` + + BarbicanKeystoneListenerTemplate `json:",inline"` + DatabaseHostname string `json:"databaseHostname"` + + TransportURLSecret string `json:"transportURLSecret,omitempty"` +} + +// BarbicanKeystoneListenerStatus defines the observed state of BarbicanKeystoneListener +type BarbicanKeystoneListenerStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + // ReadyCount of barbican API instances + ReadyCount int32 `json:"readyCount,omitempty"` + + // Map of hashes to track e.g. job status + Hash map[string]string `json:"hash,omitempty"` + + // API endpoint + //APIEndpoints map[string]string `json:"apiEndpoint,omitempty"` + + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + + // NetworkAttachments status of the deployment pods + NetworkAttachments map[string][]string `json:"networkAttachments,omitempty"` + + // Barbican Database Hostname + DatabaseHostname string `json:"databaseHostname,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// BarbicanKeystoneListener is the Schema for the barbicankeystonelistener API +type BarbicanKeystoneListener struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BarbicanKeystoneListenerSpec `json:"spec,omitempty"` + Status BarbicanKeystoneListenerStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// BarbicanKeystoneListenerList contains a list of BarbicanKeystoneListener +type BarbicanKeystoneListenerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []BarbicanKeystoneListener `json:"items"` +} + +func init() { + SchemeBuilder.Register(&BarbicanKeystoneListener{}, &BarbicanKeystoneListenerList{}) +} diff --git a/api/v1beta1/conditions.go b/api/v1beta1/conditions.go index 9061c0f..9f00d9d 100644 --- a/api/v1beta1/conditions.go +++ b/api/v1beta1/conditions.go @@ -8,6 +8,10 @@ const ( // BarbicanWorkerReadyCondition - BarbicanWorkerReadyCondition condition.Type = "BarbicanWorkerReady" + + // BarbicanKeystoneListenerReadyCondition - + BarbicanKeystoneListenerReadyCondition condition.Type = "BarbicanKeystoneListenerReady" + // BarbicanRabbitMQTransportURLReadyCondition - BarbicanRabbitMQTransportURLReadyCondition condition.Type = "BarbicanRabbitMQTransportURLReady" ) @@ -21,6 +25,10 @@ const ( BarbicanWorkerReadyInitMessage = "BarbicanWorker not started" // BarbicanWorkerReadyErrorMessage - BarbicanWorkerReadyErrorMessage = "BarbicanWorker error occured %s" + // BarbicanKeystoneListenerReadyInitMessage - + BarbicanKeystoneListenerReadyInitMessage = "BarbicanKeystoneListener not started" + // BarbicanKeystoneListenerReadyErrorMessage - + BarbicanKeystoneListenerReadyErrorMessage = "BarbicanKeystoneListener error occured %s" // BarbicanRabbitMQTransportURLReadyRunningMessage - BarbicanRabbitMQTransportURLReadyRunningMessage = "BarbicanRabbitMQTransportURL creation in progress" diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index c444afe..6232548 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -278,6 +278,142 @@ func (in *BarbicanDefaults) DeepCopy() *BarbicanDefaults { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BarbicanKeystoneListener) DeepCopyInto(out *BarbicanKeystoneListener) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanKeystoneListener. +func (in *BarbicanKeystoneListener) DeepCopy() *BarbicanKeystoneListener { + if in == nil { + return nil + } + out := new(BarbicanKeystoneListener) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BarbicanKeystoneListener) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BarbicanKeystoneListenerList) DeepCopyInto(out *BarbicanKeystoneListenerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BarbicanKeystoneListener, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanKeystoneListenerList. +func (in *BarbicanKeystoneListenerList) DeepCopy() *BarbicanKeystoneListenerList { + if in == nil { + return nil + } + out := new(BarbicanKeystoneListenerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BarbicanKeystoneListenerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BarbicanKeystoneListenerSpec) DeepCopyInto(out *BarbicanKeystoneListenerSpec) { + *out = *in + out.BarbicanTemplate = in.BarbicanTemplate + in.BarbicanKeystoneListenerTemplate.DeepCopyInto(&out.BarbicanKeystoneListenerTemplate) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanKeystoneListenerSpec. +func (in *BarbicanKeystoneListenerSpec) DeepCopy() *BarbicanKeystoneListenerSpec { + if in == nil { + return nil + } + out := new(BarbicanKeystoneListenerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BarbicanKeystoneListenerStatus) DeepCopyInto(out *BarbicanKeystoneListenerStatus) { + *out = *in + if in.Hash != nil { + in, out := &in.Hash, &out.Hash + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanKeystoneListenerStatus. +func (in *BarbicanKeystoneListenerStatus) DeepCopy() *BarbicanKeystoneListenerStatus { + if in == nil { + return nil + } + out := new(BarbicanKeystoneListenerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BarbicanKeystoneListenerTemplate) DeepCopyInto(out *BarbicanKeystoneListenerTemplate) { + *out = *in + in.BarbicanComponentTemplate.DeepCopyInto(&out.BarbicanComponentTemplate) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanKeystoneListenerTemplate. +func (in *BarbicanKeystoneListenerTemplate) DeepCopy() *BarbicanKeystoneListenerTemplate { + if in == nil { + return nil + } + out := new(BarbicanKeystoneListenerTemplate) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BarbicanList) DeepCopyInto(out *BarbicanList) { *out = *in @@ -330,6 +466,7 @@ func (in *BarbicanSpec) DeepCopyInto(out *BarbicanSpec) { } in.BarbicanAPI.DeepCopyInto(&out.BarbicanAPI) in.BarbicanWorker.DeepCopyInto(&out.BarbicanWorker) + in.BarbicanKeystoneListener.DeepCopyInto(&out.BarbicanKeystoneListener) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanSpec. diff --git a/config/crd/bases/barbican.openstack.org_barbicankeystonelisteners.yaml b/config/crd/bases/barbican.openstack.org_barbicankeystonelisteners.yaml new file mode 100644 index 0000000..f70f75e --- /dev/null +++ b/config/crd/bases/barbican.openstack.org_barbicankeystonelisteners.yaml @@ -0,0 +1,292 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null + name: barbicankeystonelisteners.barbican.openstack.org +spec: + group: barbican.openstack.org + names: + kind: BarbicanKeystoneListener + listKind: BarbicanKeystoneListenerList + plural: barbicankeystonelisteners + singular: barbicankeystonelistener + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: BarbicanKeystoneListener is the Schema for the barbicankeystonelistener + 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: BarbicanKeystoneListenerSpec defines the desired state of + BarbicanKeystoneListener + properties: + containerImage: + description: Barbican Container Image URL (will be set to environmental + default if empty) + type: string + customServiceConfig: + description: CustomServiceConfig - customize the service config using + this parameter to change service defaults, or overwrite rendered + information using raw OpenStack config format. The content gets + added to to /etc//.conf.d directory as custom.conf + file. + type: string + customServiceConfigSecrets: + description: CustomServiceConfigSecrets - customize the service config + using this parameter to specify Secrets that contain sensitive service + config data. The content of each Secret gets added to the /etc//.conf.d + directory as a custom config file. + items: + type: string + type: array + databaseHostname: + type: string + databaseInstance: + description: 'MariaDB instance name TODO(dmendiza): Is this comment + right? Right now required by the maridb-operator to get the credentials + from the instance to create the DB Might not be required in future' + type: string + databaseUser: + default: barbican + description: DatabaseUser - optional username used for barbican DB, + defaults to barbican + type: string + debug: + description: 'Debug - enable debug for different deploy stages. If + an init container is used, it runs and the actual action pod gets + started with sleep infinity TODO(dmendiza): Do we need this?' + properties: + dbInitContainer: + default: false + description: dbInitContainer enable debug (waits until /tmp/stop-init-container + disappears) + type: boolean + dbSync: + default: false + description: dbSync enable debug + type: boolean + initContainer: + default: false + description: initContainer enable debug (waits until /tmp/stop-init-container + disappears) + type: boolean + service: + default: false + description: Service enable debug + type: boolean + type: object + defaultConfigOverwrite: + additionalProperties: + type: string + description: 'ConfigOverwrite - interface to overwrite default config + files like e.g. policy.json. But can also be used to add additional + files. Those get added to the service config dir in /etc/ + . TODO: -> implement' + type: object + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment resource + names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes running + this component. Setting here overrides any global NodeSelector settings + within the Barbican CR. + type: object + passwordSelectors: + default: + database: BarbicanDatabasePassword + service: BarbicanPassword + description: 'TODO(dmendiza): Maybe we''ll add SimpleCrypto key here? + PasswordSelectors - Selectors to identify the DB and ServiceUser + password from the Secret' + properties: + database: + default: BarbicanDatabasePassword + description: Database - Selector to get the barbican database + user password from the Secret + type: string + service: + default: BarbicanPassword + description: Service - Selector to get the barbican service user + password from the Secret + type: string + type: object + rabbitMqClusterName: + default: rabbitmq + description: RabbitMQ instance name Needed to request a transportURL + that is created and used in Barbican + type: string + replicas: + default: 1 + description: Replicas of Barbican API to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: Resources - Compute Resources required by this service + (Limits/Requests). https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: "Claims lists the names of resources, defined in + spec.resourceClaims, that are used by this container. \n This + is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only be set + for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims + of the Pod where this field is used. It makes that resource + available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + secret: + description: Secret containing all passwords / keys needed + type: string + serviceAccount: + description: ServiceAccount - service account name used internally + to provide Barbican services the default SA name + type: string + serviceUser: + default: barbican + description: ServiceUser - optional username used for this service + to register in keystone + type: string + simpleCryptoBackendKEKSecret: + description: Secret containing SimpleCrypto KEK + type: string + transportURLSecret: + type: string + required: + - containerImage + - databaseHostname + - databaseInstance + - rabbitMqClusterName + - serviceAccount + type: object + status: + description: BarbicanKeystoneListenerStatus defines the observed state + of BarbicanKeystoneListener + properties: + 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 + databaseHostname: + description: Barbican Database Hostname + type: string + hash: + additionalProperties: + type: string + description: Map of hashes to track e.g. job status + type: object + networkAttachments: + additionalProperties: + items: + type: string + type: array + description: NetworkAttachments status of the deployment pods + type: object + readyCount: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file ReadyCount of barbican API instances' + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/barbican.openstack.org_barbicans.yaml b/config/crd/bases/barbican.openstack.org_barbicans.yaml index 1c5e79d..ecfe849 100644 --- a/config/crd/bases/barbican.openstack.org_barbicans.yaml +++ b/config/crd/bases/barbican.openstack.org_barbicans.yaml @@ -183,6 +183,110 @@ spec: required: - containerImage type: object + barbicanKeystoneListener: + description: BarbicanKeystoneListenerTemplate defines common Spec + elements for the KeystoneListener process + properties: + containerImage: + description: ContainerImage - Barbican Container Image URL (will + be set to environmental default if empty) + type: string + customServiceConfig: + description: CustomServiceConfig - customize the service config + using this parameter to change service defaults, or overwrite + rendered information using raw OpenStack config format. The + content gets added to to /etc//.conf.d directory + as a custom config file. + type: string + customServiceConfigSecrets: + description: CustomServiceConfigSecrets - customize the service + config using this parameter to specify Secrets that contain + sensitive service config data. The content of each Secret gets + added to the /etc//.conf.d directory as a + custom config file. + items: + type: string + type: array + defaultConfigOverwrite: + additionalProperties: + type: string + description: 'ConfigOverwrite - interface to overwrite default + config files like e.g. policy.json. But can also be used to + add additional files. Those get added to the service config + dir in /etc/ . TODO: -> implement' + type: object + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes running + this component. Setting here overrides any global NodeSelector + settings within the Barbican CR. + type: object + replicas: + default: 1 + description: Replicas of Barbican API to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: Resources - Compute Resources required by this service + (Limits/Requests). https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only be + set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + required: + - containerImage + type: object barbicanWorker: description: BarbicanWorkerTemplate defines common Spec elements for the Worker process @@ -393,6 +497,7 @@ spec: type: string required: - barbicanAPI + - barbicanKeystoneListener - barbicanWorker - containerImage - databaseInstance @@ -406,6 +511,10 @@ spec: description: ReadyCount of Barbican API instances format: int32 type: integer + barbicanKeystoneListenerReadyCount: + description: ReadyCount of Barbican KeystoneListener instances + format: int32 + type: integer barbicanWorkerReadyCount: description: ReadyCount of Barbican Worker instances format: int32 diff --git a/config/crd/patches/cainjection_in_barbicankeystonelisteners.yaml b/config/crd/patches/cainjection_in_barbicankeystonelisteners.yaml new file mode 100644 index 0000000..b0d3caa --- /dev/null +++ b/config/crd/patches/cainjection_in_barbicankeystonelisteners.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: barbicankeystonelisteners.barbican.openstack.org diff --git a/config/crd/patches/webhook_in_barbicankeystonelisteners.yaml b/config/crd/patches/webhook_in_barbicankeystonelisteners.yaml new file mode 100644 index 0000000..35cd7b0 --- /dev/null +++ b/config/crd/patches/webhook_in_barbicankeystonelisteners.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: barbicankeystonelisteners.barbican.openstack.org +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/default/manager_default_images.yaml b/config/default/manager_default_images.yaml index 1ce3755..cfdfa77 100644 --- a/config/default/manager_default_images.yaml +++ b/config/default/manager_default_images.yaml @@ -15,4 +15,6 @@ spec: value: quay.io/podified-antelope-centos9/openstack-barbican-api:current-podified - name: BARBICAN_WORKER_IMAGE_URL_DEFAULT value: quay.io/podified-antelope-centos9/openstack-barbican-worker:current-podified + - name: BARBICAN_KEYSTONE_LISTENER_IMAGE_URL_DEFAULT + value: quay.io/podified-antelope-centos9/openstack-barbican-keystone-listener:current-podified diff --git a/config/samples/barbican_v1beta1_barbican.yaml b/config/samples/barbican_v1beta1_barbican.yaml index d4421c0..afcf71f 100644 --- a/config/samples/barbican_v1beta1_barbican.yaml +++ b/config/samples/barbican_v1beta1_barbican.yaml @@ -56,3 +56,13 @@ spec: defautlConfigOverwrite: optional_policy.json: | {"some": "custom policy"} + barbicanKeystoneListener: + containerImage: quay.io/podified-antelope-centos9/openstack-barbican-keystone-listener:current-podified + nodeSelector: + optional_override: here + customServiceConfig: | + [optional] + overrides = True + defautlConfigOverwrite: + optional_policy.json: | + {"some": "custom policy"} diff --git a/controllers/barbican_controller.go b/controllers/barbican_controller.go index e5f30ea..9182d2e 100644 --- a/controllers/barbican_controller.go +++ b/controllers/barbican_controller.go @@ -146,6 +146,7 @@ func (r *BarbicanReconciler) Reconcile(ctx context.Context, req ctrl.Request) (r condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage), condition.UnknownCondition(barbicanv1beta1.BarbicanAPIReadyCondition, condition.InitReason, barbicanv1beta1.BarbicanAPIReadyInitMessage), condition.UnknownCondition(barbicanv1beta1.BarbicanWorkerReadyCondition, condition.InitReason, barbicanv1beta1.BarbicanWorkerReadyInitMessage), + condition.UnknownCondition(barbicanv1beta1.BarbicanKeystoneListenerReadyCondition, condition.InitReason, barbicanv1beta1.BarbicanKeystoneListenerReadyInitMessage), condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyInitMessage), condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), // service account, role, rolebinding conditions @@ -326,6 +327,22 @@ func (r *BarbicanReconciler) reconcileNormal(ctx context.Context, instance *barb if op != controllerutil.OperationResultNone { r.Log.Info(fmt.Sprintf("Deployment %s successfully reconciled - operation: %s", instance.Name, string(op))) } + + // create or update Barbican KeystoneListener deployment + _, op, err = r.keystoneListenerDeploymentCreateOrUpdate(ctx, instance, helper) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + barbicanv1beta1.BarbicanKeystoneListenerReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + barbicanv1beta1.BarbicanKeystoneListenerReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + if op != controllerutil.OperationResultNone { + r.Log.Info(fmt.Sprintf("Deployment %s successfully reconciled - operation: %s", instance.Name, string(op))) + } + // TODO(dmendiza): Handle API endpoints // TODO(dmendiza): Understand what Glance is doing with the API conditions and maybe do it here too @@ -571,6 +588,42 @@ func (r *BarbicanReconciler) workerDeploymentCreateOrUpdate(ctx context.Context, return deployment, op, err } +func (r *BarbicanReconciler) keystoneListenerDeploymentCreateOrUpdate(ctx context.Context, instance *barbicanv1beta1.Barbican, helper *helper.Helper) (*barbicanv1beta1.BarbicanKeystoneListener, controllerutil.OperationResult, error) { + + r.Log.Info(fmt.Sprintf("Creating barbican KeystoneListener spec. transporturlsecret: '%s'", instance.Status.TransportURLSecret)) + r.Log.Info(fmt.Sprintf("database hostname: '%s'", instance.Status.DatabaseHostname)) + keystoneListenerSpec := barbicanv1beta1.BarbicanKeystoneListenerSpec{ + BarbicanTemplate: instance.Spec.BarbicanTemplate, + BarbicanKeystoneListenerTemplate: instance.Spec.BarbicanKeystoneListener, + DatabaseHostname: instance.Status.DatabaseHostname, + TransportURLSecret: instance.Status.TransportURLSecret, + } + + deployment := &barbicanv1beta1.BarbicanKeystoneListener{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-keystone-listener", instance.Name), + Namespace: instance.Namespace, + }, + } + + op, err := controllerutil.CreateOrUpdate(ctx, r.Client, deployment, func() error { + r.Log.Info("Setting deployment spec to be keystonelistenerspec") + deployment.Spec = keystoneListenerSpec + + err := controllerutil.SetControllerReference(instance, deployment, r.Scheme) + if err != nil { + return err + } + + // Add a finalizer to prevent user from manually removing child BarbicanAPI + controllerutil.AddFinalizer(deployment, helper.GetFinalizer()) + + return nil + }) + + return deployment, op, err +} + func (r *BarbicanReconciler) reconcileInit( ctx context.Context, instance *barbicanv1beta1.Barbican, diff --git a/controllers/barbicankeystonelistener_controller.go b/controllers/barbicankeystonelistener_controller.go new file mode 100644 index 0000000..aeb0710 --- /dev/null +++ b/controllers/barbicankeystonelistener_controller.go @@ -0,0 +1,646 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + "fmt" + "time" + + "github.com/go-logr/logr" + //routev1 "github.com/openshift/api/route/v1" + barbicanv1beta1 "github.com/openstack-k8s-operators/barbican-operator/api/v1beta1" + "github.com/openstack-k8s-operators/barbican-operator/pkg/barbican" + "github.com/openstack-k8s-operators/barbican-operator/pkg/barbicankeystonelistener" + //keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common" + "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/deployment" + //"github.com/openstack-k8s-operators/lib-common/modules/common/endpoint" + "github.com/openstack-k8s-operators/lib-common/modules/common/env" + "github.com/openstack-k8s-operators/lib-common/modules/common/helper" + "github.com/openstack-k8s-operators/lib-common/modules/common/labels" + nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" + "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/util" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + + //appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + k8s_errors "k8s.io/apimachinery/pkg/api/errors" +) + +/* +// GetClient - +func (r *BarbicanAPIReconciler) GetClient() client.Client { + return r.Client +} + +// GetKClient - +func (r *BarbicanAPIReconciler) GetKClient() kubernetes.Interface { + return r.Kclient +} + +// GetLogger - +func (r *BarbicanAPIReconciler) GetLogger() logr.Logger { + return r.Log +} + +// GetScheme - +func (r *BarbicanAPIReconciler) GetScheme() *runtime.Scheme { + return r.Scheme +} +*/ + +// BarbicanKeystoneListenerReconciler reconciles a BarbicanKeystoneListener object +type BarbicanKeystoneListenerReconciler struct { + client.Client + Kclient kubernetes.Interface + Log logr.Logger + Scheme *runtime.Scheme +} + +//+kubebuilder:rbac:groups=barbican.openstack.org,resources=barbicanapis,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=barbican.openstack.org,resources=barbicanapis/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=barbican.openstack.org,resources=barbicanapis/finalizers,verbs=update + +// Reconcile BarbicanAPI +func (r *BarbicanKeystoneListenerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, _err error) { + _ = log.FromContext(ctx) + + instance := &barbicanv1beta1.BarbicanKeystoneListener{} + err := r.Client.Get(ctx, req.NamespacedName, instance) + if err != nil { + if k8s_errors.IsNotFound(err) { + // Object not found + return ctrl.Result{}, err + } + } + r.Log.Info(fmt.Sprintf("Reconciling BarbicanKeystoneListener %s", instance.Name)) + + helper, err := helper.NewHelper( + instance, + r.Client, + r.Kclient, + r.Scheme, + r.Log, + ) + if err != nil { + return ctrl.Result{}, err + } + + // Always patch the instance when this function exits + defer func() { + if instance.Status.Conditions.AllSubConditionIsTrue() { + instance.Status.Conditions.MarkTrue(condition.ReadyCondition, condition.ReadyMessage) + } else { + instance.Status.Conditions.MarkUnknown( + condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage) + instance.Status.Conditions.Set( + instance.Status.Conditions.Mirror(condition.ReadyCondition)) + } + err := helper.PatchInstance(ctx, instance) + if err != nil { + _err = err + return + } + }() + + r.Log.Info(fmt.Sprintf("Add finalizer %s", instance.Name)) + // Add Finalizer + if instance.DeletionTimestamp.IsZero() && controllerutil.AddFinalizer(instance, helper.GetFinalizer()) { + return ctrl.Result{}, nil + } + + r.Log.Info(fmt.Sprintf("initilize %s", instance.Name)) + + // Initialize Conditions + if instance.Status.Conditions == nil { + instance.Status.Conditions = condition.Conditions{} + cl := condition.CreateList( + //condition.UnknownCondition(condition.ExposeServiceReadyCondition, condition.InitReason, condition.ExposeServiceReadyInitMessage), + condition.UnknownCondition(condition.InputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), + condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage), + condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyInitMessage), + // right now we have no dedicated KeystoneServiceReadyInitMessage and KeystoneEndpointReadyInitMessage + //condition.UnknownCondition(condition.KeystoneServiceReadyCondition, condition.InitReason, ""), + //condition.UnknownCondition(condition.KeystoneEndpointReadyCondition, condition.InitReason, ""), + condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), + ) + r.Log.Info(fmt.Sprintf("calling init %s", instance.Name)) + instance.Status.Conditions.Init(&cl) + r.Log.Info(fmt.Sprintf("post init %s", instance.Name)) + + // TODO: (alee) this is ssupposed to exit here - but then it never comes back! + // Register overall status immediately to have an early feedback e.g. in the cli + return ctrl.Result{}, nil + } + r.Log.Info(fmt.Sprintf("post initiialize %s", instance.Name)) + + if instance.Status.Hash == nil { + instance.Status.Hash = map[string]string{} + } + //if instance.Status.APIEndpoints == nil { + // instance.Status.APIEndpoints = map[string]string{} + //} + //if instance.Status.ServiceIDs == nil { + // instance.Status.ServiceIDs = map[string]string{} + //} + if instance.Status.NetworkAttachments == nil { + instance.Status.NetworkAttachments = map[string][]string{} + } + + // Handle service delete + if !instance.DeletionTimestamp.IsZero() { + return r.reconcileDelete(ctx, instance, helper) + } + + r.Log.Info(fmt.Sprintf("Calling reconcile normal %s", instance.Name)) + + // Handle non-deleted clusters + return r.reconcileNormal(ctx, instance, helper) +} + +func (r *BarbicanKeystoneListenerReconciler) getSecret( + ctx context.Context, + h *helper.Helper, + instance *barbicanv1beta1.BarbicanKeystoneListener, + secretName string, + envVars *map[string]env.Setter, +) (ctrl.Result, error) { + secret, hash, err := secret.GetSecret(ctx, h, secretName, instance.Namespace) + if err != nil { + if k8s_errors.IsNotFound(err) { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.InputReadyWaitingMessage)) + return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, fmt.Errorf("Secret %s not found", secretName) + } + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.InputReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + + // Add a prefix to the var name to avoid accidental collision with other non-secret + // vars. The secret names themselves will be unique. + (*envVars)["secret-"+secret.Name] = env.SetValue(hash) + // env[secret-osp-secret] = hash? + + return ctrl.Result{}, nil +} + +func (r *BarbicanKeystoneListenerReconciler) createHashOfInputHashes( + ctx context.Context, + instance *barbicanv1beta1.BarbicanKeystoneListener, + envVars map[string]env.Setter, +) (string, bool, error) { + var hashMap map[string]string + changed := false + mergedMapVars := env.MergeEnvs([]corev1.EnvVar{}, envVars) + hash, err := util.ObjectHash(mergedMapVars) + if err != nil { + return hash, changed, err + } + r.Log.Info("[KeystoneListener] ON createHashOfInputHashes") + if hashMap, changed = util.SetHash(instance.Status.Hash, common.InputHashName, hash); changed { + instance.Status.Hash = hashMap + r.Log.Info(fmt.Sprintf("Input maps hash %s - %s", common.InputHashName, hash)) + } + return hash, changed, nil +} + +// generateServiceConfigs - create Secret which holds the service configuration +// TODO add DefaultConfigOverwrite +func (r *BarbicanKeystoneListenerReconciler) generateServiceConfigs( + ctx context.Context, + h *helper.Helper, + instance *barbicanv1beta1.BarbicanKeystoneListener, + envVars *map[string]env.Setter, +) error { + r.Log.Info("[KeystoneListener] generateServiceConfigs - reconciling") + labels := labels.GetLabels(instance, labels.GetGroupLabel(barbican.ServiceName), map[string]string{}) + + // customData hold any customization for the service. + customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig} + + r.Log.Info(fmt.Sprintf("[KeystoneListener] instance type %s", instance.GetObjectKind().GroupVersionKind().Kind)) + + for key, data := range instance.Spec.DefaultConfigOverwrite { + customData[key] = data + } + + //keystoneAPI, err := keystonev1.GetKeystoneAPI(ctx, h, instance.Namespace, map[string]string{}) + // KeystoneAPI not available we should not aggregate the error and continue + //if err != nil { + // return err + //} + //keystoneInternalURL, err := keystoneAPI.GetEndpoint(endpoint.EndpointInternal) + //if err != nil { + // return err + //} + + ospSecret, _, err := secret.GetSecret(ctx, h, instance.Spec.Secret, instance.Namespace) + if err != nil { + return err + } + + transportURLSecret, _, err := secret.GetSecret(ctx, h, instance.Spec.TransportURLSecret, instance.Namespace) + if err != nil { + return err + } + + templateParameters := map[string]interface{}{ + "DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s", + instance.Spec.DatabaseUser, + string(ospSecret.Data[instance.Spec.PasswordSelectors.Database]), + instance.Spec.DatabaseHostname, + barbican.DatabaseName, + ), + //"KeystoneAuthURL": keystoneInternalURL, + //"ServicePassword": string(ospSecret.Data[instance.Spec.PasswordSelectors.Service]), + //"ServiceUser": instance.Spec.ServiceUser, + //"ServiceURL": "https://barbican.openstack.svc:9311", + "TransportURL": string(transportURLSecret.Data["transport_url"]), + "LogFile": fmt.Sprintf("%s%s.log", barbican.BarbicanLogPath, instance.Name), + } + + return GenerateConfigsGeneric(ctx, h, instance, envVars, templateParameters, customData, labels, false) +} + +func (r *BarbicanKeystoneListenerReconciler) reconcileInit( + ctx context.Context, + instance *barbicanv1beta1.BarbicanKeystoneListener, + helper *helper.Helper, + serviceLabels map[string]string, +) (ctrl.Result, error) { + r.Log.Info(fmt.Sprintf("[KeystoneListener] Reconciling Service '%s' init", instance.Name)) + + // + // expose the service (create service, route and return the created endpoint URLs) + // + //ports := map[endpoint.Endpoint]endpoint.Data{} + //ports[endpoint.EndpointInternal] = endpoint.Data{ + // Port: barbican.BarbicanInternalPort, + //} + //ports[endpoint.EndpointPublic] = endpoint.Data{ + // Port: barbican.BarbicanPublicPort, + //} + + /* + for _, metallbcfg := range instance.Spec.ExternalEndpoints { + portCfg := ports[metallbcfg.Endpoint] + portCfg.MetalLB = &endpoint.MetalLBData{ + IPAddressPool: metallbcfg.IPAddressPool, + SharedIP: metallbcfg.SharedIP, + SharedIPKey: metallbcfg.SharedIPKey, + LoadBalancerIPs: metallbcfg.LoadBalancerIPs, + } + + ports[metallbcfg.Endpoint] = portCfg + } + + apiEndpoints, ctrlResult, err := endpoint.ExposeEndpoints( + ctx, + helper, + barbican.ServiceName, + serviceLabels, + ports, + time.Duration(5)*time.Second, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.ExposeServiceReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.ExposeServiceReadyErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.ExposeServiceReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.ExposeServiceReadyRunningMessage)) + return ctrlResult, nil + } + */ + //instance.Status.Conditions.MarkTrue(condition.ExposeServiceReadyCondition, condition.ExposeServiceReadyMessage) + + // + // Update instance status with service endpoint url from route host information + // + // TODO: need to support https default here + //if instance.Status.APIEndpoints == nil { + // instance.Status.APIEndpoints = map[string]string{} + //} + //instance.Status.APIEndpoints = apiEndpoints + + // expose service - end + + // + // create keystone endpoints + // + + //ksEndpointSpec := keystonev1.KeystoneEndpointSpec{ + // ServiceName: barbican.ServiceName, + // Endpoints: instance.Status.APIEndpoints, + //} + + /* + ksSvc := keystonev1.NewKeystoneEndpoint(instance.Name, instance.Namespace, ksEndpointSpec, serviceLabels, time.Duration(10)*time.Second) + ctrlResult, err = ksSvc.CreateOrPatch(ctx, helper) + if err != nil { + return ctrlResult, err + } + + // mirror the Status, Reason, Severity and Message of the latest keystoneendpoint condition + // into a local condition with the type condition.KeystoneEndpointReadyCondition + c := ksSvc.GetConditions().Mirror(condition.KeystoneEndpointReadyCondition) + if c != nil { + instance.Status.Conditions.Set(c) + } + + if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + // + // create keystone endpoints - end + // + + */ + r.Log.Info(fmt.Sprintf("[KeystoneListener] Reconciled Service '%s' init successfully", instance.Name)) + return ctrl.Result{}, nil +} + +func (r *BarbicanKeystoneListenerReconciler) reconcileUpdate(ctx context.Context, instance *barbicanv1beta1.BarbicanKeystoneListener, helper *helper.Helper) (ctrl.Result, error) { + r.Log.Info(fmt.Sprintf("[KeystoneListener] Reconciling Service '%s' update", instance.Name)) + + // TODO: should have minor update tasks if required + // - delete dbsync hash from status to rerun it? + + r.Log.Info(fmt.Sprintf("[KeystoneListener] Reconciled Service '%s' update successfully", instance.Name)) + return ctrl.Result{}, nil +} + +func (r *BarbicanKeystoneListenerReconciler) reconcileUpgrade(ctx context.Context, instance *barbicanv1beta1.BarbicanKeystoneListener, helper *helper.Helper) (ctrl.Result, error) { + r.Log.Info(fmt.Sprintf("[KeystoneListener] Reconciling Service '%s' upgrade", instance.Name)) + + // TODO: should have major version upgrade tasks + // -delete dbsync hash from status to rerun it? + + r.Log.Info(fmt.Sprintf("[KeystoneListener] Reconciled Service '%s' upgrade successfully", instance.Name)) + return ctrl.Result{}, nil +} + +func (r *BarbicanKeystoneListenerReconciler) reconcileDelete(ctx context.Context, instance *barbicanv1beta1.BarbicanKeystoneListener, helper *helper.Helper) (ctrl.Result, error) { + r.Log.Info(fmt.Sprintf("Reconciling Service '%s' delete", instance.Name)) + + // Remove the finalizer from our KeystoneEndpoint CR + //keystoneEndpoint, err := keystonev1.GetKeystoneEndpointWithName(ctx, helper, instance.Name, instance.Namespace) + //if err != nil && !k8s_errors.IsNotFound(err) { + // return ctrl.Result{}, err + //} + + /* + if err == nil { + if controllerutil.RemoveFinalizer(keystoneEndpoint, helper.GetFinalizer()) { + err = r.Update(ctx, keystoneEndpoint) + if err != nil && !k8s_errors.IsNotFound(err) { + return ctrl.Result{}, err + } + util.LogForObject(helper, "Removed finalizer from our KeystoneEndpoint", instance) + } + } + */ + + // Service is deleted so remove the finalizer. + controllerutil.RemoveFinalizer(instance, helper.GetFinalizer()) + r.Log.Info(fmt.Sprintf("Reconciled Service '%s' delete successfully", instance.Name)) + + return ctrl.Result{}, nil +} + +func (r *BarbicanKeystoneListenerReconciler) reconcileNormal(ctx context.Context, instance *barbicanv1beta1.BarbicanKeystoneListener, helper *helper.Helper) (ctrl.Result, error) { + r.Log.Info(fmt.Sprintf("[KeystoneListener] Reconciling Service '%s'", instance.Name)) + + configVars := make(map[string]env.Setter) + + // + // check for required OpenStack secret holding passwords for service/admin user and add hash to the vars map + // + r.Log.Info(fmt.Sprintf("[KeystoneListener] Get secret 1 '%s'", instance.Name)) + ctrlResult, err := r.getSecret(ctx, helper, instance, instance.Spec.Secret, &configVars) + if err != nil { + return ctrlResult, err + } + + // + // check for required TransportURL secret holding transport URL string + // + r.Log.Info(fmt.Sprintf("[KeystoneListener] Get secret 2 '%s'", instance.Spec.TransportURLSecret)) + ctrlResult, err = r.getSecret(ctx, helper, instance, instance.Spec.TransportURLSecret, &configVars) + if err != nil { + return ctrlResult, err + } + + // TODO (alee) cinder has some code here to retrieve secrets from the parent CR + // Seems like we may want this instead + + // TODO (alee) cinder has some code to retrieve CustomServiceConfigSecrets + // This seems like a great place to store things like HSM passwords + + r.Log.Info(fmt.Sprintf("[KeystoneListener] Got secrets '%s'", instance.Name)) + // + // create custom config for this barbican service + // + err = r.generateServiceConfigs(ctx, helper, instance, &configVars) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.ServiceConfigReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.ServiceConfigReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + + r.Log.Info(fmt.Sprintf("[KeystoneListener] Getting input hash '%s'", instance.Name)) + // + // create hash over all the different input resources to identify if any those changed + // and a restart/recreate is required. + // + inputHash, hashChanged, err := r.createHashOfInputHashes(ctx, instance, configVars) + if err != nil { + r.Log.Info("[KeystoneListener] ERR") + instance.Status.Conditions.Set(condition.FalseCondition( + condition.ServiceConfigReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.ServiceConfigReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } else if hashChanged { + r.Log.Info("[KeystoneListener] HAS CHANGED") + // Hash changed and instance status should be updated (which will be done by main defer func), + // so we need to return and reconcile again + //return ctrl.Result{}, nil + } + r.Log.Info("[KeystoneListener] CONTINUE") + instance.Status.Conditions.MarkTrue(condition.ServiceConfigReadyCondition, condition.ServiceConfigReadyMessage) + + r.Log.Info(fmt.Sprintf("[KeystoneListener] Getting service labels '%s'", instance.Name)) + serviceLabels := map[string]string{ + common.AppSelector: fmt.Sprintf(barbican.ServiceName), + } + + r.Log.Info(fmt.Sprintf("[KeystoneListener] Getting networks '%s'", instance.Name)) + // networks to attach to + for _, netAtt := range instance.Spec.NetworkAttachments { + _, err := nad.GetNADWithName(ctx, helper, netAtt, instance.Namespace) + if err != nil { + if k8s_errors.IsNotFound(err) { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.NetworkAttachmentsReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.NetworkAttachmentsReadyWaitingMessage, + netAtt)) + return ctrl.Result{RequeueAfter: time.Second * 10}, fmt.Errorf("network-attachment-definition %s not found", netAtt) + } + instance.Status.Conditions.Set(condition.FalseCondition( + condition.NetworkAttachmentsReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.NetworkAttachmentsReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + } + + r.Log.Info(fmt.Sprintf("[KeystoneListener] Getting service annotations '%s'", instance.Name)) + serviceAnnotations, err := nad.CreateNetworksAnnotation(instance.Namespace, instance.Spec.NetworkAttachments) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed create network annotation from %s: %w", + instance.Spec.NetworkAttachments, err) + } + r.Log.Info(fmt.Sprintf("[DELETE] %s", serviceAnnotations)) + + // Handle service init + ctrlResult, err = r.reconcileInit(ctx, instance, helper, serviceLabels) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + // Handle service update + ctrlResult, err = r.reconcileUpdate(ctx, instance, helper) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + // Handle service upgrade + ctrlResult, err = r.reconcileUpgrade(ctx, instance, helper) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + r.Log.Info(fmt.Sprintf("[KeystoneListener] Defining deployment '%s'", instance.Name)) + // Define a new Deployment object + deplDef := barbicankeystonelistener.Deployment(instance, inputHash, serviceLabels, serviceAnnotations) + r.Log.Info(fmt.Sprintf("[KeystoneListener] Getting deployment '%s'", instance.Name)) + depl := deployment.NewDeployment( + deplDef, + time.Duration(5)*time.Second, + ) + r.Log.Info(fmt.Sprintf("[KeystoneListener] Got deployment '%s'", instance.Name)) + ctrlResult, err = depl.CreateOrPatch(ctx, helper) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.DeploymentReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.DeploymentReadyErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.DeploymentReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.DeploymentReadyRunningMessage)) + return ctrlResult, nil + } + instance.Status.ReadyCount = depl.GetDeployment().Status.ReadyReplicas + + // verify if network attachment matches expectations + networkReady, networkAttachmentStatus, err := nad.VerifyNetworkStatusFromAnnotation(ctx, helper, instance.Spec.NetworkAttachments, serviceLabels, instance.Status.ReadyCount) + if err != nil { + return ctrl.Result{}, err + } + + instance.Status.NetworkAttachments = networkAttachmentStatus + if networkReady { + instance.Status.Conditions.MarkTrue(condition.NetworkAttachmentsReadyCondition, condition.NetworkAttachmentsReadyMessage) + } else { + err := fmt.Errorf("not all pods have interfaces with ips as configured in NetworkAttachments: %s", instance.Spec.NetworkAttachments) + instance.Status.Conditions.Set(condition.FalseCondition( + condition.NetworkAttachmentsReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.NetworkAttachmentsReadyErrorMessage, + err.Error())) + + return ctrl.Result{}, err + } + + if instance.Status.ReadyCount > 0 { + instance.Status.Conditions.MarkTrue(condition.DeploymentReadyCondition, condition.DeploymentReadyMessage) + } + // create Deployment - end + + r.Log.Info(fmt.Sprintf("Reconciled Service '%s' in barbicanAPI successfully", instance.Name)) + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *BarbicanKeystoneListenerReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&barbicanv1beta1.BarbicanKeystoneListener{}). + //Owns(&keystonev1.KeystoneEndpoint{}). + //Owns(&corev1.Service{}). + //Owns(&corev1.Secret{}). + //Owns(&appsv1.Deployment{}). + //Owns(&routev1.Route{}). + Complete(r) +} diff --git a/controllers/barbicanworker_controller.go b/controllers/barbicanworker_controller.go index 77c313d..73c214f 100644 --- a/controllers/barbicanworker_controller.go +++ b/controllers/barbicanworker_controller.go @@ -47,7 +47,8 @@ import ( k8s_errors "k8s.io/apimachinery/pkg/api/errors" ) -// BarbicanWorkerReconciler reconciles a BarbicanAPI object + +// BarbicanWorkerReconciler reconciles a BarbicanWorker object type BarbicanWorkerReconciler struct { client.Client Kclient kubernetes.Interface diff --git a/main.go b/main.go index bdecec8..a9afc99 100644 --- a/main.go +++ b/main.go @@ -142,6 +142,16 @@ func main() { os.Exit(1) } + if err = (&controllers.BarbicanKeystoneListenerReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Kclient: kclient, + Log: ctrl.Log.WithName("controllers").WithName("BarbicanKeystoneListener"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "BarbicanKeystoneListener") + os.Exit(1) + } + // Acquire environmental defaults and initialize operator defaults with them barbicanv1beta1.SetupDefaults() diff --git a/pkg/barbican/const.go b/pkg/barbican/const.go index 9a48674..67430ff 100644 --- a/pkg/barbican/const.go +++ b/pkg/barbican/const.go @@ -25,6 +25,8 @@ const ( BarbicanAPI storage.PropagationType = "BarbicanAPI" // BarbicanWorker defines the barbican-worker group BarbicanWorker storage.PropagationType = "BarbicanWorker" + // BarbicanKeystoneListener defines the barbican-keystone-listener group + BarbicanKeystoneListener storage.PropagationType = "BarbicanKeystoneListener" // Barbican is the global ServiceType that refers to all the components deployed // by the barbican operator Barbican storage.PropagationType = "Barbican" @@ -47,3 +49,8 @@ var BarbicanAPIPropagation = []storage.PropagationType{Barbican, BarbicanAPI} // It allows the BarbicanWorker pod to mount volumes destined to Barbican related // ServiceTypes var BarbicanWorkerPropagation = []storage.PropagationType{Barbican, BarbicanWorker} + +// BarbicanKeystoneListenerPropagation is the definition of the BarbicanKeystoneListener propagation group +// It allows the BarbicanKeystoneListener pod to mount volumes destined to Barbican related +// ServiceTypes +var BarbicanKeystoneListenerPropagation = []storage.PropagationType{Barbican, BarbicanKeystoneListener} diff --git a/pkg/barbicankeystonelistener/deployment.go b/pkg/barbicankeystonelistener/deployment.go new file mode 100644 index 0000000..7f485c8 --- /dev/null +++ b/pkg/barbicankeystonelistener/deployment.go @@ -0,0 +1,161 @@ +package barbicankeystonelistener + +import ( + "fmt" + + "github.com/openstack-k8s-operators/lib-common/modules/common" + "github.com/openstack-k8s-operators/lib-common/modules/common/env" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + //"k8s.io/apimachinery/pkg/util/intstr" + + barbicanv1beta1 "github.com/openstack-k8s-operators/barbican-operator/api/v1beta1" + barbican "github.com/openstack-k8s-operators/barbican-operator/pkg/barbican" +) + +const ( + // ServiceCommand - + ServiceCommand = "/usr/local/bin/kolla_start" +) + +// Deployment - returns a Barbican Keystone Listener Deployment +func Deployment( + instance *barbicanv1beta1.BarbicanKeystoneListener, + configHash string, + labels map[string]string, + annotations map[string]string, +) *appsv1.Deployment { + runAsUser := int64(0) + var config0644AccessMode int32 = 0644 + envVars := map[string]env.Setter{} + envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") + envVars["CONFIG_HASH"] = env.SetValue(configHash) + /* + livenessProbe := &corev1.Probe{ + // TODO might need tuning + TimeoutSeconds: 5, + PeriodSeconds: 3, + InitialDelaySeconds: 5, + } + readinessProbe := &corev1.Probe{ + // TODO might need tuning + TimeoutSeconds: 5, + PeriodSeconds: 5, + InitialDelaySeconds: 5, + } + */ + args := []string{"-c"} + if instance.Spec.Debug.Service { + args = append(args, common.DebugCommand) + //livenessProbe.Exec = &corev1.ExecAction{ + // Command: []string{ + // "/bin/true", + // }, + //} + //readinessProbe.Exec = livenessProbe.Exec + } else { + args = append(args, ServiceCommand) + // + // https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ + // + //livenessProbe.HTTPGet = &corev1.HTTPGetAction{ + // Path: "/healthcheck", + // Port: intstr.IntOrString{Type: intstr.Int, IntVal: int32(barbican.BarbicanPublicPort)}, + //} + //readinessProbe.HTTPGet = livenessProbe.HTTPGet + } + + keystoneListenerVolumes := []corev1.Volume{ + { + Name: "config-data-custom", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + DefaultMode: &config0644AccessMode, + SecretName: instance.Name + "-config-data", + }, + }, + }, + } + + keystoneListenerVolumes = append(keystoneListenerVolumes, barbican.GetLogVolume()...) + keystoneListenerVolumeMounts := []corev1.VolumeMount{ + { + Name: "config-data", + MountPath: "/var/lib/kolla/config_files/config.json", + SubPath: "barbican-keystone-listener-config.json", + ReadOnly: true, + }, + } + // Append LogVolume to the apiVolumes: this will be used to stream + // logging + keystoneListenerVolumeMounts = append(keystoneListenerVolumeMounts, barbican.GetLogVolumeMount()...) + + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-keystone-listener", instance.Name), + Namespace: instance.Namespace, + Labels: labels, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: labels, + }, + Replicas: instance.Spec.Replicas, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: annotations, + Labels: labels, + }, + Spec: corev1.PodSpec{ + ServiceAccountName: instance.Spec.ServiceAccount, + Containers: []corev1.Container{ + { + Name: instance.Name + "-log", + Command: []string{ + "/bin/bash", + }, + Args: []string{"-c", "tail -n+1 -F " + barbican.BarbicanLogPath + instance.Name + ".log"}, + Image: instance.Spec.ContainerImage, + SecurityContext: &corev1.SecurityContext{ + RunAsUser: &runAsUser, + }, + Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), + VolumeMounts: barbican.GetLogVolumeMount(), + Resources: instance.Spec.Resources, + //ReadinessProbe: readinessProbe, + //LivenessProbe: livenessProbe, + }, + { + Name: barbican.ServiceName + "-keystone-listener", + Command: []string{ + "/bin/bash", + }, + Args: args, + Image: instance.Spec.ContainerImage, + SecurityContext: &corev1.SecurityContext{ + RunAsUser: &runAsUser, + }, + Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), + VolumeMounts: append(barbican.GetVolumeMounts( + instance.Spec.CustomServiceConfigSecrets, + barbican.BarbicanKeystoneListenerPropagation), + keystoneListenerVolumeMounts..., + ), + Resources: instance.Spec.Resources, + //ReadinessProbe: readinessProbe, + //LivenessProbe: livenessProbe, + }, + }, + }, + }, + }, + } + deployment.Spec.Template.Spec.Volumes = append(barbican.GetVolumes( + instance.Name, + barbican.ServiceName, + instance.Spec.CustomServiceConfigSecrets, + barbican.BarbicanKeystoneListenerPropagation), + keystoneListenerVolumes...) + return deployment +} diff --git a/templates/barbican/config/00-default.conf b/templates/barbican/config/00-default.conf index ef59382..42198a8 100644 --- a/templates/barbican/config/00-default.conf +++ b/templates/barbican/config/00-default.conf @@ -21,13 +21,13 @@ interface = internal {{ end }} [keystone_notifications] -enable = false +enable = true topic = notifications [oslo_messaging_notifications] # TODO: update later once rabbit is working -#driver=messagingv2 -driver=noop +driver=messagingv2 +#driver=noop [oslo_policy] enforce_scope = true diff --git a/templates/barbican/config/barbican-keystone-listener b/templates/barbican/config/barbican-keystone-listener new file mode 100644 index 0000000..73b3f1b --- /dev/null +++ b/templates/barbican/config/barbican-keystone-listener @@ -0,0 +1,7 @@ +#!/usr/bin/python3 +import sys + +from barbican.cmd.keystone_listener import main +if __name__ == "__main__": + sys.exit(main()) + diff --git a/templates/barbican/config/barbican-keystone-listener-config.json b/templates/barbican/config/barbican-keystone-listener-config.json new file mode 100644 index 0000000..e71b860 --- /dev/null +++ b/templates/barbican/config/barbican-keystone-listener-config.json @@ -0,0 +1,39 @@ +{ + "command": "barbican-keystone-listener", + "config_files": [ + { + "source": "/var/lib/config-data/default/00-default.conf", + "dest": "/etc/barbican/barbican.conf.d/00-default.conf", + "owner": "barbican", + "perm": "0600" + }, + { + "source": "/var/lib/config-data/default/02-service.conf", + "dest": "/etc/barbican/barbican.conf.d/02-service.conf", + "owner": "barbican", + "perm": "0600", + "optional": true + }, + { + "source": "/var/lib/config-data/default/03-secrets.conf", + "dest": "/etc/barbican/barbican.conf.d/03-secrets.conf", + "owner": "barbican", + "perm": "0640", + "optional": true + }, + { + "source": "/var/lib/config-data/default/barbican-keystone-listener", + "dest": "/usr/bin/barbican-keystone-listener", + "owner": "barbican", + "perm": "0755", + "optional": true + } + ], + "permissions": [ + { + "path": "/var/log/barbican", + "owner": "barbican:barbican", + "recurse": true + } + ] +} diff --git a/templates/barbicankeystonelistener b/templates/barbicankeystonelistener new file mode 120000 index 0000000..cded8ff --- /dev/null +++ b/templates/barbicankeystonelistener @@ -0,0 +1 @@ +barbican \ No newline at end of file From b0449e3ee3cd832a2da525957fb27d0852d2cdfd Mon Sep 17 00:00:00 2001 From: Mauricio Harley Date: Tue, 24 Oct 2023 09:50:36 -0400 Subject: [PATCH 2/3] Add barbican keystone-listener finalizer --- controllers/barbican_controller.go | 38 +++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/controllers/barbican_controller.go b/controllers/barbican_controller.go index 9182d2e..a59574d 100644 --- a/controllers/barbican_controller.go +++ b/controllers/barbican_controller.go @@ -353,6 +353,23 @@ func (r *BarbicanReconciler) reconcileNormal(ctx context.Context, instance *barb func (r *BarbicanReconciler) reconcileDelete(ctx context.Context, instance *barbicanv1beta1.Barbican, helper *helper.Helper) (ctrl.Result, error) { r.Log.Info(fmt.Sprintf("Reconciling Service '%s' delete", instance.Name)) + // TODO(afaranha): Rename Keystone with Barbican + // We need to allow all KeystoneEndpoint and KeystoneService processing to finish + // in the case of a delete before we remove the finalizers. For instance, in the + // case of the Memcached dependency, if Memcached is deleted before all Keystone + // cleanup has finished, then the Keystone logic will likely hit a 500 error and + // thus its deletion will hang indefinitely. + for _, finalizer := range instance.Finalizers { + // If this finalizer is not our KeystoneAPI finalizer, then it is either + // a KeystoneService or KeystoneEndpointer finalizer, which indicates that + // there is more Keystone processing that needs to finish before we can + // allow our DB and Memcached dependencies to be potentially deleted + // themselves + if finalizer != helper.GetFinalizer() { + return ctrl.Result{}, nil + } + } + // remove db finalizer first db, err := mariadbv1.GetDatabaseByName(ctx, helper, instance.Name) if err != nil && !k8s_errors.IsNotFound(err) { @@ -381,7 +398,7 @@ func (r *BarbicanReconciler) reconcileDelete(ctx context.Context, instance *barb } } - // Remove finalizers from any existing child barbicanAPIs + // Remove finalizers from any existing child BarbicanAPIs barbicanAPI := &barbicanv1beta1.BarbicanAPI{} err = r.Get(ctx, types.NamespacedName{Name: fmt.Sprintf("%s-api", instance.Name), Namespace: instance.Namespace}, barbicanAPI) if err != nil && !k8s_errors.IsNotFound(err) { @@ -415,6 +432,23 @@ func (r *BarbicanReconciler) reconcileDelete(ctx context.Context, instance *barb } } + // Remove finalizers from Barbican Keystone Listener + barbicanKeystoneListener := &barbicanv1beta1.BarbicanKeystoneListener{} + err = r.Get(ctx, types.NamespacedName{Name: fmt.Sprintf("%s-keystone-listener", instance.Name), Namespace: instance.Namespace}, barbicanKeystoneListener) + if err != nil && !k8s_errors.IsNotFound(err) { + return ctrl.Result{}, err + } + + if err == nil { + if controllerutil.RemoveFinalizer(barbicanKeystoneListener, helper.GetFinalizer()) { + err = r.Update(ctx, barbicanKeystoneListener) + if err != nil && !k8s_errors.IsNotFound(err) { + return ctrl.Result{}, err + } + util.LogForObject(helper, fmt.Sprintf("Removed finalizer from BarbicanKeystoneListener %s", barbicanKeystoneListener.Name), barbicanKeystoneListener) + } + } + // Service is deleted so remove the finalizer. controllerutil.RemoveFinalizer(instance, helper.GetFinalizer()) r.Log.Info(fmt.Sprintf("Reconciled Service '%s' delete successfully", instance.Name)) @@ -427,6 +461,8 @@ func (r *BarbicanReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&barbicanv1beta1.Barbican{}). Owns(&barbicanv1beta1.BarbicanAPI{}). + Owns(&barbicanv1beta1.BarbicanWorker{}). + Owns(&barbicanv1beta1.BarbicanKeystoneListener{}). Owns(&mariadbv1.MariaDBDatabase{}). Owns(&keystonev1.KeystoneService{}). Owns(&corev1.ServiceAccount{}). From ed1aa4502c73a255214e9655f653d1264e0d52d5 Mon Sep 17 00:00:00 2001 From: Mauricio Harley Date: Wed, 25 Oct 2023 07:12:30 -0400 Subject: [PATCH 3/3] Add missing requeue and Owns --- api/v1beta1/barbicankeystonelistener_types.go | 3 -- controllers/barbican_controller.go | 21 ++---------- controllers/barbicanapi_controller.go | 3 +- .../barbicankeystonelistener_controller.go | 29 +++------------- controllers/barbicanworker_controller.go | 6 ++-- pkg/barbicankeystonelistener/deployment.go | 33 ------------------- templates/barbican/config/00-default.conf | 4 +-- 7 files changed, 13 insertions(+), 86 deletions(-) diff --git a/api/v1beta1/barbicankeystonelistener_types.go b/api/v1beta1/barbicankeystonelistener_types.go index b7732aa..28e69df 100644 --- a/api/v1beta1/barbicankeystonelistener_types.go +++ b/api/v1beta1/barbicankeystonelistener_types.go @@ -49,9 +49,6 @@ type BarbicanKeystoneListenerStatus struct { // Map of hashes to track e.g. job status Hash map[string]string `json:"hash,omitempty"` - // API endpoint - //APIEndpoints map[string]string `json:"apiEndpoint,omitempty"` - // Conditions Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` diff --git a/controllers/barbican_controller.go b/controllers/barbican_controller.go index a59574d..f4f0c7c 100644 --- a/controllers/barbican_controller.go +++ b/controllers/barbican_controller.go @@ -353,23 +353,6 @@ func (r *BarbicanReconciler) reconcileNormal(ctx context.Context, instance *barb func (r *BarbicanReconciler) reconcileDelete(ctx context.Context, instance *barbicanv1beta1.Barbican, helper *helper.Helper) (ctrl.Result, error) { r.Log.Info(fmt.Sprintf("Reconciling Service '%s' delete", instance.Name)) - // TODO(afaranha): Rename Keystone with Barbican - // We need to allow all KeystoneEndpoint and KeystoneService processing to finish - // in the case of a delete before we remove the finalizers. For instance, in the - // case of the Memcached dependency, if Memcached is deleted before all Keystone - // cleanup has finished, then the Keystone logic will likely hit a 500 error and - // thus its deletion will hang indefinitely. - for _, finalizer := range instance.Finalizers { - // If this finalizer is not our KeystoneAPI finalizer, then it is either - // a KeystoneService or KeystoneEndpointer finalizer, which indicates that - // there is more Keystone processing that needs to finish before we can - // allow our DB and Memcached dependencies to be potentially deleted - // themselves - if finalizer != helper.GetFinalizer() { - return ctrl.Result{}, nil - } - } - // remove db finalizer first db, err := mariadbv1.GetDatabaseByName(ctx, helper, instance.Name) if err != nil && !k8s_errors.IsNotFound(err) { @@ -615,7 +598,7 @@ func (r *BarbicanReconciler) workerDeploymentCreateOrUpdate(ctx context.Context, return err } - // Add a finalizer to prevent user from manually removing child BarbicanAPI + // Add a finalizer to prevent user from manually removing child BarbicanWorker controllerutil.AddFinalizer(deployment, helper.GetFinalizer()) return nil @@ -651,7 +634,7 @@ func (r *BarbicanReconciler) keystoneListenerDeploymentCreateOrUpdate(ctx contex return err } - // Add a finalizer to prevent user from manually removing child BarbicanAPI + // Add a finalizer to prevent user from manually removing child BarbicanKeystoneListener controllerutil.AddFinalizer(deployment, helper.GetFinalizer()) return nil diff --git a/controllers/barbicanapi_controller.go b/controllers/barbicanapi_controller.go index 4452cd8..8da1eb0 100644 --- a/controllers/barbicanapi_controller.go +++ b/controllers/barbicanapi_controller.go @@ -92,6 +92,8 @@ func (r *BarbicanAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request) // Object not found return ctrl.Result{}, err } + // Error reading the object - requeue the request. + return ctrl.Result{}, err } r.Log.Info(fmt.Sprintf("Reconciling BarbicanAPI %s", instance.Name)) @@ -621,7 +623,6 @@ func (r *BarbicanAPIReconciler) reconcileNormal(ctx context.Context, instance *b func (r *BarbicanAPIReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&barbicanv1beta1.BarbicanAPI{}). - //Owns(&keystonev1.KeystoneEndpoint{}). Owns(&corev1.Service{}). Owns(&corev1.Secret{}). Owns(&appsv1.Deployment{}). diff --git a/controllers/barbicankeystonelistener_controller.go b/controllers/barbicankeystonelistener_controller.go index aeb0710..450a9ec 100644 --- a/controllers/barbicankeystonelistener_controller.go +++ b/controllers/barbicankeystonelistener_controller.go @@ -44,33 +44,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" - //appsv1 "k8s.io/api/apps/v1" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" ) -/* -// GetClient - -func (r *BarbicanAPIReconciler) GetClient() client.Client { - return r.Client -} - -// GetKClient - -func (r *BarbicanAPIReconciler) GetKClient() kubernetes.Interface { - return r.Kclient -} - -// GetLogger - -func (r *BarbicanAPIReconciler) GetLogger() logr.Logger { - return r.Log -} - -// GetScheme - -func (r *BarbicanAPIReconciler) GetScheme() *runtime.Scheme { - return r.Scheme -} -*/ - // BarbicanKeystoneListenerReconciler reconciles a BarbicanKeystoneListener object type BarbicanKeystoneListenerReconciler struct { client.Client @@ -94,6 +72,8 @@ func (r *BarbicanKeystoneListenerReconciler) Reconcile(ctx context.Context, req // Object not found return ctrl.Result{}, err } + // Error reading the object - requeue the request. + return ctrl.Result{}, err } r.Log.Info(fmt.Sprintf("Reconciling BarbicanKeystoneListener %s", instance.Name)) @@ -637,10 +617,9 @@ func (r *BarbicanKeystoneListenerReconciler) reconcileNormal(ctx context.Context func (r *BarbicanKeystoneListenerReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&barbicanv1beta1.BarbicanKeystoneListener{}). - //Owns(&keystonev1.KeystoneEndpoint{}). //Owns(&corev1.Service{}). //Owns(&corev1.Secret{}). - //Owns(&appsv1.Deployment{}). + Owns(&appsv1.Deployment{}). //Owns(&routev1.Route{}). Complete(r) } diff --git a/controllers/barbicanworker_controller.go b/controllers/barbicanworker_controller.go index 73c214f..90408db 100644 --- a/controllers/barbicanworker_controller.go +++ b/controllers/barbicanworker_controller.go @@ -43,11 +43,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" ) - // BarbicanWorkerReconciler reconciles a BarbicanWorker object type BarbicanWorkerReconciler struct { client.Client @@ -70,6 +70,8 @@ func (r *BarbicanWorkerReconciler) Reconcile(ctx context.Context, req ctrl.Reque if k8s_errors.IsNotFound(err) { return ctrl.Result{}, err } + // Error reading the object - requeue the request. + return ctrl.Result{}, err } r.Log.Info(fmt.Sprintf("Reconciling BarbicanWorker %s", instance.Name)) @@ -477,7 +479,7 @@ func (r *BarbicanWorkerReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&barbicanv1beta1.BarbicanWorker{}). //Owns(&corev1.Service{}). //Owns(&corev1.Secret{}). - //Owns(&appsv1.Deployment{}). + Owns(&appsv1.Deployment{}). //Owns(&routev1.Route{}). Complete(r) } diff --git a/pkg/barbicankeystonelistener/deployment.go b/pkg/barbicankeystonelistener/deployment.go index 7f485c8..5c771d1 100644 --- a/pkg/barbicankeystonelistener/deployment.go +++ b/pkg/barbicankeystonelistener/deployment.go @@ -8,7 +8,6 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - //"k8s.io/apimachinery/pkg/util/intstr" barbicanv1beta1 "github.com/openstack-k8s-operators/barbican-operator/api/v1beta1" barbican "github.com/openstack-k8s-operators/barbican-operator/pkg/barbican" @@ -31,39 +30,11 @@ func Deployment( envVars := map[string]env.Setter{} envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") envVars["CONFIG_HASH"] = env.SetValue(configHash) - /* - livenessProbe := &corev1.Probe{ - // TODO might need tuning - TimeoutSeconds: 5, - PeriodSeconds: 3, - InitialDelaySeconds: 5, - } - readinessProbe := &corev1.Probe{ - // TODO might need tuning - TimeoutSeconds: 5, - PeriodSeconds: 5, - InitialDelaySeconds: 5, - } - */ args := []string{"-c"} if instance.Spec.Debug.Service { args = append(args, common.DebugCommand) - //livenessProbe.Exec = &corev1.ExecAction{ - // Command: []string{ - // "/bin/true", - // }, - //} - //readinessProbe.Exec = livenessProbe.Exec } else { args = append(args, ServiceCommand) - // - // https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ - // - //livenessProbe.HTTPGet = &corev1.HTTPGetAction{ - // Path: "/healthcheck", - // Port: intstr.IntOrString{Type: intstr.Int, IntVal: int32(barbican.BarbicanPublicPort)}, - //} - //readinessProbe.HTTPGet = livenessProbe.HTTPGet } keystoneListenerVolumes := []corev1.Volume{ @@ -123,8 +94,6 @@ func Deployment( Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), VolumeMounts: barbican.GetLogVolumeMount(), Resources: instance.Spec.Resources, - //ReadinessProbe: readinessProbe, - //LivenessProbe: livenessProbe, }, { Name: barbican.ServiceName + "-keystone-listener", @@ -143,8 +112,6 @@ func Deployment( keystoneListenerVolumeMounts..., ), Resources: instance.Spec.Resources, - //ReadinessProbe: readinessProbe, - //LivenessProbe: livenessProbe, }, }, }, diff --git a/templates/barbican/config/00-default.conf b/templates/barbican/config/00-default.conf index 42198a8..ec21131 100644 --- a/templates/barbican/config/00-default.conf +++ b/templates/barbican/config/00-default.conf @@ -22,12 +22,10 @@ interface = internal [keystone_notifications] enable = true -topic = notifications +topic = barbican_notifications [oslo_messaging_notifications] -# TODO: update later once rabbit is working driver=messagingv2 -#driver=noop [oslo_policy] enforce_scope = true