diff --git a/controllers/designate_controller.go b/controllers/designate_controller.go index f1ceeee8..1e482120 100644 --- a/controllers/designate_controller.go +++ b/controllers/designate_controller.go @@ -35,10 +35,8 @@ import ( designatev1beta1 "github.com/openstack-k8s-operators/designate-operator/api/v1beta1" "github.com/openstack-k8s-operators/designate-operator/pkg/designate" rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" - keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" "github.com/openstack-k8s-operators/lib-common/modules/common" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" - "github.com/openstack-k8s-operators/lib-common/modules/common/endpoint" "github.com/openstack-k8s-operators/lib-common/modules/common/env" "github.com/openstack-k8s-operators/lib-common/modules/common/helper" "github.com/openstack-k8s-operators/lib-common/modules/common/job" @@ -46,6 +44,7 @@ import ( nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" common_rbac "github.com/openstack-k8s-operators/lib-common/modules/common/rbac" oko_secret "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" @@ -224,6 +223,23 @@ func (r *DesignateReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return r.reconcileNormal(ctx, instance, helper) } +// fields to index to reconcile when change +const ( + passwordSecretField = ".spec.secret" + caBundleSecretNameField = ".spec.tls.caBundleSecretName" + tlsAPIInternalField = ".spec.tls.api.internal.secretName" + tlsAPIPublicField = ".spec.tls.api.public.secretName" +) + +var ( + allWatchFields = []string{ + passwordSecretField, + caBundleSecretNameField, + tlsAPIInternalField, + tlsAPIPublicField, + } +) + // SetupWithManager sets up the controller with the Manager. func (r *DesignateReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { // transportURLSecretFn - Watch for changes made to the secret associated with the RabbitMQ @@ -668,6 +684,8 @@ func (r *DesignateReconciler) reconcileNormal(ctx context.Context, instance *des instance.Spec.DesignateAPI.NetworkAttachments, err) } + instance.Status.Conditions.MarkTrue(condition.NetworkAttachmentsReadyCondition, condition.NetworkAttachmentsReadyMessage) + // Handle service init ctrlResult, err := r.reconcileInit(ctx, instance, helper, serviceLabels, serviceAnnotations) if err != nil { @@ -1044,11 +1062,19 @@ func (r *DesignateReconciler) generateServiceConfigMaps( cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), map[string]string{}) + var tlsCfg *tls.Service + if instance.Spec.DesignateAPI.TLS.Ca.CaBundleSecretName != "" { + tlsCfg = &tls.Service{} + } + // customData hold any customization for the service. // custom.conf is going to /etc//.conf.d // all other files get placed into /etc/ to allow overwrite of e.g. policy.json // TODO: make sure custom.conf can not be overwritten - customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig} + customData := map[string]string{ + common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig, + "my.cnf": designateDb.GetDatabaseClientConfig(tlsCfg), //(oschwart) for now just get the default my.cnf + } for key, data := range instance.Spec.DefaultConfigOverwrite { customData[key] = data @@ -1057,29 +1083,18 @@ func (r *DesignateReconciler) generateServiceConfigMaps( databaseAccount := designateDb.GetAccount() dbSecret := designateDb.GetSecret() - keystoneAPI, err := keystonev1.GetKeystoneAPI(ctx, h, instance.Namespace, map[string]string{}) - if err != nil { - return err - } - keystoneInternalURL, err := keystoneAPI.GetEndpoint(endpoint.EndpointInternal) - if err != nil { - return err + // We only need a minimal 00-config.conf that is only used by db-sync job, + // hence only passing the database related parameters + templateParameters := map[string]interface{}{ + "MinimalConfig": true, // This tells the template to generate a minimal config + "DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s?read_default_file=/etc/my.cnf", + databaseAccount.Spec.UserName, + string(dbSecret.Data[mariadbv1.DatabasePasswordSelector]), + instance.Status.DatabaseHostname, + designate.DatabaseName, + ), } - keystonePublicURL, err := keystoneAPI.GetEndpoint(endpoint.EndpointPublic) - if err != nil { - return err - } - - templateParameters := make(map[string]interface{}) templateParameters["ServiceUser"] = instance.Spec.ServiceUser - templateParameters["KeystoneInternalURL"] = keystoneInternalURL - templateParameters["KeystonePublicURL"] = keystonePublicURL - templateParameters["DatabaseConnection"] = fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s?read_default_file=/etc/my.cnf", - databaseAccount.Spec.UserName, - string(dbSecret.Data[mariadbv1.DatabasePasswordSelector]), - instance.Status.DatabaseHostname, - designate.DatabaseName, - ) cms := []util.Template{ // ScriptsConfigMap @@ -1103,7 +1118,7 @@ func (r *DesignateReconciler) generateServiceConfigMaps( }, } - err = oko_secret.EnsureSecrets(ctx, h, instance, cms, envVars) + err := oko_secret.EnsureSecrets(ctx, h, instance, cms, envVars) if err != nil { return err } @@ -1175,6 +1190,7 @@ func (r *DesignateReconciler) apiDeploymentCreateOrUpdate(ctx context.Context, i deployment.Spec.DatabaseAccount = instance.Spec.DatabaseAccount deployment.Spec.Secret = instance.Spec.Secret deployment.Spec.ServiceAccount = instance.RbacResourceName() + deployment.Spec.TLS = instance.Spec.DesignateAPI.TLS deployment.Spec.TransportURLSecret = instance.Status.TransportURLSecret if len(deployment.Spec.NodeSelector) == 0 { deployment.Spec.NodeSelector = instance.Spec.NodeSelector @@ -1209,6 +1225,7 @@ func (r *DesignateReconciler) centralDeploymentCreateOrUpdate(ctx context.Contex deployment.Spec.Secret = instance.Spec.Secret deployment.Spec.TransportURLSecret = instance.Status.TransportURLSecret deployment.Spec.ServiceAccount = instance.RbacResourceName() + deployment.Spec.TLS = instance.Spec.DesignateAPI.TLS.Ca if len(deployment.Spec.NodeSelector) == 0 { deployment.Spec.NodeSelector = instance.Spec.NodeSelector } @@ -1242,6 +1259,7 @@ func (r *DesignateReconciler) workerDeploymentCreateOrUpdate(ctx context.Context deployment.Spec.Secret = instance.Spec.Secret deployment.Spec.TransportURLSecret = instance.Status.TransportURLSecret deployment.Spec.ServiceAccount = instance.RbacResourceName() + deployment.Spec.TLS = instance.Spec.DesignateAPI.TLS.Ca if len(deployment.Spec.NodeSelector) == 0 { deployment.Spec.NodeSelector = instance.Spec.NodeSelector } @@ -1275,6 +1293,7 @@ func (r *DesignateReconciler) mdnsDaemonSetCreateOrUpdate(ctx context.Context, i daemonset.Spec.Secret = instance.Spec.Secret daemonset.Spec.TransportURLSecret = instance.Status.TransportURLSecret daemonset.Spec.ServiceAccount = instance.RbacResourceName() + daemonset.Spec.TLS = instance.Spec.DesignateAPI.TLS.Ca if len(daemonset.Spec.NodeSelector) == 0 { daemonset.Spec.NodeSelector = instance.Spec.NodeSelector } @@ -1308,6 +1327,7 @@ func (r *DesignateReconciler) producerDeploymentCreateOrUpdate(ctx context.Conte deployment.Spec.Secret = instance.Spec.Secret deployment.Spec.TransportURLSecret = instance.Status.TransportURLSecret deployment.Spec.ServiceAccount = instance.RbacResourceName() + deployment.Spec.TLS = instance.Spec.DesignateAPI.TLS.Ca if len(deployment.Spec.NodeSelector) == 0 { deployment.Spec.NodeSelector = instance.Spec.NodeSelector } diff --git a/controllers/designateapi_controller.go b/controllers/designateapi_controller.go index 14c11a2f..fb105a2f 100644 --- a/controllers/designateapi_controller.go +++ b/controllers/designateapi_controller.go @@ -25,13 +25,18 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/go-logr/logr" @@ -48,8 +53,9 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/helper" "github.com/openstack-k8s-operators/lib-common/modules/common/labels" nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" - "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + oko_secret "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/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" ) @@ -181,6 +187,7 @@ func (r *DesignateAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request condition.UnknownCondition(condition.KeystoneServiceReadyCondition, condition.InitReason, ""), condition.UnknownCondition(condition.KeystoneEndpointReadyCondition, condition.InitReason, ""), condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), + condition.UnknownCondition(condition.TLSInputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), ) instance.Status.Conditions.Init(&cl) @@ -215,6 +222,52 @@ func (r *DesignateAPIReconciler) SetupWithManager(ctx context.Context, mgr ctrl. // Watch for changes to any CustomServiceConfigSecrets. Global secrets // (e.g. TransportURLSecret) are handled by the top designate controller. Log := r.GetLogger(ctx) + if err := mgr.GetFieldIndexer().IndexField(ctx, &designatev1beta1.DesignateAPI{}, passwordSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateAPI) + if cr.Spec.Secret == "" { + return nil + } + return []string{cr.Spec.Secret} + }); err != nil { + return err + } + + // index caBundleSecretNameField + if err := mgr.GetFieldIndexer().IndexField(ctx, &designatev1beta1.DesignateAPI{}, caBundleSecretNameField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateAPI) + if cr.Spec.TLS.CaBundleSecretName == "" { + return nil + } + return []string{cr.Spec.TLS.CaBundleSecretName} + }); err != nil { + return err + } + + // index tlsAPIInternalField + if err := mgr.GetFieldIndexer().IndexField(ctx, &designatev1beta1.DesignateAPI{}, tlsAPIInternalField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateAPI) + if cr.Spec.TLS.API.Internal.SecretName == nil { + return nil + } + return []string{*cr.Spec.TLS.API.Internal.SecretName} + }); err != nil { + return err + } + + // index tlsAPIPublicField + if err := mgr.GetFieldIndexer().IndexField(ctx, &designatev1beta1.DesignateAPI{}, tlsAPIPublicField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateAPI) + if cr.Spec.TLS.API.Public.SecretName == nil { + return nil + } + return []string{*cr.Spec.TLS.API.Public.SecretName} + }); err != nil { + return err + } svcSecretFn := func(ctx context.Context, o client.Object) []reconcile.Request { var namespace string = o.GetNamespace() @@ -226,7 +279,7 @@ func (r *DesignateAPIReconciler) SetupWithManager(ctx context.Context, mgr ctrl. listOpts := []client.ListOption{ client.InNamespace(namespace), } - if err := r.Client.List(context.Background(), apis, listOpts...); err != nil { + if err := r.Client.List(ctx, apis, listOpts...); err != nil { Log.Error(err, "Unable to retrieve API CRs") return nil } @@ -261,7 +314,7 @@ func (r *DesignateAPIReconciler) SetupWithManager(ctx context.Context, mgr ctrl. listOpts := []client.ListOption{ client.InNamespace(o.GetNamespace()), } - if err := r.Client.List(context.Background(), designateAPIs, listOpts...); err != nil { + if err := r.Client.List(ctx, designateAPIs, listOpts...); err != nil { Log.Error(err, "Unable to retrieve DesignateAPI CRs %v") return nil } @@ -291,11 +344,49 @@ func (r *DesignateAPIReconciler) SetupWithManager(ctx context.Context, mgr ctrl. Owns(&appsv1.Deployment{}). Watches(&corev1.Secret{}, handler.EnqueueRequestsFromMapFunc(svcSecretFn)). + Watches( + &corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). Watches(&networkv1.NetworkAttachmentDefinition{}, handler.EnqueueRequestsFromMapFunc(nadFn)). Complete(r) } +func (r *DesignateAPIReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(ctx).WithName("Controllers").WithName("DesignateAPI") + + for _, field := range allWatchFields { + crList := &designatev1beta1.DesignateAPIList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(field, src.GetName()), + Namespace: src.GetNamespace(), + } + err := r.Client.List(context.TODO(), crList, listOps) + if err != nil { + return []reconcile.Request{} + } + + for _, item := range crList.Items { + l.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace())) + + requests = append(requests, + reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + }, + ) + } + } + + return requests +} + func (r *DesignateAPIReconciler) reconcileDelete(ctx context.Context, instance *designatev1beta1.DesignateAPI, helper *helper.Helper) (ctrl.Result, error) { Log := r.GetLogger(ctx) @@ -452,7 +543,11 @@ func (r *DesignateAPIReconciler) reconcileInit( } // create service - end - // TODO: TLS, pass in https as protocol, create TLS cert + // if TLS is enabled + if instance.Spec.TLS.API.Enabled(endpointType) { + // set endpoint protocol to https + data.Protocol = ptr.To(service.ProtocolHTTPS) + } apiEndpoints[string(endpointType)], err = svc.GetAPIEndpoint( svcOverride.EndpointURL, data.Protocol, data.Path) if err != nil { @@ -553,15 +648,67 @@ func (r *DesignateAPIReconciler) reconcileNormal(ctx context.Context, instance * if err != nil { return ctrlResult, err } + + instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage) + // run check OpenStack secret - end // - // check for required TransportURL secret holding transport URL string + // TLS input validation // - ctrlResult, err = r.getSecret(ctx, helper, instance, instance.Spec.TransportURLSecret, &configMapVars, "secret-") - if err != nil { - return ctrlResult, err + // Validate the CA cert secret if provided + if instance.Spec.TLS.CaBundleSecretName != "" { + hash, ctrlResult, err := tls.ValidateCACertSecret( + ctx, + helper.GetClient(), + types.NamespacedName{ + Name: instance.Spec.TLS.CaBundleSecretName, + Namespace: instance.Namespace, + }, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.TLSInputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.TLSInputErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + if hash != "" { + configMapVars[tls.CABundleKey] = env.SetValue(hash) + } + + // Validate API service certs secrets + certsHash, ctrlResult, err := instance.Spec.TLS.API.ValidateCertSecrets(ctx, helper, instance.Namespace) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.TLSInputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.TLSInputErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + configMapVars[tls.TLSHashName] = env.SetValue(certsHash) } + + // all cert input checks out so report InputReady + instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage) + + // + // check for required TransportURL secret holding transport URL string + // + // ctrlResult, err = r.getSecret(ctx, helper, instance, instance.Spec.TransportURLSecret, &configMapVars, "secret-") + // if err != nil { + // return ctrlResult, err + // } // run check TransportURL secret - end // @@ -607,7 +754,7 @@ func (r *DesignateAPIReconciler) reconcileNormal(ctx context.Context, instance * // // create custom Configmap for this designate volume service // - err = r.generateServiceConfigMaps(ctx, helper, instance, &configMapVars, serviceLabels) + err = r.generateServiceConfigMaps(ctx, helper, instance, &configMapVars) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.ServiceConfigReadyCondition, @@ -704,7 +851,16 @@ func (r *DesignateAPIReconciler) reconcileNormal(ctx context.Context, instance * // // Define a new Deployment object - deplDef := designateapi.Deployment(instance, inputHash, serviceLabels, serviceAnnotations) + deplDef, err := designateapi.Deployment(instance, inputHash, serviceLabels, serviceAnnotations) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.DeploymentReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.DeploymentReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } depl := deployment.NewDeployment( deplDef, time.Duration(5)*time.Second, @@ -812,7 +968,7 @@ func (r *DesignateAPIReconciler) getSecret( envVars *map[string]env.Setter, prefix string, ) (ctrl.Result, error) { - secret, hash, err := secret.GetSecret(ctx, h, secretName, instance.Namespace) + secret, hash, err := oko_secret.GetSecret(ctx, h, secretName, instance.Namespace) if err != nil { if k8s_errors.IsNotFound(err) { h.GetLogger().Info(fmt.Sprintf("Secret %s not found", secretName)) @@ -846,24 +1002,51 @@ func (r *DesignateAPIReconciler) generateServiceConfigMaps( h *helper.Helper, instance *designatev1beta1.DesignateAPI, envVars *map[string]env.Setter, - serviceLabels map[string]string, ) error { + Log := r.GetLogger(ctx) + Log.Info("Generating service config map") + // // create custom Configmap for designate-api-specific config input // - %-config-data configmap holding custom config for the service's designate.conf // - cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), serviceLabels) + cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), map[string]string{}) + + db, err := mariadbv1.GetDatabaseByNameAndAccount(ctx, h, designate.DatabaseName, instance.Spec.DatabaseAccount, instance.Namespace) + if err != nil { + return err + } + var tlsCfg *tls.Service + if instance.Spec.TLS.CaBundleSecretName != "" { + tlsCfg = &tls.Service{} + } // customData hold any customization for the service. // custom.conf is going to be merged into /etc/designate/conder.conf // TODO: make sure custom.conf can not be overwritten - customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig} + customData := map[string]string{ + common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig, + "my.cnf": db.GetDatabaseClientConfig(tlsCfg), //(oschwart) for now just get the default my.cnf + } for key, data := range instance.Spec.DefaultConfigOverwrite { customData[key] = data } + keystoneAPI, err := keystonev1.GetKeystoneAPI(ctx, h, instance.Namespace, map[string]string{}) + if err != nil { + return err + } + keystoneInternalURL, err := keystoneAPI.GetEndpoint(endpoint.EndpointInternal) + if err != nil { + return err + } + keystonePublicURL, err := keystoneAPI.GetEndpoint(endpoint.EndpointPublic) + if err != nil { + return err + } + customData[common.CustomServiceConfigFileName] = instance.Spec.CustomServiceConfig databaseAccount, dbSecret, err := mariadbv1.GetAccountAndSecret( @@ -893,6 +1076,25 @@ func (r *DesignateAPIReconciler) generateServiceConfigMaps( ), } + templateParameters["ServiceUser"] = instance.Spec.ServiceUser + templateParameters["KeystoneInternalURL"] = keystoneInternalURL + templateParameters["KeystonePublicURL"] = keystonePublicURL + + // create httpd vhost template parameters + httpdVhostConfig := map[string]interface{}{} + for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} { + endptConfig := map[string]interface{}{} + endptConfig["ServerName"] = fmt.Sprintf("%s-%s.%s.svc", designate.ServiceName, endpt.String(), instance.Namespace) + endptConfig["TLS"] = false // default TLS to false, and set it bellow to true if enabled + if instance.Spec.TLS.API.Enabled(endpt) { + endptConfig["TLS"] = true + endptConfig["SSLCertificateFile"] = fmt.Sprintf("/etc/pki/tls/certs/%s.crt", endpt.String()) + endptConfig["SSLCertificateKeyFile"] = fmt.Sprintf("/etc/pki/tls/private/%s.key", endpt.String()) + } + httpdVhostConfig[endpt.String()] = endptConfig + } + templateParameters["VHosts"] = httpdVhostConfig + cms := []util.Template{ // ScriptsConfigMap { @@ -915,7 +1117,15 @@ func (r *DesignateAPIReconciler) generateServiceConfigMaps( }, } - return secret.EnsureSecrets(ctx, h, instance, cms, envVars) + err = oko_secret.EnsureSecrets(ctx, h, instance, cms, envVars) + + if err != nil { + Log.Error(err, "unable to process config map") + return err + } + Log.Info("Service config map generated") + + return nil } // createHashOfInputHashes - creates a hash of hashes which gets added to the resources which requires a restart diff --git a/controllers/designatecentral_controller.go b/controllers/designatecentral_controller.go index a4558133..42af744e 100644 --- a/controllers/designatecentral_controller.go +++ b/controllers/designatecentral_controller.go @@ -25,13 +25,17 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/go-logr/logr" @@ -46,6 +50,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/labels" nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" ) @@ -160,6 +165,7 @@ func (r *DesignateCentralReconciler) Reconcile(ctx context.Context, req ctrl.Req condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage), condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyInitMessage), condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), + condition.UnknownCondition(condition.TLSInputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), ) instance.Status.Conditions.Init(&cl) @@ -192,6 +198,30 @@ func (r *DesignateCentralReconciler) SetupWithManager(ctx context.Context, mgr c // (e.g. TransportURLSecret) are handled by the top designate controller. Log := r.GetLogger(ctx) + // index passwordSecretField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &designatev1beta1.DesignateCentral{}, passwordSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateCentral) + if cr.Spec.Secret == "" { + return nil + } + return []string{cr.Spec.Secret} + }); err != nil { + return err + } + + // index caBundleSecretNameField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &designatev1beta1.DesignateCentral{}, caBundleSecretNameField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateCentral) + if cr.Spec.TLS.CaBundleSecretName == "" { + return nil + } + return []string{cr.Spec.TLS.CaBundleSecretName} + }); err != nil { + return err + } + svcSecretFn := func(ctx context.Context, o client.Object) []reconcile.Request { var namespace string = o.GetNamespace() var secretName string = o.GetName() @@ -268,12 +298,50 @@ func (r *DesignateCentralReconciler) SetupWithManager(ctx context.Context, mgr c // watch the secrets we don't own Watches(&corev1.Secret{}, handler.EnqueueRequestsFromMapFunc(svcSecretFn)). + Watches( + &corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). // watch the config CMs we don't own Watches(&corev1.ConfigMap{}, handler.EnqueueRequestsFromMapFunc(configMapFn)). Complete(r) } +func (r *DesignateCentralReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(ctx).WithName("Controllers").WithName("DesignateCentral") + + for _, field := range allWatchFields { + crList := &designatev1beta1.DesignateCentralList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(field, src.GetName()), + Namespace: src.GetNamespace(), + } + err := r.Client.List(context.TODO(), crList, listOps) + if err != nil { + return []reconcile.Request{} + } + + for _, item := range crList.Items { + l.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace())) + + requests = append(requests, + reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + }, + ) + } + } + + return requests +} + func (r *DesignateCentralReconciler) reconcileDelete(ctx context.Context, instance *designatev1beta1.DesignateCentral, helper *helper.Helper) (ctrl.Result, error) { Log := r.GetLogger(ctx) @@ -357,6 +425,38 @@ func (r *DesignateCentralReconciler) reconcileNormal(ctx context.Context, instan instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage) // run check parent Designate CR config maps - end + // + // TLS input validation + // + // Validate the CA cert secret if provided + if instance.Spec.TLS.CaBundleSecretName != "" { + hash, ctrlResult, err := tls.ValidateCACertSecret( + ctx, + helper.GetClient(), + types.NamespacedName{ + Name: instance.Spec.TLS.CaBundleSecretName, + Namespace: instance.Namespace, + }, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.TLSInputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.TLSInputErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + if hash != "" { + configMapVars[tls.CABundleKey] = env.SetValue(hash) + } + } + // all cert input checks out so report InputReady + instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage) + // // Create ConfigMaps required as input for the Service and calculate an overall hash of hashes // @@ -369,7 +469,7 @@ func (r *DesignateCentralReconciler) reconcileNormal(ctx context.Context, instan // // create custom Configmap for this designate volume service // - err = r.generateServiceConfigMaps(ctx, helper, instance, &configMapVars, serviceLabels) + err = r.generateServiceConfigMaps(ctx, helper, instance, &configMapVars) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.ServiceConfigReadyCondition, @@ -609,19 +709,30 @@ func (r *DesignateCentralReconciler) generateServiceConfigMaps( h *helper.Helper, instance *designatev1beta1.DesignateCentral, envVars *map[string]env.Setter, - serviceLabels map[string]string, ) error { // // create custom Configmap for designate-central-specific config input // - %-config-data configmap holding custom config for the service's designate.conf // - cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), serviceLabels) + cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), map[string]string{}) + + db, err := mariadbv1.GetDatabaseByNameAndAccount(ctx, h, designate.DatabaseName, instance.Spec.DatabaseAccount, instance.Namespace) + if err != nil { + return err + } + var tlsCfg *tls.Service + if instance.Spec.TLS.CaBundleSecretName != "" { + tlsCfg = &tls.Service{} + } // customData hold any customization for the service. // custom.conf is going to be merged into /etc/designate/conder.conf // TODO: make sure custom.conf can not be overwritten - customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig} + customData := map[string]string{ + common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig, + "my.cnf": db.GetDatabaseClientConfig(tlsCfg), //(oschwart) for now just get the default my.cnf + } for key, data := range instance.Spec.DefaultConfigOverwrite { customData[key] = data diff --git a/controllers/designatemdns_controller.go b/controllers/designatemdns_controller.go index 740f75a6..2db93d6c 100644 --- a/controllers/designatemdns_controller.go +++ b/controllers/designatemdns_controller.go @@ -24,13 +24,17 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/go-logr/logr" @@ -45,6 +49,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/labels" nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" ) @@ -158,6 +163,7 @@ func (r *DesignateMdnsReconciler) Reconcile(ctx context.Context, req ctrl.Reques condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage), condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyInitMessage), condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), + condition.UnknownCondition(condition.TLSInputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), ) instance.Status.Conditions.Init(&cl) @@ -190,6 +196,30 @@ func (r *DesignateMdnsReconciler) SetupWithManager(ctx context.Context, mgr ctrl // (e.g. TransportURLSecret) are handled by the top designate controller. Log := r.GetLogger(ctx) + // index passwordSecretField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &designatev1beta1.DesignateMdns{}, passwordSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateMdns) + if cr.Spec.Secret == "" { + return nil + } + return []string{cr.Spec.Secret} + }); err != nil { + return err + } + + // index caBundleSecretNameField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &designatev1beta1.DesignateMdns{}, caBundleSecretNameField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateMdns) + if cr.Spec.TLS.CaBundleSecretName == "" { + return nil + } + return []string{cr.Spec.TLS.CaBundleSecretName} + }); err != nil { + return err + } + svcSecretFn := func(ctx context.Context, o client.Object) []reconcile.Request { var namespace string = o.GetNamespace() var secretName string = o.GetName() @@ -266,12 +296,50 @@ func (r *DesignateMdnsReconciler) SetupWithManager(ctx context.Context, mgr ctrl // watch the secrets we don't own Watches(&corev1.Secret{}, handler.EnqueueRequestsFromMapFunc(svcSecretFn)). + Watches( + &corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). // watch the config CMs we don't own Watches(&corev1.ConfigMap{}, handler.EnqueueRequestsFromMapFunc(configMapFn)). Complete(r) } +func (r *DesignateMdnsReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(ctx).WithName("Controllers").WithName("DesignateMdns") + + for _, field := range allWatchFields { + crList := &designatev1beta1.DesignateMdnsList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(field, src.GetName()), + Namespace: src.GetNamespace(), + } + err := r.Client.List(context.TODO(), crList, listOps) + if err != nil { + return []reconcile.Request{} + } + + for _, item := range crList.Items { + l.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace())) + + requests = append(requests, + reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + }, + ) + } + } + + return requests +} + func (r *DesignateMdnsReconciler) reconcileDelete(ctx context.Context, instance *designatev1beta1.DesignateMdns, helper *helper.Helper) (ctrl.Result, error) { Log := r.GetLogger(ctx) @@ -355,6 +423,38 @@ func (r *DesignateMdnsReconciler) reconcileNormal(ctx context.Context, instance instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage) // run check parent Designate CR config maps - end + // + // TLS input validation + // + // Validate the CA cert secret if provided + if instance.Spec.TLS.CaBundleSecretName != "" { + hash, ctrlResult, err := tls.ValidateCACertSecret( + ctx, + helper.GetClient(), + types.NamespacedName{ + Name: instance.Spec.TLS.CaBundleSecretName, + Namespace: instance.Namespace, + }, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.TLSInputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.TLSInputErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + if hash != "" { + configMapVars[tls.CABundleKey] = env.SetValue(hash) + } + } + // all cert input checks out so report InputReady + instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage) + // // Create ConfigMaps required as input for the Service and calculate an overall hash of hashes // @@ -367,7 +467,7 @@ func (r *DesignateMdnsReconciler) reconcileNormal(ctx context.Context, instance // // create custom Configmap for this designate volume service // - err = r.generateServiceConfigMaps(ctx, helper, instance, &configMapVars, serviceLabels) + err = r.generateServiceConfigMaps(ctx, helper, instance, &configMapVars) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.ServiceConfigReadyCondition, @@ -608,19 +708,30 @@ func (r *DesignateMdnsReconciler) generateServiceConfigMaps( h *helper.Helper, instance *designatev1beta1.DesignateMdns, envVars *map[string]env.Setter, - serviceLabels map[string]string, ) error { // // create custom Configmap for designate-mdns-specific config input // - %-config-data configmap holding custom config for the service's designate.conf // - cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), serviceLabels) + cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), map[string]string{}) + + db, err := mariadbv1.GetDatabaseByNameAndAccount(ctx, h, designate.DatabaseName, instance.Spec.DatabaseAccount, instance.Namespace) + if err != nil { + return err + } + var tlsCfg *tls.Service + if instance.Spec.TLS.CaBundleSecretName != "" { + tlsCfg = &tls.Service{} + } // customData hold any customization for the service. // custom.conf is going to be merged into /etc/designate/conder.conf // TODO: make sure custom.conf can not be overwritten - customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig} + customData := map[string]string{ + common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig, + "my.cnf": db.GetDatabaseClientConfig(tlsCfg), //(oschwart) for now just get the default my.cnf + } for key, data := range instance.Spec.DefaultConfigOverwrite { customData[key] = data diff --git a/controllers/designateproducer_controller.go b/controllers/designateproducer_controller.go index 84d89e60..945e5a43 100644 --- a/controllers/designateproducer_controller.go +++ b/controllers/designateproducer_controller.go @@ -22,18 +22,6 @@ import ( "fmt" "time" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - k8s_errors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "github.com/go-logr/logr" designatev1beta1 "github.com/openstack-k8s-operators/designate-operator/api/v1beta1" "github.com/openstack-k8s-operators/designate-operator/pkg/designate" @@ -46,8 +34,24 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/labels" nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + k8s_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" ) // GetClient - @@ -160,6 +164,7 @@ func (r *DesignateProducerReconciler) Reconcile(ctx context.Context, req ctrl.Re condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage), condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyInitMessage), condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), + condition.UnknownCondition(condition.TLSInputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), ) instance.Status.Conditions.Init(&cl) @@ -192,6 +197,30 @@ func (r *DesignateProducerReconciler) SetupWithManager(ctx context.Context, mgr // (e.g. TransportURLSecret) are handled by the top designate controller. Log := r.GetLogger(ctx) + // index passwordSecretField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &designatev1beta1.DesignateProducer{}, passwordSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateProducer) + if cr.Spec.Secret == "" { + return nil + } + return []string{cr.Spec.Secret} + }); err != nil { + return err + } + + // index caBundleSecretNameField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &designatev1beta1.DesignateProducer{}, caBundleSecretNameField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateProducer) + if cr.Spec.TLS.CaBundleSecretName == "" { + return nil + } + return []string{cr.Spec.TLS.CaBundleSecretName} + }); err != nil { + return err + } + svcSecretFn := func(ctx context.Context, o client.Object) []reconcile.Request { var namespace string = o.GetNamespace() var secretName string = o.GetName() @@ -268,12 +297,50 @@ func (r *DesignateProducerReconciler) SetupWithManager(ctx context.Context, mgr // watch the secrets we don't own Watches(&corev1.Secret{}, handler.EnqueueRequestsFromMapFunc(svcSecretFn)). + Watches( + &corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). // watch the config CMs we don't own Watches(&corev1.ConfigMap{}, handler.EnqueueRequestsFromMapFunc(configMapFn)). Complete(r) } +func (r *DesignateProducerReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(ctx).WithName("Controllers").WithName("DesignateProducer") + + for _, field := range allWatchFields { + crList := &designatev1beta1.DesignateProducerList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(field, src.GetName()), + Namespace: src.GetNamespace(), + } + err := r.Client.List(context.TODO(), crList, listOps) + if err != nil { + return []reconcile.Request{} + } + + for _, item := range crList.Items { + l.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace())) + + requests = append(requests, + reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + }, + ) + } + } + + return requests +} + func (r *DesignateProducerReconciler) reconcileDelete(ctx context.Context, instance *designatev1beta1.DesignateProducer, helper *helper.Helper) (ctrl.Result, error) { Log := r.GetLogger(ctx) @@ -357,6 +424,38 @@ func (r *DesignateProducerReconciler) reconcileNormal(ctx context.Context, insta instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage) // run check parent Designate CR config maps - end + // + // TLS input validation + // + // Validate the CA cert secret if provided + if instance.Spec.TLS.CaBundleSecretName != "" { + hash, ctrlResult, err := tls.ValidateCACertSecret( + ctx, + helper.GetClient(), + types.NamespacedName{ + Name: instance.Spec.TLS.CaBundleSecretName, + Namespace: instance.Namespace, + }, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.TLSInputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.TLSInputErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + if hash != "" { + configMapVars[tls.CABundleKey] = env.SetValue(hash) + } + } + // all cert input checks out so report InputReady + instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage) + // // Create ConfigMaps required as input for the Service and calculate an overall hash of hashes // @@ -369,7 +468,7 @@ func (r *DesignateProducerReconciler) reconcileNormal(ctx context.Context, insta // // create custom Configmap for this designate volume service // - err = r.generateServiceConfigMaps(ctx, helper, instance, &configMapVars, serviceLabels) + err = r.generateServiceConfigMaps(ctx, helper, instance, &configMapVars) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.ServiceConfigReadyCondition, @@ -609,19 +708,30 @@ func (r *DesignateProducerReconciler) generateServiceConfigMaps( h *helper.Helper, instance *designatev1beta1.DesignateProducer, envVars *map[string]env.Setter, - serviceLabels map[string]string, ) error { // // create custom Configmap for designate-producer-specific config input // - %-config-data configmap holding custom config for the service's designate.conf // - cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), serviceLabels) + cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), map[string]string{}) + + db, err := mariadbv1.GetDatabaseByNameAndAccount(ctx, h, designate.DatabaseName, instance.Spec.DatabaseAccount, instance.Namespace) + if err != nil { + return err + } + var tlsCfg *tls.Service + if instance.Spec.TLS.CaBundleSecretName != "" { + tlsCfg = &tls.Service{} + } // customData hold any customization for the service. // custom.conf is going to be merged into /etc/designate/conder.conf // TODO: make sure custom.conf can not be overwritten - customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig} + customData := map[string]string{ + common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig, + "my.cnf": db.GetDatabaseClientConfig(tlsCfg), //(oschwart) for now just get the default my.cnf + } for key, data := range instance.Spec.DefaultConfigOverwrite { customData[key] = data diff --git a/controllers/designateworker_controller.go b/controllers/designateworker_controller.go index bb813887..79c2ab2b 100644 --- a/controllers/designateworker_controller.go +++ b/controllers/designateworker_controller.go @@ -24,13 +24,17 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/go-logr/logr" @@ -45,6 +49,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/labels" nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" ) @@ -159,6 +164,7 @@ func (r *DesignateWorkerReconciler) Reconcile(ctx context.Context, req ctrl.Requ condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage), condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyInitMessage), condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), + condition.UnknownCondition(condition.TLSInputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), ) instance.Status.Conditions.Init(&cl) @@ -190,6 +196,31 @@ func (r *DesignateWorkerReconciler) SetupWithManager(ctx context.Context, mgr ct // Watch for changes to any CustomServiceConfigSecrets. Global secrets // (e.g. TransportURLSecret) are handled by the top designate controller. Log := r.GetLogger(ctx) + + // index passwordSecretField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &designatev1beta1.DesignateWorker{}, passwordSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateWorker) + if cr.Spec.Secret == "" { + return nil + } + return []string{cr.Spec.Secret} + }); err != nil { + return err + } + + // index caBundleSecretNameField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &designatev1beta1.DesignateWorker{}, caBundleSecretNameField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*designatev1beta1.DesignateWorker) + if cr.Spec.TLS.CaBundleSecretName == "" { + return nil + } + return []string{cr.Spec.TLS.CaBundleSecretName} + }); err != nil { + return err + } + svcSecretFn := func(ctx context.Context, o client.Object) []reconcile.Request { var namespace string = o.GetNamespace() var secretName string = o.GetName() @@ -266,12 +297,50 @@ func (r *DesignateWorkerReconciler) SetupWithManager(ctx context.Context, mgr ct // watch the secrets we don't own Watches(&corev1.Secret{}, handler.EnqueueRequestsFromMapFunc(svcSecretFn)). + Watches( + &corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). // watch the config CMs we don't own Watches(&corev1.ConfigMap{}, handler.EnqueueRequestsFromMapFunc(configMapFn)). Complete(r) } +func (r *DesignateWorkerReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(ctx).WithName("Controllers").WithName("DesignateWorker") + + for _, field := range allWatchFields { + crList := &designatev1beta1.DesignateWorkerList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(field, src.GetName()), + Namespace: src.GetNamespace(), + } + err := r.Client.List(context.TODO(), crList, listOps) + if err != nil { + return []reconcile.Request{} + } + + for _, item := range crList.Items { + l.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace())) + + requests = append(requests, + reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + }, + ) + } + } + + return requests +} + func (r *DesignateWorkerReconciler) reconcileDelete(ctx context.Context, instance *designatev1beta1.DesignateWorker, helper *helper.Helper) (ctrl.Result, error) { Log := r.GetLogger(ctx) Log.Info(fmt.Sprintf("Reconciling Service '%s' delete", instance.Name)) @@ -352,6 +421,38 @@ func (r *DesignateWorkerReconciler) reconcileNormal(ctx context.Context, instanc instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage) // run check parent Designate CR config maps - end + // + // TLS input validation + // + // Validate the CA cert secret if provided + if instance.Spec.TLS.CaBundleSecretName != "" { + hash, ctrlResult, err := tls.ValidateCACertSecret( + ctx, + helper.GetClient(), + types.NamespacedName{ + Name: instance.Spec.TLS.CaBundleSecretName, + Namespace: instance.Namespace, + }, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.TLSInputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.TLSInputErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + if hash != "" { + configMapVars[tls.CABundleKey] = env.SetValue(hash) + } + } + // all cert input checks out so report InputReady + instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage) + // // Create ConfigMaps required as input for the Service and calculate an overall hash of hashes // @@ -364,7 +465,7 @@ func (r *DesignateWorkerReconciler) reconcileNormal(ctx context.Context, instanc // // create custom Configmap for this designate volume service // - err = r.generateServiceConfigMaps(ctx, helper, instance, &configMapVars, serviceLabels) + err = r.generateServiceConfigMaps(ctx, helper, instance, &configMapVars) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.ServiceConfigReadyCondition, @@ -602,19 +703,30 @@ func (r *DesignateWorkerReconciler) generateServiceConfigMaps( h *helper.Helper, instance *designatev1beta1.DesignateWorker, envVars *map[string]env.Setter, - serviceLabels map[string]string, ) error { // // create custom Configmap for designate-worker-specific config input // - %-config-data configmap holding custom config for the service's designate.conf // - cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), serviceLabels) + cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designate.ServiceName), map[string]string{}) + + db, err := mariadbv1.GetDatabaseByNameAndAccount(ctx, h, designate.DatabaseName, instance.Spec.DatabaseAccount, instance.Namespace) + if err != nil { + return err + } + var tlsCfg *tls.Service + if instance.Spec.TLS.CaBundleSecretName != "" { + tlsCfg = &tls.Service{} + } // customData hold any customization for the service. // custom.conf is going to be merged into /etc/designate/conder.conf // TODO: make sure custom.conf can not be overwritten - customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig} + customData := map[string]string{ + common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig, + "my.cnf": db.GetDatabaseClientConfig(tlsCfg), //(oschwart) for now just get the default my.cnf + } for key, data := range instance.Spec.DefaultConfigOverwrite { customData[key] = data diff --git a/pkg/designate/dbsync.go b/pkg/designate/dbsync.go index 338c45df..7e1df5d3 100644 --- a/pkg/designate/dbsync.go +++ b/pkg/designate/dbsync.go @@ -35,15 +35,21 @@ func DbSyncJob( annotations map[string]string, ) *batchv1.Job { runAsUser := int64(0) - initVolumeMounts := getInitVolumeMounts() - volumeMounts := GetServiceVolumeMounts("db-sync") - volumes := getVolumes(instance.Name) + initVolumeMounts := GetInitVolumeMounts() + volumeMounts := GetVolumeMounts("db-sync") + volumes := GetVolumes(instance.Name) args := []string{"-c", DBSyncCommand} envVars := map[string]env.Setter{} envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") + // add CA cert if defined + if instance.Spec.DesignateAPI.TLS.CaBundleSecretName != "" { + volumes = append(volumes, instance.Spec.DesignateAPI.TLS.CreateVolume()) + volumeMounts = append(volumeMounts, instance.Spec.DesignateAPI.TLS.CreateVolumeMounts(nil)...) + } + job := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: ServiceName + "-db-sync", diff --git a/pkg/designate/initcontainer.go b/pkg/designate/initcontainer.go index 6c460177..085ba943 100644 --- a/pkg/designate/initcontainer.go +++ b/pkg/designate/initcontainer.go @@ -93,7 +93,7 @@ func InitContainer(init APIDetails) []corev1.Container { }, Args: args, Env: envs, - VolumeMounts: getInitVolumeMounts(), + VolumeMounts: GetInitVolumeMounts(), }, } } diff --git a/pkg/designate/volumes.go b/pkg/designate/volumes.go index 09e834a5..1395b66d 100644 --- a/pkg/designate/volumes.go +++ b/pkg/designate/volumes.go @@ -29,7 +29,7 @@ const ( // GetVolumes - returns the volumes used for the service deployment and for // any jobs needs access for the full service configuration func GetVolumes(baseConfigMapName string) []corev1.Volume { - var scriptMode int32 = 0740 + var scriptMode int32 = 0755 var configMode int32 = 0640 return []corev1.Volume{ @@ -81,9 +81,9 @@ func GetInitVolumeMounts() []corev1.VolumeMount { } } -// GetServiceVolumeMounts - VolumeMounts to get access to the merged +// GetVolumeMounts - VolumeMounts to get access to the merged // configuration -func GetServiceVolumeMounts(serviceName string) []corev1.VolumeMount { +func GetVolumeMounts(serviceName string) []corev1.VolumeMount { return []corev1.VolumeMount{ { Name: scriptVolume, @@ -103,58 +103,3 @@ func GetServiceVolumeMounts(serviceName string) []corev1.VolumeMount { }, } } - -// ############################################################################# -// getVolumes -func getVolumes(name string) []corev1.Volume { - var scriptsVolumeDefaultMode int32 = 0755 - var config0640AccessMode int32 = 0640 - - return []corev1.Volume{ - { - Name: "scripts", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - DefaultMode: &scriptsVolumeDefaultMode, - SecretName: name + "-scripts", - }, - }, - }, - { - Name: "config-data", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - DefaultMode: &config0640AccessMode, - SecretName: name + "-config-data", - }, - }, - }, - { - Name: "config-data-merged", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{Medium: ""}, - }, - }, - } -} - -// getInitVolumeMounts - general init task VolumeMounts -func getInitVolumeMounts() []corev1.VolumeMount { - return []corev1.VolumeMount{ - { - Name: "scripts", - MountPath: "/usr/local/bin/container-scripts", - ReadOnly: true, - }, - { - Name: "config-data", - MountPath: "/var/lib/config-data/default", - ReadOnly: true, - }, - { - Name: "config-data-merged", - MountPath: "/var/lib/config-data/merged", - ReadOnly: false, - }, - } -} diff --git a/pkg/designateapi/deployment.go b/pkg/designateapi/deployment.go index f4a5e6a9..cccae378 100644 --- a/pkg/designateapi/deployment.go +++ b/pkg/designateapi/deployment.go @@ -16,11 +16,15 @@ limitations under the License. package designateapi import ( + "fmt" + designatev1beta1 "github.com/openstack-k8s-operators/designate-operator/api/v1beta1" designate "github.com/openstack-k8s-operators/designate-operator/pkg/designate" common "github.com/openstack-k8s-operators/lib-common/modules/common" "github.com/openstack-k8s-operators/lib-common/modules/common/affinity" "github.com/openstack-k8s-operators/lib-common/modules/common/env" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -39,8 +43,9 @@ func Deployment( configHash string, labels map[string]string, annotations map[string]string, -) *appsv1.Deployment { +) (*appsv1.Deployment, error) { runAsUser := int64(0) + initVolumeMounts := designate.GetInitVolumeMounts() livenessProbe := &corev1.Probe{ // TODO might need tuning @@ -63,13 +68,49 @@ func Deployment( } readinessProbe.HTTPGet = livenessProbe.HTTPGet + if instance.Spec.TLS.API.Enabled(service.EndpointPublic) { + livenessProbe.HTTPGet.Scheme = corev1.URISchemeHTTPS + readinessProbe.HTTPGet.Scheme = corev1.URISchemeHTTPS + } + + // create Volume and VolumeMounts + volumes := getVolumes(instance.Name) + volumeMounts := getVolumeMounts("designate-api") + + // add CA cert if defined + if instance.Spec.TLS.CaBundleSecretName != "" { + volumes = append(volumes, instance.Spec.TLS.CreateVolume()) + volumeMounts = append(volumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...) + } + + for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} { + if instance.Spec.TLS.API.Enabled(endpt) { + var tlsEndptCfg tls.GenericService + switch endpt { + case service.EndpointPublic: + tlsEndptCfg = instance.Spec.TLS.API.Public + case service.EndpointInternal: + tlsEndptCfg = instance.Spec.TLS.API.Internal + } + + svc, err := tlsEndptCfg.ToService() + if err != nil { + return nil, err + } + volumes = append(volumes, svc.CreateVolume(endpt.String())) + volumeMounts = append(volumeMounts, svc.CreateVolumeMounts(endpt.String())...) + } + } + envVars := map[string]env.Setter{} envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") envVars["CONFIG_HASH"] = env.SetValue(configHash) + serviceName := fmt.Sprintf("%s-api", designate.ServiceName) + deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ - Name: instance.Name, + Name: serviceName, Namespace: instance.Namespace, Labels: labels, }, @@ -85,12 +126,10 @@ func Deployment( }, Spec: corev1.PodSpec{ ServiceAccountName: instance.Spec.ServiceAccount, - Volumes: designate.GetVolumes( - designate.GetOwningDesignateName(instance), - ), + Volumes: volumes, Containers: []corev1.Container{ { - Name: designate.ServiceName + "-api", + Name: serviceName, Command: []string{ "/bin/bash", }, @@ -100,7 +139,7 @@ func Deployment( RunAsUser: &runAsUser, }, Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), - VolumeMounts: designate.GetServiceVolumeMounts("designate-api"), + VolumeMounts: volumeMounts, Resources: instance.Spec.Resources, ReadinessProbe: readinessProbe, LivenessProbe: livenessProbe, @@ -118,7 +157,7 @@ func Deployment( deployment.Spec.Template.Spec.Affinity = affinity.DistributePods( common.AppSelector, []string{ - designate.ServiceName, + serviceName, }, corev1.LabelHostname, ) @@ -133,9 +172,9 @@ func Deployment( OSPSecret: instance.Spec.Secret, TransportURLSecret: instance.Spec.TransportURLSecret, UserPasswordSelector: instance.Spec.PasswordSelectors.Service, - VolumeMounts: designate.GetInitVolumeMounts(), + VolumeMounts: initVolumeMounts, } deployment.Spec.Template.Spec.InitContainers = designate.InitContainer(initContainerDetails) - return deployment + return deployment, nil } diff --git a/pkg/designateapi/volumes.go b/pkg/designateapi/volumes.go new file mode 100644 index 00000000..72f5d0f9 --- /dev/null +++ b/pkg/designateapi/volumes.go @@ -0,0 +1,48 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package designateapi + +import ( + "github.com/openstack-k8s-operators/designate-operator/pkg/designate" + corev1 "k8s.io/api/core/v1" +) + +// getVolumes - service volumes +func getVolumes(name string) []corev1.Volume { + + volumes := []corev1.Volume{ + { + Name: "designate-run", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{Medium: ""}, + }, + }, + } + + return append(designate.GetVolumes(name), volumes...) +} + +// getVolumeMounts - general VolumeMounts +func getVolumeMounts(serviceName string) []corev1.VolumeMount { + + // The API pod has an extra volume so the API and the provider agent can + // communicate with each other. + volumeMounts := []corev1.VolumeMount{ + { + Name: "designate-run", + MountPath: "/run/designate", + ReadOnly: false, + }, + } + return append(designate.GetVolumeMounts(serviceName), volumeMounts...) +} diff --git a/pkg/designatebackendbind9/deployment.go b/pkg/designatebackendbind9/deployment.go index f64f900e..2a5db57d 100644 --- a/pkg/designatebackendbind9/deployment.go +++ b/pkg/designatebackendbind9/deployment.go @@ -16,6 +16,8 @@ limitations under the License. package designatebackendbind9 import ( + "fmt" + designatev1beta1 "github.com/openstack-k8s-operators/designate-operator/api/v1beta1" designate "github.com/openstack-k8s-operators/designate-operator/pkg/designate" common "github.com/openstack-k8s-operators/lib-common/modules/common" @@ -47,6 +49,11 @@ func Deployment( // designateUser := int64(42411) // designateGroup := int64(42411) + volumes := designate.GetVolumes( + designate.GetOwningDesignateName(instance), + ) + volumeMounts := designate.GetVolumeMounts("designate-backendbind9") + livenessProbe := &corev1.Probe{ // TODO might need tuning TimeoutSeconds: 15, @@ -96,6 +103,8 @@ func Deployment( envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") envVars["CONFIG_HASH"] = env.SetValue(configHash) + serviceName := fmt.Sprintf("%s-backendbind9", designate.ServiceName) + deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: instance.Name, @@ -114,12 +123,10 @@ func Deployment( }, Spec: corev1.PodSpec{ ServiceAccountName: instance.Spec.ServiceAccount, - Volumes: designate.GetVolumes( - designate.GetOwningDesignateName(instance), - ), + Volumes: volumes, Containers: []corev1.Container{ { - Name: designate.ServiceName + "-backendbind9", + Name: serviceName, // NOTE: dkehn@redhat.com This sleep is to hold // the pod until the bind9 is manually setup Command: []string{ @@ -134,7 +141,7 @@ func Deployment( RunAsUser: &rootUser, }, Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), - VolumeMounts: designate.GetServiceVolumeMounts("designate-backendbind9"), + VolumeMounts: volumeMounts, Resources: instance.Spec.Resources, StartupProbe: startupProbe, LivenessProbe: livenessProbe, @@ -152,7 +159,7 @@ func Deployment( deployment.Spec.Template.Spec.Affinity = affinity.DistributePods( common.AppSelector, []string{ - designate.ServiceName, + serviceName, }, corev1.LabelHostname, ) diff --git a/pkg/designatecentral/deployment.go b/pkg/designatecentral/deployment.go index 8da88fdd..79a5f122 100644 --- a/pkg/designatecentral/deployment.go +++ b/pkg/designatecentral/deployment.go @@ -16,6 +16,8 @@ limitations under the License. package designatecentral import ( + "fmt" + designatev1beta1 "github.com/openstack-k8s-operators/designate-operator/api/v1beta1" designate "github.com/openstack-k8s-operators/designate-operator/pkg/designate" common "github.com/openstack-k8s-operators/lib-common/modules/common" @@ -46,6 +48,11 @@ func Deployment( // designateUser := int64(42411) // designateGroup := int64(42411) + volumes := designate.GetVolumes( + designate.GetOwningDesignateName(instance), + ) + volumeMounts := designate.GetVolumeMounts("designate-central") + livenessProbe := &corev1.Probe{ // TODO might need tuning TimeoutSeconds: 10, @@ -70,6 +77,14 @@ func Deployment( envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") envVars["CONFIG_HASH"] = env.SetValue(configHash) + serviceName := fmt.Sprintf("%s-central", designate.ServiceName) + + // Add the CA bundle + if instance.Spec.TLS.CaBundleSecretName != "" { + volumes = append(volumes, instance.Spec.TLS.CreateVolume()) + volumeMounts = append(volumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...) + } + deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: instance.Name, @@ -88,12 +103,10 @@ func Deployment( }, Spec: corev1.PodSpec{ ServiceAccountName: instance.Spec.ServiceAccount, - Volumes: designate.GetVolumes( - designate.GetOwningDesignateName(instance), - ), + Volumes: volumes, Containers: []corev1.Container{ { - Name: designate.ServiceName + "-central", + Name: serviceName, Command: []string{ "/bin/bash", }, @@ -103,7 +116,7 @@ func Deployment( RunAsUser: &rootUser, }, Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), - VolumeMounts: designate.GetServiceVolumeMounts("designate-central"), + VolumeMounts: volumeMounts, Resources: instance.Spec.Resources, StartupProbe: startupProbe, LivenessProbe: livenessProbe, @@ -121,7 +134,7 @@ func Deployment( deployment.Spec.Template.Spec.Affinity = affinity.DistributePods( common.AppSelector, []string{ - designate.ServiceName, + serviceName, }, corev1.LabelHostname, ) diff --git a/pkg/designatemdns/daemonset.go b/pkg/designatemdns/daemonset.go index f1de1251..3e9b3111 100644 --- a/pkg/designatemdns/daemonset.go +++ b/pkg/designatemdns/daemonset.go @@ -16,6 +16,8 @@ limitations under the License. package designatemdns import ( + "fmt" + designatev1beta1 "github.com/openstack-k8s-operators/designate-operator/api/v1beta1" designate "github.com/openstack-k8s-operators/designate-operator/pkg/designate" common "github.com/openstack-k8s-operators/lib-common/modules/common" @@ -42,6 +44,11 @@ func DaemonSet( ) *appsv1.DaemonSet { rootUser := int64(0) + volumes := designate.GetVolumes( + designate.GetOwningDesignateName(instance), + ) + volumeMounts := designate.GetVolumeMounts("designate-mdns") + livenessProbe := &corev1.Probe{ // TODO might need tuning TimeoutSeconds: 15, @@ -67,6 +74,14 @@ func DaemonSet( envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") envVars["CONFIG_HASH"] = env.SetValue(configHash) + serviceName := fmt.Sprintf("%s-mdns", designate.ServiceName) + + // Add the CA bundle + if instance.Spec.TLS.CaBundleSecretName != "" { + volumes = append(volumes, instance.Spec.TLS.CreateVolume()) + volumeMounts = append(volumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...) + } + daemonset := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: instance.Name, @@ -84,12 +99,10 @@ func DaemonSet( }, Spec: corev1.PodSpec{ ServiceAccountName: instance.Spec.ServiceAccount, - Volumes: designate.GetVolumes( - designate.GetOwningDesignateName(instance), - ), + Volumes: volumes, Containers: []corev1.Container{ { - Name: designate.ServiceName + "-mdns", + Name: serviceName, Command: []string{ "/bin/bash", }, @@ -99,7 +112,7 @@ func DaemonSet( RunAsUser: &rootUser, }, Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), - VolumeMounts: designate.GetServiceVolumeMounts("designate-mdns"), + VolumeMounts: volumeMounts, Resources: instance.Spec.Resources, ReadinessProbe: readinessProbe, LivenessProbe: livenessProbe, @@ -117,7 +130,7 @@ func DaemonSet( daemonset.Spec.Template.Spec.Affinity = affinity.DistributePods( common.AppSelector, []string{ - designate.ServiceName, + serviceName, }, corev1.LabelHostname, ) diff --git a/pkg/designateproducer/deployment.go b/pkg/designateproducer/deployment.go index 17aee57b..d7f27b1c 100644 --- a/pkg/designateproducer/deployment.go +++ b/pkg/designateproducer/deployment.go @@ -16,6 +16,8 @@ limitations under the License. package designateproducer import ( + "fmt" + designatev1beta1 "github.com/openstack-k8s-operators/designate-operator/api/v1beta1" designate "github.com/openstack-k8s-operators/designate-operator/pkg/designate" common "github.com/openstack-k8s-operators/lib-common/modules/common" @@ -42,6 +44,11 @@ func Deployment( ) *appsv1.Deployment { rootAsUser := int64(0) + volumes := designate.GetVolumes( + designate.GetOwningDesignateName(instance), + ) + volumeMounts := designate.GetVolumeMounts("designate-producer") + livenessProbe := &corev1.Probe{ // TODO might need tuning TimeoutSeconds: 10, @@ -66,6 +73,14 @@ func Deployment( envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") envVars["CONFIG_HASH"] = env.SetValue(configHash) + serviceName := fmt.Sprintf("%s-producer", designate.ServiceName) + + // Add the CA bundle + if instance.Spec.TLS.CaBundleSecretName != "" { + volumes = append(volumes, instance.Spec.TLS.CreateVolume()) + volumeMounts = append(volumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...) + } + deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: instance.Name, @@ -84,12 +99,10 @@ func Deployment( }, Spec: corev1.PodSpec{ ServiceAccountName: instance.Spec.ServiceAccount, - Volumes: designate.GetVolumes( - designate.GetOwningDesignateName(instance), - ), + Volumes: volumes, Containers: []corev1.Container{ { - Name: designate.ServiceName + "-producer", + Name: serviceName, Command: []string{ "/bin/bash", }, @@ -99,7 +112,7 @@ func Deployment( RunAsUser: &rootAsUser, }, Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), - VolumeMounts: designate.GetServiceVolumeMounts("designate-producer"), + VolumeMounts: volumeMounts, Resources: instance.Spec.Resources, StartupProbe: startupProbe, LivenessProbe: livenessProbe, @@ -117,7 +130,7 @@ func Deployment( deployment.Spec.Template.Spec.Affinity = affinity.DistributePods( common.AppSelector, []string{ - designate.ServiceName, + serviceName, }, corev1.LabelHostname, ) diff --git a/pkg/designateworker/deployment.go b/pkg/designateworker/deployment.go index 478e93b8..59631339 100644 --- a/pkg/designateworker/deployment.go +++ b/pkg/designateworker/deployment.go @@ -16,6 +16,8 @@ limitations under the License. package designateworker import ( + "fmt" + designatev1beta1 "github.com/openstack-k8s-operators/designate-operator/api/v1beta1" designate "github.com/openstack-k8s-operators/designate-operator/pkg/designate" common "github.com/openstack-k8s-operators/lib-common/modules/common" @@ -42,6 +44,11 @@ func Deployment( ) *appsv1.Deployment { rootUser := int64(0) + volumes := designate.GetVolumes( + designate.GetOwningDesignateName(instance), + ) + volumeMounts := designate.GetVolumeMounts("designate-worker") + livenessProbe := &corev1.Probe{ // TODO might need tuning TimeoutSeconds: 10, @@ -66,6 +73,14 @@ func Deployment( envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") envVars["CONFIG_HASH"] = env.SetValue(configHash) + serviceName := fmt.Sprintf("%s-worker", designate.ServiceName) + + // Add the CA bundle + if instance.Spec.TLS.CaBundleSecretName != "" { + volumes = append(volumes, instance.Spec.TLS.CreateVolume()) + volumeMounts = append(volumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...) + } + deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: instance.Name, @@ -84,12 +99,10 @@ func Deployment( }, Spec: corev1.PodSpec{ ServiceAccountName: instance.Spec.ServiceAccount, - Volumes: designate.GetVolumes( - designate.GetOwningDesignateName(instance), - ), + Volumes: volumes, Containers: []corev1.Container{ { - Name: designate.ServiceName + "-worker", + Name: serviceName, Command: []string{ "/bin/bash", }, @@ -99,7 +112,7 @@ func Deployment( RunAsUser: &rootUser, }, Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), - VolumeMounts: designate.GetServiceVolumeMounts("designate-worker"), + VolumeMounts: volumeMounts, Resources: instance.Spec.Resources, StartupProbe: startupProbe, LivenessProbe: livenessProbe, @@ -117,7 +130,7 @@ func Deployment( deployment.Spec.Template.Spec.Affinity = affinity.DistributePods( common.AppSelector, []string{ - designate.ServiceName, + serviceName, }, corev1.LabelHostname, ) diff --git a/templates/designate/config/db-sync-config.json b/templates/designate/config/db-sync-config.json index f209ade8..e0ef5857 100644 --- a/templates/designate/config/db-sync-config.json +++ b/templates/designate/config/db-sync-config.json @@ -6,14 +6,15 @@ "dest": "/etc/designate/designate.conf", "owner": "designate", "perm": "0600" + }, + { + "source": "/var/lib/config-data/merged/my.cnf", + "dest": "/etc/my.cnf", + "owner": "designate", + "perm": "0644" } ], "permissions": [ - { - "path": "/var/log/designate", - "owner": "designate:designate", - "recurse": true - }, { "path": "/run/designate", "owner": "designate:designate", diff --git a/templates/designate/config/designate-backendbind9-config.json b/templates/designate/config/designate-backendbind9-config.json index 96597a1f..80687683 100644 --- a/templates/designate/config/designate-backendbind9-config.json +++ b/templates/designate/config/designate-backendbind9-config.json @@ -24,6 +24,12 @@ "dest": "/etc/named/rndc.key", "owner": "named:named", "perm": "0644" - } + }, + { + "source": "/var/lib/config-data/merged/my.cnf", + "dest": "/etc/my.cnf", + "owner": "designate", + "perm": "0644" + } ] } diff --git a/templates/designate/config/designate-central-config.json b/templates/designate/config/designate-central-config.json index 64fcd5f5..69c634b8 100644 --- a/templates/designate/config/designate-central-config.json +++ b/templates/designate/config/designate-central-config.json @@ -6,6 +6,12 @@ "dest": "/etc/designate/designate.conf", "owner": "root:designate", "perm": "0750" + }, + { + "source": "/var/lib/config-data/merged/my.cnf", + "dest": "/etc/my.cnf", + "owner": "designate", + "perm": "0644" } ] } diff --git a/templates/designate/config/designate-mdns-config.json b/templates/designate/config/designate-mdns-config.json index 34044a18..a36dc78b 100644 --- a/templates/designate/config/designate-mdns-config.json +++ b/templates/designate/config/designate-mdns-config.json @@ -6,6 +6,12 @@ "dest": "/etc/designate/designate.conf", "owner": "root:designate", "perm": "0750" + }, + { + "source": "/var/lib/config-data/merged/my.cnf", + "dest": "/etc/my.cnf", + "owner": "designate", + "perm": "0644" } ] } diff --git a/templates/designate/config/designate-producer-config.json b/templates/designate/config/designate-producer-config.json index 57c87e17..1048b2ae 100644 --- a/templates/designate/config/designate-producer-config.json +++ b/templates/designate/config/designate-producer-config.json @@ -6,6 +6,12 @@ "dest": "/etc/designate/designate.conf", "owner": "root:designate", "perm": "0750" + }, + { + "source": "/var/lib/config-data/merged/my.cnf", + "dest": "/etc/my.cnf", + "owner": "designate", + "perm": "0644" } ] } diff --git a/templates/designate/config/designate-worker-config.json b/templates/designate/config/designate-worker-config.json index ba473185..e458ed33 100644 --- a/templates/designate/config/designate-worker-config.json +++ b/templates/designate/config/designate-worker-config.json @@ -6,6 +6,12 @@ "dest": "/etc/designate/designate.conf", "owner": "root:designate", "perm": "0750" + }, + { + "source": "/var/lib/config-data/merged/my.cnf", + "dest": "/etc/my.cnf", + "owner": "designate", + "perm": "0644" } ] } diff --git a/templates/designate/config/designate.conf b/templates/designate/config/designate.conf index 8da85ac2..4272843d 100644 --- a/templates/designate/config/designate.conf +++ b/templates/designate/config/designate.conf @@ -1,8 +1,6 @@ [DEFAULT] debug=True rpc_response_timeout=60 -log_file=/var/log/designate/designate.log -log_dir=/var/log/designate quota_api_export_size=1000 quota_recordset_records=20 @@ -67,8 +65,6 @@ stats_update_threads=4 # heartbeat_key=FIXMEkey1 [keystone_authtoken] -www_authenticate_uri={{ .KeystonePublicURL }} -auth_url={{ .KeystoneInternalURL }} username={{ .ServiceUser }} # password=FIXMEpw3 project_name=service @@ -79,9 +75,10 @@ auth_type=password # memcached_servers=FIXMEhost1:11211 # region_name=regionOne -# interface=internal +#interface=internal # cafile=/opt/stack/data/ca-bundle.pem +#cafile=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem [keystone] region_name=RegionOne diff --git a/templates/designate/config/httpd.conf b/templates/designate/config/httpd.conf deleted file mode 100644 index e8c255bc..00000000 --- a/templates/designate/config/httpd.conf +++ /dev/null @@ -1,54 +0,0 @@ -ServerTokens Prod -ServerSignature Off -TraceEnable Off -PidFile run/httpd.pid -ServerRoot "/etc/httpd" -ServerName "localhost.localdomain" - -User apache -Group apache - -Listen 9001 - -TypesConfig /etc/mime.types - -Include conf.modules.d/*.conf -# XXX: To disable SSL -#+ exec /usr/sbin/httpd -#AH00526: Syntax error on line 85 of /etc/httpd/conf.d/ssl.conf: -#SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty -#Include conf.d/*.conf -#Include conf.d/*.conf - -LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined -LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" proxy - -SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded -CustomLog /dev/stdout combined env=!forwarded -CustomLog /dev/stdout proxy env=forwarded - - - = 2.4> - ErrorLogFormat "%M" - - ErrorLog /dev/stdout - SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded - CustomLog /dev/stdout combined env=!forwarded - CustomLog /dev/stdout proxy env=forwarded - - ## WSGI configuration - WSGIProcessGroup designate-wsgi - WSGIApplicationGroup %{GLOBAL} - WSGIPassAuthorization On - WSGIDaemonProcess designate-wsgi processes=5 threads=1 user=designate group=designate display-name=%{GROUP} - WSGIScriptAlias / /usr/bin/designate-api-wsgi - - -Alias /designate-api /usr/bin/designate-wsgi - - SetHandler wsgi-script - Options +ExecCGI - WSGIProcessGroup designate-api - WSGIApplicationGroup %{GLOBAL} - WSGIPassAuthorization On - diff --git a/templates/designateapi/bin/bootstrap.sh b/templates/designateapi/bin/bootstrap.sh new file mode 100755 index 00000000..3b3b1f63 --- /dev/null +++ b/templates/designateapi/bin/bootstrap.sh @@ -0,0 +1,21 @@ +#!/bin//bash +# +# Copyright 2024 Red Hat Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +set -ex + +OPTS="--config-file /etc/designate/designate.conf" +designate-manage ${OPTS} upgrade head + +exit 0 diff --git a/templates/designateapi/bin/init.sh b/templates/designateapi/bin/init.sh new file mode 100755 index 00000000..b70b22b3 --- /dev/null +++ b/templates/designateapi/bin/init.sh @@ -0,0 +1,53 @@ +#!/bin//bash +# +# Copyright 2024 Red Hat Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +set -ex + +# This script generates the designate.conf/logging.conf file and +# copies the result to the ephemeral /var/lib/config-data/merged volume. +# +# Secrets are obtained from ENV variables. +export PASSWORD=${AdminPassword:?"Please specify a AdminPassword variable."} +export TRANSPORTURL=${TransportURL:-""} + +VERBOSE="True" + +SVC_CFG=/etc/designate/designate.conf +SVC_CFG_MERGED=/var/lib/config-data/merged/designate.conf + +# expect that the common.sh is in the same dir as the calling script +SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +. ${SCRIPTPATH}/common.sh --source-only + +# Copy default service config from container image as base +cp -a ${SVC_CFG} ${SVC_CFG_MERGED} + +# Merge all templates from config CM +for dir in /var/lib/config-data/default; do + merge_config_dir ${dir} +done + +# set secrets in the config-data +crudini --set ${SVC_CFG_MERGED} keystone_authtoken password $PASSWORD +if [ -n "$TRANSPORTURL" ]; then + crudini --set ${SVC_CFG_MERGED} DEFAULT transport_url $TRANSPORTURL +fi +if [ -n "$BACKENDURL" ]; then + crudini --set ${SVC_CFG_MERGED} coordination backend_url $BACKENDURL +fi + +# NOTE:dkehn - REMOVED because Kolla_set & start copy eveyrthing. +# I'm doing this to get the designate.conf w/all the tags with values. +cp -a ${SVC_CFG_MERGED} ${SVC_CFG} diff --git a/templates/designate/config/designate-api-config.json b/templates/designateapi/config/designate-api-config.json similarity index 55% rename from templates/designate/config/designate-api-config.json rename to templates/designateapi/config/designate-api-config.json index 0f77e5ed..fa284a3c 100644 --- a/templates/designate/config/designate-api-config.json +++ b/templates/designateapi/config/designate-api-config.json @@ -13,6 +13,12 @@ "owner": "designate", "perm": "0600" }, + { + "source": "/var/lib/config-data/merged/my.cnf", + "dest": "/etc/my.cnf", + "owner": "designate", + "perm": "0644" + }, { "source": "/var/lib/config-data/merged/httpd.conf", "dest": "/etc/httpd/conf/httpd.conf", @@ -20,18 +26,29 @@ "perm": "0644" }, { - "source": "/var/lib/config-data/merged/logging.conf", - "dest": "/etc/designate/logging.conf", + "source": "/var/lib/config-data/merged/ssl.conf", + "dest": "/etc/httpd/conf.d/ssl.conf", "owner": "root", "perm": "0644" + }, + { + "source": "/var/lib/config-data/tls/certs/*", + "dest": "/etc/pki/tls/certs/", + "owner": "designate", + "perm": "0440", + "optional": true, + "merge": true + }, + { + "source": "/var/lib/config-data/tls/private/*", + "dest": "/etc/pki/tls/private/", + "owner": "designate", + "perm": "0400", + "optional": true, + "merge": true } ], "permissions": [ - { - "path": "/var/log/designate", - "owner": "designate:designate", - "recurse": true - }, { "path": "/run/designate", "owner": "designate:designate", diff --git a/templates/designateapi/config/designate.conf b/templates/designateapi/config/designate.conf new file mode 100644 index 00000000..4249a975 --- /dev/null +++ b/templates/designateapi/config/designate.conf @@ -0,0 +1,97 @@ +[DEFAULT] +debug=True +rpc_response_timeout=60 + +quota_api_export_size=1000 +quota_recordset_records=20 +quota_zone_records=500 +quota_zone_recordsets=500 +quota_zones=10 +root-helper=sudo +state_path=/etc/designate/data +debug=True +transport_url=rabbit://stackrabbit:secret@10.0.110.9:5672/ + +healthcheck_enabled=True + +[database] +connection={{ .DatabaseConnection }} + +[storage:sqlalchemy] +connection={{ .DatabaseConnection }} + +[coordination] +backend_url=memcached://127.0.0.1:11211 + + +[service:api] +quotas_verify_project_id=True +auth_strategy=keystone +enable_api_admin=True +enable_api_v2=True +enable_host_header=True +enabled_extensions_admin=quotas +enabled_extension_v2 = + +[service:central] +workers=2 + +[service:mdns] +workers=2 +listen=0.0.0.0:5354 + +[service:producer] +workers=2 + +[service:worker] +workers=2 +poll_retry_interval=5 +poll_max_retries=6 + +[oslo_messaging_notifications] +topics=notifications +driver=messagingv2 + +[oslo_concurrency] +lock_path=/opt/stack/data/designate + +[oslo_policy] +enforce_scope=True +enforce_new_defaults=True + +[health_manager] +health_update_threads=4 +stats_update_threads=4 +# heartbeat_key=FIXMEkey1 + +[keystone_authtoken] +www_authenticate_uri={{ .KeystonePublicURL }} +auth_url={{ .KeystoneInternalURL }} +username={{ .ServiceUser }} +# password=FIXMEpw3 +project_name=service +project_domain_name=Default +user_domain_name=Default +auth_type=password +memcache_use_advanced_pool=True +memcached_servers=inet:[memcached-0.memcached]:11211 +# memcached_servers=FIXMEhost1:11211 +service_token_roles_required = true +region_name=regionOne +interface=internal + +# cafile=/opt/stack/data/ca-bundle.pem +# cafile=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem + +# [keystone] +# region_name=RegionOne + +[service_auth] +project_domain_name=Default +project_name=service +user_domain_name=Default +password=FIXMEpw3 +username=designate +auth_type=password +auth_url={{ .KeystoneInternalURL }} +region_name=regionOne diff --git a/templates/designateapi/config/httpd.conf b/templates/designateapi/config/httpd.conf new file mode 100644 index 00000000..dd163818 --- /dev/null +++ b/templates/designateapi/config/httpd.conf @@ -0,0 +1,54 @@ +ServerTokens Prod +ServerSignature Off +TraceEnable Off +PidFile run/httpd.pid +ServerRoot "/etc/httpd" +ServerName "localhost.localdomain" + +User apache +Group apache + +Listen 9001 + +TypesConfig /etc/mime.types + +Include conf.modules.d/*.conf + +Include conf.d/*.conf + +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" proxy + +SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded +CustomLog /dev/stdout combined env=!forwarded +CustomLog /dev/stdout proxy env=forwarded + +{{ range $endpt, $vhost := .VHosts }} + # {{ $endpt }} vhost {{ $vhost.ServerName }} configuration + + ServerName {{ $vhost.ServerName }} + = 2.4> + ErrorLogFormat "%M" + + ErrorLog /dev/stdout + SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded + CustomLog /dev/stdout combined env=!forwarded + CustomLog /dev/stdout proxy env=forwarded + + {{- if $vhost.TLS }} + SetEnvIf X-Forwarded-Proto https HTTPS=1 + + ## SSL directives + SSLEngine on + SSLCertificateFile "{{ $vhost.SSLCertificateFile }}" + SSLCertificateKeyFile "{{ $vhost.SSLCertificateKeyFile }}" + {{- end }} + + ## WSGI configuration + WSGIProcessGroup {{ $endpt }} + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + WSGIDaemonProcess {{ $endpt }} processes=5 threads=1 user=designate group=designate display-name={{ $endpt }} + WSGIScriptAlias / "/usr/bin/designate-api-wsgi" + +{{ end }} diff --git a/templates/designateapi/config/ssl.conf b/templates/designateapi/config/ssl.conf new file mode 100644 index 00000000..e3da4ecb --- /dev/null +++ b/templates/designateapi/config/ssl.conf @@ -0,0 +1,21 @@ + + SSLRandomSeed startup builtin + SSLRandomSeed startup file:/dev/urandom 512 + SSLRandomSeed connect builtin + SSLRandomSeed connect file:/dev/urandom 512 + + AddType application/x-x509-ca-cert .crt + AddType application/x-pkcs7-crl .crl + + SSLPassPhraseDialog builtin + SSLSessionCache "shmcb:/var/cache/mod_ssl/scache(512000)" + SSLSessionCacheTimeout 300 + Mutex default + SSLCryptoDevice builtin + SSLHonorCipherOrder On + SSLUseStapling Off + SSLStaplingCache "shmcb:/run/httpd/ssl_stapling(32768)" + SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!RC4:!3DES + SSLProtocol all -SSLv2 -SSLv3 -TLSv1 + SSLOptions StdEnvVars + diff --git a/templates/designateunbound/config/unbound.conf b/templates/designateunbound/config/unbound.conf index 1aba707c..5563ee7a 100644 --- a/templates/designateunbound/config/unbound.conf +++ b/templates/designateunbound/config/unbound.conf @@ -6,8 +6,6 @@ server: # XXX(beagles) figure out how to insert acl for the user's config. We can't # merge it here because it will just obliterate what's already there. - logfile: /var/log/unbound/unbound.log - log-queries: no hide-identity: yes hide-version: yes diff --git a/tests/kuttl/common/assert-sample-deployment.yaml b/tests/kuttl/common/assert-sample-deployment.yaml index f86ae25c..18df5dc9 100644 --- a/tests/kuttl/common/assert-sample-deployment.yaml +++ b/tests/kuttl/common/assert-sample-deployment.yaml @@ -21,14 +21,8 @@ spec: customServiceConfig: | [DEFAULT] debug = true -status: - designateAPIReadyCount: 1 - designateCentralReadyCount: 1 - # designateMdnsReadyCount: 1 - designateProducerReadyCount: 1 - designateWorkerReadyCount: 1 - designateUnboundReadyCount: 1 - transportURLSecret: rabbitmq-transport-url-designate-designate-transport +# status: +# transportURLSecret: rabbitmq-transport-url-designate-designate-transport --- apiVersion: v1 kind: Service diff --git a/tests/kuttl/tests/designate_tls/02-assert.yaml b/tests/kuttl/tests/designate_tls/02-assert.yaml index 52ba967c..5de5d92f 100644 --- a/tests/kuttl/tests/designate_tls/02-assert.yaml +++ b/tests/kuttl/tests/designate_tls/02-assert.yaml @@ -16,7 +16,6 @@ spec: [DEFAULT] debug = true databaseAccount: designate - replicas: 1 secret: osp-secret serviceUser: designate tls: @@ -69,3 +68,184 @@ spec: serviceUser: designate tls: caBundleSecretName: combined-ca-bundle +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: designate-api +spec: + template: + metadata: + labels: + service: designate + spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: service + operator: In + values: + - designate-api + topologyKey: kubernetes.io/hostname + weight: 100 + containers: + - args: + - -c + - /usr/local/bin/kolla_set_configs && /usr/local/bin/kolla_start + command: + - /bin/bash + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 9001 + scheme: HTTPS + initialDelaySeconds: 3 + periodSeconds: 13 + successThreshold: 1 + timeoutSeconds: 15 + name: designate-api + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 9001 + scheme: HTTPS + initialDelaySeconds: 5 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 15 + volumeMounts: + - mountPath: /usr/local/bin/container-scripts + name: scripts + readOnly: true + - mountPath: /var/lib/config-data/merged + name: config-data-merged + - mountPath: /var/lib/kolla/config_files/config.json + name: config-data-merged + readOnly: true + subPath: designate-api-config.json + - mountPath: /run/designate + name: designate-run + - mountPath: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem + name: combined-ca-bundle + readOnly: true + subPath: tls-ca-bundle.pem + - mountPath: /var/lib/config-data/tls/certs/internal.crt + name: internal-tls-certs + readOnly: true + subPath: tls.crt + - mountPath: /var/lib/config-data/tls/private/internal.key + name: internal-tls-certs + readOnly: true + subPath: tls.key + - mountPath: /var/lib/config-data/tls/certs/public.crt + name: public-tls-certs + readOnly: true + subPath: tls.crt + - mountPath: /var/lib/config-data/tls/private/public.key + name: public-tls-certs + readOnly: true + subPath: tls.key + initContainers: + - args: + - -c + - /usr/local/bin/container-scripts/init.sh + command: + - /bin/bash + env: + - name: AdminPassword + valueFrom: + secretKeyRef: + key: DesignatePassword + name: osp-secret + - name: TransportURL + valueFrom: + secretKeyRef: + key: transport_url + name: rabbitmq-transport-url-designate-designate-transport + - name: DatabaseHost + value: openstack.designate-kuttl-tests.svc + - name: DatabaseName + value: designate + imagePullPolicy: IfNotPresent + name: init + resources: {} + volumeMounts: + - mountPath: /usr/local/bin/container-scripts + name: scripts + readOnly: true + - mountPath: /var/lib/config-data/default + name: config-data + readOnly: true + - mountPath: /var/lib/config-data/merged + name: config-data-merged + restartPolicy: Always + serviceAccount: designate-designate + serviceAccountName: designate-designate + volumes: + - name: scripts + secret: + secretName: designate-api-scripts + defaultMode: 493 + - name: config-data + secret: + secretName: designate-api-config-data + defaultMode: 416 + - emptyDir: {} + name: config-data-merged + - emptyDir: {} + name: designate-run + - name: combined-ca-bundle + secret: + secretName: combined-ca-bundle + defaultMode: 292 + - name: internal-tls-certs + secret: + secretName: cert-designate-internal-svc + defaultMode: 256 + - name: public-tls-certs + secret: + secretName: cert-designate-public-svc + defaultMode: 256 +--- +# the openshift annotations can't be checked through the deployment above +apiVersion: v1 +kind: Pod +metadata: + annotations: + openshift.io/scc: anyuid + labels: + service: designate +--- +apiVersion: v1 +kind: Service +metadata: + labels: + endpoint: internal + service: designate + name: designate-internal +spec: + ports: + - name: designate-internal + selector: + service: designate + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + endpoint: public + service: designate + name: designate-public +spec: + ports: + - name: designate-public + selector: + service: designate + type: ClusterIP diff --git a/tests/kuttl/tests/designate_tls/02-deploy.yaml b/tests/kuttl/tests/designate_tls/02-deploy.yaml index fce33c7a..7af46ae8 100644 --- a/tests/kuttl/tests/designate_tls/02-deploy.yaml +++ b/tests/kuttl/tests/designate_tls/02-deploy.yaml @@ -30,15 +30,6 @@ spec: debug = true tls: caBundleSecretName: combined-ca-bundle - designateBackendbind9: - databaseAccount: designate - serviceUser: designate - secret: osp-secret - customServiceConfig: | - [DEFAULT] - debug = true - tls: - caBundleSecretName: combined-ca-bundle designateCentral: databaseAccount: designate serviceUser: designate