diff --git a/api/bases/placement.openstack.org_placementapis.yaml b/api/bases/placement.openstack.org_placementapis.yaml
index 40e1a833..fb688c69 100644
--- a/api/bases/placement.openstack.org_placementapis.yaml
+++ b/api/bases/placement.openstack.org_placementapis.yaml
@@ -365,6 +365,36 @@ spec:
description: ServiceUser - optional username used for this service
to register in keystone
type: string
+ tls:
+ description: TLS - Parameters related to the TLS
+ properties:
+ api:
+ description: API tls type which encapsulates for API services
+ properties:
+ internal:
+ description: Internal GenericService - holds the secret for
+ the internal endpoint
+ properties:
+ secretName:
+ description: SecretName - holding the cert, key for the
+ service
+ type: string
+ type: object
+ public:
+ description: Public GenericService - holds the secret for
+ the public endpoint
+ properties:
+ secretName:
+ description: SecretName - holding the cert, key for the
+ service
+ type: string
+ type: object
+ type: object
+ caBundleSecretName:
+ description: CaBundleSecretName - holding the CA certs in a pre-created
+ bundle file
+ type: string
+ type: object
required:
- containerImage
- databaseInstance
diff --git a/api/v1beta1/placementapi_types.go b/api/v1beta1/placementapi_types.go
index 587a773d..8b3065ac 100644
--- a/api/v1beta1/placementapi_types.go
+++ b/api/v1beta1/placementapi_types.go
@@ -19,6 +19,7 @@ package v1beta1
import (
condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
"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"
corev1 "k8s.io/api/core/v1"
@@ -115,6 +116,11 @@ type PlacementAPISpec struct {
// +kubebuilder:validation:Optional
// Override, provides the ability to override the generated manifest of several child resources.
Override APIOverrideSpec `json:"override,omitempty"`
+
+ // +kubebuilder:validation:Optional
+ // +operator-sdk:csv:customresourcedefinitions:type=spec
+ // TLS - Parameters related to the TLS
+ TLS tls.API `json:"tls,omitempty"`
}
// APIOverrideSpec to override the generated manifest of several child resources.
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index 5067acac..202b2c1d 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -184,6 +184,7 @@ func (in *PlacementAPISpec) DeepCopyInto(out *PlacementAPISpec) {
copy(*out, *in)
}
in.Override.DeepCopyInto(&out.Override)
+ in.TLS.DeepCopyInto(&out.TLS)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementAPISpec.
diff --git a/config/crd/bases/placement.openstack.org_placementapis.yaml b/config/crd/bases/placement.openstack.org_placementapis.yaml
index 40e1a833..fb688c69 100644
--- a/config/crd/bases/placement.openstack.org_placementapis.yaml
+++ b/config/crd/bases/placement.openstack.org_placementapis.yaml
@@ -365,6 +365,36 @@ spec:
description: ServiceUser - optional username used for this service
to register in keystone
type: string
+ tls:
+ description: TLS - Parameters related to the TLS
+ properties:
+ api:
+ description: API tls type which encapsulates for API services
+ properties:
+ internal:
+ description: Internal GenericService - holds the secret for
+ the internal endpoint
+ properties:
+ secretName:
+ description: SecretName - holding the cert, key for the
+ service
+ type: string
+ type: object
+ public:
+ description: Public GenericService - holds the secret for
+ the public endpoint
+ properties:
+ secretName:
+ description: SecretName - holding the cert, key for the
+ service
+ type: string
+ type: object
+ type: object
+ caBundleSecretName:
+ description: CaBundleSecretName - holding the CA certs in a pre-created
+ bundle file
+ type: string
+ type: object
required:
- containerImage
- databaseInstance
diff --git a/config/samples/placement_v1beta1_placementtls.yaml b/config/samples/placement_v1beta1_placementtls.yaml
new file mode 100644
index 00000000..3819b3dd
--- /dev/null
+++ b/config/samples/placement_v1beta1_placementtls.yaml
@@ -0,0 +1,24 @@
+apiVersion: placement.openstack.org/v1beta1
+kind: PlacementAPI
+metadata:
+ name: placement
+spec:
+ serviceUser: placement
+ customServiceConfig: |
+ [DEFAULT]
+ debug = true
+ databaseInstance: openstack
+ databaseUser: placement
+ debug:
+ dbSync: false
+ service: false
+ preserveJobs: false
+ replicas: 1
+ secret: placement-secret
+ tls:
+ api:
+ internal:
+ secretName: cert-internal-svc
+ public:
+ secretName: cert-public-svc
+ caBundleSecretName: combined-ca-bundle
diff --git a/controllers/placementapi_controller.go b/controllers/placementapi_controller.go
index 638615ff..35362f55 100644
--- a/controllers/placementapi_controller.go
+++ b/controllers/placementapi_controller.go
@@ -22,14 +22,18 @@ import (
"time"
apimeta "k8s.io/apimachinery/pkg/api/meta"
+ "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"
"sigs.k8s.io/controller-runtime/pkg/source"
@@ -48,6 +52,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"
"github.com/openstack-k8s-operators/lib-common/modules/common/service"
+ "github.com/openstack-k8s-operators/lib-common/modules/common/tls"
util "github.com/openstack-k8s-operators/lib-common/modules/common/util"
mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1"
@@ -291,6 +296,7 @@ func (r *PlacementAPIReconciler) 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),
// service account, role, rolebinding conditions
condition.UnknownCondition(condition.ServiceAccountReadyCondition, condition.InitReason, condition.ServiceAccountReadyInitMessage),
condition.UnknownCondition(condition.RoleReadyCondition, condition.InitReason, condition.RoleReadyInitMessage),
@@ -318,8 +324,73 @@ func (r *PlacementAPIReconciler) 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 *PlacementAPIReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ // index passwordSecretField
+ if err := mgr.GetFieldIndexer().IndexField(context.Background(), &placementv1.PlacementAPI{}, passwordSecretField, func(rawObj client.Object) []string {
+ // Extract the secret name from the spec, if one is provided
+ cr := rawObj.(*placementv1.PlacementAPI)
+ if cr.Spec.Secret == "" {
+ return nil
+ }
+ return []string{cr.Spec.Secret}
+ }); err != nil {
+ return err
+ }
+
+ // index caBundleSecretNameField
+ if err := mgr.GetFieldIndexer().IndexField(context.Background(), &placementv1.PlacementAPI{}, caBundleSecretNameField, func(rawObj client.Object) []string {
+ // Extract the secret name from the spec, if one is provided
+ cr := rawObj.(*placementv1.PlacementAPI)
+ if cr.Spec.TLS.CaBundleSecretName == "" {
+ return nil
+ }
+ return []string{cr.Spec.TLS.CaBundleSecretName}
+ }); err != nil {
+ return err
+ }
+
+ // index tlsAPIInternalField
+ if err := mgr.GetFieldIndexer().IndexField(context.Background(), &placementv1.PlacementAPI{}, tlsAPIInternalField, func(rawObj client.Object) []string {
+ // Extract the secret name from the spec, if one is provided
+ cr := rawObj.(*placementv1.PlacementAPI)
+ 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(context.Background(), &placementv1.PlacementAPI{}, tlsAPIPublicField, func(rawObj client.Object) []string {
+ // Extract the secret name from the spec, if one is provided
+ cr := rawObj.(*placementv1.PlacementAPI)
+ if cr.Spec.TLS.API.Public.SecretName == nil {
+ return nil
+ }
+ return []string{*cr.Spec.TLS.API.Public.SecretName}
+ }); err != nil {
+ return err
+ }
+
return ctrl.NewControllerManagedBy(mgr).
For(&placementv1.PlacementAPI{}).
Owns(&mariadbv1.MariaDBDatabase{}).
@@ -333,11 +404,47 @@ func (r *PlacementAPIReconciler) SetupWithManager(mgr ctrl.Manager) error {
Owns(&corev1.ServiceAccount{}).
Owns(&rbacv1.Role{}).
Owns(&rbacv1.RoleBinding{}).
- Watches(&source.Kind{Type: &corev1.Secret{}},
- handler.EnqueueRequestsFromMapFunc(r.GetSecretMapperFor(&placementv1.PlacementAPIList{}, context.TODO()))).
+ Watches(
+ &source.Kind{Type: &corev1.Secret{}},
+ handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc),
+ builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
+ ).
Complete(r)
}
+func (r *PlacementAPIReconciler) findObjectsForSrc(src client.Object) []reconcile.Request {
+ requests := []reconcile.Request{}
+
+ l := log.FromContext(context.Background()).WithName("Controllers").WithName("PlacementAPI")
+
+ for _, field := range allWatchFields {
+ crList := &placementv1.PlacementAPIList{}
+ listOps := &client.ListOptions{
+ FieldSelector: fields.OneTermEqualSelector(field, src.GetName()),
+ Namespace: src.GetNamespace(),
+ }
+ err := r.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 *PlacementAPIReconciler) reconcileDelete(ctx context.Context, instance *placementv1.PlacementAPI, helper *helper.Helper) (ctrl.Result, error) {
Log := r.GetLogger(ctx)
Log.Info("Reconciling Service delete")
@@ -573,7 +680,12 @@ func (r *PlacementAPIReconciler) 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 {
@@ -770,6 +882,51 @@ func (r *PlacementAPIReconciler) reconcileNormal(ctx context.Context, instance *
return ctrl.Result{}, err
}
+ // 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)
+ }
+ }
+
+ // 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)
+
+ instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage)
//
// create hash over all the different input resources to identify if any those changed
// and a restart/recreate is required.
@@ -790,7 +947,8 @@ func (r *PlacementAPIReconciler) reconcileNormal(ctx context.Context, instance *
//
serviceLabels := map[string]string{
- common.AppSelector: placement.ServiceName,
+ common.AppSelector: placement.ServiceName,
+ common.OwnerSelector: instance.Name,
}
// networks to attach to
@@ -823,7 +981,7 @@ func (r *PlacementAPIReconciler) reconcileNormal(ctx context.Context, instance *
}
// Handle service init
- ctrlResult, err := r.reconcileInit(ctx, instance, helper, serviceLabels, serviceAnnotations)
+ ctrlResult, err = r.reconcileInit(ctx, instance, helper, serviceLabels, serviceAnnotations)
if err != nil {
return ctrlResult, err
} else if (ctrlResult != ctrl.Result{}) {
@@ -851,8 +1009,14 @@ func (r *PlacementAPIReconciler) reconcileNormal(ctx context.Context, instance *
//
// Define a new Deployment object
- deplDef := placement.Deployment(instance, inputHash, serviceLabels, serviceAnnotations)
+ deplDef, err := placement.Deployment(ctx, helper, 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(
@@ -964,6 +1128,21 @@ func (r *PlacementAPIReconciler) generateServiceConfigMaps(
"log_file": "/var/log/placement/placement-api.log",
}
+ // 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("placement-%s.%s.svc", 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
{
diff --git a/pkg/placement/dbsync.go b/pkg/placement/dbsync.go
index 31ec2775..52b12838 100644
--- a/pkg/placement/dbsync.go
+++ b/pkg/placement/dbsync.go
@@ -44,6 +44,16 @@ func DbSyncJob(
envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS")
envVars["KOLLA_BOOTSTRAP"] = env.SetValue("true")
+ // create Volume and VolumeMounts
+ volumes := getVolumes(instance.Name)
+ volumeMounts := getVolumeMounts("dbsync")
+
+ // 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)...)
+ }
+
job := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: instance.Name + "-db-sync",
@@ -70,16 +80,15 @@ func DbSyncJob(
RunAsUser: ptr.To(PlacementUserID),
},
Env: env.MergeEnvs([]corev1.EnvVar{}, envVars),
- VolumeMounts: getVolumeMounts("dbsync"),
+ VolumeMounts: volumeMounts,
},
},
+ Volumes: volumes,
},
},
},
}
- job.Spec.Template.Spec.Volumes = getVolumes(ServiceName)
-
initContainerDetails := APIDetails{
ContainerImage: instance.Spec.ContainerImage,
DatabaseHost: instance.Status.DatabaseHostname,
@@ -88,7 +97,6 @@ func DbSyncJob(
OSPSecret: instance.Spec.Secret,
DBPasswordSelector: instance.Spec.PasswordSelectors.Database,
UserPasswordSelector: instance.Spec.PasswordSelectors.Service,
- VolumeMounts: getInitVolumeMounts(),
}
job.Spec.Template.Spec.InitContainers = initContainer(initContainerDetails)
diff --git a/pkg/placement/deployment.go b/pkg/placement/deployment.go
index dbda004a..290c3adb 100644
--- a/pkg/placement/deployment.go
+++ b/pkg/placement/deployment.go
@@ -16,9 +16,14 @@ limitations under the License.
package placement
import (
+ "context"
+
common "github.com/openstack-k8s-operators/lib-common/modules/common"
affinity "github.com/openstack-k8s-operators/lib-common/modules/common/affinity"
env "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/service"
+ "github.com/openstack-k8s-operators/lib-common/modules/common/tls"
placementv1 "github.com/openstack-k8s-operators/placement-operator/api/v1beta1"
@@ -31,11 +36,13 @@ import (
// Deployment func
func Deployment(
+ ctx context.Context,
+ helper *helper.Helper,
instance *placementv1.PlacementAPI,
configHash string,
labels map[string]string,
annotations map[string]string,
-) *appsv1.Deployment {
+) (*appsv1.Deployment, error) {
livenessProbe := &corev1.Probe{
// TODO might need tuning
TimeoutSeconds: 5,
@@ -74,12 +81,46 @@ func Deployment(
readinessProbe.HTTPGet = &corev1.HTTPGetAction{
Port: intstr.IntOrString{Type: intstr.Int, IntVal: int32(PlacementPublicPort)},
}
+
+ if instance.Spec.TLS.API.Enabled(service.EndpointPublic) {
+ livenessProbe.HTTPGet.Scheme = corev1.URISchemeHTTPS
+ readinessProbe.HTTPGet.Scheme = corev1.URISchemeHTTPS
+ }
}
envVars := map[string]env.Setter{}
envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS")
envVars["CONFIG_HASH"] = env.SetValue(configHash)
+ // create Volume and VolumeMounts
+ volumes := getVolumes(instance.Name)
+ volumeMounts := getVolumeMounts("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())...)
+ }
+ }
+
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: instance.Name,
@@ -97,6 +138,7 @@ func Deployment(
},
Spec: corev1.PodSpec{
ServiceAccountName: instance.RbacResourceName(),
+ Volumes: volumes,
Containers: []corev1.Container{
{
Name: instance.Name + "-log",
@@ -116,7 +158,7 @@ func Deployment(
RunAsUser: ptr.To(PlacementUserID),
},
Env: env.MergeEnvs([]corev1.EnvVar{}, envVars),
- VolumeMounts: getVolumeMounts("api"),
+ VolumeMounts: volumeMounts,
Resources: instance.Spec.Resources,
ReadinessProbe: readinessProbe,
LivenessProbe: livenessProbe,
@@ -132,7 +174,7 @@ func Deployment(
RunAsUser: ptr.To(PlacementUserID),
},
Env: env.MergeEnvs([]corev1.EnvVar{}, envVars),
- VolumeMounts: getVolumeMounts("api"),
+ VolumeMounts: volumeMounts,
Resources: instance.Spec.Resources,
ReadinessProbe: readinessProbe,
LivenessProbe: livenessProbe,
@@ -143,7 +185,6 @@ func Deployment(
},
}
- deployment.Spec.Template.Spec.Volumes = getVolumes(instance.Name)
// If possible two pods of the same service should not
// run on the same worker node. If this is not possible
// the get still created on the same worker node.
@@ -170,5 +211,5 @@ func Deployment(
}
deployment.Spec.Template.Spec.InitContainers = initContainer(initContainerDetails)
- return deployment
+ return deployment, nil
}
diff --git a/templates/placementapi/config/httpd.conf b/templates/placementapi/config/httpd.conf
index a42952e2..f114b9bf 100644
--- a/templates/placementapi/config/httpd.conf
+++ b/templates/placementapi/config/httpd.conf
@@ -28,22 +28,47 @@ TransferLog /dev/stdout
CustomLog /dev/stdout combined env=!forwarded
CustomLog /dev/stdout proxy env=forwarded
+{{ range $endpt, $vhost := .VHosts }}
+# {{ $endpt }} vhost {{ $vhost.ServerName }} configuration
= 2.4>
ErrorLogFormat "%M"
+ ServerName {{ $vhost.ServerName }}
+
+ ## Vhost docroot
ErrorLog /dev/stdout
SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded
CustomLog /dev/stdout combined env=!forwarded
CustomLog /dev/stdout proxy env=forwarded
+ ServerSignature Off
+ CustomLog /dev/stdout combined
+
+{{- if $vhost.TLS }}
+ SetEnvIf X-Forwarded-Proto https HTTPS=1
+
+ ## SSL directives
+ SSLEngine on
+ SSLCertificateFile "{{ $vhost.SSLCertificateFile }}"
+ SSLCertificateKeyFile "{{ $vhost.SSLCertificateKeyFile }}"
+{{- end }}
+
+ ## Directories, there should at least be a declaration for /var/www/cgi-bin/placement
+
+ Options -Indexes +FollowSymLinks +MultiViews
+ AllowOverride None
+ Require all granted
+
## WSGI configuration
WSGIProcessGroup placement-api
WSGIApplicationGroup %{GLOBAL}
WSGIPassAuthorization On
- WSGIDaemonProcess placement-api processes=3 threads=1 user=placement group=placement
+ WSGIDaemonProcess {{ $endpt }} display-name={{ $endpt }} group=placement processes=3 threads=1 user=placement
+ WSGIProcessGroup {{ $endpt }}
WSGIScriptAlias / /usr/bin/placement-api
+{{ end }}
Alias /placement-api /usr/bin/placement-api
diff --git a/templates/placementapi/config/placement-api-config.json b/templates/placementapi/config/placement-api-config.json
index 57e2d8ff..a0aed17a 100644
--- a/templates/placementapi/config/placement-api-config.json
+++ b/templates/placementapi/config/placement-api-config.json
@@ -18,7 +18,30 @@
"dest": "/etc/httpd/conf/httpd.conf",
"owner": "apache",
"perm": "0644"
+ },
+ {
+ "source": "/var/lib/config-data/merged/ssl.conf",
+ "dest": "/etc/httpd/conf.d/ssl.conf",
+ "owner": "apache",
+ "perm": "0644"
+ },
+ {
+ "source": "/var/lib/config-data/tls/certs/*",
+ "dest": "/etc/pki/tls/certs/",
+ "owner": "placement",
+ "perm": "0440",
+ "optional": true,
+ "merge": true
+ },
+ {
+ "source": "/var/lib/config-data/tls/private/*",
+ "dest": "/etc/pki/tls/private/",
+ "owner": "placement",
+ "perm": "0400",
+ "optional": true,
+ "merge": true
}
+
],
"permissions": [
{
diff --git a/templates/placementapi/config/ssl.conf b/templates/placementapi/config/ssl.conf
new file mode 100644
index 00000000..e3da4ecb
--- /dev/null
+++ b/templates/placementapi/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/tests/functional/base_test.go b/tests/functional/base_test.go
index 1512a13a..e3da2a8b 100644
--- a/tests/functional/base_test.go
+++ b/tests/functional/base_test.go
@@ -28,19 +28,22 @@ import (
)
type Names struct {
- Namespace string
- PlacementAPIName types.NamespacedName
- ConfigMapName types.NamespacedName
- DBSyncJobName types.NamespacedName
- MariaDBDatabaseName types.NamespacedName
- DeploymentName types.NamespacedName
- PublicServiceName types.NamespacedName
- InternalServiceName types.NamespacedName
- KeystoneServiceName types.NamespacedName
- KeystoneEndpointName types.NamespacedName
- ServiceAccountName types.NamespacedName
- RoleName types.NamespacedName
- RoleBindingName types.NamespacedName
+ Namespace string
+ PlacementAPIName types.NamespacedName
+ ConfigMapName types.NamespacedName
+ DBSyncJobName types.NamespacedName
+ MariaDBDatabaseName types.NamespacedName
+ DeploymentName types.NamespacedName
+ PublicServiceName types.NamespacedName
+ InternalServiceName types.NamespacedName
+ KeystoneServiceName types.NamespacedName
+ KeystoneEndpointName types.NamespacedName
+ ServiceAccountName types.NamespacedName
+ RoleName types.NamespacedName
+ RoleBindingName types.NamespacedName
+ CaBundleSecretName types.NamespacedName
+ InternalCertSecretName types.NamespacedName
+ PublicCertSecretName types.NamespacedName
}
func CreateNames(placementAPIName types.NamespacedName) Names {
@@ -82,6 +85,15 @@ func CreateNames(placementAPIName types.NamespacedName) Names {
RoleBindingName: types.NamespacedName{
Namespace: placementAPIName.Namespace,
Name: "placement-" + placementAPIName.Name + "-rolebinding"},
+ CaBundleSecretName: types.NamespacedName{
+ Namespace: placementAPIName.Namespace,
+ Name: CABundleSecretName},
+ InternalCertSecretName: types.NamespacedName{
+ Namespace: placementAPIName.Namespace,
+ Name: InternalCertSecretName},
+ PublicCertSecretName: types.NamespacedName{
+ Namespace: placementAPIName.Namespace,
+ Name: PublicCertSecretName},
}
}
@@ -92,6 +104,25 @@ func GetDefaultPlacementAPISpec() map[string]interface{} {
}
}
+func GetTLSPlacementAPISpec(names Names) map[string]interface{} {
+ return map[string]interface{}{
+ "databaseInstance": "openstack",
+ "replicas": 1,
+ "secret": SecretName,
+ "tls": map[string]interface{}{
+ "api": map[string]interface{}{
+ "internal": map[string]interface{}{
+ "secretName": names.InternalCertSecretName.Name,
+ },
+ "public": map[string]interface{}{
+ "secretName": names.PublicCertSecretName.Name,
+ },
+ },
+ "caBundleSecretName": names.CaBundleSecretName.Name,
+ },
+ }
+}
+
func CreatePlacementAPI(name types.NamespacedName, spec map[string]interface{}) client.Object {
raw := map[string]interface{}{
diff --git a/tests/functional/placementapi_controller_test.go b/tests/functional/placementapi_controller_test.go
index 94253519..42ef4d2f 100644
--- a/tests/functional/placementapi_controller_test.go
+++ b/tests/functional/placementapi_controller_test.go
@@ -31,6 +31,7 @@ import (
)
var _ = Describe("PlacementAPI controller", func() {
+
BeforeEach(func() {
// lib-common uses OPERATOR_TEMPLATES env var to locate the "templates"
// directory of the operator. We need to set them othervise lib-common
@@ -99,6 +100,7 @@ var _ = Describe("PlacementAPI controller", func() {
condition.ServiceAccountReadyCondition,
condition.RoleReadyCondition,
condition.RoleBindingReadyCondition,
+ condition.TLSInputReadyCondition,
}
placement := GetPlacementAPI(names.PlacementAPIName)
@@ -413,7 +415,7 @@ var _ = Describe("PlacementAPI controller", func() {
deployment := th.GetDeployment(names.DeploymentName)
Expect(int(*deployment.Spec.Replicas)).To(Equal(1))
- Expect(deployment.Spec.Selector.MatchLabels).To(Equal(map[string]string{"service": "placement"}))
+ Expect(deployment.Spec.Selector.MatchLabels).To(Equal(map[string]string{"service": "placement", "owner": names.PlacementAPIName.Name}))
Expect(deployment.Spec.Template.Spec.ServiceAccountName).To(Equal(names.ServiceAccountName.Name))
Expect(len(deployment.Spec.Template.Spec.Containers)).To(Equal(2))
@@ -711,4 +713,122 @@ var _ = Describe("PlacementAPI controller", func() {
})
})
+
+ When("A PlacementAPI is created with TLS", func() {
+ BeforeEach(func() {
+ DeferCleanup(k8sClient.Delete, ctx, th.CreateCABundleSecret(names.CaBundleSecretName))
+ DeferCleanup(k8sClient.Delete, ctx, th.CreateCertSecret(names.InternalCertSecretName))
+ DeferCleanup(k8sClient.Delete, ctx, th.CreateCertSecret(names.PublicCertSecretName))
+
+ spec := GetTLSPlacementAPISpec(names)
+ placement := CreatePlacementAPI(names.PlacementAPIName, spec)
+ DeferCleanup(th.DeleteInstance, placement)
+
+ DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(namespace))
+ DeferCleanup(k8sClient.Delete, ctx, CreatePlacementAPISecret(namespace, SecretName))
+
+ serviceSpec := corev1.ServiceSpec{Ports: []corev1.ServicePort{{Port: 3306}}}
+ DeferCleanup(
+ mariadb.DeleteDBService,
+ mariadb.CreateDBService(namespace, "openstack", serviceSpec),
+ )
+ mariadb.SimulateMariaDBDatabaseCompleted(names.MariaDBDatabaseName)
+ keystone.SimulateKeystoneServiceReady(names.KeystoneServiceName)
+ keystone.SimulateKeystoneEndpointReady(names.KeystoneEndpointName)
+ th.SimulateJobSuccess(names.DBSyncJobName)
+ DeferCleanup(th.DeleteInstance, placement)
+ })
+
+ It("it creates deployment with CA and service certs mounted", func() {
+ j := th.GetDeployment(names.DeploymentName)
+
+ container := j.Spec.Template.Spec.Containers[0]
+
+ // CA bundle
+ th.AssertVolumeExists(names.CaBundleSecretName.Name, j.Spec.Template.Spec.Volumes)
+ th.AssertVolumeMountExists(names.CaBundleSecretName.Name, "tls-ca-bundle.pem", j.Spec.Template.Spec.Containers[0].VolumeMounts)
+
+ // service certs
+ th.AssertVolumeExists(names.InternalCertSecretName.Name, j.Spec.Template.Spec.Volumes)
+ th.AssertVolumeExists(names.PublicCertSecretName.Name, j.Spec.Template.Spec.Volumes)
+ th.AssertVolumeMountExists(names.PublicCertSecretName.Name, "tls.key", j.Spec.Template.Spec.Containers[0].VolumeMounts)
+ th.AssertVolumeMountExists(names.PublicCertSecretName.Name, "tls.crt", j.Spec.Template.Spec.Containers[0].VolumeMounts)
+ th.AssertVolumeMountExists(names.InternalCertSecretName.Name, "tls.key", j.Spec.Template.Spec.Containers[0].VolumeMounts)
+ th.AssertVolumeMountExists(names.InternalCertSecretName.Name, "tls.crt", j.Spec.Template.Spec.Containers[0].VolumeMounts)
+
+ Expect(container.ReadinessProbe.HTTPGet.Scheme).To(Equal(corev1.URISchemeHTTPS))
+ Expect(container.LivenessProbe.HTTPGet.Scheme).To(Equal(corev1.URISchemeHTTPS))
+
+ configDataMap := th.GetConfigMap(names.ConfigMapName)
+ Expect(configDataMap).ShouldNot(BeNil())
+ Expect(configDataMap.Data).Should(HaveKey("httpd.conf"))
+ Expect(configDataMap.Data).Should(HaveKey("ssl.conf"))
+ configData := string(configDataMap.Data["httpd.conf"])
+ Expect(configData).Should(ContainSubstring("SSLEngine on"))
+ Expect(configData).Should(ContainSubstring("SSLCertificateFile \"/etc/pki/tls/certs/internal.crt\""))
+ Expect(configData).Should(ContainSubstring("SSLCertificateKeyFile \"/etc/pki/tls/private/internal.key\""))
+ Expect(configData).Should(ContainSubstring("SSLCertificateFile \"/etc/pki/tls/certs/public.crt\""))
+ Expect(configData).Should(ContainSubstring("SSLCertificateKeyFile \"/etc/pki/tls/private/public.key\""))
+ })
+ })
+})
+
+var _ = Describe("PlacementAPI reconfiguration", func() {
+ BeforeEach(func() {
+ err := os.Setenv("OPERATOR_TEMPLATES", "../../templates")
+ Expect(err).NotTo(HaveOccurred())
+ })
+
+ When("TLS certs are reconfigured", func() {
+ BeforeEach(func() {
+
+ DeferCleanup(k8sClient.Delete, ctx, th.CreateCABundleSecret(names.CaBundleSecretName))
+ DeferCleanup(k8sClient.Delete, ctx, th.CreateCertSecret(names.InternalCertSecretName))
+ DeferCleanup(k8sClient.Delete, ctx, th.CreateCertSecret(names.PublicCertSecretName))
+ DeferCleanup(th.DeleteInstance, CreatePlacementAPI(names.PlacementAPIName, GetTLSPlacementAPISpec(names)))
+ DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(namespace))
+ DeferCleanup(k8sClient.Delete, ctx, CreatePlacementAPISecret(namespace, SecretName))
+
+ spec := GetTLSPlacementAPISpec(names)
+ placement := CreatePlacementAPI(names.PlacementAPIName, spec)
+
+ serviceSpec := corev1.ServiceSpec{Ports: []corev1.ServicePort{{Port: 3306}}}
+ DeferCleanup(
+ mariadb.DeleteDBService,
+ mariadb.CreateDBService(namespace, "openstack", serviceSpec),
+ )
+ mariadb.SimulateMariaDBDatabaseCompleted(names.MariaDBDatabaseName)
+ keystone.SimulateKeystoneServiceReady(names.KeystoneServiceName)
+ keystone.SimulateKeystoneEndpointReady(names.KeystoneEndpointName)
+ th.SimulateJobSuccess(names.DBSyncJobName)
+ DeferCleanup(th.DeleteInstance, placement)
+ th.SimulateDeploymentReplicaReady(names.DeploymentName)
+
+ th.ExpectCondition(
+ names.PlacementAPIName,
+ ConditionGetterFunc(PlacementConditionGetter),
+ condition.ReadyCondition,
+ corev1.ConditionTrue,
+ )
+ })
+
+ It("reconfigures the API pod", func() {
+ // Grab the current config hash
+ originalHash := GetEnvVarValue(
+ th.GetDeployment(names.DeploymentName).Spec.Template.Spec.Containers[0].Env, "CONFIG_HASH", "")
+ Expect(originalHash).NotTo(BeEmpty())
+
+ // Change the content of the CA secret
+ th.UpdateSecret(names.CaBundleSecretName, "tls-ca-bundle.pem", []byte("DifferentCAData"))
+
+ // Assert that the deployment is updated
+ Eventually(func(g Gomega) {
+ newHash := GetEnvVarValue(
+ th.GetDeployment(names.DeploymentName).Spec.Template.Spec.Containers[0].Env, "CONFIG_HASH", "")
+ g.Expect(newHash).NotTo(BeEmpty())
+ g.Expect(newHash).NotTo(Equal(originalHash))
+ }, timeout, interval).Should(Succeed())
+ })
+
+ })
})
diff --git a/tests/functional/suite_test.go b/tests/functional/suite_test.go
index d7f8508b..f57c9428 100644
--- a/tests/functional/suite_test.go
+++ b/tests/functional/suite_test.go
@@ -73,6 +73,12 @@ const (
SecretName = "test-osp-secret"
+ PublicCertSecretName = "public-tls-certs"
+
+ InternalCertSecretName = "internal-tls-certs"
+
+ CABundleSecretName = "combined-ca-bundle"
+
interval = time.Millisecond * 200
)
diff --git a/tests/kuttl/common/assert_sample_deployment.yaml b/tests/kuttl/common/assert_sample_deployment.yaml
index 4dfd90ca..63a0f91b 100644
--- a/tests/kuttl/common/assert_sample_deployment.yaml
+++ b/tests/kuttl/common/assert_sample_deployment.yaml
@@ -76,6 +76,10 @@ status:
reason: Ready
status: "True"
type: ServiceConfigReady
+ - message: Input data complete
+ reason: Ready
+ status: "True"
+ type: TLSInputReady
---
apiVersion: apps/v1
kind: Deployment
diff --git a/tests/kuttl/common/tls_certificates.yaml b/tests/kuttl/common/tls_certificates.yaml
new file mode 100644
index 00000000..23a865f2
--- /dev/null
+++ b/tests/kuttl/common/tls_certificates.yaml
@@ -0,0 +1,31 @@
+# Hardcoded certs secret, so kuttl doesn't require cert-manager at test runtime
+apiVersion: v1
+kind: Secret
+metadata:
+ name: combined-ca-bundle
+ labels:
+ service: placement
+data:
+ tls-ca-bundle.pem: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNLZ0F3SUJBZ0lRUHhtRFFscmxjNTNhb215RVU5MU9pakFLQmdncWhrak9QUVFEQWpBZU1Sd3cKR2dZRFZRUURFeE5yZFhSMGJDMXpaV3htYzJsbmJtVmtMV05oTUI0WERUSXpNVEF4T0RFeU1EazFNMW9YRFRJMApNREV4TmpFeU1EazFNMW93SGpFY01Cb0dBMVVFQXhNVGEzVjBkR3d0YzJWc1puTnBaMjVsWkMxallUQlpNQk1HCkJ5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCSVdJY0JiR0cveEg4Lzlkc2lMbkJCdnRqcEZoQ2JRM3U4R0EKZXBVcnhTY25XM0hrZ2hrc1BCVE12M3NCeGdnVFQwL0Eva0dtazRYTkJ0dElnbUZJaFBpalFqQkFNQTRHQTFVZApEd0VCL3dRRUF3SUNwREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlFKaDd3VklFYjgxcFlsCkl3RDAraTBwSnlCTjNqQUtCZ2dxaGtqT1BRUURBZ05KQURCR0FpRUF2a3h5RzZjNzltSDlRWHRIVWFSM014REkKUUVRRGVtL1hZR3VGY1ZCUDJpQUNJUUNFeEZqeStQUTBkNFU5dEJacTVOd1gzdmxibnQxVlNCYWE5VFIrNkNkbAozdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tIyByb290Y2EtaW50ZXJuYWwKLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmekNDQVNhZ0F3SUJBZ0lRUWxlcTNZcDBtU2kwVDNiTm03Q29UVEFLQmdncWhrak9QUVFEQWpBZ01SNHcKSEFZRFZRUURFeFZ5YjI5MFkyRXRhM1YwZEd3dGFXNTBaWEp1WVd3d0hoY05NalF3TVRFMU1URTBOelUwV2hjTgpNelF3TVRFeU1URTBOelUwV2pBZ01SNHdIQVlEVlFRREV4VnliMjkwWTJFdGEzVjBkR3d0YVc1MFpYSnVZV3d3CldUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTRk9rNHJPUldVUGhoTjUrK09EN1I2MW5Gb1lBY0QKenpvUS91SW93NktjeGhwRWNQTDFxb3ZZUGxUYUJabEh3c2FpNE50VHA4aDA1RHVRSGZKOE9JNXFvMEl3UURBTwpCZ05WSFE4QkFmOEVCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXE3TGtFSk1TCm1MOVpKWjBSOUluKzZkclhycEl3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnVlN1K00ydnZ3QlF3eTJHMVlhdkkKQld2RGtSNlRla0I5U0VqdzJIblRSMWtDSUZSNFNkWGFPQkFGWjVHa2RLWCtSY2IzaDFIZm52eFJEVW96bTl2agphenp3Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KIyByb290Y2EtcHVibGljCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlCZXpDQ0FTS2dBd0lCQWdJUU5IREdZc0JzNzk4aWJERDdxL28ybGpBS0JnZ3Foa2pPUFFRREFqQWVNUnd3CkdnWURWUVFERXhOeWIyOTBZMkV0YTNWMGRHd3RjSFZpYkdsak1CNFhEVEkwTURFeE5URXdNVFV6TmxvWERUTTAKTURFeE1qRXdNVFV6Tmxvd0hqRWNNQm9HQTFVRUF4TVRjbTl2ZEdOaExXdDFkSFJzTFhCMVlteHBZekJaTUJNRwpCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkQ3OGF2WHFocmhDNXc4czlXa2Q0SXBiZUV1MDNDUitYWFVkCmtEek9SeXhhOXdjY0lkRGl2YkdKakpGWlRUY1ZtYmpxMUJNWXNqcjEyVUlFNUVUM1ZscWpRakJBTUE0R0ExVWQKRHdFQi93UUVBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJUS0ppeldVSjllVUtpMQpkczBscjZjNnNEN0VCREFLQmdncWhrak9QUVFEQWdOSEFEQkVBaUJJWndZcTYxQnFNSmFCNlVjRm9Sc3hlY3dICjV6L3pNT2RyT3llMG1OaThKZ0lnUUxCNHdES3JwZjl0WDJsb00rMHVUb3BBRFNZSW5yY2ZWdTRGQnVZVTNJZz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: cert-internal-svc
+ labels:
+ service: placement
+data:
+ ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkVENDQVJxZ0F3SUJBZ0lRTkZUVDE2eTc0RGJaOGJTL25ESDBkakFLQmdncWhrak9QUVFEQWpBYU1SZ3cKRmdZRFZRUURFdzl5YjI5MFkyRXRhVzUwWlhKdVlXd3dIaGNOTWpRd01URXdNVFV5T0RBMFdoY05NalF3TkRBNQpNVFV5T0RBMFdqQWFNUmd3RmdZRFZRUURFdzl5YjI5MFkyRXRhVzUwWlhKdVlXd3dXVEFUQmdjcWhrak9QUUlCCkJnZ3Foa2pPUFFNQkJ3TkNBQVFjK2d5OVFCNmw1NFNBQlkxUTJKZWx5MEhSTGEvMzlkRUxzU2RhNnJDRENKQWwKWjJ2bGlGbUo5WVlJNCtSbGRIejJWNXYvYjBpK2x0RjcxMGZ1OHJTbW8wSXdRREFPQmdOVkhROEJBZjhFQkFNQwpBcVF3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVeUsyc0hXaUxHNnR6bWlVbENkUmhsRTJLCnNHSXdDZ1lJS29aSXpqMEVBd0lEU1FBd1JnSWhBSzVtTi9zQlBVcXAwckd1QjhnMVRxY21KR3ZMVUpyNjlnaEEKaEozMldCT1BBaUVBbEtwU0dVTzhac25UcVQrQ1hWbXNuWkxBcVJMV1NhbUI5U2NyczNDZ05zWT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+ tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNhekNDQWhHZ0F3SUJBZ0lSQU1GRmpzWkpHY3BuaVBFNXNmQytrOEV3Q2dZSUtvWkl6ajBFQXdJd0dqRVkKTUJZR0ExVUVBeE1QY205dmRHTmhMV2x1ZEdWeWJtRnNNQjRYRFRJME1ERXhOVEV4TkRnMU1sb1hEVE0wTURFeApNakV4TkRnMU1sb3dBRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNRzhQSWwzCnc4RXdXMHdUUG5qRURpU2dTdVI4WHJaajcrSjYyUkJMTHJ3ZUxKdWd1Wm1MaUh3M09uSldWa0hEOVpaZzlYSGUKbGZ6UDY3Wi8rYXBNMzJ5VWJTVUcrRjlBdXlGMHRTK2lPODFkUFRSY1luNzVBK0xWdnk1UkVpOGIvTFkzNTNPbgpxUEhuK2kyeTNLUC9HZkhjSi9lVlVXNFJkV2wyTHEyejRtRDRUK2twS0VwSnRGSTJQa2lrSVNOV2RRdmtEeW1WClF3a1B3U01FVy9yaEdGL2s3b0gvVWtwdy9wU1N1R0M2a1lpSnlwOTFHT0xCMlVoc254Z3dLelh5VS9MdGFrZXoKS2RHSFUvNUNLTTRKczg0ZnlNTDBBNXMxalpZQXZEWkVLNEgvYVpCb3EzV0NoQ1R4WWhIOVVuczhIQy9KbHJCMApHaitwVHNuaEc2cUlFQ2tDQXdFQUFhT0JoakNCZ3pBT0JnTlZIUThCQWY4RUJBTUNCYUF3RXdZRFZSMGxCQXd3CkNnWUlLd1lCQlFVSEF3RXdEQVlEVlIwVEFRSC9CQUl3QURBZkJnTlZIU01FR0RBV2dCVElyYXdkYUlzYnEzT2EKSlNVSjFHR1VUWXF3WWpBdEJnTlZIUkVCQWY4RUl6QWhnaDlyWlhsemRHOXVaUzFwYm5SbGNtNWhiQzV2Y0dWdQpjM1JoWTJzdWMzWmpNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJRTFJYXcxcnRnU0ROZmxBSjJRek9VQjJxU1llCk03ZWdsaXZLVW01cmVOZThBaUVBMU93SGcwQ1YxOUNhYUpSSi9SS25UcXNJTGhNdjBEUVNPdnFwbWc0MWZDTT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+ tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBd2J3OGlYZkR3VEJiVEJNK2VNUU9KS0JLNUh4ZXRtUHY0bnJaRUVzdXZCNHNtNkM1Cm1ZdUlmRGM2Y2xaV1FjUDFsbUQxY2Q2Vi9NL3J0bi81cWt6ZmJKUnRKUWI0WDBDN0lYUzFMNkk3elYwOU5GeGkKZnZrRDR0Vy9MbEVTTHh2OHRqZm5jNmVvOGVmNkxiTGNvLzhaOGR3bjk1VlJiaEYxYVhZdXJiUGlZUGhQNlNrbwpTa20wVWpZK1NLUWhJMVoxQytRUEtaVkRDUS9CSXdSYit1RVlYK1R1Z2Y5U1NuRCtsSks0WUxxUmlJbktuM1VZCjRzSFpTR3lmR0RBck5mSlQ4dTFxUjdNcDBZZFQva0lvemdtenpoL0l3dlFEbXpXTmxnQzhOa1FyZ2Y5cGtHaXIKZFlLRUpQRmlFZjFTZXp3Y0w4bVdzSFFhUDZsT3llRWJxb2dRS1FJREFRQUJBb0lCQVFDQ0hweUdNK05OY040UQo1V2Z6RXJMeEZKdlloRlBVcXFDbWU1NG9uR1ppUU4zekZPc3pYbzBuNkt3ZnVTOHI4cUtUQXNJM1hhbGRhSVRIClNZTDFSN1pVSmdoOGN3Y0VhdVNFbnU5R2MrODRpbVFlTStLUHAwNWQzdlFOOXJPQTRvcEVGSjRtaHJnbzZZYVYKaE9rK1dJc2piNXVFWlV5UTRiYjdRejRzdW9IVVlDYXFkVGlqU1lYQzNOd092YUlwa3pTNEo0cU5CUlhyYnNWSwowaGt4ZFNIY1hKNEREN0hybktpcEsxT2xUbUVObVZYbmlaNnRPcWc2eUNFeXFteWN0UnlUVTZRRzVPbVM2clJVCm82Z25EclA1TlgwRUhuakY3b1lka0JVbGJxWk96UHVGbG5CdUVKOFpTUEtOZHA5ejhuS3lqbGJiL0YxWGRDdEkKZERhVUhmREZBb0dCQVBFZkZZbDhPb2VhU21oSElKcGpxd3RCanYrRjFuOXNJbHNuZWNyQ1JmN243RW53d1hXaQpReStXQ3l6aDJGRVVad1dod2RQeXFJT3NVaG1vaXBIQmN3NlVUaW9xalM4SlpvSDlURFBQUEd5OXIwMHZwRkNuCnFkdjNXMkhWVytRckMrWk1nc2ZKdUlTTnFtbFdFeHpCNFBJQWRHQTdKVzFMY0ZCcG1Zd25DdXZyQW9HQkFNMncKbS94cVRhMmgySjFnNUI0elE5UnBhM280SEoyL2pTaHEzNW9heVNGNWJDYWtnWGRxek0yU0FwQ0x4dzlvY3doRAp3WWRaMWliaHl6b1dDQVZZZ0RlaXViUi96ZTN3Nzk4NktScUNmNnptMk5HOEoxODVDZDdKSjBiaTZBTTgvalpTCnFqWkJIK0FqanF2aFFJM0FMMEdzNlFvc2Z3L3hOL2k1cG00UWM5TTdBb0dBZjZCbFpQVmxnWnN3WVV1c3ZTdWUKUUlIOTc5Qm12ZUY5dWVRR09rVmtpVTAzSzlnTWZuaFp1WmxnNXV2UDlQS29xVGw2Zi9aRUxoWUxDdHZFSk94UgpPMWxTbWswVmw5MFE3aU1scjVLMHVCWWE4TzhUdVVGVnprRjZsQ2s3ejJUZGtwUFM4VzhiaE1YN2VtLytBODIzCmhFQ3JXTGhWMGlrSkZQY2dPQ2YrUnVzQ2dZQTYvcld1cnhxNmUxb3l3WENNVE8zZWhhSUMrd2NTSTdlcjZRTmIKSXVXZlNVRkEwQndtRVNiT3ExczY5Q3hTK2dWTVVJcTRkSWJjdmhSWkE2cW5SZHY0bVI2a2E2ZTM0RXdjZllUKwppb0Z1S1FQMUcvODY2NVF1SndteDVqRGZoT1h3MU1MbkxzU2l0L0FhMGs5K21LbTFMNC9qa0NHZGcvVW16TEMwCmp0bDVzd0tCZ0VPTVI3ODVLT2hyNXFoWmE2b0MvU25JeEptS1FxTWdXU0NGV1pGMDZrVlRnSmthb1hwUEl0bUIKOUZGbE1nTTJSeC91S2V3YTNDSTdQK240ek1uYSswTmhDL0RwNkMxVFVsVWlrcnJYQ3I5a1NPR2dXaEFISDljTwozRENvdkhOcE1PaG51dnhoMlpDeTdYbjFJeGgxWXdlYnVobFZzeTFvR0tDQ0lJb00rOVg1Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: cert-public-svc
+ labels:
+ service: placement
+data:
+ ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlekNDQVNLZ0F3SUJBZ0lRTkhER1lzQnM3OThpYkREN3EvbzJsakFLQmdncWhrak9QUVFEQWpBZU1Sd3cKR2dZRFZRUURFeE55YjI5MFkyRXRhM1YwZEd3dGNIVmliR2xqTUI0WERUSTBNREV4TlRFd01UVXpObG9YRFRNMApNREV4TWpFd01UVXpObG93SGpFY01Cb0dBMVVFQXhNVGNtOXZkR05oTFd0MWRIUnNMWEIxWW14cFl6QlpNQk1HCkJ5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRDc4YXZYcWhyaEM1dzhzOVdrZDRJcGJlRXUwM0NSK1hYVWQKa0R6T1J5eGE5d2NjSWREaXZiR0pqSkZaVFRjVm1ianExQk1Zc2pyMTJVSUU1RVQzVmxxalFqQkFNQTRHQTFVZApEd0VCL3dRRUF3SUNwREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlRLSml6V1VKOWVVS2kxCmRzMGxyNmM2c0Q3RUJEQUtCZ2dxaGtqT1BRUURBZ05IQURCRUFpQklad1lxNjFCcU1KYUI2VWNGb1JzeGVjd0gKNXovek1PZHJPeWUwbU5pOEpnSWdRTEI0d0RLcnBmOXRYMmxvTSswdVRvcEFEU1lJbnJjZlZ1NEZCdVlVM0lnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
+ tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNiVENDQWhPZ0F3SUJBZ0lSQUtacXlMbUhLNC9VRTZmMi9LNWxiQnN3Q2dZSUtvWkl6ajBFQXdJd0hqRWMKTUJvR0ExVUVBeE1UY205dmRHTmhMV3QxZEhSc0xYQjFZbXhwWXpBZUZ3MHlOREF4TVRVeE1ESXdOVFJhRncwegpOREF4TVRJeE1ESXdOVFJhTUFBd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUMxCjhDcFJRVG1abHNzSUlmZ2hIK2ltUUtFMFdZVlJOeS8vMVM0aDVtV2tBcUZiVkhoUmptbFJ2cCtQUWpKOU16TDUKMXpXdmYxandEQ2pzYUxvL2FwSW9OSXJIcjN4TTRoYWl0emU0RjFwZzNoL3MvblExNWN5Q2U5dHdHR0RuWEllMwo2djBuNE9LNnAwSWJjcVk2Q1RBMTBwcGJZa3V6bzdVRkx6ZWxsc1ZhRlhzZ21JWDg4bTRXNmNBTi84cjJPWUI3Ck9HM0ZNOXAxSUFxT0hyT21EelFlTldqOUVjQy9TSCs5MGg4c1FyY1pvMWtWa1g1b2tpSUhDZjRlc2o3Q08rTGgKR3lsTmZyRzl6QTlPM0c3QVNDWVdPVWwyZTBhNHhZbE9QMmI4ejFEV3NIMTBVYXVsZHlRQXNtbkhtaW1VNzBmKwpEazZkQ1hXVHN4cGZ2cXphOVR4YkFnTUJBQUdqZ1lRd2dZRXdEZ1lEVlIwUEFRSC9CQVFEQWdXZ01CTUdBMVVkCkpRUU1NQW9HQ0NzR0FRVUZCd01CTUF3R0ExVWRFd0VCL3dRQ01BQXdId1lEVlIwakJCZ3dGb0FVeWlZczFsQ2YKWGxDb3RYYk5KYStuT3JBK3hBUXdLd1lEVlIwUkFRSC9CQ0V3SDRJZGEyVjVjM1J2Ym1VdGNIVmliR2xqTG05dwpaVzV6ZEdGamF5NXpkbU13Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnTzAzT2JmNm9uV2RiZG4xa282OVpuTFhMCmtQSHFYU3VRNlcxTDFvY3NDR3NDSVFEakEyVm9pWVdYN0hzSjVGNkZYV3FsZnl0RmduVVgvTmhvT1lIVnB2TWQKSGc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
+ tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdGZBcVVVRTVtWmJMQ0NINElSL29wa0NoTkZtRlVUY3YvOVV1SWVabHBBS2hXMVI0ClVZNXBVYjZmajBJeWZUTXkrZGMxcjM5WThBd283R2k2UDJxU0tEU0t4Njk4VE9JV29yYzN1QmRhWU40ZjdQNTAKTmVYTWdudmJjQmhnNTF5SHQrcjlKK0RpdXFkQ0czS21PZ2t3TmRLYVcySkxzNk8xQlM4M3BaYkZXaFY3SUppRgovUEp1RnVuQURmL0s5am1BZXpodHhUUGFkU0FLamg2enBnODBIalZvL1JIQXYwaC92ZElmTEVLM0dhTlpGWkYrCmFKSWlCd24rSHJJK3dqdmk0UnNwVFg2eHZjd1BUdHh1d0VnbUZqbEpkbnRHdU1XSlRqOW0vTTlRMXJCOWRGR3IKcFhja0FMSnB4NW9wbE85SC9nNU9uUWwxazdNYVg3NnMydlU4V3dJREFRQUJBb0lCQUd0eVdvdUNLYkk3Qzh6Ugp3dWhOSCtpUFlxUzMrYlB0RTd2UytsdXE1WHZtMGNST0xvQjd5bGNzYks3K09UTVhlWk56TlpGZmMvYlFONXJtCmZwZlZLRnYySzcraU01WjBMMG9KU2k2K0cvSDVQSUdLQkxlUDd5ZGdYa2ZsSGRXRkgrSE9OWlBIakI4UGlFc04KZW4zcnp6ejZFNDdFamxDWTdkOFI4NXNuWDRYREN2bG1CQnhvcnpqVERuK1dTWWpKS09SSk5zY3oxQXFYR1VjVwpQaHRNYkwybC8zN2hPbTA4SjRRWXowTWduOWE5VUFXLzFNS2lXbHVpc1NHNG9YaFNPS1hkdk1IS3VxS09sUDJzCk9xWjBlR3JBNmpKdWlmZVY2Q2NIU2p3VUgwdHpiMmdZQVM2cm5RQlREbFkxR1I4Skx0YWhWREtqdUwyV0hjclkKbHhCOGZBRUNnWUVBMWpxc01weFo5cG9LNkpmRDZzVTU0ZUU1UWlNYlB0MERRZjlYU1J6NW5zQXlraHdKWEZDVwpKWTNiU3BhcGREeEgzakpDQ0VzN3NSWUhUeDRzNVJQSldtWC9oZTFwVEs4TDVlaFV2TmVudG5nVTB3aE4rZVEzCjl4Sk1VbHVYdGkvU0FpNi9jQk5HY1ZjQjFGRStmVzY0VDhqYVVQakRrL0Z2dFRXOXkzZnNVZnNDZ1lFQTJXbXMKYStuZ2RaS24rVTlCMlFCTXB0K0RLL0txNVF6RW1qZDVMSjJMb3FLUjhGbjlpVVZoUVljUEpobDJVV3VjTTl0RQp0QUlYdEY0anVUejlqUUNMMGQ5a09DeCswdTBKUFZJejdlVmFFVGs1enF0azRsTnhVUkJhQ3pCUEJkdjZJd3BDCkR4UXJWRXBXYlMra1JvYTNKSElOdHdjUWt2Nk50N1JIajd1WEVTRUNnWUJsREozbTdZc2Q0QUZmUHg4Qm9YQXgKRkt5T2ZzSytQei9uSkl0R2lHMVNMWFJ0S041ZGRnR3N5eUh5SitqY1ZBYk9UMFNJWnZ4TUJva0NEOGk3Y1Q3Ygo3aHErVUlNSDBkVzU1NEg0NVh4TmZJek9FaSs5dktHTllFc3gyZFJROG5PTDVnTVUyWEt6eVllcVgzd3JiRXR5CkR0cXpzUE9IMkMySiswU0FNaHY5ZXdLQmdRQ2szeWs5TUwvaUNWUk9rTmNybTdtRk5xeS9rQ2dleU43eTRDeUoKTS9RbllrZHYwSjZmRWJrZU96QzJ3TXBrRmtuL1hVR3RqSVN6YUV5STlnS0ZnaXVGL1hWL3orWmhTQllncFl6eAoxR0xIK3ZDbWxIMU4wTjkzRFFKcng3ZTFoc3NhOVhXQS85ZVg5VU96UzFTMWt3V2hvc2haeXdhN29rU1FVaXVPCmlVQ1hZUUtCZ1FEUVZUVHc3WUY3QzNTVmg5OWRObUdTaHV2LzZ2aTJmNDlOMklGMURNQ1haaEpoOUVZck9TV2kKY05oakxGRFhmdzVlZlFURWU3Ykx5bTJGVDd0YnZFSm5USHFyakVuUDRUWExqZnczL3RiQ3RxWVNZRlRqdThFUApadHVwd21ZWjhFVU1pSnVHS2l2SExmSjk2dy8xR21BOHVCZUVtV05YRW9FUU1ySmxuM3g5d3c9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
diff --git a/tests/kuttl/tests/placement_deploy_tls/00-cleanup-placement.yaml b/tests/kuttl/tests/placement_deploy_tls/00-cleanup-placement.yaml
new file mode 120000
index 00000000..e067bd15
--- /dev/null
+++ b/tests/kuttl/tests/placement_deploy_tls/00-cleanup-placement.yaml
@@ -0,0 +1 @@
+../../common/cleanup-placement.yaml
\ No newline at end of file
diff --git a/tests/kuttl/tests/placement_deploy_tls/01-assert.yaml b/tests/kuttl/tests/placement_deploy_tls/01-assert.yaml
new file mode 100644
index 00000000..119adc66
--- /dev/null
+++ b/tests/kuttl/tests/placement_deploy_tls/01-assert.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: cert-internal-svc
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: cert-public-svc
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: combined-ca-bundle
diff --git a/tests/kuttl/tests/placement_deploy_tls/01-tls_certificates.yaml b/tests/kuttl/tests/placement_deploy_tls/01-tls_certificates.yaml
new file mode 120000
index 00000000..b63d9bfb
--- /dev/null
+++ b/tests/kuttl/tests/placement_deploy_tls/01-tls_certificates.yaml
@@ -0,0 +1 @@
+../../common/tls_certificates.yaml
\ No newline at end of file
diff --git a/tests/kuttl/tests/placement_deploy_tls/02-placementapi_deploy_tls.yaml b/tests/kuttl/tests/placement_deploy_tls/02-placementapi_deploy_tls.yaml
new file mode 120000
index 00000000..11447fab
--- /dev/null
+++ b/tests/kuttl/tests/placement_deploy_tls/02-placementapi_deploy_tls.yaml
@@ -0,0 +1 @@
+../../../../config/samples/placement_v1beta1_placementtls.yaml
\ No newline at end of file
diff --git a/tests/kuttl/tests/placement_deploy_tls/03-assert.yaml b/tests/kuttl/tests/placement_deploy_tls/03-assert.yaml
new file mode 100644
index 00000000..684418c3
--- /dev/null
+++ b/tests/kuttl/tests/placement_deploy_tls/03-assert.yaml
@@ -0,0 +1,243 @@
+apiVersion: placement.openstack.org/v1beta1
+kind: PlacementAPI
+metadata:
+ finalizers:
+ - PlacementAPI
+ name: placement
+spec:
+ customServiceConfig: |
+ [DEFAULT]
+ debug = true
+ databaseInstance: openstack
+ databaseUser: placement
+ debug:
+ dbSync: false
+ service: false
+ passwordSelectors:
+ database: PlacementDatabasePassword
+ service: PlacementPassword
+ preserveJobs: false
+ replicas: 1
+ secret: osp-secret
+ serviceUser: placement
+ tls:
+ api:
+ internal:
+ secretName: cert-internal-svc
+ public:
+ secretName: cert-public-svc
+ caBundleSecretName: combined-ca-bundle
+status:
+ databaseHostname: openstack
+ readyCount: 1
+ conditions:
+ - message: Setup complete
+ reason: Ready
+ status: "True"
+ type: Ready
+ - message: DB create completed
+ reason: Ready
+ status: "True"
+ type: DBReady
+ - message: DBsync completed
+ reason: Ready
+ status: "True"
+ type: DBSyncReady
+ - message: Deployment completed
+ reason: Ready
+ status: "True"
+ type: DeploymentReady
+ - message: Exposing service completed
+ reason: Ready
+ status: "True"
+ type: ExposeServiceReady
+ - message: Input data complete
+ reason: Ready
+ status: "True"
+ type: InputReady
+ - message: Setup complete
+ reason: Ready
+ status: "True"
+ type: KeystoneEndpointReady
+ - message: Setup complete
+ reason: Ready
+ status: "True"
+ type: KeystoneServiceReady
+ - message: NetworkAttachments completed
+ reason: Ready
+ status: "True"
+ type: NetworkAttachmentsReady
+ - message: RoleBinding created
+ reason: Ready
+ status: "True"
+ type: RoleBindingReady
+ - message: Role created
+ reason: Ready
+ status: "True"
+ type: RoleReady
+ - message: ServiceAccount created
+ reason: Ready
+ status: "True"
+ type: ServiceAccountReady
+ - message: Service config create completed
+ reason: Ready
+ status: "True"
+ type: ServiceConfigReady
+ - message: Input data complete
+ reason: Ready
+ status: "True"
+ type: TLSInputReady
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: placement
+spec:
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ service: placement
+ spec:
+ affinity:
+ podAntiAffinity:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - podAffinityTerm:
+ labelSelector:
+ matchExpressions:
+ - key: service
+ operator: In
+ values:
+ - placement
+ topologyKey: kubernetes.io/hostname
+ weight: 1
+ containers:
+ - args:
+ - --single-child
+ - --
+ - /usr/bin/tail
+ - -n+1
+ - -F
+ - /var/log/placement/placement-api.log
+ command:
+ - /usr/bin/dumb-init
+ imagePullPolicy: IfNotPresent
+ livenessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /
+ port: 8778
+ scheme: HTTPS
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ successThreshold: 1
+ timeoutSeconds: 5
+ name: placement-log
+ readinessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /
+ port: 8778
+ scheme: HTTPS
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ successThreshold: 1
+ timeoutSeconds: 5
+ resources: {}
+ securityContext:
+ runAsUser: 42482
+ terminationMessagePath: /dev/termination-log
+ terminationMessagePolicy: File
+ 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: placement-api-config.json
+ - mountPath: /var/log/placement
+ name: logs
+ - 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
+ - args:
+ - -c
+ - /usr/local/bin/kolla_start
+ command:
+ - /bin/bash
+ imagePullPolicy: IfNotPresent
+ livenessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /
+ port: 8778
+ scheme: HTTPS
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ successThreshold: 1
+ timeoutSeconds: 5
+ name: placement-api
+ readinessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /
+ port: 8778
+ scheme: HTTPS
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ successThreshold: 1
+ timeoutSeconds: 5
+ resources: {}
+ initContainers:
+ - args:
+ - -c
+ - /usr/local/bin/container-scripts/init.sh
+ command:
+ - /bin/bash
+ env:
+ - name: DatabasePassword
+ valueFrom:
+ secretKeyRef:
+ key: PlacementDatabasePassword
+ name: osp-secret
+ - name: PlacementPassword
+ valueFrom:
+ secretKeyRef:
+ key: PlacementPassword
+ name: osp-secret
+ - name: DatabaseHost
+ value: openstack
+ - name: DatabaseName
+ value: placement
+ - name: DatabaseUser
+ value: placement
+ imagePullPolicy: IfNotPresent
+ name: init
+ resources: {}
+ restartPolicy: Always
+ securityContext: {}
+ serviceAccount: placement-placement
+ serviceAccountName: placement-placement
+status:
+ availableReplicas: 1
+ replicas: 1
diff --git a/tests/kuttl/tests/placement_deploy_tls/03-patch_placement_deploy.yaml b/tests/kuttl/tests/placement_deploy_tls/03-patch_placement_deploy.yaml
new file mode 120000
index 00000000..82362fc7
--- /dev/null
+++ b/tests/kuttl/tests/placement_deploy_tls/03-patch_placement_deploy.yaml
@@ -0,0 +1 @@
+../../common/patch_placement_deploy.yaml
\ No newline at end of file
diff --git a/tests/kuttl/tests/placement_deploy_tls/04-cleanup-placement.yaml b/tests/kuttl/tests/placement_deploy_tls/04-cleanup-placement.yaml
new file mode 120000
index 00000000..e067bd15
--- /dev/null
+++ b/tests/kuttl/tests/placement_deploy_tls/04-cleanup-placement.yaml
@@ -0,0 +1 @@
+../../common/cleanup-placement.yaml
\ No newline at end of file
diff --git a/tests/kuttl/tests/placement_deploy_tls/04-errors.yaml b/tests/kuttl/tests/placement_deploy_tls/04-errors.yaml
new file mode 120000
index 00000000..b05cc60f
--- /dev/null
+++ b/tests/kuttl/tests/placement_deploy_tls/04-errors.yaml
@@ -0,0 +1 @@
+../../common/errors_cleanup_placement.yaml
\ No newline at end of file