diff --git a/api/bases/nova.openstack.org_novaapis.yaml b/api/bases/nova.openstack.org_novaapis.yaml index f2755b768..c587d6921 100644 --- a/api/bases/nova.openstack.org_novaapis.yaml +++ b/api/bases/nova.openstack.org_novaapis.yaml @@ -113,6 +113,13 @@ spec: in /etc/ . type: object keystoneAuthURL: + description: KeystoneAuthURL configures the keystone API endpoint + to be used by the service for authentication and authorization + type: string + keystonePublicAuthURL: + description: KeystonePublicAuthURL configures the public keystone + API endpoint. This can be different from KeystoneAuthURL. The service + uses this value to redirect unauthenticated users. type: string networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource @@ -383,6 +390,7 @@ spec: - apiDatabaseHostname - cell0DatabaseHostname - keystoneAuthURL + - keystonePublicAuthURL - registeredCells - secret - serviceAccount diff --git a/api/v1beta1/novaapi_types.go b/api/v1beta1/novaapi_types.go index 51edc35b8..7a3b639a4 100644 --- a/api/v1beta1/novaapi_types.go +++ b/api/v1beta1/novaapi_types.go @@ -100,8 +100,16 @@ type NovaAPISpec struct { ServiceUser string `json:"serviceUser"` // +kubebuilder:validation:Required + // KeystoneAuthURL configures the keystone API endpoint to be used + // by the service for authentication and authorization KeystoneAuthURL string `json:"keystoneAuthURL"` + // +kubebuilder:validation:Required + // KeystonePublicAuthURL configures the public keystone API endpoint. This + // can be different from KeystoneAuthURL. The service uses this value + // to redirect unauthenticated users. + KeystonePublicAuthURL string `json:"keystonePublicAuthURL"` + // +kubebuilder:validation:Optional // +kubebuilder:default="nova_api" // APIDatabaseUser - username to use when accessing the API DB diff --git a/config/crd/bases/nova.openstack.org_novaapis.yaml b/config/crd/bases/nova.openstack.org_novaapis.yaml index f2755b768..c587d6921 100644 --- a/config/crd/bases/nova.openstack.org_novaapis.yaml +++ b/config/crd/bases/nova.openstack.org_novaapis.yaml @@ -113,6 +113,13 @@ spec: in /etc/ . type: object keystoneAuthURL: + description: KeystoneAuthURL configures the keystone API endpoint + to be used by the service for authentication and authorization + type: string + keystonePublicAuthURL: + description: KeystonePublicAuthURL configures the public keystone + API endpoint. This can be different from KeystoneAuthURL. The service + uses this value to redirect unauthenticated users. type: string networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource @@ -383,6 +390,7 @@ spec: - apiDatabaseHostname - cell0DatabaseHostname - keystoneAuthURL + - keystonePublicAuthURL - registeredCells - secret - serviceAccount diff --git a/controllers/nova_controller.go b/controllers/nova_controller.go index 63cca6500..b95f607d7 100644 --- a/controllers/nova_controller.go +++ b/controllers/nova_controller.go @@ -222,7 +222,8 @@ func (r *NovaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (resul return ctrl.Result{}, nil } - keystoneAuthURL, err := r.getKeystoneAuthURL(ctx, h, instance) + keystoneInternalAuthURL, keystonePublicAuthURL, err := r.getKeystoneAuthURL( + ctx, h, instance) if err != nil { return ctrl.Result{}, err } @@ -430,7 +431,7 @@ func (r *NovaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (resul cell, status, err := r.ensureCell( ctx, h, instance, cellName, cellTemplate, cellDB.Database, apiDB, cellMQ.TransportURL, - keystoneAuthURL, secret, + keystoneInternalAuthURL, secret, ) cells[cellName] = cell switch status { @@ -497,7 +498,8 @@ func (r *NovaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (resul result, err = r.ensureAPI( ctx, h, instance, cell0Template, - cellDBs[novav1.Cell0Name].Database, apiDB, keystoneAuthURL, + cellDBs[novav1.Cell0Name].Database, apiDB, + keystoneInternalAuthURL, keystonePublicAuthURL, topLevelSecretName, ) if err != nil { @@ -506,7 +508,7 @@ func (r *NovaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (resul result, err = r.ensureScheduler( ctx, h, instance, cell0Template, - cellDBs[novav1.Cell0Name].Database, apiDB, keystoneAuthURL, + cellDBs[novav1.Cell0Name].Database, apiDB, keystoneInternalAuthURL, topLevelSecretName, ) if err != nil { @@ -516,7 +518,7 @@ func (r *NovaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (resul if *instance.Spec.MetadataServiceTemplate.Enabled { result, err = r.ensureMetadata( ctx, h, instance, cell0Template, - cellDBs[novav1.Cell0Name].Database, apiDB, keystoneAuthURL, + cellDBs[novav1.Cell0Name].Database, apiDB, keystoneInternalAuthURL, topLevelSecretName, ) if err != nil { @@ -815,7 +817,8 @@ func (r *NovaReconciler) ensureAPI( cell0Template novav1.NovaCellTemplate, cell0DB *database.Database, apiDB *database.Database, - keystoneAuthURL string, + keystoneInternalAuthURL string, + keystonePublicAuthURL string, secretName string, ) (ctrl.Result, error) { // TODO(gibi): Pass down a narrowed secret that only hold @@ -836,11 +839,12 @@ func (r *NovaReconciler) ensureAPI( Resources: instance.Spec.APIServiceTemplate.Resources, NetworkAttachments: instance.Spec.APIServiceTemplate.NetworkAttachments, }, - Override: instance.Spec.APIServiceTemplate.Override, - KeystoneAuthURL: keystoneAuthURL, - ServiceUser: instance.Spec.ServiceUser, - ServiceAccount: instance.RbacResourceName(), - RegisteredCells: instance.Status.RegisteredCells, + Override: instance.Spec.APIServiceTemplate.Override, + KeystoneAuthURL: keystoneInternalAuthURL, + KeystonePublicAuthURL: keystonePublicAuthURL, + ServiceUser: instance.Spec.ServiceUser, + ServiceAccount: instance.RbacResourceName(), + RegisteredCells: instance.Status.RegisteredCells, } api := &novav1.NovaAPI{ ObjectMeta: metav1.ObjectMeta{ @@ -1067,22 +1071,29 @@ func (r *NovaReconciler) getKeystoneAuthURL( ctx context.Context, h *helper.Helper, instance *novav1.Nova, -) (string, error) { +) (string, string, error) { // TODO(gibi): change lib-common to take the name of the KeystoneAPI as // parameter instead of labels. Then use instance.Spec.KeystoneInstance as // the name. keystoneAPI, err := keystonev1.GetKeystoneAPI(ctx, h, instance.Namespace, map[string]string{}) if err != nil { - return "", err + return "", "", err } // NOTE(gibi): we use the internal endpoint as that is expected to be // available on the external compute nodes as well and we want to keep // thing consistent - authURL, err := keystoneAPI.GetEndpoint(endpoint.EndpointInternal) + internalAuthURL, err := keystoneAPI.GetEndpoint(endpoint.EndpointInternal) + if err != nil { + return "", "", err + } + // NOTE(gibi): but there is one case the www_authenticate_uri of nova-api + // the we need to configure the public keystone endpoint + publicAuthURL, err := keystoneAPI.GetEndpoint(endpoint.EndpointInternal) if err != nil { - return "", err + return "", "", err } - return authURL, nil + + return internalAuthURL, publicAuthURL, nil } func (r *NovaReconciler) reconcileDelete( diff --git a/controllers/novaapi_controller.go b/controllers/novaapi_controller.go index ad90fa56f..67317c9a3 100644 --- a/controllers/novaapi_controller.go +++ b/controllers/novaapi_controller.go @@ -327,8 +327,11 @@ func (r *NovaAPIReconciler) generateConfigs( ) error { templateParameters := map[string]interface{}{ - "service_name": "nova-api", - "keystone_internal_url": instance.Spec.KeystoneAuthURL, + "service_name": "nova-api", + "keystone_internal_url": instance.Spec.KeystoneAuthURL, + // NOTE(gibi): As per the definition of www_authenticate_uri this + // always needs to point to the public keystone endpoint. + "www_authenticate_uri": instance.Spec.KeystonePublicAuthURL, "nova_keystone_user": instance.Spec.ServiceUser, "nova_keystone_password": string(secret.Data[ServicePasswordSelector]), "api_db_name": instance.Spec.APIDatabaseUser, // fixme diff --git a/templates/nova.conf b/templates/nova.conf index 9430bdeb2..64ea2a582 100644 --- a/templates/nova.conf +++ b/templates/nova.conf @@ -183,7 +183,9 @@ connection = mysql+pymysql://{{ .api_db_user }}:{{ .api_db_password }}@{{ .api_d {{end}} [keystone_authtoken] -www_authenticate_uri = {{ .keystone_internal_url }} +{{ if eq .service_name "nova-api"}} +www_authenticate_uri = {{ .www_authenticate_uri}} +{{end}} auth_url = {{ .keystone_internal_url }} auth_type = password project_domain_name = {{ .default_project_domain }} diff --git a/test/functional/base_test.go b/test/functional/base_test.go index dde6d8d39..357585de4 100644 --- a/test/functional/base_test.go +++ b/test/functional/base_test.go @@ -46,7 +46,8 @@ func GetDefaultNovaAPISpec(novaNames NovaNames) map[string]interface{} { "secret": novaNames.InternalTopLevelSecretName.Name, "apiDatabaseHostname": "nova-api-db-hostname", "cell0DatabaseHostname": "nova-cell0-db-hostname", - "keystoneAuthURL": "keystone-auth-url", + "keystoneAuthURL": "keystone-internal-auth-url", + "keystonePublicAuthURL": "keystone-public-auth-url", "containerImage": ContainerImage, "serviceAccount": "nova", "registeredCells": map[string]string{}, diff --git a/test/functional/novaapi_controller_test.go b/test/functional/novaapi_controller_test.go index 8ec75a794..32b5aae0c 100644 --- a/test/functional/novaapi_controller_test.go +++ b/test/functional/novaapi_controller_test.go @@ -179,6 +179,8 @@ var _ = Describe("NovaAPI controller", func() { Expect(configData).Should(ContainSubstring("service_token_roles_required = true")) Expect(configData).Should(ContainSubstring("enabled_apis=osapi_compute")) Expect(configData).Should(ContainSubstring("osapi_compute_workers=1")) + Expect(configData).Should(ContainSubstring("auth_url = keystone-internal-auth-url")) + Expect(configData).Should(ContainSubstring("www_authenticate_uri = keystone-public-auth-url")) Expect(configDataMap.Data).Should(HaveKey("02-nova-override.conf")) extraData := string(configDataMap.Data["02-nova-override.conf"]) Expect(extraData).To(Equal("foo=bar"))