diff --git a/internal/controller/ironic_controller.go b/internal/controller/ironic_controller.go index 33da9343..aaa7f821 100644 --- a/internal/controller/ironic_controller.go +++ b/internal/controller/ironic_controller.go @@ -376,28 +376,41 @@ func (r *IronicReconciler) reconcileNormal(ctx context.Context, instance *ironic // // check for required OpenStack secret holding passwords for service/admin user and add hash to the vars map // - ospSecret, hash, err := oko_secret.GetSecret(ctx, helper, instance.Spec.Secret, instance.Namespace) + // Associate to PasswordSelectors.Service field a password validator to + // ensure pwd invalid detected patterns are rejected. + validateFields := map[string]oko_secret.Validator{ + instance.Spec.PasswordSelectors.Service: oko_secret.PasswordValidator{}, + } + hash, ctrlResult, err := oko_secret.VerifySecretFields( + ctx, + types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Spec.Secret, + }, + validateFields, + helper.GetClient(), + time.Duration(10)*time.Second, + ) if err != nil { - if k8s_errors.IsNotFound(err) { - // Since the OpenStack secret should have been manually created by the user and referenced in the spec, - // we treat this as a warning because it means that the service will not be able to start. - Log.Info(fmt.Sprintf("OpenStack secret %s not found", instance.Spec.Secret)) - instance.Status.Conditions.Set(condition.FalseCondition( - condition.InputReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.InputReadyWaitingMessage)) - return ctrl.Result{}, nil - } instance.Status.Conditions.Set(condition.FalseCondition( condition.InputReadyCondition, condition.ErrorReason, condition.SeverityWarning, condition.InputReadyErrorMessage, err.Error())) - return ctrl.Result{}, err + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + // Since the service secret should have been manually created by the user and referenced in the spec, + // we treat this as a warning because it means that the service will not be able to start. + log.FromContext(ctx).Info(fmt.Sprintf("OpenStack secret %s not found", instance.Spec.Secret)) + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.InputReadyWaitingMessage)) + return ctrlResult, err } - configMapVars[ospSecret.Name] = env.SetValue(hash) + configMapVars[instance.Spec.Secret] = env.SetValue(hash) instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage) // run check OpenStack secret - end @@ -480,7 +493,7 @@ func (r *IronicReconciler) reconcileNormal(ctx context.Context, instance *ironic } // Handle service upgrade - ctrlResult, err := r.reconcileUpgrade(ctx, instance, helper, serviceLabels) + ctrlResult, err = r.reconcileUpgrade(ctx, instance, helper, serviceLabels) if err != nil { return ctrlResult, err } else if (ctrlResult != ctrl.Result{}) { diff --git a/internal/controller/ironicapi_controller.go b/internal/controller/ironicapi_controller.go index 0c268f78..b3545c7e 100644 --- a/internal/controller/ironicapi_controller.go +++ b/internal/controller/ironicapi_controller.go @@ -676,57 +676,81 @@ func (r *IronicAPIReconciler) reconcileNormal(ctx context.Context, instance *iro // // check for required OpenStack secret holding passwords for service/admin user and add hash to the vars map // - ospSecret, hash, err := secret.GetSecret(ctx, helper, instance.Spec.Secret, instance.Namespace) + // Associate to PasswordSelectors.Service field a password validator to + // ensure pwd invalid detected patterns are rejected. + validateFields := map[string]secret.Validator{ + instance.Spec.PasswordSelectors.Service: secret.PasswordValidator{}, + } + hash, ctrlResult, err := secret.VerifySecretFields( + ctx, + types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Spec.Secret, + }, + validateFields, + helper.GetClient(), + time.Duration(10)*time.Second, + ) if err != nil { - if k8s_errors.IsNotFound(err) { - // Since the OpenStack secret should have been manually created by the user and referenced in the spec, - // we treat this as a warning because it means that the service will not be able to start. - Log.Info(fmt.Sprintf("OpenStack secret %s not found", instance.Spec.Secret)) - instance.Status.Conditions.Set(condition.FalseCondition( - condition.InputReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.InputReadyWaitingMessage)) - return ctrl.Result{RequeueAfter: time.Second * 10}, nil - } instance.Status.Conditions.Set(condition.FalseCondition( condition.InputReadyCondition, condition.ErrorReason, condition.SeverityWarning, condition.InputReadyErrorMessage, err.Error())) - return ctrl.Result{}, err + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + // Since the service secret should have been manually created by the user and referenced in the spec, + // we treat this as a warning because it means that the service will not be able to start. + log.FromContext(ctx).Info(fmt.Sprintf("OpenStack secret %s not found", instance.Spec.Secret)) + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.InputReadyWaitingMessage)) + return ctrlResult, err } - configMapVars[ospSecret.Name] = env.SetValue(hash) + configMapVars[instance.Spec.Secret] = env.SetValue(hash) // run check OpenStack secret - end // // check for required TransportURL secret holding transport URL string // if instance.Spec.RPCTransport == "oslo" { - transportURLSecret, hash, err := secret.GetSecret(ctx, helper, instance.Spec.TransportURLSecret, instance.Namespace) + // transportURLFields are not pure password fields. We do not associate a + // password validator and we only verify that the entry exists in the + // secret + transportValidateFields := map[string]secret.Validator{ + "transport_url": secret.NoOpValidator{}, + } + hash, ctrlResult, err = secret.VerifySecretFields( + ctx, + types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Spec.TransportURLSecret, + }, + transportValidateFields, + helper.GetClient(), + time.Duration(10)*time.Second, + ) if err != nil { - if k8s_errors.IsNotFound(err) { - // Since the TransportURL secret should have been previously automatically created by the parent - // Ironic CR and then referenced in this instance's spec, we treat this as a warning because it - // means that the service will not be able to start. - Log.Info(fmt.Sprintf("TransportURL secret %s not found", instance.Spec.TransportURLSecret)) - instance.Status.Conditions.Set(condition.FalseCondition( - condition.InputReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.InputReadyWaitingMessage)) - return ctrl.Result{RequeueAfter: time.Second * 10}, nil - } instance.Status.Conditions.Set(condition.FalseCondition( condition.InputReadyCondition, condition.ErrorReason, condition.SeverityWarning, condition.InputReadyErrorMessage, err.Error())) - return ctrl.Result{}, err + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + Log.Info(fmt.Sprintf("TransportURL secret %s not found", instance.Spec.TransportURLSecret)) + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.InputReadyWaitingMessage)) + return ctrlResult, err } - configMapVars[transportURLSecret.Name] = env.SetValue(hash) + configMapVars[instance.Spec.TransportURLSecret] = env.SetValue(hash) } // run check TransportURL secret - end @@ -877,7 +901,7 @@ func (r *IronicAPIReconciler) reconcileNormal(ctx context.Context, instance *iro } // Handle service init - ctrlResult, err := r.reconcileInit(ctx, instance, helper, serviceLabels) + ctrlResult, err = r.reconcileInit(ctx, instance, helper, serviceLabels) if err != nil { return ctrlResult, err } else if (ctrlResult != ctrl.Result{}) { diff --git a/internal/controller/ironicconductor_controller.go b/internal/controller/ironicconductor_controller.go index ca27088f..c2319c5d 100644 --- a/internal/controller/ironicconductor_controller.go +++ b/internal/controller/ironicconductor_controller.go @@ -550,57 +550,82 @@ func (r *IronicConductorReconciler) reconcileNormal(ctx context.Context, instanc // // check for required OpenStack secret holding passwords for service/admin user and add hash to the vars map // - ospSecret, hash, err := secret.GetSecret(ctx, helper, instance.Spec.Secret, instance.Namespace) + // Associate to PasswordSelectors.Service field a password validator to + // ensure pwd invalid detected patterns are rejected. + validateFields := map[string]secret.Validator{ + instance.Spec.PasswordSelectors.Service: secret.PasswordValidator{}, + } + hash, ctrlResult, err := secret.VerifySecretFields( + ctx, + types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Spec.Secret, + }, + validateFields, + helper.GetClient(), + time.Duration(10)*time.Second, + ) if err != nil { - if k8s_errors.IsNotFound(err) { - // Since the OpenStack secret should have been manually created by the user and referenced in the spec, - // we treat this as a warning because it means that the service will not be able to start. - Log.Info(fmt.Sprintf("OpenStack secret %s not found", instance.Spec.Secret)) - instance.Status.Conditions.Set(condition.FalseCondition( - condition.InputReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.InputReadyWaitingMessage)) - return ctrl.Result{RequeueAfter: time.Second * 10}, nil - } instance.Status.Conditions.Set(condition.FalseCondition( condition.InputReadyCondition, condition.ErrorReason, condition.SeverityWarning, condition.InputReadyErrorMessage, err.Error())) - return ctrl.Result{}, err + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + // Since the service secret should have been manually created by the user and referenced in the spec, + // we treat this as a warning because it means that the service will not be able to start. + log.FromContext(ctx).Info(fmt.Sprintf("OpenStack secret %s not found", instance.Spec.Secret)) + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.InputReadyWaitingMessage)) + return ctrlResult, err } - configMapVars[ospSecret.Name] = env.SetValue(hash) + configMapVars[instance.Spec.Secret] = env.SetValue(hash) // run check OpenStack secret - end // // check for required TransportURL secret holding transport URL string // if instance.Spec.RPCTransport == "oslo" { - transportURLSecret, hash, err := secret.GetSecret(ctx, helper, instance.Spec.TransportURLSecret, instance.Namespace) + // transportURLFields are not pure password fields. We do not associate a + // password validator and we only verify that the entry exists in the + // secret + transportValidateFields := map[string]secret.Validator{ + "transport_url": secret.NoOpValidator{}, + } + hash, ctrlResult, err = secret.VerifySecretFields( + ctx, + types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Spec.TransportURLSecret, + }, + transportValidateFields, + helper.GetClient(), + time.Duration(10)*time.Second, + ) + // NOTE: This should be moved to TransportURLSecretReadyCondition if err != nil { - if k8s_errors.IsNotFound(err) { - // Since the TransportURL secret should have been previously automatically created by the parent - // Ironic CR and then referenced in this instance's spec, we treat this as a warning because it - // means that the service will not be able to start. - Log.Info(fmt.Sprintf("TransportURL secret %s not found", instance.Spec.TransportURLSecret)) - instance.Status.Conditions.Set(condition.FalseCondition( - condition.InputReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.InputReadyWaitingMessage)) - return ctrl.Result{RequeueAfter: time.Second * 10}, nil - } instance.Status.Conditions.Set(condition.FalseCondition( condition.InputReadyCondition, condition.ErrorReason, condition.SeverityWarning, condition.InputReadyErrorMessage, err.Error())) - return ctrl.Result{}, err + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + Log.Info(fmt.Sprintf("TransportURL secret %s not found", instance.Spec.TransportURLSecret)) + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.InputReadyWaitingMessage)) + return ctrlResult, err } - configMapVars[transportURLSecret.Name] = env.SetValue(hash) + configMapVars[instance.Spec.TransportURLSecret] = env.SetValue(hash) } // run check TransportURL secret - end @@ -772,7 +797,7 @@ func (r *IronicConductorReconciler) reconcileNormal(ctx context.Context, instanc time.Duration(5)*time.Second, ) - ctrlResult, err := ss.CreateOrPatch(ctx, helper) + ctrlResult, err = ss.CreateOrPatch(ctx, helper) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.DeploymentReadyCondition, diff --git a/internal/controller/ironicinspector_controller.go b/internal/controller/ironicinspector_controller.go index a5a815a2..6262e7d4 100644 --- a/internal/controller/ironicinspector_controller.go +++ b/internal/controller/ironicinspector_controller.go @@ -614,58 +614,77 @@ func (r *IronicInspectorReconciler) reconcileConfigMapsAndSecrets( // check for required OpenStack secret holding passwords for // service/admin user and add hash to the vars map // - ospSecret, hash, err := oko_secret.GetSecret( + // ensure pwd invalid detected patterns are rejected. + validateFields := map[string]oko_secret.Validator{ + instance.Spec.PasswordSelectors.Service: oko_secret.PasswordValidator{}, + } + hash, ctrlResult, err := oko_secret.VerifySecretFields( ctx, - helper, - instance.Spec.Secret, - instance.Namespace) + types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Spec.Secret, + }, + validateFields, + helper.GetClient(), + time.Duration(10)*time.Second, + ) if err != nil { - if k8s_errors.IsNotFound(err) { - // Since the OpenStack secret should have been manually created by the user and referenced in the spec, - // we treat this as a warning because it means that the service will not be able to start. - Log.Info(fmt.Sprintf("OpenStack secret %s not found", instance.Spec.Secret)) - instance.Status.Conditions.Set( - condition.FalseCondition( - condition.InputReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.InputReadyWaitingMessage)) - return ctrl.Result{RequeueAfter: time.Second * 10}, "", nil - } - instance.Status.Conditions.Set( - condition.FalseCondition( - condition.InputReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.InputReadyErrorMessage, - err.Error())) - return ctrl.Result{}, "", err + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.InputReadyErrorMessage, + err.Error())) + return ctrlResult, "", err + } else if (ctrlResult != ctrl.Result{}) { + // Since the service secret should have been manually created by the user and referenced in the spec, + // we treat this as a warning because it means that the service will not be able to start. + log.FromContext(ctx).Info(fmt.Sprintf("OpenStack secret %s not found", instance.Spec.Secret)) + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.InputReadyWaitingMessage)) + return ctrlResult, "", err } - configMapVars[ospSecret.Name] = env.SetValue(hash) + configMapVars[instance.Spec.Secret] = env.SetValue(hash) // check for required TransportURL secret and add hash to the vars map if instance.Status.TransportURLSecret != "" { - transportURLSecret, hash, err := oko_secret.GetSecret(ctx, helper, instance.Status.TransportURLSecret, instance.Namespace) + // transportURLFields are not pure password fields. We do not associate a + // password validator and we only verify that the entry exists in the + // secret + transportValidateFields := map[string]oko_secret.Validator{ + "transport_url": oko_secret.NoOpValidator{}, + } + hash, ctrlResult, err = oko_secret.VerifySecretFields( + ctx, + types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Status.TransportURLSecret, + }, + transportValidateFields, + helper.GetClient(), + time.Duration(10)*time.Second, + ) if err != nil { - if k8s_errors.IsNotFound(err) { - // This controller itself creates the TransportURL secret, so we treat this as an info. - Log.Info(fmt.Sprintf("TransportURL secret %s not found", instance.Status.TransportURLSecret)) - instance.Status.Conditions.Set(condition.FalseCondition( - condition.InputReadyCondition, - condition.RequestedReason, - condition.SeverityInfo, - condition.InputReadyWaitingMessage)) - return ctrl.Result{RequeueAfter: time.Second * 10}, "", nil - } instance.Status.Conditions.Set(condition.FalseCondition( condition.InputReadyCondition, condition.ErrorReason, condition.SeverityWarning, condition.InputReadyErrorMessage, err.Error())) - return ctrl.Result{}, "", err + return ctrlResult, "", err + } else if (ctrlResult != ctrl.Result{}) { + Log.Info(fmt.Sprintf("TransportURL secret %s not found", instance.Status.TransportURLSecret)) + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.InputReadyWaitingMessage)) + return ctrlResult, "", err } - configMapVars[transportURLSecret.Name] = env.SetValue(hash) + configMapVars[instance.Status.TransportURLSecret] = env.SetValue(hash) } instance.Status.Conditions.MarkTrue( diff --git a/internal/controller/ironicneutronagent_controller.go b/internal/controller/ironicneutronagent_controller.go index 76112297..05693552 100644 --- a/internal/controller/ironicneutronagent_controller.go +++ b/internal/controller/ironicneutronagent_controller.go @@ -479,53 +479,78 @@ func (r *IronicNeutronAgentReconciler) reconcileConfigMapsAndSecrets( // ConfigMap configMapVars := make(map[string]env.Setter) - // check for required OpenStack secret holding passwords for service/admin user and add hash to the vars map - ospSecret, hash, err := secret.GetSecret(ctx, helper, instance.Spec.Secret, instance.Namespace) + // Associate to PasswordSelectors.Service field a password validator to + // ensure pwd invalid detected patterns are rejected. + validateFields := map[string]secret.Validator{ + instance.Spec.PasswordSelectors.Service: secret.PasswordValidator{}, + } + hash, ctrlResult, err := secret.VerifySecretFields( + ctx, + types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Spec.Secret, + }, + validateFields, + helper.GetClient(), + time.Duration(10)*time.Second, + ) if err != nil { - if k8s_errors.IsNotFound(err) { - // Since the OpenStack secret should have been manually created by the user and referenced in the spec, - // we treat this as a warning because it means that the service will not be able to start. - Log.Info(fmt.Sprintf("OpenStack secret %s not found", instance.Spec.Secret)) - instance.Status.Conditions.Set(condition.FalseCondition( - condition.InputReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.InputReadyWaitingMessage)) - return ctrl.Result{RequeueAfter: time.Second * 10}, "", nil - } instance.Status.Conditions.Set(condition.FalseCondition( condition.InputReadyCondition, condition.ErrorReason, condition.SeverityWarning, condition.InputReadyErrorMessage, err.Error())) - return ctrl.Result{}, "", err + return ctrlResult, "", err + } else if (ctrlResult != ctrl.Result{}) { + // Since the service secret should have been manually created by the user and referenced in the spec, + // we treat this as a warning because it means that the service will not be able to start. + log.FromContext(ctx).Info(fmt.Sprintf("OpenStack secret %s not found", instance.Spec.Secret)) + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.InputReadyWaitingMessage)) + return ctrlResult, "", err } - configMapVars[ospSecret.Name] = env.SetValue(hash) + configMapVars[instance.Spec.Secret] = env.SetValue(hash) // check for required TransportURL secret and add hash to the vars map if instance.Status.TransportURLSecret != "" { - transportURLSecret, hash, err := secret.GetSecret(ctx, helper, instance.Status.TransportURLSecret, instance.Namespace) + // transportURLFields are not pure password fields. We do not associate a + // password validator and we only verify that the entry exists in the + // secret + transportValidateFields := map[string]secret.Validator{ + "transport_url": secret.NoOpValidator{}, + } + hash, ctrlResult, err = secret.VerifySecretFields( + ctx, + types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Status.TransportURLSecret, + }, + transportValidateFields, + helper.GetClient(), + time.Duration(10)*time.Second, + ) if err != nil { - if k8s_errors.IsNotFound(err) { - // This controller itself creates the TransportURL secret, so we treat this as an info. - Log.Info(fmt.Sprintf("TransportURL secret %s not found", instance.Status.TransportURLSecret)) - instance.Status.Conditions.Set(condition.FalseCondition( - condition.InputReadyCondition, - condition.RequestedReason, - condition.SeverityInfo, - condition.InputReadyWaitingMessage)) - return ctrl.Result{RequeueAfter: time.Second * 10}, "", nil - } instance.Status.Conditions.Set(condition.FalseCondition( condition.InputReadyCondition, condition.ErrorReason, condition.SeverityWarning, condition.InputReadyErrorMessage, err.Error())) - return ctrl.Result{}, "", err + return ctrlResult, "", err + } else if (ctrlResult != ctrl.Result{}) { + Log.Info(fmt.Sprintf("TransportURL secret %s not found", instance.Status.TransportURLSecret)) + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.InputReadyWaitingMessage)) + return ctrlResult, "", err } - configMapVars[transportURLSecret.Name] = env.SetValue(hash) + configMapVars[instance.Status.TransportURLSecret] = env.SetValue(hash) } instance.Status.Conditions.MarkTrue( diff --git a/test/functional/base_test.go b/test/functional/base_test.go index e3542e49..5456ffd2 100644 --- a/test/functional/base_test.go +++ b/test/functional/base_test.go @@ -86,6 +86,8 @@ type IronicNames struct { PublicCertSecretName types.NamespacedName CaBundleSecretName types.NamespacedName IronicTopologies []types.NamespacedName + IronicPassword string + IronicInvalidPassword string } func GetIronicNames( @@ -275,6 +277,8 @@ func GetIronicNames( Name: fmt.Sprintf("%s-nagent-topology", ironicName.Name), }, }, + IronicPassword: "12345678", + IronicInvalidPassword: "c^sometext02%text%text02$someText&", } } @@ -282,10 +286,10 @@ func CreateIronicSecret(namespace string, name string) *corev1.Secret { return th.CreateSecret( types.NamespacedName{Namespace: namespace, Name: name}, map[string][]byte{ - "IronicPassword": []byte("12345678"), - "IronicInspectorPassword": []byte("12345678"), - "IronicDatabasePassword": []byte("12345678"), - "IronicInspectorDatabasePassword": []byte("12345678"), + "IronicPassword": []byte(ironicNames.IronicPassword), + "IronicInspectorPassword": []byte(ironicNames.IronicPassword), + "IronicDatabasePassword": []byte(ironicNames.IronicPassword), + "IronicInspectorDatabasePassword": []byte(ironicNames.IronicPassword), }, ) } @@ -729,3 +733,16 @@ func GetSampleTopologySpec(label string) (map[string]any, []corev1.TopologySprea } return topologySpec, topologySpecObj } + +// CreateIronicInvalidSecret creates a secret with an invalid password for testing +func CreateIronicInvalidSecret(namespace string, name string) *corev1.Secret { + return th.CreateSecret( + types.NamespacedName{Namespace: namespace, Name: name}, + map[string][]byte{ + "IronicPassword": []byte(ironicNames.IronicInvalidPassword), + "IronicInspectorPassword": []byte(ironicNames.IronicInvalidPassword), + "IronicDatabasePassword": []byte(ironicNames.IronicInvalidPassword), + "IronicInspectorDatabasePassword": []byte(ironicNames.IronicInvalidPassword), + }, + ) +} diff --git a/test/functional/ironic_controller_test.go b/test/functional/ironic_controller_test.go index 28ca5469..b44fc94d 100644 --- a/test/functional/ironic_controller_test.go +++ b/test/functional/ironic_controller_test.go @@ -229,6 +229,51 @@ var _ = Describe("Ironic controller", func() { }) }) + When("Ironic is created with an invalid password", func() { + BeforeEach(func() { + DeferCleanup( + k8sClient.Delete, + ctx, + CreateIronicInvalidSecret(ironicNames.Namespace, SecretName), + ) + DeferCleanup( + k8sClient.Delete, + ctx, + CreateMessageBusSecret(ironicNames.Namespace, MessageBusSecretName), + ) + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + ironicNames.Namespace, + "openstack", + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}, + }, + ), + ) + DeferCleanup( + keystone.DeleteKeystoneAPI, + keystone.CreateKeystoneAPI(ironicNames.Namespace)) + spec := GetDefaultIronicSpec() + spec["transportURLSecret"] = MessageBusSecretName + DeferCleanup( + th.DeleteInstance, + CreateIronic(ironicNames.IronicName, spec), + ) + }) + It("rejects the password and reports InputReadyCondition as False", func() { + expectedErrMsg := "Input data error occurred password does not meet the requirements" + th.ExpectConditionWithDetails( + ironicNames.IronicName, + ConditionGetterFunc(IronicConditionGetter), + condition.InputReadyCondition, + corev1.ConditionFalse, + condition.ErrorReason, + expectedErrMsg, + ) + }) + }) + When("Deployment rollout is progressing", func() { var inaName types.NamespacedName BeforeEach(func() {