From d74eb777fe54eb32158e66e5da5cd7b37d53f2a2 Mon Sep 17 00:00:00 2001 From: Martin Schuppert Date: Wed, 13 Sep 2023 17:15:59 +0200 Subject: [PATCH] [TLS] TLS for public endpoints terminated at a route Changes openstacklient * CRD to allows to pass in CA secret * use kolla to run the openstackclient and update the environment CA on start with passed in CA secret to validate endpoint certs. Adds CRD parameters to configure TLS for public and internal TLS. * per default self signed root CA + issuer get created for public and internal certs * public issuer can be provided by the user by referencing a named issuer in the namespace. Then this one is used. * user can provide a CA secret for certs to be added to the combined CA secret the openstack-operator creates to pass into services / openstackclient * refactors the current route create for followup on TLS-E to create certs for each service endpoint. * when TLS for public endpoint is enabled a Cert for the route gets automatically created and added to the route CR. TODO: * adding envtest coverage Jira: OSP-26299 Depends-On: https://github.com/openstack-k8s-operators/lib-common/pull/351 Depends-On: https://github.com/openstack-k8s-operators/keystone-operator/pull/318 Depends-On: https://github.com/openstack-k8s-operators/tcib/pull/82 --- Dockerfile | 8 +- ...client.openstack.org_openstackclients.yaml | 6 +- ....openstack.org_openstackcontrolplanes.yaml | 44 +++ apis/client/v1beta1/openstackclient_types.go | 14 +- apis/client/v1beta1/zz_generated.deepcopy.go | 1 + apis/core/v1beta1/conditions.go | 15 + .../v1beta1/openstackcontrolplane_types.go | 68 ++++ apis/core/v1beta1/zz_generated.deepcopy.go | 71 ++++ apis/go.mod | 16 +- apis/go.sum | 24 +- ...client.openstack.org_openstackclients.yaml | 6 +- ....openstack.org_openstackcontrolplanes.yaml | 44 +++ ...nstack-operator.clusterserviceversion.yaml | 35 ++ config/rbac/role.yaml | 28 ++ .../core_v1beta1_openstackcontrolplane.yaml | 8 + ...enstackcontrolplane_network_isolation.yaml | 8 + .../client/openstackclient_controller.go | 72 +++- .../core/openstackcontrolplane_controller.go | 12 +- go.mod | 19 +- go.sum | 30 +- main.go | 2 + pkg/openstack/ca.go | 275 +++++++++++++ pkg/openstack/cinder.go | 2 +- pkg/openstack/common.go | 368 ++++++++++++------ pkg/openstack/glance.go | 2 +- pkg/openstack/heat.go | 4 +- pkg/openstack/horizon.go | 2 +- pkg/openstack/ironic.go | 4 +- pkg/openstack/keystone.go | 2 +- pkg/openstack/manila.go | 2 +- pkg/openstack/neutron.go | 2 +- pkg/openstack/nova.go | 4 +- pkg/openstack/octavia.go | 2 +- pkg/openstack/openstackclient.go | 9 +- pkg/openstack/placement.go | 2 +- pkg/openstack/swift.go | 2 +- pkg/openstackclient/funcs.go | 117 ++++-- templates/openstackclient/config/config.json | 25 ++ 38 files changed, 1127 insertions(+), 228 deletions(-) create mode 100644 pkg/openstack/ca.go create mode 100644 templates/openstackclient/config/config.json diff --git a/Dockerfile b/Dockerfile index d46809273..6fc93f1ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,8 @@ RUN if [ ! -f $CACHITO_ENV_FILE ]; then go mod download ; fi # Build manager RUN if [ -f $CACHITO_ENV_FILE ] ; then source $CACHITO_ENV_FILE ; fi ; CGO_ENABLED=0 GO111MODULE=on go build ${GO_BUILD_EXTRA_ARGS} -a -o ${DEST_ROOT}/manager main.go +RUN cp -r templates ${DEST_ROOT}/templates + # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM $OPERATOR_BASE_IMAGE @@ -55,13 +57,17 @@ LABEL com.redhat.component="${IMAGE_COMPONENT}" \ io.openshift.tags="${IMAGE_TAGS}" ### DO NOT EDIT LINES ABOVE -ENV USER_UID=$USER_ID +ENV USER_UID=$USER_ID \ + OPERATOR_TEMPLATES=/usr/share/openstack-operator/templates/ WORKDIR / # Install operator binary to WORKDIR COPY --from=builder ${DEST_ROOT}/manager . +# Install templates +COPY --from=builder ${DEST_ROOT}/templates ${OPERATOR_TEMPLATES} + USER $USER_ID ENV PATH="/:${PATH}" diff --git a/apis/bases/client.openstack.org_openstackclients.yaml b/apis/bases/client.openstack.org_openstackclients.yaml index 753c8839f..08c34b789 100644 --- a/apis/bases/client.openstack.org_openstackclients.yaml +++ b/apis/bases/client.openstack.org_openstackclients.yaml @@ -36,6 +36,8 @@ spec: type: object spec: properties: + caSecretName: + type: string containerImage: type: string nodeSelector: @@ -43,13 +45,13 @@ spec: type: string type: object openStackConfigMap: + default: openstack-config type: string openStackConfigSecret: + default: openstack-config-secret type: string required: - containerImage - - openStackConfigMap - - openStackConfigSecret type: object status: properties: diff --git a/apis/bases/core.openstack.org_openstackcontrolplanes.yaml b/apis/bases/core.openstack.org_openstackcontrolplanes.yaml index 0152515a5..cd6907883 100644 --- a/apis/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/apis/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -8933,6 +8933,31 @@ spec: - secret type: object type: object + openstackclient: + properties: + enabled: + default: false + type: boolean + template: + properties: + caSecretName: + type: string + containerImage: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + openStackConfigMap: + default: openstack-config + type: string + openStackConfigSecret: + default: openstack-config-secret + type: string + required: + - containerImage + type: object + type: object ovn: properties: enabled: @@ -13742,6 +13767,25 @@ spec: - swiftStorage type: object type: object + tls: + properties: + caSecretName: + type: string + internalEndpoints: + properties: + enabled: + default: true + type: boolean + type: object + publicEndpoints: + properties: + enabled: + default: true + type: boolean + issuer: + type: string + type: object + type: object required: - secret - storageClass diff --git a/apis/client/v1beta1/openstackclient_types.go b/apis/client/v1beta1/openstackclient_types.go index d12ff693d..b04b63773 100644 --- a/apis/client/v1beta1/openstackclient_types.go +++ b/apis/client/v1beta1/openstackclient_types.go @@ -15,6 +15,7 @@ package v1beta1 import ( condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -31,16 +32,25 @@ type OpenStackClientSpec struct { // +kubebuilder:validation:Required // ContainerImage for the the OpenstackClient container (will be set to environmental default if empty) ContainerImage string `json:"containerImage"` - // +kubebuilder:validation:Required + + // +kubebuilder:validation:Optional + // +kubebuilder:default=openstack-config // OpenStackConfigMap is the name of the ConfigMap containing the clouds.yaml OpenStackConfigMap string `json:"openStackConfigMap"` - // +kubebuilder:validation:Required + + // +kubebuilder:validation:Optional + // +kubebuilder:default=openstack-config-secret // OpenStackConfigSecret is the name of the Secret containing the secure.yaml OpenStackConfigSecret string `json:"openStackConfigSecret"` // +kubebuilder:validation:Optional // NodeSelector to target subset of worker nodes running control plane services (currently only applies to KeystoneAPI and PlacementAPI) NodeSelector map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:optional + //+operator-sdk:csv:customresourcedefinitions:type=spec + // Secret containing any CA certificates which should be added to deployment pods + tls.Ca `json:",inline"` } // OpenStackClientStatus defines the observed state of OpenStackClient diff --git a/apis/client/v1beta1/zz_generated.deepcopy.go b/apis/client/v1beta1/zz_generated.deepcopy.go index 6d43290bf..b662b728e 100644 --- a/apis/client/v1beta1/zz_generated.deepcopy.go +++ b/apis/client/v1beta1/zz_generated.deepcopy.go @@ -110,6 +110,7 @@ func (in *OpenStackClientSpec) DeepCopyInto(out *OpenStackClientSpec) { (*out)[key] = val } } + out.Ca = in.Ca } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackClientSpec. diff --git a/apis/core/v1beta1/conditions.go b/apis/core/v1beta1/conditions.go index 900b30888..6a06ba06f 100644 --- a/apis/core/v1beta1/conditions.go +++ b/apis/core/v1beta1/conditions.go @@ -102,6 +102,9 @@ const ( // OpenStackControlPlaneDNSReadyCondition Status=True condition which indicates if DNSMasq is configured and operational OpenStackControlPlaneDNSReadyCondition condition.Type = "OpenStackControlPlaneDNSReadyCondition" + // OpenStackControlPlaneCAReadyCondition Status=True condition which indicates if the CAs are configured and operational + OpenStackControlPlaneCAReadyCondition condition.Type = "OpenStackControlPlaneCAReadyCondition" + // OpenStackControlPlaneCeilometerReadyCondition Status=True condition which indicates if OpenStack Ceilometer service is configured and operational OpenStackControlPlaneCeilometerReadyCondition condition.Type = "OpenStackControlPlaneCeilometerReady" @@ -384,4 +387,16 @@ const ( // OpenStackControlPlaneExposeServiceReadyMessage OpenStackControlPlaneExposeServiceReadyMessage = "OpenStackControlPlane %s service exposed" + + // OpenStackControlPlaneCAReadyInitMessage + OpenStackControlPlaneCAReadyInitMessage = "OpenStackControlPlane CAs not started" + + // OpenStackControlPlaneCAReadyMessage + OpenStackControlPlaneCAReadyMessage = "OpenStackControlPlane CAs completed" + + // OpenStackControlPlaneCAReadyRunningMessage + OpenStackControlPlaneCAReadyRunningMessage = "OpenStackControlPlane CAs in progress" + + // OpenStackControlPlaneCAReadyErrorMessage + OpenStackControlPlaneCAReadyErrorMessage = "OpenStackControlPlane CAs %s %s error occured %s" ) diff --git a/apis/core/v1beta1/openstackcontrolplane_types.go b/apis/core/v1beta1/openstackcontrolplane_types.go index dde7f7e87..29344b77b 100644 --- a/apis/core/v1beta1/openstackcontrolplane_types.go +++ b/apis/core/v1beta1/openstackcontrolplane_types.go @@ -27,6 +27,7 @@ import ( keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/route" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" "github.com/openstack-k8s-operators/lib-common/modules/storage" manilav1 "github.com/openstack-k8s-operators/manila-operator/api/v1beta1" @@ -34,6 +35,7 @@ import ( neutronv1 "github.com/openstack-k8s-operators/neutron-operator/api/v1beta1" novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" + "github.com/openstack-k8s-operators/openstack-operator/apis/client/v1beta1" ovnv1 "github.com/openstack-k8s-operators/ovn-operator/api/v1beta1" placementv1 "github.com/openstack-k8s-operators/placement-operator/api/v1beta1" swiftv1 "github.com/openstack-k8s-operators/swift-operator/api/v1beta1" @@ -69,6 +71,11 @@ type OpenStackControlPlaneSpec struct { // NodeSelector to target subset of worker nodes running control plane services (currently only applies to KeystoneAPI and PlacementAPI) NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // +kubebuilder:validation:Optional + //+operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS TLSSection `json:"tls,omitempty"` + // +kubebuilder:validation:Optional //+operator-sdk:csv:customresourcedefinitions:type=spec // DNS - Parameters related to the DNSMasq service @@ -158,6 +165,11 @@ type OpenStackControlPlaneSpec struct { // Redis - Parameters related to the Redis service Redis RedisSection `json:"redis,omitempty"` + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // OpenStackClient - Parameters related to the OpenStackClient + OpenStackClient OpenStackClientSection `json:"openstackclient,omitempty"` + // ExtraMounts containing conf files and credentials that should be provided // to the underlying operators. // This struct can be defined in the top level CR and propagated to the @@ -168,6 +180,47 @@ type OpenStackControlPlaneSpec struct { ExtraMounts []OpenStackExtraVolMounts `json:"extraMounts,omitempty"` } +// TLSSection defines the desired state of TLS configuration +type TLSSection struct { + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // PublicEndpoints tls configuration + PublicEndpoints TLSPublicEndpointSection `json:"publicEndpoints,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // InternalEndpoints tls configuration + InternalEndpoints TLSInternalEndpointSection `json:"internalEndpoints,omitempty"` + + // +kubebuilder:validation:optional + //+operator-sdk:csv:customresourcedefinitions:type=spec + // Secret containing any additional CA certificates, which should be added to deployment pods + tls.Ca `json:",inline"` +} + +// TLSPublicEndpointSection defines the desired state of public TLSEndpoint configuration +type TLSPublicEndpointSection struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=true + // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors={"urn:alm:descriptor:com.tectonic.ui:booleanSwitch"} + // Enabled - Whether TLS should be enabled for endpoint type + Enabled bool `json:"enabled"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // Issuer - cert-manager issuer to be used for the endpoint type. If not specified a self signed will be created. + Issuer *string `json:"issuer,omitempty"` +} + +// TLSInternalEndpointSection defines the desired state of internal TLSEndpoint configuration +type TLSInternalEndpointSection struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=true + // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors={"urn:alm:descriptor:com.tectonic.ui:booleanSwitch"} + // Enabled - Whether TLS should be enabled for endpoint type + Enabled bool `json:"enabled"` +} + // DNSMasqSection defines the desired state of DNSMasq service type DNSMasqSection struct { // +kubebuilder:validation:Optional @@ -561,6 +614,20 @@ type RedisSection struct { Templates map[string]redisv1.RedisSpec `json:"templates,omitempty"` } +// OpenStackClientSection defines the desired state of the OpenStackClient +type OpenStackClientSection struct { + // +kubebuilder:validation:Optional + // Enabled - Whether the Redis service should be deployed and managed + // +kubebuilder:default=false + // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors={"urn:alm:descriptor:com.tectonic.ui:booleanSwitch"} + Enabled bool `json:"enabled"` + + // +kubebuilder:validation:Optional + //+operator-sdk:csv:customresourcedefinitions:type=spec + // Template - Overrides to use when creating the OpenStackClient Resource + Template v1beta1.OpenStackClientSpec `json:"template,omitempty"` +} + // OpenStackControlPlaneStatus defines the observed state of OpenStackControlPlane type OpenStackControlPlaneStatus struct { //+operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"} @@ -642,6 +709,7 @@ func (instance *OpenStackControlPlane) InitConditions() { condition.UnknownCondition(OpenStackControlPlaneSwiftReadyCondition, condition.InitReason, OpenStackControlPlaneSwiftReadyInitMessage), condition.UnknownCondition(OpenStackControlPlaneOctaviaReadyCondition, condition.InitReason, OpenStackControlPlaneOctaviaReadyInitMessage), condition.UnknownCondition(OpenStackControlPlaneRedisReadyCondition, condition.InitReason, OpenStackControlPlaneRedisReadyInitMessage), + condition.UnknownCondition(OpenStackControlPlaneCAReadyCondition, condition.InitReason, OpenStackControlPlaneCAReadyInitMessage), // Also add the overall status condition as Unknown condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage), diff --git a/apis/core/v1beta1/zz_generated.deepcopy.go b/apis/core/v1beta1/zz_generated.deepcopy.go index bbe5b7cdc..1985cb9ce 100644 --- a/apis/core/v1beta1/zz_generated.deepcopy.go +++ b/apis/core/v1beta1/zz_generated.deepcopy.go @@ -325,6 +325,22 @@ func (in *OctaviaSection) DeepCopy() *OctaviaSection { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenStackClientSection) DeepCopyInto(out *OpenStackClientSection) { + *out = *in + in.Template.DeepCopyInto(&out.Template) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackClientSection. +func (in *OpenStackClientSection) DeepCopy() *OpenStackClientSection { + if in == nil { + return nil + } + out := new(OpenStackClientSection) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OpenStackControlPlane) DeepCopyInto(out *OpenStackControlPlane) { *out = *in @@ -409,6 +425,7 @@ func (in *OpenStackControlPlaneSpec) DeepCopyInto(out *OpenStackControlPlaneSpec (*out)[key] = val } } + in.TLS.DeepCopyInto(&out.TLS) in.DNS.DeepCopyInto(&out.DNS) in.Keystone.DeepCopyInto(&out.Keystone) in.Placement.DeepCopyInto(&out.Placement) @@ -429,6 +446,7 @@ func (in *OpenStackControlPlaneSpec) DeepCopyInto(out *OpenStackControlPlaneSpec in.Swift.DeepCopyInto(&out.Swift) in.Octavia.DeepCopyInto(&out.Octavia) in.Redis.DeepCopyInto(&out.Redis) + in.OpenStackClient.DeepCopyInto(&out.OpenStackClient) if in.ExtraMounts != nil { in, out := &in.ExtraMounts, &out.ExtraMounts *out = make([]OpenStackExtraVolMounts, len(*in)) @@ -645,3 +663,56 @@ func (in *SwiftSection) DeepCopy() *SwiftSection { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSInternalEndpointSection) DeepCopyInto(out *TLSInternalEndpointSection) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSInternalEndpointSection. +func (in *TLSInternalEndpointSection) DeepCopy() *TLSInternalEndpointSection { + if in == nil { + return nil + } + out := new(TLSInternalEndpointSection) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSPublicEndpointSection) DeepCopyInto(out *TLSPublicEndpointSection) { + *out = *in + if in.Issuer != nil { + in, out := &in.Issuer, &out.Issuer + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSPublicEndpointSection. +func (in *TLSPublicEndpointSection) DeepCopy() *TLSPublicEndpointSection { + if in == nil { + return nil + } + out := new(TLSPublicEndpointSection) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSSection) DeepCopyInto(out *TLSSection) { + *out = *in + in.PublicEndpoints.DeepCopyInto(&out.PublicEndpoints) + out.InternalEndpoints = in.InternalEndpoints + out.Ca = in.Ca +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSSection. +func (in *TLSSection) DeepCopy() *TLSSection { + if in == nil { + return nil + } + out := new(TLSSection) + in.DeepCopyInto(out) + return out +} diff --git a/apis/go.mod b/apis/go.mod index 8a442fb9f..1a29fad21 100644 --- a/apis/go.mod +++ b/apis/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/onsi/ginkgo/v2 v2.12.1 - github.com/onsi/gomega v1.27.10 + github.com/onsi/gomega v1.28.0 github.com/openstack-k8s-operators/cinder-operator/api v0.1.2-0.20230927112537-42169dd3965b github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-adaea00db401 github.com/openstack-k8s-operators/heat-operator/api v0.1.1-0.20230928022009-63f4c93693b8 @@ -12,7 +12,7 @@ require ( github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79 github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984 - github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17 + github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20231001084618-12369665b166 github.com/openstack-k8s-operators/manila-operator/api v0.1.1-0.20230928195132-c19d2f673d7d github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230928152002-65395552e015 github.com/openstack-k8s-operators/neutron-operator/api v0.1.1-0.20230927135156-a3209100b1f3 @@ -55,7 +55,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.1 // indirect - github.com/gophercloud/gophercloud v1.6.0 // indirect + github.com/gophercloud/gophercloud v1.7.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -65,7 +65,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openshift/api v3.9.0+incompatible // indirect - github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17 //indirect + github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166 //indirect github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17 github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.16.0 // indirect @@ -109,3 +109,11 @@ replace ( // mschuppert: map to latest commit from release-4.13 tag // must consistent within modules and service operators replace github.com/openshift/api => github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 //allow-merging + +replace github.com/openstack-k8s-operators/lib-common/modules/common => github.com/stuggi/lib-common/modules/common v0.0.0-20230929092549-f68a217ec028 + +replace github.com/openstack-k8s-operators/lib-common/modules/certmanager => github.com/stuggi/lib-common/modules/certmanager v0.0.0-20230929092549-f68a217ec028 + +replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e + +replace github.com/openstack-k8s-operators/horizon-operator/api => github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0 diff --git a/apis/go.sum b/apis/go.sum index cb97e01a4..858a10ced 100644 --- a/apis/go.sum +++ b/apis/go.sum @@ -91,8 +91,8 @@ github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3/go.mod h1:79YE0hCXdHa github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gophercloud/gophercloud v1.6.0 h1:JwJN1bauRnWPba5ueWs9IluONHteXPWjjK+MvfM4krY= -github.com/gophercloud/gophercloud v1.6.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.7.0 h1:fyJGKh0LBvIZKLvBWvQdIgkaV5yTM3Jh9EYUh+UNCAs= +github.com/gophercloud/gophercloud v1.7.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= @@ -124,8 +124,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= +github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 h1:rncLxJBpFGqBztyxCMwNRnMjhhIDOWHJowi6q8G6koI= github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4= github.com/openstack-k8s-operators/cinder-operator/api v0.1.2-0.20230927112537-42169dd3965b h1:BpSoGmbOxwJFGZFMHM3BxYZ+1TvZOZaHTQ/z14h4D8w= @@ -134,18 +134,12 @@ github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-a github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-adaea00db401/go.mod h1:U+jK0QoSnON5s4rA3cnhqIrBAMYq5bhVDQOoXym7+yE= github.com/openstack-k8s-operators/heat-operator/api v0.1.1-0.20230928022009-63f4c93693b8 h1:cbtGPROaKwVB1bHmmGsde2WIB+IqfYz2UBnaYutpvCA= github.com/openstack-k8s-operators/heat-operator/api v0.1.1-0.20230928022009-63f4c93693b8/go.mod h1:xxjs1gg1sfw8szoIQtUjHgh/wiicduipTzs5Rsp9Gao= -github.com/openstack-k8s-operators/horizon-operator/api v0.1.1-0.20230927133505-e0a30ad65a01 h1:RXdu3rtbLRn+8zdDPT/SPA7NFluO1R5y8NdBwjujYn8= -github.com/openstack-k8s-operators/horizon-operator/api v0.1.1-0.20230927133505-e0a30ad65a01/go.mod h1:mA0trgqY20GlCn7Q5hLeRv5lozsF5Y+ZPCFTsjd5mjE= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e h1:J6ufJlFvx57q0VX0bDs0Qy/pNHf1VRtOzrvWP4UYMqQ= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e/go.mod h1:mREHg4cn89igZAkvaMnrv+9YFWK2tDhK/HdqJPV/+hw= github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79 h1:3femyaSRudGF40dUEfp/Kn/MOdKpCvPfy5j85GAKQ2Y= github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79/go.mod h1:NR5xmmZQz/v1EgGfSrL/4yCbQpRbaWLoIx1CgpGiWck= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984 h1:eJofMw2iWLhz8fusIjOa5ck6yHR1zHhohCRAVfX5CCY= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984/go.mod h1:8d02elP1XA66ANyD+J0MkWIO7WqFKihqV10m+gFZq0o= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17 h1:n5QmZLJfPtKbNnPVqqSQkLU1X/NMmW3CbML3yjBUjyY= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:kZS5rqVWBZeCyYor2PeQB9IEZ19mGaeL/to3x8F9OJg= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17 h1:d3HP0nEGNd8cQ088beQbyWzcgdMppD+Zs8HeoMdzRws= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:+iJZo5alCeOGD/524hWWdlINA6zqY+MjfWT7cDcbvBE= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166 h1:zOnRGMdgq2XvOCCtF1lY4tFhKx3jXrcrtOiZZ1PR6M8= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166/go.mod h1:LOXXvTQCwhOBNd+0FTlgllpa3wqlkI6Vf3Q5QVRVPlw= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17 h1:epXz35gpdGp/CbYzAT926971T9kpO6r1v2GX8I1HHGM= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:kKFAr7wZw3mX83hlQVbf2hV7TGhNNxVgMSNt9YtUPzI= github.com/openstack-k8s-operators/manila-operator/api v0.1.1-0.20230928195132-c19d2f673d7d h1:mU35EkNUuS0MzswKxSq/yKGKiy+r8Yv4Z+k65vfRByM= @@ -199,6 +193,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0 h1:NbFgQkSuS+1FGK7y+PgmlzoE7BqfhUwK8DtooIKG908= +github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0/go.mod h1:mA0trgqY20GlCn7Q5hLeRv5lozsF5Y+ZPCFTsjd5mjE= +github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e h1:TEUaan+so+oBtqoMwoLRKDtX+kGfAVzyrh8S5nVHgAM= +github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e/go.mod h1:5v0ngxNmFp8QsINo2bufx1/COJc0q6jm3FMhP3xIAWE= +github.com/stuggi/lib-common/modules/common v0.0.0-20230929092549-f68a217ec028 h1:5YBnqpChMqNM4D8o9jGkNT482ARP8Rb/njy6JijTefk= +github.com/stuggi/lib-common/modules/common v0.0.0-20230929092549-f68a217ec028/go.mod h1:kZS5rqVWBZeCyYor2PeQB9IEZ19mGaeL/to3x8F9OJg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= diff --git a/config/crd/bases/client.openstack.org_openstackclients.yaml b/config/crd/bases/client.openstack.org_openstackclients.yaml index 753c8839f..08c34b789 100644 --- a/config/crd/bases/client.openstack.org_openstackclients.yaml +++ b/config/crd/bases/client.openstack.org_openstackclients.yaml @@ -36,6 +36,8 @@ spec: type: object spec: properties: + caSecretName: + type: string containerImage: type: string nodeSelector: @@ -43,13 +45,13 @@ spec: type: string type: object openStackConfigMap: + default: openstack-config type: string openStackConfigSecret: + default: openstack-config-secret type: string required: - containerImage - - openStackConfigMap - - openStackConfigSecret type: object status: properties: diff --git a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml index 0152515a5..cd6907883 100644 --- a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -8933,6 +8933,31 @@ spec: - secret type: object type: object + openstackclient: + properties: + enabled: + default: false + type: boolean + template: + properties: + caSecretName: + type: string + containerImage: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + openStackConfigMap: + default: openstack-config + type: string + openStackConfigSecret: + default: openstack-config-secret + type: string + required: + - containerImage + type: object + type: object ovn: properties: enabled: @@ -13742,6 +13767,25 @@ spec: - swiftStorage type: object type: object + tls: + properties: + caSecretName: + type: string + internalEndpoints: + properties: + enabled: + default: true + type: boolean + type: object + publicEndpoints: + properties: + enabled: + default: true + type: boolean + issuer: + type: string + type: object + type: object required: - secret - storageClass diff --git a/config/manifests/bases/openstack-operator.clusterserviceversion.yaml b/config/manifests/bases/openstack-operator.clusterserviceversion.yaml index f9e92873d..68323be43 100644 --- a/config/manifests/bases/openstack-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/openstack-operator.clusterserviceversion.yaml @@ -237,6 +237,18 @@ spec: - description: Template - Overrides to use when creating Octavia Resources displayName: Template path: octavia.template + - description: OpenStackClient - Parameters related to the OpenStackClient + displayName: Open Stack Client + path: openstackclient + - description: Enabled - Whether the Redis service should be deployed and managed + displayName: Enabled + path: openstackclient.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: Template - Overrides to use when creating the OpenStackClient + Resource + displayName: Template + path: openstackclient.template - description: Ovn - Overrides to use when creating the OVN Services displayName: Ovn path: ovn @@ -315,6 +327,29 @@ spec: - description: Template - Overrides to use when creating Swift Resources displayName: Template path: swift.template + - description: TLS - Parameters related to the TLS + displayName: TLS + path: tls + - description: InternalEndpoints tls configuration + displayName: Internal Endpoints + path: tls.internalEndpoints + - description: Enabled - Whether TLS should be enabled for endpoint type + displayName: Enabled + path: tls.internalEndpoints.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: PublicEndpoints tls configuration + displayName: Public Endpoints + path: tls.publicEndpoints + - description: Enabled - Whether TLS should be enabled for endpoint type + displayName: Enabled + path: tls.publicEndpoints.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: Issuer - cert-manager issuer to be used for the endpoint type. + If not specified a self signed will be created. + displayName: Issuer + path: tls.publicEndpoints.issuer statusDescriptors: - description: Conditions displayName: Conditions diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 6c13715fa..1d3dd158b 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -27,6 +27,30 @@ rules: - list - update - watch +- apiGroups: + - cert-manager.io + resources: + - certificates + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - cert-manager.io + resources: + - issuers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - cinder.openstack.org resources: @@ -78,8 +102,12 @@ rules: resources: - secrets verbs: + - create + - delete - get - list + - patch + - update - watch - apiGroups: - "" diff --git a/config/samples/core_v1beta1_openstackcontrolplane.yaml b/config/samples/core_v1beta1_openstackcontrolplane.yaml index 8ad4d51cd..4a1623aeb 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane.yaml @@ -5,6 +5,14 @@ metadata: spec: secret: osp-secret storageClass: local-storage + tls: + internalEndpoints: + enabled: false + publicEndpoints: + enabled: true + openstackclient: + template: + containerImage: quay.io/mschuppe/openstack-openstackclient:current-podified keystone: template: databaseInstance: openstack diff --git a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml index 0619f35d2..bff45b8cb 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml @@ -5,6 +5,14 @@ metadata: spec: secret: osp-secret storageClass: local-storage + tls: + internalEndpoints: + enabled: false + publicEndpoints: + enabled: true + openstackclient: + template: + containerImage: quay.io/mschuppe/openstack-openstackclient:current-podified dns: template: override: diff --git a/controllers/client/openstackclient_controller.go b/controllers/client/openstackclient_controller.go index 5ecaf3fd4..6001a54a3 100644 --- a/controllers/client/openstackclient_controller.go +++ b/controllers/client/openstackclient_controller.go @@ -30,8 +30,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/configmap" + "github.com/openstack-k8s-operators/lib-common/modules/common/env" helper "github.com/openstack-k8s-operators/lib-common/modules/common/helper" common_rbac "github.com/openstack-k8s-operators/lib-common/modules/common/rbac" "github.com/openstack-k8s-operators/lib-common/modules/common/secret" @@ -172,6 +174,42 @@ func (r *OpenStackClientReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{RequeueAfter: time.Duration(5) * time.Second}, nil } + clientLabels := map[string]string{ + common.AppSelector: "openstackclient", + } + + configVars := make(map[string]env.Setter) + + cms := []util.Template{ + // ConfigMap holding kolla config + { + Name: fmt.Sprintf("%s-config-data", instance.Name), + Namespace: instance.Namespace, + Type: util.TemplateTypeConfig, + InstanceType: instance.Kind, + ConfigOptions: nil, + Labels: clientLabels, + }, + } + err = configmap.EnsureConfigMaps(ctx, helper, instance, cms, &configVars) + if err != nil { + if k8s_errors.IsNotFound(err) { + instance.Status.Conditions.Set(condition.FalseCondition( + clientv1.OpenStackClientReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + clientv1.OpenStackClientConfigMapWaitingMessage)) + return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, nil + } + instance.Status.Conditions.Set(condition.FalseCondition( + clientv1.OpenStackClientReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + clientv1.OpenStackClientReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + _, configMapHash, err := configmap.GetConfigMapAndHashWithName(ctx, helper, instance.Spec.OpenStackConfigMap, instance.Namespace) if err != nil { if k8s_errors.IsNotFound(err) { @@ -190,6 +228,7 @@ func (r *OpenStackClientReconciler) Reconcile(ctx context.Context, req ctrl.Requ err.Error())) return ctrl.Result{}, err } + configVars[instance.Spec.OpenStackConfigMap] = env.SetValue(configMapHash) _, secretHash, err := secret.GetSecret(ctx, helper, instance.Spec.OpenStackConfigSecret, instance.Namespace) if err != nil { @@ -209,6 +248,34 @@ func (r *OpenStackClientReconciler) Reconcile(ctx context.Context, req ctrl.Requ err.Error())) return ctrl.Result{}, err } + configVars[instance.Spec.OpenStackConfigSecret] = env.SetValue(secretHash) + + if instance.Spec.CaSecretName != "" { + _, secretHash, err = secret.GetSecret(ctx, helper, instance.Spec.OpenStackConfigSecret, instance.Namespace) + if err != nil { + if k8s_errors.IsNotFound(err) { + instance.Status.Conditions.Set(condition.FalseCondition( + clientv1.OpenStackClientReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + clientv1.OpenStackClientSecretWaitingMessage)) + return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, nil + } + instance.Status.Conditions.Set(condition.FalseCondition( + clientv1.OpenStackClientReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + clientv1.OpenStackClientReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + configVars[instance.Spec.OpenStackConfigSecret] = env.SetValue(secretHash) + } + + configVarsHash, err := util.HashOfInputHashes(configVars) + if err != nil { + return ctrl.Result{}, err + } instance.Status.Conditions.Set(condition.FalseCondition( clientv1.OpenStackClientReadyCondition, @@ -216,10 +283,7 @@ func (r *OpenStackClientReconciler) Reconcile(ctx context.Context, req ctrl.Requ condition.SeverityInfo, clientv1.OpenStackClientInputReady)) - clientLabels := map[string]string{ - "app": "openstackclient", - } - pod := openstackclient.ClientPod(instance, clientLabels, configMapHash, secretHash) + pod := openstackclient.ClientPod(instance, clientLabels, configVarsHash) op, err := controllerutil.CreateOrPatch(ctx, r.Client, pod, func() error { pod.Spec.Containers[0].Image = instance.Spec.ContainerImage diff --git a/controllers/core/openstackcontrolplane_controller.go b/controllers/core/openstackcontrolplane_controller.go index 2ce0515f1..fc74de612 100644 --- a/controllers/core/openstackcontrolplane_controller.go +++ b/controllers/core/openstackcontrolplane_controller.go @@ -94,6 +94,9 @@ type OpenStackControlPlaneReconciler struct { //+kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=get;list;watch;create;update;patch;delete; //+kubebuilder:rbac:groups=route.openshift.io,resources=routes/custom-host,verbs=create;update;patch //+kubebuilder:rbac:groups=core,resources=services,verbs=get;list; +//+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete; +//+kubebuilder:rbac:groups=cert-manager.io,resources=issuers,verbs=get;list;watch;create;update;patch;delete; +//+kubebuilder:rbac:groups=cert-manager.io,resources=certificates,verbs=get;list;watch;create;update;patch;delete; // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -164,7 +167,14 @@ func (r *OpenStackControlPlaneReconciler) Reconcile(ctx context.Context, req ctr func (r *OpenStackControlPlaneReconciler) reconcileNormal(ctx context.Context, instance *corev1beta1.OpenStackControlPlane, helper *helper.Helper) (ctrl.Result, error) { - ctrlResult, err := openstack.ReconcileDNSMasqs(ctx, instance, helper) + ctrlResult, err := openstack.ReconcileCAs(ctx, instance, helper) + if err != nil { + return ctrl.Result{}, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + ctrlResult, err = openstack.ReconcileDNSMasqs(ctx, instance, helper) if err != nil { return ctrl.Result{}, err } else if (ctrlResult != ctrl.Result{}) { diff --git a/go.mod b/go.mod index e1e53abd7..4fa1dbb27 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,12 @@ module github.com/openstack-k8s-operators/openstack-operator go 1.19 require ( + github.com/cert-manager/cert-manager v1.11.5 github.com/ghodss/yaml v1.0.0 github.com/go-logr/logr v1.2.4 github.com/imdario/mergo v0.3.16 github.com/onsi/ginkgo/v2 v2.12.1 - github.com/onsi/gomega v1.27.10 + github.com/onsi/gomega v1.28.0 github.com/openstack-k8s-operators/cinder-operator/api v0.1.2-0.20230927112537-42169dd3965b github.com/openstack-k8s-operators/dataplane-operator/api v0.1.1-0.20230928164440-a955424c43ef github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-adaea00db401 @@ -16,7 +17,8 @@ require ( github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79 github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984 - github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17 + github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.0.0-20231001084618-12369665b166 + github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20231001084618-12369665b166 github.com/openstack-k8s-operators/manila-operator/api v0.1.1-0.20230928195132-c19d2f673d7d github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230928152002-65395552e015 github.com/openstack-k8s-operators/neutron-operator/api v0.1.1-0.20230927135156-a3209100b1f3 @@ -46,6 +48,7 @@ require ( github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.2.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/tools v0.13.0 // indirect + sigs.k8s.io/gateway-api v0.6.0 // indirect ) require ( @@ -66,7 +69,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.1 // indirect - github.com/gophercloud/gophercloud v1.6.0 // indirect + github.com/gophercloud/gophercloud v1.7.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -75,7 +78,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openshift/api v3.9.0+incompatible - github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17 //indirect + github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166 //indirect github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17 //indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.16.0 // indirect @@ -121,3 +124,11 @@ replace github.com/openstack-k8s-operators/openstack-operator/apis => ./apis // mschuppert: map to latest commit from release-4.13 tag // must consistent within modules and service operators replace github.com/openshift/api => github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 //allow-merging + +replace github.com/openstack-k8s-operators/lib-common/modules/common => github.com/stuggi/lib-common/modules/common v0.0.0-20230929092549-f68a217ec028 + +replace github.com/openstack-k8s-operators/lib-common/modules/certmanager => github.com/stuggi/lib-common/modules/certmanager v0.0.0-20230929092549-f68a217ec028 + +replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e + +replace github.com/openstack-k8s-operators/horizon-operator/api => github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0 diff --git a/go.sum b/go.sum index aacff7d8c..37d120805 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cert-manager/cert-manager v1.11.5 h1:K2LurvwIE4hIhODQZnkOW6ljYe3lVMAliS/to+gI05o= +github.com/cert-manager/cert-manager v1.11.5/go.mod h1:zNOyoTEwdn9Rtj5Or2pjBY1Bqwtw4vBElP2fKSP8/g8= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -94,8 +96,8 @@ github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3/go.mod h1:79YE0hCXdHa github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gophercloud/gophercloud v1.6.0 h1:JwJN1bauRnWPba5ueWs9IluONHteXPWjjK+MvfM4krY= -github.com/gophercloud/gophercloud v1.6.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.7.0 h1:fyJGKh0LBvIZKLvBWvQdIgkaV5yTM3Jh9EYUh+UNCAs= +github.com/gophercloud/gophercloud v1.7.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= @@ -131,8 +133,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= +github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 h1:rncLxJBpFGqBztyxCMwNRnMjhhIDOWHJowi6q8G6koI= github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4= github.com/openstack-k8s-operators/cinder-operator/api v0.1.2-0.20230927112537-42169dd3965b h1:BpSoGmbOxwJFGZFMHM3BxYZ+1TvZOZaHTQ/z14h4D8w= @@ -143,18 +145,12 @@ github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-a github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-adaea00db401/go.mod h1:U+jK0QoSnON5s4rA3cnhqIrBAMYq5bhVDQOoXym7+yE= github.com/openstack-k8s-operators/heat-operator/api v0.1.1-0.20230928022009-63f4c93693b8 h1:cbtGPROaKwVB1bHmmGsde2WIB+IqfYz2UBnaYutpvCA= github.com/openstack-k8s-operators/heat-operator/api v0.1.1-0.20230928022009-63f4c93693b8/go.mod h1:xxjs1gg1sfw8szoIQtUjHgh/wiicduipTzs5Rsp9Gao= -github.com/openstack-k8s-operators/horizon-operator/api v0.1.1-0.20230927133505-e0a30ad65a01 h1:RXdu3rtbLRn+8zdDPT/SPA7NFluO1R5y8NdBwjujYn8= -github.com/openstack-k8s-operators/horizon-operator/api v0.1.1-0.20230927133505-e0a30ad65a01/go.mod h1:mA0trgqY20GlCn7Q5hLeRv5lozsF5Y+ZPCFTsjd5mjE= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e h1:J6ufJlFvx57q0VX0bDs0Qy/pNHf1VRtOzrvWP4UYMqQ= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e/go.mod h1:mREHg4cn89igZAkvaMnrv+9YFWK2tDhK/HdqJPV/+hw= github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79 h1:3femyaSRudGF40dUEfp/Kn/MOdKpCvPfy5j85GAKQ2Y= github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79/go.mod h1:NR5xmmZQz/v1EgGfSrL/4yCbQpRbaWLoIx1CgpGiWck= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984 h1:eJofMw2iWLhz8fusIjOa5ck6yHR1zHhohCRAVfX5CCY= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984/go.mod h1:8d02elP1XA66ANyD+J0MkWIO7WqFKihqV10m+gFZq0o= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17 h1:n5QmZLJfPtKbNnPVqqSQkLU1X/NMmW3CbML3yjBUjyY= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:kZS5rqVWBZeCyYor2PeQB9IEZ19mGaeL/to3x8F9OJg= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17 h1:d3HP0nEGNd8cQ088beQbyWzcgdMppD+Zs8HeoMdzRws= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:+iJZo5alCeOGD/524hWWdlINA6zqY+MjfWT7cDcbvBE= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166 h1:zOnRGMdgq2XvOCCtF1lY4tFhKx3jXrcrtOiZZ1PR6M8= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166/go.mod h1:LOXXvTQCwhOBNd+0FTlgllpa3wqlkI6Vf3Q5QVRVPlw= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17 h1:epXz35gpdGp/CbYzAT926971T9kpO6r1v2GX8I1HHGM= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:kKFAr7wZw3mX83hlQVbf2hV7TGhNNxVgMSNt9YtUPzI= github.com/openstack-k8s-operators/manila-operator/api v0.1.1-0.20230928195132-c19d2f673d7d h1:mU35EkNUuS0MzswKxSq/yKGKiy+r8Yv4Z+k65vfRByM= @@ -216,6 +212,14 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0 h1:NbFgQkSuS+1FGK7y+PgmlzoE7BqfhUwK8DtooIKG908= +github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0/go.mod h1:mA0trgqY20GlCn7Q5hLeRv5lozsF5Y+ZPCFTsjd5mjE= +github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e h1:TEUaan+so+oBtqoMwoLRKDtX+kGfAVzyrh8S5nVHgAM= +github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e/go.mod h1:5v0ngxNmFp8QsINo2bufx1/COJc0q6jm3FMhP3xIAWE= +github.com/stuggi/lib-common/modules/certmanager v0.0.0-20230929092549-f68a217ec028 h1:xGMg0VEW/pocBqHvh1ThLG4MauSAkpvGCrzSY29+lfk= +github.com/stuggi/lib-common/modules/certmanager v0.0.0-20230929092549-f68a217ec028/go.mod h1:BCSnLQLasZ03YaLIvKPKjY5DZNHtQASTOSJF/79ZSLc= +github.com/stuggi/lib-common/modules/common v0.0.0-20230929092549-f68a217ec028 h1:5YBnqpChMqNM4D8o9jGkNT482ARP8Rb/njy6JijTefk= +github.com/stuggi/lib-common/modules/common v0.0.0-20230929092549-f68a217ec028/go.mod h1:kZS5rqVWBZeCyYor2PeQB9IEZ19mGaeL/to3x8F9OJg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -383,6 +387,8 @@ k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSn k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +sigs.k8s.io/gateway-api v0.6.0 h1:v2FqrN2ROWZLrSnI2o91taHR8Sj3s+Eh3QU7gLNWIqA= +sigs.k8s.io/gateway-api v0.6.0/go.mod h1:EYJT+jlPWTeNskjV0JTki/03WX1cyAnBhwBJfYHpV/0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= diff --git a/main.go b/main.go index 8d2816934..346521765 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,7 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" + certmgrv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" cinderv1 "github.com/openstack-k8s-operators/cinder-operator/api/v1beta1" dataplanev1beta1 "github.com/openstack-k8s-operators/dataplane-operator/api/v1beta1" glancev1 "github.com/openstack-k8s-operators/glance-operator/api/v1beta1" @@ -102,6 +103,7 @@ func init() { utilruntime.Must(clientv1.AddToScheme(scheme)) utilruntime.Must(redisv1.AddToScheme(scheme)) utilruntime.Must(routev1.AddToScheme(scheme)) + utilruntime.Must(certmgrv1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/pkg/openstack/ca.go b/pkg/openstack/ca.go new file mode 100644 index 000000000..4fa69ee17 --- /dev/null +++ b/pkg/openstack/ca.go @@ -0,0 +1,275 @@ +package openstack + +import ( + "context" + "time" + + certmgrv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" + certmgrmetav1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + "github.com/openstack-k8s-operators/lib-common/modules/certmanager" + "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/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/util" + + corev1 "github.com/openstack-k8s-operators/openstack-operator/apis/core/v1beta1" + + ctrl "sigs.k8s.io/controller-runtime" +) + +const ( + // CombinedCASecret - + CombinedCASecret = "combined-ca-bundle" + // DefaultPublicCAName - + DefaultPublicCAName = "rootca-" + string(service.EndpointPublic) + // DefaultInternalCAName - + DefaultInternalCAName = "rootca-" + string(service.EndpointInternal) +) + +// ReconcileCAs - +func ReconcileCAs(ctx context.Context, instance *corev1.OpenStackControlPlane, helper *helper.Helper) (ctrl.Result, error) { + // create selfsigned-issuer + issuerReq := certmanager.SelfSignedIssuer( + "selfsigned-issuer", + instance.GetNamespace(), + map[string]string{}, + ) + /* + // Cleanuo? + if !instance.Spec.TLS.Enabled { + if err := cert.Delete(ctx, helper); err != nil { + return ctrl.Result{}, err + } + instance.Status.Conditions.Remove(corev1beta1.OpenStackControlPlaneCAsReadyCondition) + + return ctrl.Result{}, nil + } + */ + + helper.GetLogger().Info("Reconciling CAs", "Namespace", instance.Namespace, "Name", issuerReq.Name) + + issuer := certmanager.NewIssuer(issuerReq, 5) + ctrlResult, err := issuer.CreateOrPatch(ctx, helper) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + issuerReq.Kind, + issuerReq.GetName(), + err.Error())) + + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + corev1.OpenStackControlPlaneCAReadyRunningMessage)) + + return ctrlResult, nil + } + + caCerts := map[string]string{} + + // create RootCA cert and Issuer that uses the generated CA certificate to issue certs + if instance.Spec.TLS.PublicEndpoints.Enabled && instance.Spec.TLS.PublicEndpoints.Issuer == nil { + caCert, ctrlResult, err := createRootCACertAndIssuer(ctx, instance, helper, issuerReq, DefaultPublicCAName) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + caCerts[DefaultPublicCAName] = string(caCert) + } + if instance.Spec.TLS.InternalEndpoints.Enabled { + caCert, ctrlResult, err := createRootCACertAndIssuer(ctx, instance, helper, issuerReq, DefaultInternalCAName) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + caCerts[DefaultInternalCAName] = string(caCert) + } + instance.Status.Conditions.MarkTrue(corev1.OpenStackControlPlaneCAReadyCondition, corev1.OpenStackControlPlaneCAReadyMessage) + + // create/update combined CA secret + if instance.Spec.TLS.CaSecretName != "" { + caSecret, _, err := secret.GetSecret(ctx, helper, instance.Spec.TLS.CaSecretName, instance.Namespace) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + "secret", + instance.Spec.TLS.CaSecretName, + err.Error())) + + return ctrlResult, err + } + + for key, ca := range caSecret.Data { + key := instance.Spec.TLS.CaSecretName + "-" + key + caCerts[key] = string(ca) + } + } + + saSecretTemplate := []util.Template{ + { + Name: CombinedCASecret, + Namespace: instance.Namespace, + Type: util.TemplateTypeNone, + InstanceType: instance.Kind, + AdditionalTemplate: nil, + Annotations: map[string]string{}, + Labels: map[string]string{ + CombinedCASecret: "", + }, + ConfigOptions: nil, + CustomData: caCerts, + }, + } + + if err := secret.EnsureSecrets(ctx, helper, instance, saSecretTemplate, nil); err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + "secret", + CombinedCASecret, + err.Error())) + + return ctrlResult, err + } + + return ctrl.Result{}, nil +} + +func createRootCACertAndIssuer( + ctx context.Context, + instance *corev1.OpenStackControlPlane, + helper *helper.Helper, + selfsignedIssuerReq *certmgrv1.Issuer, + caName string, +) (string, ctrl.Result, error) { + var caCert string + // create RootCA Certificate used to sign certificates + caCertReq := certmanager.Cert( + caName, + instance.Namespace, + map[string]string{}, + certmgrv1.CertificateSpec{ + IsCA: true, + CommonName: caName, + SecretName: caName, + PrivateKey: &certmgrv1.CertificatePrivateKey{ + Algorithm: "ECDSA", + Size: 256, + }, + IssuerRef: certmgrmetav1.ObjectReference{ + Name: selfsignedIssuerReq.Name, + Kind: selfsignedIssuerReq.Kind, + Group: selfsignedIssuerReq.GroupVersionKind().Group, + }, + }) + cert := certmanager.NewCertificate(caCertReq, 5) + + ctrlResult, err := cert.CreateOrPatch(ctx, helper) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + caCertReq.Kind, + caCertReq.Name, + err.Error())) + + return caCert, ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + corev1.OpenStackControlPlaneCAReadyRunningMessage)) + + return caCert, ctrlResult, nil + } + + // create Issuer that uses the generated CA certificate to issue certs + issuerReq := certmanager.CAIssuer( + caCertReq.Name, + instance.GetNamespace(), + map[string]string{}, + caCertReq.Name, + ) + + issuer := certmanager.NewIssuer(issuerReq, 5) + ctrlResult, err = issuer.CreateOrPatch(ctx, helper) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + issuerReq.Kind, + issuerReq.GetName(), + err.Error())) + + return caCert, ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + corev1.OpenStackControlPlaneCAReadyRunningMessage)) + + return caCert, ctrlResult, nil + } + + caCert, ctrlResult, err = getCAFromSecret(ctx, instance, helper, caName) + if err != nil { + return caCert, ctrl.Result{}, err + } else if (ctrlResult != ctrl.Result{}) { + return caCert, ctrlResult, nil + } + + return caCert, ctrl.Result{}, nil +} + +func getCAFromSecret( + ctx context.Context, + instance *corev1.OpenStackControlPlane, + helper *helper.Helper, + caName string, +) (string, ctrl.Result, error) { + caSecret, ctrlResult, err := secret.GetDataFromSecret(ctx, helper, caName, time.Duration(5), "ca.crt") + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + "secret", + caName, + err.Error())) + + return caSecret, ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + corev1.OpenStackControlPlaneCAReadyRunningMessage)) + + return caSecret, ctrlResult, nil + } + + return caSecret, ctrl.Result{}, nil +} diff --git a/pkg/openstack/cinder.go b/pkg/openstack/cinder.go index a0be02e74..547e0a9e0 100644 --- a/pkg/openstack/cinder.go +++ b/pkg/openstack/cinder.go @@ -68,7 +68,7 @@ func ReconcileCinder(ctx context.Context, instance *corev1beta1.OpenStackControl } var ctrlResult reconcile.Result - instance.Spec.Cinder.Template.CinderAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Cinder.Template.CinderAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/common.go b/pkg/openstack/common.go index b4f9c6ffb..b761e1c24 100644 --- a/pkg/openstack/common.go +++ b/pkg/openstack/common.go @@ -6,6 +6,7 @@ import ( "time" routev1 "github.com/openshift/api/route/v1" + "github.com/openstack-k8s-operators/lib-common/modules/certmanager" "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" @@ -13,9 +14,11 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/service" "github.com/openstack-k8s-operators/lib-common/modules/common/util" corev1 "github.com/openstack-k8s-operators/openstack-operator/apis/core/v1beta1" + k8s_corev1 "k8s.io/api/core/v1" 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" "sigs.k8s.io/controller-runtime/pkg/client" @@ -52,17 +55,37 @@ func AddServiceComponentLabel(svcOverride service.RoutedOverrideSpec, value stri return svcOverride } +// EndpointDetails - endpoint details +type EndpointDetails struct { + Name string + Namespace string + Type service.Endpoint + Labels map[string]string + Service ServiceDetails + Route RouteDetails + Hostname *string + EndpointURL string + TLS TLSDetails +} + +// TLSDetails - tls settings for the endpoint +type TLSDetails struct { + Issuer *string + PublicEndpoint bool + InternalEndpoint bool + InternalCA string +} + +// ServiceDetails - service details +type ServiceDetails struct { + Spec *k8s_corev1.Service + OverrideSpec service.RoutedOverrideSpec +} + // RouteDetails - route details type RouteDetails struct { - RouteName string - Namespace string - Endpoint service.Endpoint - RouteOverrideSpec *route.OverrideSpec - ServiceLabel map[string]string - ServiceSpec *k8s_corev1.Service - endpointURL string - hostname *string - route *routev1.Route + Route *routev1.Route + OverrideSpec route.OverrideSpec } // GetRoutesListWithLabel - Get all routes in namespace of the obj matching label selector @@ -86,166 +109,267 @@ func GetRoutesListWithLabel( return routeList, nil } -// EnsureRoute - -func EnsureRoute( +// EnsureEndpointConfig - +func EnsureEndpointConfig( ctx context.Context, instance *corev1.OpenStackControlPlane, helper *helper.Helper, owner metav1.Object, svcs *k8s_corev1.ServiceList, svcOverrides map[service.Endpoint]service.RoutedOverrideSpec, - overrideSpec *route.OverrideSpec, + routeOverrideSpec *route.OverrideSpec, condType condition.Type, ) (map[service.Endpoint]service.RoutedOverrideSpec, ctrl.Result, error) { + for _, svc := range svcs.Items { + ed := EndpointDetails{ + Name: svc.Name, + Namespace: svc.Namespace, + Type: service.Endpoint(svc.Annotations[service.AnnotationEndpointKey]), + Service: ServiceDetails{ + Spec: &svc, + }, + } + if routeOverrideSpec != nil { + ed.Route.OverrideSpec = *routeOverrideSpec + } + + // check if there is a custom issuer name reference provided + if ed.Type == service.EndpointPublic && instance.Spec.TLS.PublicEndpoints.Enabled { + if instance.Spec.TLS.PublicEndpoints.Issuer != nil { + ed.TLS.Issuer = instance.Spec.TLS.PublicEndpoints.Issuer + } else { + ed.TLS.Issuer = ptr.To(DefaultPublicCAName) + } + ed.TLS.PublicEndpoint = true + } + if ed.Type == service.EndpointInternal && instance.Spec.TLS.InternalEndpoints.Enabled { + ed.TLS.Issuer = ptr.To(DefaultInternalCAName) + ed.TLS.InternalEndpoint = true + // TODO: for TLSE create TLS cert for service + } - cleanCondition := true + ed.Service.OverrideSpec = svcOverrides[ed.Type] - for _, svc := range svcs.Items { - rd := RouteDetails{ - RouteName: svc.Name, - Namespace: svc.Namespace, - Endpoint: service.Endpoint(svc.Annotations[service.AnnotationEndpointKey]), - RouteOverrideSpec: overrideSpec, - ServiceSpec: &svc, - } - svcOverride := svcOverrides[rd.Endpoint] - - // check if there is already a route with common.AppSelector from the service - if svcLabelVal, ok := svc.Labels[common.AppSelector]; ok { - routes, err := GetRoutesListWithLabel( - ctx, - helper, - instance.Namespace, - map[string]string{common.AppSelector: svcLabelVal}, - ) + if ed.Type == service.EndpointPublic { + ctrlResult, err := ed.ensureRoute(ctx, instance, helper, &svc, owner, condType) if err != nil { - return svcOverrides, ctrl.Result{}, err + return svcOverrides, ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return svcOverrides, ctrlResult, nil } + } - // check the routes if name changed where we are the owner - for _, r := range routes.Items { - instanceRef := metav1.OwnerReference{ - APIVersion: instance.APIVersion, - Kind: instance.Kind, - Name: instance.GetName(), - UID: instance.GetUID(), - BlockOwnerDeletion: ptr.To(true), - Controller: ptr.To(true), - } + // update override for the service with the endpoint url + if ed.EndpointURL != "" { + // Any trailing path will be added on the service-operator level. + ed.Service.OverrideSpec.EndpointURL = &ed.EndpointURL + instance.Status.Conditions.MarkTrue(condType, corev1.OpenStackControlPlaneExposeServiceReadyMessage, owner.GetName()) + } - owner := metav1.GetControllerOf(&r.ObjectMeta) - - // Delete the route if the service was changed not to expose a route - if svc.ObjectMeta.Annotations[service.AnnotationIngressCreateKey] == "false" && - r.Spec.To.Name == svc.Name && - owner != nil && owner.UID == instance.GetUID() { - // Delete any other owner refs from ref list to not block deletion until owners are gone - r.SetOwnerReferences([]metav1.OwnerReference{instanceRef}) - - // Delete route - err := helper.GetClient().Delete(ctx, &r) - if err != nil && !k8s_errors.IsNotFound(err) { - err = fmt.Errorf("Error deleting route %s: %w", r.Name, err) - return svcOverrides, ctrl.Result{}, err - } - - if svcOverride.EndpointURL != nil { - svcOverride.EndpointURL = nil - helper.GetLogger().Info(fmt.Sprintf("Service %s override endpointURL removed", svc.Name)) - } - } - } + svcOverrides[ed.Type] = ed.Service.OverrideSpec + } + + return svcOverrides, ctrl.Result{}, nil +} + +func (ed *EndpointDetails) ensureRoute( + ctx context.Context, + instance *corev1.OpenStackControlPlane, + helper *helper.Helper, + svc *k8s_corev1.Service, + owner metav1.Object, + condType condition.Type, +) (ctrl.Result, error) { + // check if there is already a route with common.AppSelector from the service + if svcLabelVal, ok := svc.Labels[common.AppSelector]; ok { + routes, err := GetRoutesListWithLabel( + ctx, + helper, + instance.Namespace, + map[string]string{common.AppSelector: svcLabelVal}, + ) + if err != nil { + return ctrl.Result{}, err } - // If the service has the create ingress annotation and its a default ClusterIP service -> create route - if svc.ObjectMeta.Annotations[service.AnnotationIngressCreateKey] == "true" && svc.Spec.Type == k8s_corev1.ServiceTypeClusterIP { - if instance.Status.Conditions.Get(condType) == nil { - instance.Status.Conditions.Set(condition.UnknownCondition( - condType, - condition.InitReason, - corev1.OpenStackControlPlaneExposeServiceReadyInitMessage, - owner.GetName(), - svc.Name, - )) + // check the routes if name changed where we are the owner + for _, r := range routes.Items { + instanceRef := metav1.OwnerReference{ + APIVersion: instance.APIVersion, + Kind: instance.Kind, + Name: instance.GetName(), + UID: instance.GetUID(), + BlockOwnerDeletion: ptr.To(true), + Controller: ptr.To(true), } - if svcOverride.EmbeddedLabelsAnnotations == nil { - svcOverride.EmbeddedLabelsAnnotations = &service.EmbeddedLabelsAnnotations{} - } + owner := metav1.GetControllerOf(&r.ObjectMeta) - if labelVal, ok := svcOverride.EmbeddedLabelsAnnotations.Labels[common.AppSelector]; ok { - rd.ServiceLabel = map[string]string{common.AppSelector: labelVal} - } + // Delete the route if the service was changed not to expose a route + if svc.ObjectMeta.Annotations[service.AnnotationIngressCreateKey] == "false" && + r.Spec.To.Name == ed.Name && + owner != nil && owner.UID == instance.GetUID() { + // Delete any other owner refs from ref list to not block deletion until owners are gone + r.SetOwnerReferences([]metav1.OwnerReference{instanceRef}) - ctrlResult, err := rd.CreateRoute(ctx, helper, owner) - if err != nil { - instance.Status.Conditions.Set(condition.FalseCondition( - condType, - condition.ErrorReason, - condition.SeverityWarning, - corev1.OpenStackControlPlaneExposeServiceReadyErrorMessage, - owner.GetName(), - rd.RouteName, - err.Error())) + // Delete route + err := helper.GetClient().Delete(ctx, &r) + if err != nil && !k8s_errors.IsNotFound(err) { + err = fmt.Errorf("Error deleting route %s: %w", r.Name, err) + return ctrl.Result{}, err + } - return svcOverrides, ctrlResult, err - } else if (ctrlResult != ctrl.Result{}) { - return svcOverrides, ctrlResult, nil + if ed.Service.OverrideSpec.EndpointURL != nil { + ed.Service.OverrideSpec.EndpointURL = nil + helper.GetLogger().Info(fmt.Sprintf("Service %s override endpointURL removed", svc.Name)) + } } + } + } - cleanCondition = false + // If the service has the create ingress annotation and its a default ClusterIP service -> create route + if svc.ObjectMeta.Annotations[service.AnnotationIngressCreateKey] == "true" && svc.Spec.Type == k8s_corev1.ServiceTypeClusterIP { + if instance.Status.Conditions.Get(condType) == nil { + instance.Status.Conditions.Set(condition.UnknownCondition( + condType, + condition.InitReason, + corev1.OpenStackControlPlaneExposeServiceReadyInitMessage, + owner.GetName(), + svc.Name, + )) + } + if ed.Service.OverrideSpec.EmbeddedLabelsAnnotations == nil { + ed.Service.OverrideSpec.EmbeddedLabelsAnnotations = &service.EmbeddedLabelsAnnotations{} + } - // update override for the service with the route endpoint url - if rd.endpointURL != "" { - // Any trailing path will be added on the service-operator level. - svcOverride.EndpointURL = &rd.endpointURL - instance.Status.Conditions.MarkTrue(condType, corev1.OpenStackControlPlaneExposeServiceReadyMessage, owner.GetName()) - } + if labelVal, ok := ed.Service.OverrideSpec.EmbeddedLabelsAnnotations.Labels[common.AppSelector]; ok { + ed.Labels = map[string]string{common.AppSelector: labelVal} } - svcOverrides[rd.Endpoint] = svcOverride - } + ctrlResult, err := ed.CreateRoute(ctx, helper, owner) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condType, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneExposeServiceReadyErrorMessage, + owner.GetName(), + ed.Name, + err.Error())) + + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } - if cleanCondition { + return ctrl.Result{}, nil + } else { instance.Status.Conditions.Remove(condType) } - return svcOverrides, ctrl.Result{}, nil + return ctrl.Result{}, nil } // CreateRoute - -func (rd *RouteDetails) CreateRoute( +func (ed *EndpointDetails) CreateRoute( ctx context.Context, helper *helper.Helper, owner metav1.Object, ) (ctrl.Result, error) { - // TODO TLS - route, err := route.NewRoute( + // initialize the route with any custom provided route override + enptRoute, err := route.NewRoute( route.GenericRoute(&route.GenericRouteDetails{ - Name: rd.RouteName, - Namespace: rd.Namespace, - Labels: rd.ServiceLabel, - ServiceName: rd.ServiceSpec.Name, - TargetPortName: rd.ServiceSpec.Name, + Name: ed.Name, + Namespace: ed.Namespace, + Labels: ed.Labels, + ServiceName: ed.Service.Spec.Name, + TargetPortName: ed.Service.Spec.Name, }), time.Duration(5)*time.Second, - rd.RouteOverrideSpec, + []route.OverrideSpec{ed.Route.OverrideSpec}, ) if err != nil { return ctrl.Result{}, err } - route.OwnerReferences = append(route.OwnerReferences, owner) + enptRoute.OwnerReferences = append(enptRoute.OwnerReferences, owner) + + // if route TLS is disabled -> create the route + // if TLS is enabled and the route does not yet exist -> create the route + // to get the hostname for creating the cert + serviceRoute := &routev1.Route{} + err = helper.GetClient().Get(ctx, types.NamespacedName{Name: ed.Name, Namespace: ed.Namespace}, serviceRoute) + if (ed.TLS.PublicEndpoint && err != nil && k8s_errors.IsNotFound(err)) || !ed.TLS.PublicEndpoint { + ctrlResult, err := enptRoute.CreateOrPatch(ctx, helper) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } - ctrlResult, err := route.CreateOrPatch(ctx, helper) - if err != nil { - return ctrlResult, err - } else if (ctrlResult != ctrl.Result{}) { - return ctrlResult, nil + ed.Hostname = ptr.To(enptRoute.GetHostname()) + } else if err != nil { + return ctrl.Result{}, err + } else { + ed.Hostname = &serviceRoute.Spec.Host } - rd.hostname = ptr.To(route.GetHostname()) - rd.endpointURL = "http://" + *rd.hostname - rd.route = route.GetRoute() + // if the issuer is provided TLS is enabled + if ed.TLS.Issuer != nil { + //create the cert + certSecret, ctrlResult, err := certmanager.EnsureCert( + ctx, + helper, + *ed.TLS.Issuer, + ed.Name, + nil, + []string{*ed.Hostname}, ed.Labels) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + // create default TLS route override + tlsConfig := &routev1.TLSConfig{ + Termination: routev1.TLSTerminationEdge, + Certificate: string(certSecret.Data["tls.crt"]), + Key: string(certSecret.Data["tls.key"]), + CACertificate: string(certSecret.Data["ca.crt"]), + InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect, + } + // for TLSE use routev1.TLSTerminationReencrypt + if ed.TLS.InternalEndpoint { + tlsConfig.Termination = routev1.TLSTerminationReencrypt + tlsConfig.DestinationCACertificate = ed.TLS.InternalCA + } + + enptRoute, err = route.NewRoute( + enptRoute.GetRoute(), + time.Duration(5)*time.Second, + []route.OverrideSpec{ + { + Spec: &route.Spec{ + TLS: tlsConfig, + }, + }, + ed.Route.OverrideSpec, + }, + ) + if err != nil { + return ctrl.Result{}, err + } + + ctrlResult, err = enptRoute.CreateOrPatch(ctx, helper) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + ed.EndpointURL = "https://" + *ed.Hostname + } else { + ed.EndpointURL = "http://" + *ed.Hostname + } return ctrl.Result{}, nil } diff --git a/pkg/openstack/glance.go b/pkg/openstack/glance.go index 447235f64..eec84b8d9 100644 --- a/pkg/openstack/glance.go +++ b/pkg/openstack/glance.go @@ -72,7 +72,7 @@ func ReconcileGlance(ctx context.Context, instance *corev1beta1.OpenStackControl } var ctrlResult reconcile.Result - serviceOverrides, ctrlResult, err = EnsureRoute( + serviceOverrides, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/heat.go b/pkg/openstack/heat.go index 033699b2d..5b46ec379 100644 --- a/pkg/openstack/heat.go +++ b/pkg/openstack/heat.go @@ -77,7 +77,7 @@ func ReconcileHeat(ctx context.Context, instance *corev1beta1.OpenStackControlPl } var ctrlResult reconcile.Result - instance.Spec.Heat.Template.HeatAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Heat.Template.HeatAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, @@ -107,7 +107,7 @@ func ReconcileHeat(ctx context.Context, instance *corev1beta1.OpenStackControlPl } var ctrlResult reconcile.Result - instance.Spec.Heat.Template.HeatCfnAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Heat.Template.HeatCfnAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/horizon.go b/pkg/openstack/horizon.go index ce61ed5d7..00002e758 100644 --- a/pkg/openstack/horizon.go +++ b/pkg/openstack/horizon.go @@ -79,7 +79,7 @@ func ReconcileHorizon(ctx context.Context, instance *corev1beta1.OpenStackContro } var ctrlResult reconcile.Result - serviceOverrides, ctrlResult, err = EnsureRoute( + serviceOverrides, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/ironic.go b/pkg/openstack/ironic.go index 44d0f5d74..1acd5e2c6 100644 --- a/pkg/openstack/ironic.go +++ b/pkg/openstack/ironic.go @@ -77,7 +77,7 @@ func ReconcileIronic(ctx context.Context, instance *corev1beta1.OpenStackControl } var ctrlResult reconcile.Result - instance.Spec.Ironic.Template.IronicAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Ironic.Template.IronicAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, @@ -107,7 +107,7 @@ func ReconcileIronic(ctx context.Context, instance *corev1beta1.OpenStackControl } var ctrlResult reconcile.Result - instance.Spec.Ironic.Template.IronicInspector.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Ironic.Template.IronicInspector.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/keystone.go b/pkg/openstack/keystone.go index 4aa507c12..c4b9dadad 100644 --- a/pkg/openstack/keystone.go +++ b/pkg/openstack/keystone.go @@ -68,7 +68,7 @@ func ReconcileKeystoneAPI(ctx context.Context, instance *corev1beta1.OpenStackCo } var ctrlResult reconcile.Result - instance.Spec.Keystone.Template.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Keystone.Template.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/manila.go b/pkg/openstack/manila.go index 735a804fc..5d570ebec 100644 --- a/pkg/openstack/manila.go +++ b/pkg/openstack/manila.go @@ -69,7 +69,7 @@ func ReconcileManila(ctx context.Context, instance *corev1beta1.OpenStackControl } var ctrlResult reconcile.Result - instance.Spec.Manila.Template.ManilaAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Manila.Template.ManilaAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/neutron.go b/pkg/openstack/neutron.go index 7a15f0e26..0825f496d 100644 --- a/pkg/openstack/neutron.go +++ b/pkg/openstack/neutron.go @@ -68,7 +68,7 @@ func ReconcileNeutron(ctx context.Context, instance *corev1beta1.OpenStackContro } var ctrlResult reconcile.Result - instance.Spec.Neutron.Template.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Neutron.Template.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/nova.go b/pkg/openstack/nova.go index 58ebf5e1f..701022b88 100644 --- a/pkg/openstack/nova.go +++ b/pkg/openstack/nova.go @@ -105,7 +105,7 @@ func ReconcileNova(ctx context.Context, instance *corev1beta1.OpenStackControlPl } var ctrlResult reconcile.Result - instance.Spec.Nova.Template.APIServiceTemplate.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Nova.Template.APIServiceTemplate.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, @@ -147,7 +147,7 @@ func ReconcileNova(ctx context.Context, instance *corev1beta1.OpenStackControlPl } var ctrlResult reconcile.Result - routedOverrideSpec, ctrlResult, err := EnsureRoute( + routedOverrideSpec, ctrlResult, err := EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/octavia.go b/pkg/openstack/octavia.go index 6715197f1..5ebded59b 100644 --- a/pkg/openstack/octavia.go +++ b/pkg/openstack/octavia.go @@ -84,7 +84,7 @@ func ReconcileOctavia(ctx context.Context, instance *corev1beta1.OpenStackContro } var ctrlResult reconcile.Result - instance.Spec.Octavia.Template.OctaviaAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Octavia.Template.OctaviaAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/openstackclient.go b/pkg/openstack/openstackclient.go index 2481daab4..588027149 100644 --- a/pkg/openstack/openstackclient.go +++ b/pkg/openstack/openstackclient.go @@ -43,10 +43,11 @@ func ReconcileOpenStackClient(ctx context.Context, instance *corev1.OpenStackCon helper.GetLogger().Info("Reconciling OpenStackClient", "OpenStackClient.Namespace", instance.Namespace, "OpenStackClient.Name", openstackclient.Name) op, err := controllerutil.CreateOrPatch(ctx, helper.GetClient(), openstackclient, func() error { - // the following are created/owned by keystoneclient - openstackclient.Spec.OpenStackConfigMap = "openstack-config" - openstackclient.Spec.OpenStackConfigSecret = "openstack-config-secret" - openstackclient.Spec.NodeSelector = instance.Spec.NodeSelector + instance.Spec.OpenStackClient.Template.DeepCopyInto(&openstackclient.Spec) + + if instance.Spec.TLS.PublicEndpoints.Enabled || instance.Spec.TLS.InternalEndpoints.Enabled { + openstackclient.Spec.Ca.CaSecretName = CombinedCASecret + } err := controllerutil.SetControllerReference(helper.GetBeforeObject(), openstackclient, helper.GetScheme()) if err != nil { diff --git a/pkg/openstack/placement.go b/pkg/openstack/placement.go index 388d5a1dd..1c66971e9 100644 --- a/pkg/openstack/placement.go +++ b/pkg/openstack/placement.go @@ -67,7 +67,7 @@ func ReconcilePlacementAPI(ctx context.Context, instance *corev1beta1.OpenStackC } var ctrlResult reconcile.Result - instance.Spec.Placement.Template.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Placement.Template.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/swift.go b/pkg/openstack/swift.go index 006c3a50c..b4d98b262 100644 --- a/pkg/openstack/swift.go +++ b/pkg/openstack/swift.go @@ -68,7 +68,7 @@ func ReconcileSwift(ctx context.Context, instance *corev1beta1.OpenStackControlP } var ctrlResult reconcile.Result - instance.Spec.Swift.Template.SwiftProxy.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Swift.Template.SwiftProxy.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstackclient/funcs.go b/pkg/openstackclient/funcs.go index cf9fd7044..2bbe45132 100644 --- a/pkg/openstackclient/funcs.go +++ b/pkg/openstackclient/funcs.go @@ -13,10 +13,17 @@ limitations under the License. package openstackclient import ( + env "github.com/openstack-k8s-operators/lib-common/modules/common/env" clientv1 "github.com/openstack-k8s-operators/openstack-operator/apis/client/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + +const ( + // ServiceCommand - + ServiceCommand = "sudo -E /usr/local/bin/kolla_set_configs && sudo -E /usr/local/bin/kolla_start" ) // ClientPod func @@ -24,7 +31,6 @@ func ClientPod( instance *clientv1.OpenStackClient, labels map[string]string, configHash string, - secretHash string, ) *corev1.Pod { clientPod := &corev1.Pod{ @@ -34,55 +40,60 @@ func ClientPod( }, } - var terminationGracePeriodSeconds int64 = 0 - runAsUser := int64(42401) - runAsGroup := int64(42401) + envVars := map[string]env.Setter{} + envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") + envVars["OS_CLOUD"] = env.SetValue("default") + envVars["CONFIG_HASH"] = env.SetValue(configHash) + clientPod.ObjectMeta = metav1.ObjectMeta{ Name: instance.Name, Namespace: instance.Namespace, Labels: labels, } - clientPod.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds + clientPod.Spec.TerminationGracePeriodSeconds = ptr.To[int64](0) clientPod.Spec.ServiceAccountName = instance.RbacResourceName() - clientPod.Spec.Containers = []corev1.Container{ - { - Name: "openstackclient", - Image: instance.Spec.ContainerImage, - Command: []string{"/bin/sleep"}, - Args: []string{"infinity"}, - SecurityContext: &corev1.SecurityContext{ - RunAsUser: &runAsUser, - RunAsGroup: &runAsGroup, + clientContainer := corev1.Container{ + Name: "openstackclient", + Image: instance.Spec.ContainerImage, + Command: []string{ + "/bin/bash", + }, + Args: []string{"-c", ServiceCommand}, + SecurityContext: &corev1.SecurityContext{ + RunAsUser: ptr.To[int64](42401), + RunAsGroup: ptr.To[int64](42401), + }, + Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), + VolumeMounts: []corev1.VolumeMount{ + { + Name: "openstack-config", + MountPath: "/var/lib/config-data/clouds.yaml", + SubPath: "clouds.yaml", }, - Env: []corev1.EnvVar{ - { - Name: "OS_CLOUD", - Value: "default", - }, - { - Name: "CONFIG_HASH", - Value: configHash, - }, - { - Name: "SECRET_HASH", - Value: secretHash, - }, + { + Name: "openstack-config-secret", + MountPath: "/var/lib/config-data/secure.yaml", + SubPath: "secure.yaml", }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "openstack-config", - MountPath: "/etc/openstack/clouds.yaml", - SubPath: "clouds.yaml", - }, - { - Name: "openstack-config-secret", - MountPath: "/etc/openstack/secure.yaml", - SubPath: "secure.yaml", - }, + { + Name: "config-data", + MountPath: "/var/lib/kolla/config_files/config.json", + SubPath: "config.json", + ReadOnly: true, }, }, } - clientPod.Spec.Volumes = clientPodVolumes(instance, labels, configHash, secretHash) + if instance.Spec.CaSecretName != "" { + clientContainer.VolumeMounts = append(clientContainer.VolumeMounts, + corev1.VolumeMount{ + Name: "ca", + MountPath: "/var/lib/config-data/ca-certificates", + }) + } + + clientPod.Spec.Containers = []corev1.Container{clientContainer} + + clientPod.Spec.Volumes = clientPodVolumes(instance, labels) if instance.Spec.NodeSelector != nil && len(instance.Spec.NodeSelector) > 0 { clientPod.Spec.NodeSelector = instance.Spec.NodeSelector } @@ -93,11 +104,8 @@ func ClientPod( func clientPodVolumes( instance *clientv1.OpenStackClient, labels map[string]string, - configHash string, - secretHash string, ) []corev1.Volume { - - return []corev1.Volume{ + volumes := []corev1.Volume{ { Name: "openstack-config", VolumeSource: corev1.VolumeSource{ @@ -116,6 +124,29 @@ func clientPodVolumes( }, }, }, + { + Name: "config-data", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: instance.Name + "-config-data", + }, + }, + }, + }, + } + + if instance.Spec.CaSecretName != "" { + volumes = append(volumes, + corev1.Volume{ + Name: "ca", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: instance.Spec.CaSecretName, + }, + }, + }) } + return volumes } diff --git a/templates/openstackclient/config/config.json b/templates/openstackclient/config/config.json new file mode 100644 index 000000000..4f818bc34 --- /dev/null +++ b/templates/openstackclient/config/config.json @@ -0,0 +1,25 @@ +{ + "command": "/bin/sleep infinity", + "config_files": [ + { + "source": "/var/lib/config-data/clouds.yaml", + "dest": "/home/cloud-admin/.config/openstack/clouds.yaml", + "owner": "cloud-admin", + "perm": "0644" + }, + { + "source": "/var/lib/config-data/secure.yaml", + "dest": "/home/cloud-admin/.config/openstack/secure.yaml", + "owner": "cloud-admin", + "perm": "0644" + }, + { + "source": "/var/lib/config-data/ca/*", + "dest": "/etc/pki/ca-trust/source/anchors/", + "owner": "root", + "perm": "0644", + "merge": true, + "optional": true + } + ] +}