From ed33e4a643ac76f6a5a9b9ec3277d4074bcc5ec3 Mon Sep 17 00:00:00 2001 From: Martin Schuppert Date: Fri, 11 Aug 2023 12:00:49 +0200 Subject: [PATCH] Create horizon route and svc overrides Creates the route for the horizon, also allows to customize the route via override. Generats the service override for the env with what is configured in the externalEndpoints, or specified in the service template override. Depends-On: https://github.com/openstack-k8s-operators/lib-common/pull/313 Depends-On: https://github.com/openstack-k8s-operators/keystone-operator/pull/289 Depends-On: https://github.com/openstack-k8s-operators/horizon-operator/pull/202 Jira: OSP-26690 --- ....openstack.org_openstackcontrolplanes.yaml | 105 +++++++++++++++++- apis/core/v1beta1/conditions.go | 3 + .../v1beta1/openstackcontrolplane_types.go | 12 ++ apis/core/v1beta1/zz_generated.deepcopy.go | 21 ++++ ....openstack.org_openstackcontrolplanes.yaml | 105 +++++++++++++++++- ...controlplane_galera_network_isolation.yaml | 2 + ...ne_galera_network_isolation_3replicas.yaml | 2 + ...enstackcontrolplane_network_isolation.yaml | 2 + ...ckcontrolplane_network_isolation_ceph.yaml | 2 + pkg/openstack/horizon.go | 56 ++++++++++ 10 files changed, 306 insertions(+), 4 deletions(-) diff --git a/apis/bases/core.openstack.org_openstackcontrolplanes.yaml b/apis/bases/core.openstack.org_openstackcontrolplanes.yaml index b841c6b94..87fc4c1b6 100644 --- a/apis/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/apis/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -3943,6 +3943,107 @@ spec: type: object horizon: properties: + apiOverride: + properties: + route: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + alternateBackends: + items: + properties: + kind: + enum: + - Service + - "" + type: string + name: + type: string + weight: + format: int32 + maximum: 256 + minimum: 0 + type: integer + type: object + maxItems: 3 + type: array + host: + maxLength: 253 + pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ + type: string + path: + pattern: ^/ + type: string + port: + properties: + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - targetPort + type: object + subdomain: + maxLength: 253 + pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ + type: string + tls: + properties: + caCertificate: + type: string + certificate: + type: string + destinationCACertificate: + type: string + insecureEdgeTerminationPolicy: + type: string + key: + type: string + termination: + enum: + - edge + - reencrypt + - passthrough + type: string + required: + - termination + type: object + to: + properties: + kind: + enum: + - Service + - "" + type: string + name: + type: string + weight: + format: int32 + maximum: 256 + minimum: 0 + type: integer + type: object + wildcardPolicy: + enum: + - None + - Subdomain + - "" + type: string + type: object + type: object + type: object enabled: default: false type: boolean @@ -3973,7 +4074,7 @@ spec: override: properties: service: - items: + additionalProperties: properties: endpointURL: type: string @@ -4019,7 +4120,7 @@ spec: type: string type: object type: object - type: array + type: object type: object preserveJobs: default: false diff --git a/apis/core/v1beta1/conditions.go b/apis/core/v1beta1/conditions.go index 31bb79e91..b2cf3c6a0 100644 --- a/apis/core/v1beta1/conditions.go +++ b/apis/core/v1beta1/conditions.go @@ -81,6 +81,9 @@ const ( // OpenStackControlPlaneHorizonReadyCondition Status=True condition which indicates if Horizon is configured and operational OpenStackControlPlaneHorizonReadyCondition condition.Type = "OpenStackControlPlaneHorizonReady" + // OpenStackControlPlaneExposeHorizonReadyCondition Status=True condition which indicates if Horizon is exposed via a route + OpenStackControlPlaneExposeHorizonReadyCondition condition.Type = "OpenStackControlPlaneExposeHorizonReady" + // OpenStackControlPlaneClientReadyCondition Status=True condition which indicates if OpenStackClient is configured and operational OpenStackControlPlaneClientReadyCondition condition.Type = "OpenStackControlPlaneClientReady" diff --git a/apis/core/v1beta1/openstackcontrolplane_types.go b/apis/core/v1beta1/openstackcontrolplane_types.go index abbc3c06f..d1ba8a491 100644 --- a/apis/core/v1beta1/openstackcontrolplane_types.go +++ b/apis/core/v1beta1/openstackcontrolplane_types.go @@ -508,6 +508,18 @@ type HorizonSection struct { // +kubebuilder:validation:Optional // Template - Overrides to use when creating the Horizon services Template horizonv1.HorizonSpec `json:"template,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // APIOverride, provides the ability to override the generated manifest of several child resources. + APIOverride HorizonOverride `json:"apiOverride,omitempty"` +} + +// HorizonOverride to override the generated manifest of several child resources. +type HorizonOverride struct { + // +kubebuilder:validation:Optional + // Route overrides to use when creating the public service endpoint + Route *route.OverrideSpec `json:"route,omitempty"` } // CeilometerSection defines the desired state of OpenStack Telemetry services diff --git a/apis/core/v1beta1/zz_generated.deepcopy.go b/apis/core/v1beta1/zz_generated.deepcopy.go index aa52bca90..0e49e1f36 100644 --- a/apis/core/v1beta1/zz_generated.deepcopy.go +++ b/apis/core/v1beta1/zz_generated.deepcopy.go @@ -137,10 +137,31 @@ func (in *HeatSection) DeepCopy() *HeatSection { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HorizonOverride) DeepCopyInto(out *HorizonOverride) { + *out = *in + if in.Route != nil { + in, out := &in.Route, &out.Route + *out = new(route.OverrideSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizonOverride. +func (in *HorizonOverride) DeepCopy() *HorizonOverride { + if in == nil { + return nil + } + out := new(HorizonOverride) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HorizonSection) DeepCopyInto(out *HorizonSection) { *out = *in in.Template.DeepCopyInto(&out.Template) + in.APIOverride.DeepCopyInto(&out.APIOverride) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizonSection. diff --git a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml index b841c6b94..87fc4c1b6 100644 --- a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -3943,6 +3943,107 @@ spec: type: object horizon: properties: + apiOverride: + properties: + route: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + alternateBackends: + items: + properties: + kind: + enum: + - Service + - "" + type: string + name: + type: string + weight: + format: int32 + maximum: 256 + minimum: 0 + type: integer + type: object + maxItems: 3 + type: array + host: + maxLength: 253 + pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ + type: string + path: + pattern: ^/ + type: string + port: + properties: + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - targetPort + type: object + subdomain: + maxLength: 253 + pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ + type: string + tls: + properties: + caCertificate: + type: string + certificate: + type: string + destinationCACertificate: + type: string + insecureEdgeTerminationPolicy: + type: string + key: + type: string + termination: + enum: + - edge + - reencrypt + - passthrough + type: string + required: + - termination + type: object + to: + properties: + kind: + enum: + - Service + - "" + type: string + name: + type: string + weight: + format: int32 + maximum: 256 + minimum: 0 + type: integer + type: object + wildcardPolicy: + enum: + - None + - Subdomain + - "" + type: string + type: object + type: object + type: object enabled: default: false type: boolean @@ -3973,7 +4074,7 @@ spec: override: properties: service: - items: + additionalProperties: properties: endpointURL: type: string @@ -4019,7 +4120,7 @@ spec: type: string type: object type: object - type: array + type: object type: object preserveJobs: default: false diff --git a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml index bdcd77706..58f5dc9fe 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml @@ -123,6 +123,8 @@ spec: networkAttachments: - internalapi horizon: + apiOverride: + route: {} template: replicas: 1 secret: osp-secret diff --git a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation_3replicas.yaml b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation_3replicas.yaml index b4f0137d4..9fa0ca427 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation_3replicas.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation_3replicas.yaml @@ -123,6 +123,8 @@ spec: networkAttachments: - internalapi horizon: + apiOverride: + route: {} template: replicas: 1 secret: osp-secret diff --git a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml index f7a864b99..386452076 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml @@ -111,6 +111,8 @@ spec: networkAttachments: - internalapi horizon: + apiOverride: + route: {} template: replicas: 1 secret: osp-secret diff --git a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation_ceph.yaml b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation_ceph.yaml index d0c701e31..0fa54a0f2 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation_ceph.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation_ceph.yaml @@ -156,6 +156,8 @@ spec: networkAttachments: - internalapi horizon: + apiOverride: + route: {} template: replicas: 1 secret: osp-secret diff --git a/pkg/openstack/horizon.go b/pkg/openstack/horizon.go index 84f7a6ed8..2791c7e27 100644 --- a/pkg/openstack/horizon.go +++ b/pkg/openstack/horizon.go @@ -4,14 +4,20 @@ import ( "context" "fmt" + "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/helper" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" horizonv1 "github.com/openstack-k8s-operators/horizon-operator/api/v1beta1" corev1beta1 "github.com/openstack-k8s-operators/openstack-operator/apis/core/v1beta1" + k8s_errors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" ) @@ -29,11 +35,61 @@ func ReconcileHorizon(ctx context.Context, instance *corev1beta1.OpenStackContro return res, err } instance.Status.Conditions.Remove(corev1beta1.OpenStackControlPlaneHorizonReadyCondition) + instance.Status.Conditions.Remove(corev1beta1.OpenStackControlPlaneExposeHorizonReadyCondition) return ctrl.Result{}, nil } + + // add selector to service overrides + for _, endpointType := range []service.Endpoint{service.EndpointPublic, service.EndpointInternal} { + if instance.Spec.Horizon.Template.Override.Service == nil { + instance.Spec.Horizon.Template.Override.Service = map[string]service.OverrideSpec{} + } + instance.Spec.Horizon.Template.Override.Service[string(endpointType)] = + AddServiceComponentLabel( + ptr.To(instance.Spec.Horizon.Template.Override.Service[string(endpointType)]), + horizon.Name) + } + + // When component services got created check if there is the need to create a route + if err := helper.GetClient().Get(ctx, types.NamespacedName{Name: "horizon", Namespace: instance.Namespace}, horizon); err != nil { + if !k8s_errors.IsNotFound(err) { + return ctrl.Result{}, err + } + } + + if horizon.Status.Conditions.IsTrue(condition.ExposeServiceReadyCondition) { + svcs, err := service.GetServicesListWithLabel( + ctx, + helper, + instance.Namespace, + map[string]string{common.AppSelector: horizon.Name}, + ) + if err != nil { + return ctrl.Result{}, err + } + + var ctrlResult reconcile.Result + instance.Spec.Horizon.Template.Override.Service, ctrlResult, err = EnsureRoute( + ctx, + instance, + helper, + horizon, + svcs, + instance.Spec.Horizon.Template.Override.Service, + instance.Spec.Horizon.APIOverride.Route, + corev1beta1.OpenStackControlPlaneExposeHorizonReadyCondition, + ) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + } + helper.GetLogger().Info("Reconcile Horizon", "horizon.Namespace", instance.Namespace, "horizon.Name", "horizon") op, err := controllerutil.CreateOrPatch(ctx, helper.GetClient(), horizon, func() error { instance.Spec.Horizon.Template.DeepCopyInto(&horizon.Spec) + err := controllerutil.SetControllerReference(helper.GetBeforeObject(), horizon, helper.GetScheme()) if err != nil { return err