diff --git a/api/v1alpha1/timestampauthority_types.go b/api/v1alpha1/timestampauthority_types.go index cdb7a8a8c..a8540ead6 100644 --- a/api/v1alpha1/timestampauthority_types.go +++ b/api/v1alpha1/timestampauthority_types.go @@ -22,6 +22,7 @@ import ( ) // TimestampAuthoritySpec defines the desired state of TimestampAuthority +// +kubebuilder:validation:XValidation:rule=!(has(self.signer.certificateChain.certificateChainRef) && (has(self.signer.certificateChain.intermediateCA) || has(self.signer.certificateChain.leafCA) || has(self.signer.certificateChain.rootCA))),message="when certificateChainRef is set, intermediateCA, leafCA, and rootCA must not be set" type TimestampAuthoritySpec struct { //Define whether you want to export service or not ExternalAccess ExternalAccess `json:"externalAccess,omitempty"` @@ -68,13 +69,13 @@ type CertificateChain struct { CertificateChainRef *SecretKeySelector `json:"certificateChainRef,omitempty"` //Root Certificate Authority Config //+optional - RootCA TsaCertificateAuthority `json:"rootCA,omitempty"` + RootCA *TsaCertificateAuthority `json:"rootCA,omitempty"` //Intermediate Certificate Authority Config //+optional - IntermediateCA []TsaCertificateAuthority `json:"intermediateCA,omitempty"` + IntermediateCA []*TsaCertificateAuthority `json:"intermediateCA,omitempty"` //Leaf Certificate Authority Config //+optional - LeafCA TsaCertificateAuthority `json:"leafCA,omitempty"` + LeafCA *TsaCertificateAuthority `json:"leafCA,omitempty"` } // TSA Certificate Authority configuration diff --git a/api/v1alpha1/timestampauthority_types_test.go b/api/v1alpha1/timestampauthority_types_test.go index 37bd7c273..c80e7654e 100644 --- a/api/v1alpha1/timestampauthority_types_test.go +++ b/api/v1alpha1/timestampauthority_types_test.go @@ -27,21 +27,21 @@ var _ = Describe("TSA", func() { Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) Expect(fetched).To(Equal(created)) - fetched.Spec.Signer.CertificateChain.RootCA = TsaCertificateAuthority{ + fetched.Spec.Signer.CertificateChain.RootCA = &TsaCertificateAuthority{ CommonName: "root_test1.com", OrganizationName: "root_test1", OrganizationEmail: "root_test1@test.com", } Expect(k8sClient.Update(context.Background(), fetched)).To(Succeed()) - fetched.Spec.Signer.CertificateChain.IntermediateCA[0] = TsaCertificateAuthority{ + fetched.Spec.Signer.CertificateChain.IntermediateCA[0] = &TsaCertificateAuthority{ CommonName: "intermediate_test1.com", OrganizationName: "intermediate_test1", OrganizationEmail: "intermediate_test1@test.com", } Expect(k8sClient.Update(context.Background(), fetched)).To(Succeed()) - fetched.Spec.Signer.CertificateChain.LeafCA = TsaCertificateAuthority{ + fetched.Spec.Signer.CertificateChain.LeafCA = &TsaCertificateAuthority{ CommonName: "leaf_test1.com", OrganizationName: "leaf_test1", OrganizationEmail: "leaf_test1@test.com", @@ -244,8 +244,42 @@ var _ = Describe("TSA", func() { Expect(k8sClient.Create(context.Background(), invalidObject)). To(MatchError(ContainSubstring("only one signer should be configured at any time"))) }) - }) + It("intermediateCA, leafCA and rootCA cant be set if certificateChainRef is", func() { + invalidObject := generateTSAObject("invalidObj") + invalidObject.Spec.Signer = TimestampAuthoritySigner{ + CertificateChain: CertificateChain{ + CertificateChainRef: &SecretKeySelector{ + Key: "cert_chain", + LocalObjectReference: LocalObjectReference{Name: "cert_chain"}, + }, + RootCA: &TsaCertificateAuthority{ + CommonName: "root_test.com", + OrganizationName: "root_test", + OrganizationEmail: "root_test@test.com", + }, + IntermediateCA: []*TsaCertificateAuthority{ + { + CommonName: "intermediate_test.com", + OrganizationName: "intermediate_test", + OrganizationEmail: "intermediate_test@test.com", + }, + }, + LeafCA: &TsaCertificateAuthority{ + CommonName: "leaf_test.com", + OrganizationName: "leaf_test", + OrganizationEmail: "leaf_test@test.com", + }, + }, + Kms: &KMS{ + KeyResource: "Key_resource", + }, + } + Expect(apierrors.IsInvalid(k8sClient.Create(context.Background(), invalidObject))).To(BeTrue()) + Expect(k8sClient.Create(context.Background(), invalidObject)). + To(MatchError(ContainSubstring("when certificateChainRef is set, intermediateCA, leafCA, and rootCA must not be set"))) + }) + }) }) }) @@ -258,19 +292,19 @@ func generateTSAObject(name string) *TimestampAuthority { Spec: TimestampAuthoritySpec{ Signer: TimestampAuthoritySigner{ CertificateChain: CertificateChain{ - RootCA: TsaCertificateAuthority{ + RootCA: &TsaCertificateAuthority{ CommonName: "root_test.com", OrganizationName: "root_test", OrganizationEmail: "root_test@test.com", }, - IntermediateCA: []TsaCertificateAuthority{ + IntermediateCA: []*TsaCertificateAuthority{ { CommonName: "intermediate_test.com", OrganizationName: "intermediate_test", OrganizationEmail: "intermediate_test@test.com", }, }, - LeafCA: TsaCertificateAuthority{ + LeafCA: &TsaCertificateAuthority{ CommonName: "leaf_test.com", OrganizationName: "leaf_test", OrganizationEmail: "leaf_test@test.com", diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 8d1642b6f..7d7f7754d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -239,15 +239,27 @@ func (in *CertificateChain) DeepCopyInto(out *CertificateChain) { *out = new(SecretKeySelector) **out = **in } - in.RootCA.DeepCopyInto(&out.RootCA) + if in.RootCA != nil { + in, out := &in.RootCA, &out.RootCA + *out = new(TsaCertificateAuthority) + (*in).DeepCopyInto(*out) + } if in.IntermediateCA != nil { in, out := &in.IntermediateCA, &out.IntermediateCA - *out = make([]TsaCertificateAuthority, len(*in)) + *out = make([]*TsaCertificateAuthority, len(*in)) for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TsaCertificateAuthority) + (*in).DeepCopyInto(*out) + } } } - in.LeafCA.DeepCopyInto(&out.LeafCA) + if in.LeafCA != nil { + in, out := &in.LeafCA, &out.LeafCA + *out = new(TsaCertificateAuthority) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateChain. diff --git a/bundle/manifests/rhtas.redhat.com_securesigns.yaml b/bundle/manifests/rhtas.redhat.com_securesigns.yaml index 0750b62d8..91fcb86aa 100644 --- a/bundle/manifests/rhtas.redhat.com_securesigns.yaml +++ b/bundle/manifests/rhtas.redhat.com_securesigns.yaml @@ -1584,6 +1584,12 @@ spec: required: - signer type: object + x-kubernetes-validations: + - message: when certificateChainRef is set, intermediateCA, leafCA, + and rootCA must not be set + rule: '!(has(self.signer.certificateChain.certificateChainRef) && + (has(self.signer.certificateChain.intermediateCA) || has(self.signer.certificateChain.leafCA) + || has(self.signer.certificateChain.rootCA)))' tuf: default: keys: diff --git a/bundle/manifests/rhtas.redhat.com_timestampauthorities.yaml b/bundle/manifests/rhtas.redhat.com_timestampauthorities.yaml index b19d442dd..33db98315 100644 --- a/bundle/manifests/rhtas.redhat.com_timestampauthorities.yaml +++ b/bundle/manifests/rhtas.redhat.com_timestampauthorities.yaml @@ -742,6 +742,11 @@ spec: required: - signer type: object + x-kubernetes-validations: + - message: when certificateChainRef is set, intermediateCA, leafCA, and + rootCA must not be set + rule: '!(has(self.signer.certificateChain.certificateChainRef) && (has(self.signer.certificateChain.intermediateCA) + || has(self.signer.certificateChain.leafCA) || has(self.signer.certificateChain.rootCA)))' status: description: TimestampAuthorityStatus defines the observed state of TimestampAuthority properties: diff --git a/config/crd/bases/rhtas.redhat.com_securesigns.yaml b/config/crd/bases/rhtas.redhat.com_securesigns.yaml index 0d2a7943b..2db1cc324 100644 --- a/config/crd/bases/rhtas.redhat.com_securesigns.yaml +++ b/config/crd/bases/rhtas.redhat.com_securesigns.yaml @@ -1584,6 +1584,12 @@ spec: required: - signer type: object + x-kubernetes-validations: + - message: when certificateChainRef is set, intermediateCA, leafCA, + and rootCA must not be set + rule: '!(has(self.signer.certificateChain.certificateChainRef) && + (has(self.signer.certificateChain.intermediateCA) || has(self.signer.certificateChain.leafCA) + || has(self.signer.certificateChain.rootCA)))' tuf: default: keys: diff --git a/config/crd/bases/rhtas.redhat.com_timestampauthorities.yaml b/config/crd/bases/rhtas.redhat.com_timestampauthorities.yaml index 363236639..2a85787f8 100644 --- a/config/crd/bases/rhtas.redhat.com_timestampauthorities.yaml +++ b/config/crd/bases/rhtas.redhat.com_timestampauthorities.yaml @@ -742,6 +742,11 @@ spec: required: - signer type: object + x-kubernetes-validations: + - message: when certificateChainRef is set, intermediateCA, leafCA, and + rootCA must not be set + rule: '!(has(self.signer.certificateChain.certificateChainRef) && (has(self.signer.certificateChain.intermediateCA) + || has(self.signer.certificateChain.leafCA) || has(self.signer.certificateChain.rootCA)))' status: description: TimestampAuthorityStatus defines the observed state of TimestampAuthority properties: diff --git a/internal/controller/common/action/base_action.go b/internal/controller/common/action/base_action.go index 1846d7dee..5e62a2051 100644 --- a/internal/controller/common/action/base_action.go +++ b/internal/controller/common/action/base_action.go @@ -17,6 +17,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "github.com/go-logr/logr" + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" client2 "sigs.k8s.io/controller-runtime/pkg/client" @@ -253,6 +254,18 @@ func EnsureAnnotations(managedAnnotations ...string) EnsureOption { } } +func EnsureNTPConfig() EnsureOption { + return func(current client.Object, expected client.Object) error { + currentTSA, ok1 := current.(*rhtasv1alpha1.TimestampAuthority) + expectedTSA, ok2 := expected.(*rhtasv1alpha1.TimestampAuthority) + if !ok1 || !ok2 { + return fmt.Errorf("EnsureNTPConfig: objects are not of type *rhtasv1alpha1.TimestampAuthority") + } + currentTSA.Spec.NTPMonitoring = expectedTSA.Spec.NTPMonitoring + return nil + } +} + func getRouteSelectorLabels(currentSpec, expectedSpec reflect.Value) (reflect.Value, reflect.Value) { var currentRouteSelectorLabels, expectedRouteSelectorLabels reflect.Value getRouteSelectorLabels := func(spec reflect.Value, fieldName string) reflect.Value { diff --git a/internal/controller/securesign/actions/ensure_tsa.go b/internal/controller/securesign/actions/ensure_tsa.go index 2e977bf56..8d3323122 100644 --- a/internal/controller/securesign/actions/ensure_tsa.go +++ b/internal/controller/securesign/actions/ensure_tsa.go @@ -55,7 +55,7 @@ func (i tsaAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesig return i.Failed(err) } - if updated, err = i.Ensure(ctx, tsa, action.EnsureSpec(), action.EnsureRouteSelectorLabels()); err != nil { + if updated, err = i.Ensure(ctx, tsa, action.EnsureSpec(), action.EnsureRouteSelectorLabels(), action.EnsureNTPConfig()); err != nil { meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ Type: TSACondition, Status: v1.ConditionFalse, diff --git a/internal/controller/tsa/actions/deployment.go b/internal/controller/tsa/actions/deployment.go index 1eb0dd570..9d4be97b5 100644 --- a/internal/controller/tsa/actions/deployment.go +++ b/internal/controller/tsa/actions/deployment.go @@ -27,6 +27,12 @@ func (i deployAction) Name() string { func (i deployAction) CanHandle(ctx context.Context, instance *rhtasv1alpha1.TimestampAuthority) bool { c := meta.FindStatusCondition(instance.GetConditions(), constants.Ready) + if instance.Spec.Signer.CertificateChain.CertificateChainRef == nil && + (instance.Spec.Signer.CertificateChain.RootCA == nil || + instance.Spec.Signer.CertificateChain.LeafCA == nil) { + return false + } + return (c.Reason == constants.Ready || c.Reason == constants.Creating) } @@ -40,16 +46,18 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Timest deployment, err := tsaUtils.CreateTimestampAuthorityDeployment(instance, DeploymentName, RBACName, labels) if err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSAServerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: TSAServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) } if err = controllerutil.SetControllerReference(instance, deployment, i.Client.Scheme()); err != nil { @@ -58,26 +66,29 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Timest if updated, err = i.Ensure(ctx, deployment); err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSAServerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: TSAServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create TSA Server: %w", err), instance) } if updated { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSAServerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Creating, - Message: "TSA server deployment created", + Type: TSAServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "TSA server deployment created", + ObservedGeneration: instance.Generation, }) return i.StatusUpdate(ctx, instance) } else { diff --git a/internal/controller/tsa/actions/generate_signer.go b/internal/controller/tsa/actions/generate_signer.go index 966fc3296..89fe1cb7b 100644 --- a/internal/controller/tsa/actions/generate_signer.go +++ b/internal/controller/tsa/actions/generate_signer.go @@ -5,19 +5,17 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "encoding/json" "fmt" - "maps" "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common" "github.com/securesign/operator/internal/controller/common/action" - "github.com/securesign/operator/internal/controller/common/utils/kubernetes" k8sutils "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/internal/controller/constants" tsaUtils "github.com/securesign/operator/internal/controller/tsa/utils" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" - apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -40,8 +38,16 @@ func (g generateSigner) Name() string { func (g generateSigner) CanHandle(_ context.Context, instance *v1alpha1.TimestampAuthority) bool { c := meta.FindStatusCondition(instance.GetConditions(), constants.Ready) - return (c.Reason == constants.Pending || c.Reason == constants.Ready) && (instance.Status.Signer == nil || - !equality.Semantic.DeepDerivative(instance.Spec.Signer, *instance.Status.Signer)) + switch { + case c == nil: + return false + case !(c.Reason == constants.Pending || c.Reason == constants.Ready): + return false + case !meta.IsStatusConditionTrue(instance.GetConditions(), TSASignerCondition): + return true + default: + return !equality.Semantic.DeepDerivative(instance.Spec.Signer, *instance.Status.Signer) + } } func (g generateSigner) Handle(ctx context.Context, instance *v1alpha1.TimestampAuthority) *action.Result { @@ -51,11 +57,74 @@ func (g generateSigner) Handle(ctx context.Context, instance *v1alpha1.Timestamp if meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Reason != constants.Pending { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Pending, - }, - ) + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Pending, + ObservedGeneration: instance.Generation, + }) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: TSASignerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + ObservedGeneration: instance.Generation, + }) + return g.StatusUpdate(ctx, instance) + } + + anno, err := g.secretAnnotations(instance.Spec.Signer) + if err != nil { + return g.Failed(err) + } + + if instance.Status.Signer != nil { + secret, err := k8sutils.GetSecret(g.Client, instance.Namespace, instance.Status.Signer.CertificateChain.CertificateChainRef.Name) + if err != nil { + return g.Failed(fmt.Errorf("can't load CA secret %w", err)) + } + + if equality.Semantic.DeepDerivative(anno, secret.GetAnnotations()) { + if !meta.IsStatusConditionTrue(instance.GetConditions(), TSASignerCondition) { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: TSASignerCondition, + Status: metav1.ConditionTrue, + Reason: "Resolved", + ObservedGeneration: instance.Generation, + }) + return g.StatusUpdate(ctx, instance) + } + return g.Continue() + } + } + // invalidate + instance.Status.Signer = instance.Spec.Signer.DeepCopy() + + //Check if a secret for the TSA cert already exists and validate + partialSecrets, err := k8sutils.ListSecrets(ctx, g.Client, instance.Namespace, TSACertCALabel) + if err != nil { + g.Logger.Error(err, "problem with listing secrets", "namespace", instance.Namespace) + } + + for _, partialSecret := range partialSecrets.Items { + if equality.Semantic.DeepDerivative(anno, partialSecret.GetAnnotations()) && !meta.IsStatusConditionTrue(instance.GetConditions(), TSASignerCondition) { + g.alignStatusFields(partialSecret.Name, instance) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: TSASignerCondition, + Status: metav1.ConditionTrue, + Reason: "Resolved", + ObservedGeneration: instance.Generation, + }) + continue + } + + // invalidate certificate + if err := constants.RemoveLabel(ctx, &partialSecret, g.Client, TSACertCALabel); err != nil { + g.Logger.Error(err, "can't remove label from TSA signer secret", "Name", partialSecret.Name) + } + message := fmt.Sprintf("Removed '%s' label from %s secret", TSACertCALabel, partialSecret.Name) + g.Recorder.Event(instance, v1.EventTypeNormal, "CertificateSecretLabelRemoved", message) + g.Logger.Info(message) + } + if meta.IsStatusConditionTrue(instance.GetConditions(), TSASignerCondition) { return g.StatusUpdate(ctx, instance) } @@ -65,16 +134,18 @@ func (g generateSigner) Handle(ctx context.Context, instance *v1alpha1.Timestamp if err != nil { g.Logger.Error(err, "error resolving keys for timestamp authority") meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSASignerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: TSASignerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Pending, - Message: "Resolving keys", + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Pending, + Message: "Resolving keys", + ObservedGeneration: instance.Generation, }) g.StatusUpdate(ctx, instance) // swallow error and retry @@ -86,16 +157,18 @@ func (g generateSigner) Handle(ctx context.Context, instance *v1alpha1.Timestamp if err != nil { g.Logger.Error(err, "error resolving certificate chain for timestamp authority") meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSASignerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: TSASignerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Pending, - Message: "Resolving keys", + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Pending, + Message: "Resolving keys", + ObservedGeneration: instance.Generation, }) g.StatusUpdate(ctx, instance) // swallow error and retry @@ -103,94 +176,34 @@ func (g generateSigner) Handle(ctx context.Context, instance *v1alpha1.Timestamp } labels := constants.LabelsFor(ComponentName, DeploymentName, instance.Name) - secretLabels := map[string]string{ - TSACertCALabel: "certificateChain", - } - maps.Copy(secretLabels, labels) - - certificateChain := k8sutils.CreateImmutableSecret(fmt.Sprintf("tsa-signer-config-%s", instance.Name), instance.Namespace, tsaCertChainConfig.ToMap(), secretLabels) - //Check if a secret for the signer config already exists, if it does remove the label - secret, err := kubernetes.FindSecret(ctx, g.Client, instance.Namespace, TSACertCALabel) - if err != nil && !apierrors.IsNotFound(err) { - g.Logger.Error(err, "problem with finding secret", "namespace", instance.Namespace) - } - - if secret != nil { - if err := constants.RemoveLabel(ctx, secret, g.Client, TSACertCALabel); err != nil { - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSASignerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), - }) - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), - }) - return g.FailedWithStatusUpdate(ctx, err, instance) - } - message := fmt.Sprintf("Removed '%s' label from %s secret", TSACertCALabel, secret.Name) - g.Recorder.Event(instance, v1.EventTypeNormal, "CertificateChainSecretLabelRemoved", message) - g.Logger.Info(message) - } + labels[TSACertCALabel] = "certificateChain" + certificateChain := k8sutils.CreateImmutableSecret(fmt.Sprintf("tsa-signer-config-%s", instance.Name), instance.Namespace, tsaCertChainConfig.ToMap(), labels) + certificateChain.Annotations = anno if _, err := g.Ensure(ctx, certificateChain); err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSASignerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: TSASignerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) return g.FailedWithStatusUpdate(ctx, err, instance) } - - if instance.Status.Signer == nil { - instance.Status.Signer = new(v1alpha1.TimestampAuthoritySigner) - } - if instance.Spec.Signer.File == nil && instance.Spec.Signer.CertificateChain.CertificateChainRef == nil { - instance.Spec.Signer.File = new(v1alpha1.File) - } - instance.Spec.Signer.DeepCopyInto(instance.Status.Signer) - - if instance.Spec.Signer.CertificateChain.CertificateChainRef == nil { - instance.Status.Signer.CertificateChain.CertificateChainRef = &v1alpha1.SecretKeySelector{ - Key: "certificateChain", - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: certificateChain.Name, - }, - } - - if instance.Spec.Signer.File.PrivateKeyRef == nil { - instance.Status.Signer.File.PrivateKeyRef = &v1alpha1.SecretKeySelector{ - Key: "leafPrivateKey", - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: certificateChain.Name, - }, - } - } - - if instance.Spec.Signer.File.PasswordRef == nil && len(tsaCertChainConfig.LeafPrivateKeyPassword) > 0 { - instance.Status.Signer.File.PasswordRef = &v1alpha1.SecretKeySelector{ - Key: "leafPrivateKeyPassword", - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: certificateChain.Name, - }, - } - } - } - + g.Recorder.Event(instance, v1.EventTypeNormal, "TSACertUpdated", "TSA certificate secret updated") + g.alignStatusFields(certificateChain.Name, instance) meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSASignerCondition, - Status: metav1.ConditionTrue, - Reason: "Resolved", + Type: TSASignerCondition, + Status: metav1.ConditionTrue, + Reason: "Resolved", + ObservedGeneration: instance.Generation, }) return g.StatusUpdate(ctx, instance) } @@ -227,31 +240,33 @@ func (g generateSigner) handleSignerKeys(instance *v1alpha1.TimestampAuthority, } } else { - if ref := instance.Spec.Signer.CertificateChain.RootCA.PrivateKeyRef; ref != nil { - key, err := k8sutils.GetSecretData(g.Client, instance.Namespace, ref) - if err != nil { - return nil, err - } - config.RootPrivateKey = key + if instance.Spec.Signer.CertificateChain.RootCA != nil { + if ref := instance.Spec.Signer.CertificateChain.RootCA.PrivateKeyRef; ref != nil { + key, err := k8sutils.GetSecretData(g.Client, instance.Namespace, ref) + if err != nil { + return nil, err + } + config.RootPrivateKey = key - if ref := instance.Spec.Signer.CertificateChain.RootCA.PasswordRef; ref != nil { - password, err := k8sutils.GetSecretData(g.Client, instance.Namespace, ref) + if ref := instance.Spec.Signer.CertificateChain.RootCA.PasswordRef; ref != nil { + password, err := k8sutils.GetSecretData(g.Client, instance.Namespace, ref) + if err != nil { + return nil, err + } + config.RootPrivateKeyPassword = password + } + } else { + config.RootPrivateKeyPassword = common.GeneratePassword(8) + key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) if err != nil { return nil, err } - config.RootPrivateKeyPassword = password - } - } else { - config.RootPrivateKeyPassword = common.GeneratePassword(8) - key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - return nil, err - } - rootCAPrivKey, err := tsaUtils.CreatePrivateKey(key, config.RootPrivateKeyPassword) - if err != nil { - return nil, err + rootCAPrivKey, err := tsaUtils.CreatePrivateKey(key, config.RootPrivateKeyPassword) + if err != nil { + return nil, err + } + config.RootPrivateKey = rootCAPrivKey } - config.RootPrivateKey = rootCAPrivKey } for index, intermediateCA := range instance.Spec.Signer.CertificateChain.IntermediateCA { @@ -285,33 +300,34 @@ func (g generateSigner) handleSignerKeys(instance *v1alpha1.TimestampAuthority, } } - if ref := instance.Spec.Signer.CertificateChain.LeafCA.PrivateKeyRef; ref != nil { - key, err := k8sutils.GetSecretData(g.Client, instance.Namespace, ref) - if err != nil { - return nil, err - } - config.LeafPrivateKey = key + if instance.Spec.Signer.CertificateChain.LeafCA != nil { + if ref := instance.Spec.Signer.CertificateChain.LeafCA.PrivateKeyRef; ref != nil { + key, err := k8sutils.GetSecretData(g.Client, instance.Namespace, ref) + if err != nil { + return nil, err + } + config.LeafPrivateKey = key - if ref := instance.Spec.Signer.CertificateChain.LeafCA.PasswordRef; ref != nil { - password, err := k8sutils.GetSecretData(g.Client, instance.Namespace, ref) + if ref := instance.Spec.Signer.CertificateChain.LeafCA.PasswordRef; ref != nil { + password, err := k8sutils.GetSecretData(g.Client, instance.Namespace, ref) + if err != nil { + return nil, err + } + config.LeafPrivateKeyPassword = password + } + } else { + config.LeafPrivateKeyPassword = common.GeneratePassword(8) + key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) if err != nil { return nil, err } - config.LeafPrivateKeyPassword = password - } - } else { - config.LeafPrivateKeyPassword = common.GeneratePassword(8) - key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - return nil, err - } - leafCAPrivKey, err := tsaUtils.CreatePrivateKey(key, config.LeafPrivateKeyPassword) - if err != nil { - return nil, err + leafCAPrivKey, err := tsaUtils.CreatePrivateKey(key, config.LeafPrivateKeyPassword) + if err != nil { + return nil, err + } + config.LeafPrivateKey = leafCAPrivKey } - config.LeafPrivateKey = leafCAPrivKey } - } return config, nil } @@ -324,11 +340,60 @@ func (g generateSigner) handleCertificateChain(ctx context.Context, instance *v1 } config.CertificateChain = certificateChain } else { - certificateChain, err := tsaUtils.CreateTSACertChain(ctx, instance, DeploymentName, g.Client, config) - if err != nil { - return nil, err + if instance.Spec.Signer.CertificateChain.RootCA != nil && instance.Spec.Signer.CertificateChain.LeafCA != nil { + certificateChain, err := tsaUtils.CreateTSACertChain(ctx, instance, DeploymentName, g.Client, config) + if err != nil { + return nil, err + } + config.CertificateChain = certificateChain } - config.CertificateChain = certificateChain } return config, nil } + +func (g generateSigner) alignStatusFields(secretName string, instance *v1alpha1.TimestampAuthority) { + if instance.Status.Signer == nil { + instance.Status.Signer = new(v1alpha1.TimestampAuthoritySigner) + } + if instance.Spec.Signer.File == nil && instance.Spec.Signer.CertificateChain.CertificateChainRef == nil { + instance.Spec.Signer.File = new(v1alpha1.File) + } + instance.Spec.Signer.DeepCopyInto(instance.Status.Signer) + + if instance.Spec.Signer.CertificateChain.CertificateChainRef == nil { + instance.Status.Signer.CertificateChain.CertificateChainRef = &v1alpha1.SecretKeySelector{ + Key: "certificateChain", + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: secretName, + }, + } + + if instance.Spec.Signer.File.PrivateKeyRef == nil { + instance.Status.Signer.File.PrivateKeyRef = &v1alpha1.SecretKeySelector{ + Key: "leafPrivateKey", + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: secretName, + }, + } + } + + if instance.Spec.Signer.File.PasswordRef == nil { + instance.Status.Signer.File.PasswordRef = &v1alpha1.SecretKeySelector{ + Key: "leafPrivateKeyPassword", + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: secretName, + }, + } + } + } +} + +func (g generateSigner) secretAnnotations(signerConfig v1alpha1.TimestampAuthoritySigner) (map[string]string, error) { + annotations := make(map[string]string) + bytes, err := json.Marshal(signerConfig) + if err != nil { + return nil, fmt.Errorf("failed to marshal signer configuration: %w", err) + } + annotations[constants.LabelNamespace+"/signerConfiguration"] = string(bytes) + return annotations, nil +} diff --git a/internal/controller/tsa/actions/generate_signer_test.go b/internal/controller/tsa/actions/generate_signer_test.go index 9949e8c51..bd1d3d215 100644 --- a/internal/controller/tsa/actions/generate_signer_test.go +++ b/internal/controller/tsa/actions/generate_signer_test.go @@ -2,18 +2,20 @@ package actions import ( "context" + "encoding/json" "testing" "github.com/securesign/operator/test/e2e/support/tas/tsa" + v1 "k8s.io/api/core/v1" . "github.com/onsi/gomega" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/internal/controller/constants" - testAction "github.com/securesign/operator/internal/testing/action" common "github.com/securesign/operator/internal/testing/common/tsa" "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -54,6 +56,13 @@ func Test_SignerCanHandle(t *testing.T) { { name: "status is not nil", testCase: func(instance *rhtasv1alpha1.TimestampAuthority) { + instance.Status.Conditions = []metav1.Condition{ + { + Type: "TSASignerCondition", + Status: metav1.ConditionTrue, + Reason: constants.Ready, + }, + } instance.Status.Signer = &instance.Spec.Signer }, expected: false, @@ -63,15 +72,15 @@ func Test_SignerCanHandle(t *testing.T) { testCase: func(instance *rhtasv1alpha1.TimestampAuthority) { instance.Status.Signer = &rhtasv1alpha1.TimestampAuthoritySigner{ CertificateChain: rhtasv1alpha1.CertificateChain{ - RootCA: rhtasv1alpha1.TsaCertificateAuthority{ + RootCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "new_org", }, - IntermediateCA: []rhtasv1alpha1.TsaCertificateAuthority{ + IntermediateCA: []*rhtasv1alpha1.TsaCertificateAuthority{ { OrganizationName: "new_org", }, }, - LeafCA: rhtasv1alpha1.TsaCertificateAuthority{ + LeafCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "new_org", }, }, @@ -99,7 +108,7 @@ func Test_SignerHandle(t *testing.T) { testCase func(Gomega, action.Action[*rhtasv1alpha1.TimestampAuthority], client.WithWatch, *rhtasv1alpha1.TimestampAuthority) bool }{ { - name: "generate certificate keys and certs", + name: "generate all resources", setup: func(instance *rhtasv1alpha1.TimestampAuthority) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) { instance.Status.Conditions[0].Reason = constants.Pending return common.TsaTestSetup(instance, t, nil, NewGenerateSignerAction(), []client.Object{}...) @@ -107,32 +116,26 @@ func Test_SignerHandle(t *testing.T) { testCase: func(g Gomega, _ action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { secret, err := kubernetes.FindSecret(context.TODO(), client, instance.Namespace, TSACertCALabel) - if err != nil { - t.Errorf("Unable to find secret: %s", err) - return false - } - if !g.Expect(instance.Status.Signer).NotTo(BeNil()) { - t.Error("Status Signer should not be nil") - return false - } - if !g.Expect(secret.Name).To(Equal(instance.Status.Signer.CertificateChain.CertificateChainRef.Name)) { - t.Errorf("Secret name mismatch: expected %v, got %v", instance.Status.Signer.CertificateChain.CertificateChainRef.Name, secret.Name) - return false - } - if !g.Expect(secret.Name).To(Equal(instance.Status.Signer.File.PrivateKeyRef.Name)) { - t.Errorf("Secret name mismatch: expected %v, got %v", instance.Status.Signer.File.PrivateKeyRef.Name, secret.Name) - return false - } - return g.Expect(meta.FindStatusCondition(instance.Status.Conditions, TSASignerCondition).Reason).To(Equal("Resolved")) + g.Expect(err).NotTo(HaveOccurred(), "Unable to find secret") + + g.Expect(instance.Status.Signer).NotTo(BeNil(), "Status Signer should not be nil") + + g.Expect(secret.Name).To(Equal(instance.Status.Signer.CertificateChain.CertificateChainRef.Name), "Secret name mismatch for CertificateChainRef") + g.Expect(secret.Name).To(Equal(instance.Status.Signer.File.PrivateKeyRef.Name), "Secret name mismatch for PrivateKeyRef") + g.Expect(secret.Annotations).To(Equal(generateSecretAnnotations(instance.Spec.Signer)), "Secret annotation mismatch") + + g.Expect(meta.IsStatusConditionTrue(instance.Status.Conditions, TSASignerCondition)).To(BeTrue()) + + return true }, }, { - name: "generate certs", + name: "generate certs with user-specified keys", setup: func(instance *rhtasv1alpha1.TimestampAuthority) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) { instance.Status.Conditions[0].Reason = constants.Pending instance.Spec.Signer = rhtasv1alpha1.TimestampAuthoritySigner{ CertificateChain: rhtasv1alpha1.CertificateChain{ - RootCA: rhtasv1alpha1.TsaCertificateAuthority{ + RootCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "Red Hat", PrivateKeyRef: &rhtasv1alpha1.SecretKeySelector{ LocalObjectReference: rhtasv1alpha1.LocalObjectReference{ @@ -147,7 +150,7 @@ func Test_SignerHandle(t *testing.T) { Key: "rootPrivateKeyPassword", }, }, - IntermediateCA: []rhtasv1alpha1.TsaCertificateAuthority{ + IntermediateCA: []*rhtasv1alpha1.TsaCertificateAuthority{ { OrganizationName: "Red Hat", PrivateKeyRef: &rhtasv1alpha1.SecretKeySelector{ @@ -164,7 +167,7 @@ func Test_SignerHandle(t *testing.T) { }, }, }, - LeafCA: rhtasv1alpha1.TsaCertificateAuthority{ + LeafCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "Red Hat", PrivateKeyRef: &rhtasv1alpha1.SecretKeySelector{ LocalObjectReference: rhtasv1alpha1.LocalObjectReference{ @@ -182,36 +185,32 @@ func Test_SignerHandle(t *testing.T) { }, } - obj := []client.Object{} - client := testAction.FakeClientBuilder().WithObjects(instance).Build() secret := tsa.CreateSecrets(instance.Namespace, "tsa-test-secret") - obj = append(obj, secret) - return common.TsaTestSetup(instance, t, client, NewGenerateSignerAction(), obj...) + return common.TsaTestSetup(instance, t, nil, NewGenerateSignerAction(), secret) }, testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { secret, err := kubernetes.FindSecret(context.TODO(), client, instance.Namespace, TSACertCALabel) - if err != nil { - t.Errorf("Unable to find secret: %s", err) - return false - } - if !g.Expect(instance.Status.Signer).NotTo(BeNil()) { - t.Error("Status Signer should not be nil") - return false - } - if !g.Expect(secret.Name).To(Equal(instance.Status.Signer.CertificateChain.CertificateChainRef.Name)) { - t.Errorf("Secret name mismatch: expected %v, got %v", instance.Status.Signer.CertificateChain.CertificateChainRef.Name, secret.Name) - return false - } - if !g.Expect(secret.Name).To(Equal(instance.Status.Signer.File.PrivateKeyRef.Name)) { - t.Errorf("Secret name mismatch: expected %v, got %v", instance.Status.Signer.File.PrivateKeyRef.Name, secret.Name) - return false - } - return g.Expect(meta.FindStatusCondition(instance.Status.Conditions, TSASignerCondition).Reason).To(Equal("Resolved")) + g.Expect(err).NotTo(HaveOccurred(), "Unable to find secret") + + g.Expect(instance.Status.Signer).NotTo(BeNil(), "Status Signer should not be nil") + + g.Expect(secret.Name).To(Equal(instance.Status.Signer.CertificateChain.CertificateChainRef.Name), "Secret name mismatch for CertificateChainRef") + g.Expect(secret.Name).To(Equal(instance.Status.Signer.File.PrivateKeyRef.Name), "Secret name mismatch for PrivateKeyRef") + + g.Expect(instance.Status.Signer.CertificateChain.RootCA.PrivateKeyRef.Name).To(Equal("tsa-test-secret")) + g.Expect(instance.Status.Signer.CertificateChain.LeafCA.PrivateKeyRef.Name).To(Equal("tsa-test-secret")) + g.Expect(instance.Status.Signer.CertificateChain.IntermediateCA[0].PrivateKeyRef.Name).To(Equal("tsa-test-secret")) + + g.Expect(secret.Annotations).To(Equal(generateSecretAnnotations(instance.Spec.Signer)), "Secret annotation mismatch") + + g.Expect(meta.IsStatusConditionTrue(instance.Status.Conditions, TSASignerCondition)).To(BeTrue()) + + return true }, }, { - name: "predefined keys and certs", + name: "user-spec keys and certs", setup: func(instance *rhtasv1alpha1.TimestampAuthority) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) { instance.Status.Conditions[0].Reason = constants.Pending instance.Spec.Signer = rhtasv1alpha1.TimestampAuthoritySigner{ @@ -238,54 +237,28 @@ func Test_SignerHandle(t *testing.T) { }, }, } - obj := []client.Object{} - client := testAction.FakeClientBuilder().WithObjects(instance).Build() secret := tsa.CreateSecrets(instance.Namespace, "tsa-test-secret") - obj = append(obj, secret) - return common.TsaTestSetup(instance, t, client, NewGenerateSignerAction(), obj...) + return common.TsaTestSetup(instance, t, nil, NewGenerateSignerAction(), secret) }, testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { - _, err := kubernetes.FindSecret(context.TODO(), client, instance.Namespace, TSACertCALabel) - if err != nil { - t.Errorf("Unable to find secret: %s", err) - return false - } + secret, err := kubernetes.FindSecret(context.TODO(), client, instance.Namespace, TSACertCALabel) + g.Expect(err).NotTo(HaveOccurred(), "Unable to find secret") - if !g.Expect(instance.Status.Signer).NotTo(BeNil()) { - t.Error("Status Signer should not be nil") - return false - } + g.Expect(instance.Status.Signer).NotTo(BeNil(), "Status Signer should not be nil") + + g.Expect(instance.Status.Signer.CertificateChain.CertificateChainRef.Name).To(Equal("tsa-test-secret")) + + g.Expect(secret.Annotations).To(Equal(generateSecretAnnotations(instance.Spec.Signer)), "Secret annotation mismatch") + + g.Expect(meta.IsStatusConditionTrue(instance.Status.Conditions, TSASignerCondition)).To(BeTrue()) - return g.Expect(meta.FindStatusCondition(instance.Status.Conditions, TSASignerCondition).Reason).To(Equal("Resolved")) + return true }, }, { - name: "should update secret resource", + name: "update cert secret resource on key change", setup: func(instance *rhtasv1alpha1.TimestampAuthority) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) { instance.Status.Conditions[0].Reason = constants.Pending - client := testAction.FakeClientBuilder().WithObjects(instance).Build() - secret := tsa.CreateSecrets(instance.Namespace, "tsa-test-secret") - return common.TsaTestSetup(instance, t, client, NewGenerateSignerAction(), secret) - }, - testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { - secret, err := kubernetes.FindSecret(context.TODO(), client, instance.Namespace, TSACertCALabel) - if err != nil { - t.Errorf("Failed to find secret: %v", err) - return false - } - if !g.Expect(instance.Status.Signer).NotTo(BeNil()) { - t.Error("Signer should not be nil") - return false - } - if !g.Expect(secret.Name).To(Equal(instance.Status.Signer.CertificateChain.CertificateChainRef.Name)) { - t.Errorf("Secret name mismatch: expected %v, got %v", instance.Status.Signer.CertificateChain.CertificateChainRef.Name, secret.Name) - return false - } - if !g.Expect(secret.Name).To(Equal(instance.Status.Signer.File.PrivateKeyRef.Name)) { - t.Errorf("Private key ref name mismatch: expected %v, got %v", instance.Status.Signer.File.PrivateKeyRef.Name, secret.Name) - return false - } - oldSecretName := secret.Name instance.Spec.Signer = rhtasv1alpha1.TimestampAuthoritySigner{ CertificateChain: rhtasv1alpha1.CertificateChain{ CertificateChainRef: &rhtasv1alpha1.SecretKeySelector{ @@ -310,47 +283,80 @@ func Test_SignerHandle(t *testing.T) { }, }, } - - if err := client.Update(context.TODO(), instance); err != nil { - t.Errorf("Failed to update instance: %v", err) - return false - } - _ = a.Handle(context.TODO(), instance) - secret, err = kubernetes.FindSecret(context.TODO(), client, instance.Namespace, TSACertCALabel) - if err != nil { - t.Errorf("Failed to find updated secret: %v", err) - return false - } - if !g.Expect(secret.Name).ToNot(Equal(oldSecretName)) { - t.Errorf("Secret name should have changed: old=%v, new=%v", oldSecretName, secret.Name) - return false + instance.Status.Signer = &rhtasv1alpha1.TimestampAuthoritySigner{ + CertificateChain: rhtasv1alpha1.CertificateChain{ + CertificateChainRef: &rhtasv1alpha1.SecretKeySelector{ + LocalObjectReference: rhtasv1alpha1.LocalObjectReference{ + Name: "old", + }, + Key: "certificateChain", + }, + }, + File: &rhtasv1alpha1.File{ + PrivateKeyRef: &rhtasv1alpha1.SecretKeySelector{ + LocalObjectReference: rhtasv1alpha1.LocalObjectReference{ + Name: "old", + }, + Key: "leafPrivateKey", + }, + PasswordRef: &rhtasv1alpha1.SecretKeySelector{ + LocalObjectReference: rhtasv1alpha1.LocalObjectReference{ + Name: "old", + }, + Key: "leafPrivateKeyPassword", + }, + }, } - return g.Expect(meta.FindStatusCondition(instance.Status.Conditions, TSASignerCondition).Reason).To(Equal("Resolved")) + + secret := tsa.CreateSecrets(instance.Namespace, "tsa-test-secret") + old := tsa.CreateSecrets(instance.Namespace, "old") + old.Annotations = generateSecretAnnotations(*instance.Status.Signer) + return common.TsaTestSetup(instance, t, nil, NewGenerateSignerAction(), secret, old) + }, + testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], cli client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { + secret, err := kubernetes.FindSecret(context.TODO(), cli, instance.Namespace, TSACertCALabel) + g.Expect(err).NotTo(HaveOccurred(), "Failed to find secret") + + g.Expect(instance.Status.Signer).NotTo(BeNil(), "Signer should not be nil") + + g.Expect(instance.Status.Signer.CertificateChain.CertificateChainRef.Name).To(Equal("tsa-test-secret"), "Secret name mismatch for CertificateChainRef") + g.Expect(instance.Status.Signer.File.PrivateKeyRef.Name).To(Equal("tsa-test-secret"), "Private key ref name mismatch for PrivateKeyRef") + + g.Expect(secret.Annotations).To(Equal(generateSecretAnnotations(instance.Spec.Signer)), "Secret annotation mismatch") + + old := &v1.Secret{} + g.Expect(cli.Get(context.TODO(), client.ObjectKey{Name: "old", Namespace: "default"}, old)).To(Succeed()) + g.Expect(old.Labels).ToNot(HaveKey(TSACertCALabel)) + + g.Expect(meta.IsStatusConditionTrue(instance.Status.Conditions, TSASignerCondition)).To(BeTrue()) + + return true }, }, { - name: "should remove label after new secret creation", + name: "update cert secret resource on cert field change", setup: func(instance *rhtasv1alpha1.TimestampAuthority) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) { instance.Status.Conditions[0].Reason = constants.Pending - client := testAction.FakeClientBuilder().WithObjects(instance).Build() - secret := tsa.CreateSecrets(instance.Namespace, "tsa-test-secret") - return common.TsaTestSetup(instance, t, client, NewGenerateSignerAction(), secret) - }, - testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { - secret, err := kubernetes.FindSecret(context.TODO(), client, instance.Namespace, TSACertCALabel) - if err != nil { - t.Errorf("Failed to find secret: %v", err) - return false - } - if _, exists := secret.Labels["rhtas.redhat.com/tsa.certchain.pem"]; !exists { - t.Error("Label 'rhtas.redhat.com/tsa.certchain.pem' is not present in secret") - return false - } instance.Spec.Signer = rhtasv1alpha1.TimestampAuthoritySigner{ + CertificateChain: rhtasv1alpha1.CertificateChain{ + RootCA: &rhtasv1alpha1.TsaCertificateAuthority{ + OrganizationName: "Red Hat", + }, + IntermediateCA: []*rhtasv1alpha1.TsaCertificateAuthority{ + { + OrganizationName: "Red Hat", + }, + }, + LeafCA: &rhtasv1alpha1.TsaCertificateAuthority{ + OrganizationName: "Red Hat", + }, + }, + } + instance.Status.Signer = &rhtasv1alpha1.TimestampAuthoritySigner{ CertificateChain: rhtasv1alpha1.CertificateChain{ CertificateChainRef: &rhtasv1alpha1.SecretKeySelector{ LocalObjectReference: rhtasv1alpha1.LocalObjectReference{ - Name: "tsa-test-secret", + Name: "old", }, Key: "certificateChain", }, @@ -358,36 +364,81 @@ func Test_SignerHandle(t *testing.T) { File: &rhtasv1alpha1.File{ PrivateKeyRef: &rhtasv1alpha1.SecretKeySelector{ LocalObjectReference: rhtasv1alpha1.LocalObjectReference{ - Name: "tsa-test-secret", + Name: "old", }, Key: "leafPrivateKey", }, PasswordRef: &rhtasv1alpha1.SecretKeySelector{ LocalObjectReference: rhtasv1alpha1.LocalObjectReference{ - Name: "tsa-test-secret", + Name: "old", }, Key: "leafPrivateKeyPassword", }, }, } - if err := client.Update(context.TODO(), instance); err != nil { - t.Errorf("Failed to update instance: %v", err) - return false - } - _ = a.Handle(context.TODO(), instance) + old := tsa.CreateSecrets(instance.Namespace, "old") + old.Annotations = generateSecretAnnotations(*instance.Status.Signer) + return common.TsaTestSetup(instance, t, nil, NewGenerateSignerAction(), old) + }, + testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], cli client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { + secret, err := kubernetes.FindSecret(context.TODO(), cli, instance.Namespace, TSACertCALabel) + g.Expect(err).NotTo(HaveOccurred(), "Failed to find secret") - oldSecret, err := kubernetes.GetSecret(client, instance.Namespace, secret.Name) - if err != nil { - t.Errorf("Failed to get old secret: %v", err) - return false - } + g.Expect(instance.Status.Signer).NotTo(BeNil(), "Signer should not be nil") + g.Expect(instance.Status.Signer.CertificateChain.CertificateChainRef.Name).NotTo(Equal("old"), "Signer should be updated") + + g.Expect(instance.Status.Signer.CertificateChain.RootCA.OrganizationName).To(Equal("Red Hat"), "Secret name mismatch for CertificateChainRef") + + g.Expect(secret.Annotations).To(Equal(generateSecretAnnotations(instance.Spec.Signer)), "Secret annotation mismatch") + + old := &v1.Secret{} + g.Expect(cli.Get(context.TODO(), client.ObjectKey{Name: "old", Namespace: "default"}, old)).To(Succeed()) + g.Expect(old.Labels).ToNot(HaveKey(TSACertCALabel)) - if _, exists := oldSecret.Labels["rhtas.redhat.com/tsa.certchain.pem"]; exists { - t.Error("Label 'rhtas.redhat.com/tsa.certchain.pem' should have been removed") - return false + g.Expect(meta.IsStatusConditionTrue(instance.Status.Conditions, TSASignerCondition)).To(BeTrue()) + + return true + }, + }, + { + name: "find existing secret", + setup: func(instance *rhtasv1alpha1.TimestampAuthority) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) { + instance.Status.Conditions[0].Reason = constants.Pending + instance.Spec.Signer = rhtasv1alpha1.TimestampAuthoritySigner{ + CertificateChain: rhtasv1alpha1.CertificateChain{ + RootCA: &rhtasv1alpha1.TsaCertificateAuthority{ + OrganizationName: "Red Hat", + }, + IntermediateCA: []*rhtasv1alpha1.TsaCertificateAuthority{ + { + OrganizationName: "Red Hat", + }, + }, + LeafCA: &rhtasv1alpha1.TsaCertificateAuthority{ + OrganizationName: "Red Hat", + }, + }, } + secret := tsa.CreateSecrets(instance.Namespace, "secret") + secret.Annotations = generateSecretAnnotations(instance.Spec.Signer) + secret.Labels = map[string]string{TSACertCALabel: "fake"} + return common.TsaTestSetup(instance, t, nil, NewGenerateSignerAction(), secret) + }, + testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], cli client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { + secret, err := kubernetes.FindSecret(context.TODO(), cli, instance.Namespace, TSACertCALabel) + g.Expect(err).NotTo(HaveOccurred(), "Failed to find secret") + + g.Expect(instance.Status.Signer).NotTo(BeNil(), "Signer should not be nil") + g.Expect(instance.Status.Signer.CertificateChain.CertificateChainRef.Name).To(Equal("secret"), "Signer should be updated") + + g.Expect(instance.Status.Signer.CertificateChain.RootCA.OrganizationName).To(Equal("Red Hat"), "Secret name mismatch for CertificateChainRef") + + g.Expect(secret.Labels).To(HaveKey(TSACertCALabel), "Secret labels mismatch") + + g.Expect(meta.IsStatusConditionTrue(instance.Status.Conditions, TSASignerCondition)).To(BeTrue()) + return true }, }, @@ -396,10 +447,18 @@ func Test_SignerHandle(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) instance := common.GenerateTSAInstance() - client, action := tt.setup(instance) - g.Expect(client).NotTo(BeNil()) - g.Expect(action).NotTo(BeNil()) - g.Expect(tt.testCase(g, action, client, instance)).To(BeTrue()) + cli, act := tt.setup(instance) + g.Expect(cli).NotTo(BeNil(), "Client should not be nil") + g.Expect(act).NotTo(BeNil(), "Action should not be nil") + g.Expect(cli.Get(context.TODO(), client.ObjectKeyFromObject(instance), instance)).To(Succeed()) + g.Expect(tt.testCase(g, act, cli, instance)).To(BeTrue()) }) } } + +func generateSecretAnnotations(signer rhtasv1alpha1.TimestampAuthoritySigner) map[string]string { + annotations := make(map[string]string) + bytes, _ := json.Marshal(signer) + annotations[constants.LabelNamespace+"/signerConfiguration"] = string(bytes) + return annotations +} diff --git a/internal/controller/tsa/actions/ingress.go b/internal/controller/tsa/actions/ingress.go index 60560e2c9..ae1775646 100644 --- a/internal/controller/tsa/actions/ingress.go +++ b/internal/controller/tsa/actions/ingress.go @@ -56,16 +56,18 @@ func (i ingressAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Times labelKeys := maps.Keys(instance.Spec.ExternalAccess.RouteSelectorLabels) if updated, err = i.Ensure(ctx, ingress, action.EnsureSpec(), action.EnsureRouteSelectorLabels(labelKeys...)); err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSAServerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: TSAServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) return i.FailedWithStatusUpdate(ctx, err, instance) } @@ -75,10 +77,11 @@ func (i ingressAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Times route.SetLabels(ingress.GetLabels()) if _, err = i.Ensure(ctx, route, action.EnsureRouteSelectorLabels(labelKeys...)); err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) } for key, value := range ingress.GetLabels() { @@ -90,10 +93,11 @@ func (i ingressAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Times if updated { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSAServerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Creating, - Message: "Ingress created", + Type: TSAServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Ingress created", + ObservedGeneration: instance.Generation, }) return i.StatusUpdate(ctx, instance) } else { diff --git a/internal/controller/tsa/actions/initialize.go b/internal/controller/tsa/actions/initialize.go index e88cc73d0..d9a205911 100644 --- a/internal/controller/tsa/actions/initialize.go +++ b/internal/controller/tsa/actions/initialize.go @@ -51,16 +51,18 @@ func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Ti if !ok { i.Logger.Info("Waiting for deployment") meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Initialize, - Message: "Waiting for deployment to be ready", + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + ObservedGeneration: instance.Generation, }) meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSAServerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Initialize, - Message: "Waiting for deployment to be ready", + Type: TSAServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + ObservedGeneration: instance.Generation, }) return i.StatusUpdate(ctx, instance) } @@ -81,10 +83,10 @@ func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Ti } meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: TSAServerCondition, - Status: metav1.ConditionTrue, Reason: constants.Ready}) + Status: metav1.ConditionTrue, Reason: constants.Ready, ObservedGeneration: instance.Generation}) meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, - Status: metav1.ConditionTrue, Reason: constants.Ready}) + Status: metav1.ConditionTrue, Reason: constants.Ready, ObservedGeneration: instance.Generation}) return i.StatusUpdate(ctx, instance) } diff --git a/internal/controller/tsa/actions/monitoring.go b/internal/controller/tsa/actions/monitoring.go index b8bf455b6..18d8cdc75 100644 --- a/internal/controller/tsa/actions/monitoring.go +++ b/internal/controller/tsa/actions/monitoring.go @@ -56,10 +56,11 @@ func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Ti if _, err = i.Ensure(ctx, role); err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create monitoring role: %w", err), instance) } @@ -83,10 +84,11 @@ func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Ti if _, err = i.Ensure(ctx, roleBinding); err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create monitoring RoleBinding: %w", err), instance) } @@ -111,10 +113,11 @@ func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Ti if _, err = i.Ensure(ctx, serviceMonitor); err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create serviceMonitor: %w", err), instance) } diff --git a/internal/controller/tsa/actions/ntpMonitoring.go b/internal/controller/tsa/actions/ntpMonitoring.go index a888a9ec5..7dd77c08f 100644 --- a/internal/controller/tsa/actions/ntpMonitoring.go +++ b/internal/controller/tsa/actions/ntpMonitoring.go @@ -3,8 +3,8 @@ package actions import ( "context" "fmt" + "reflect" - "github.com/securesign/operator/api/v1alpha1" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" @@ -15,10 +15,17 @@ import ( "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels2 "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) +const ( + cmName = "ntp-monitoring-config-" + ntpConfigLabel = "ntp-monitoring-conf" + ntpConfigName = "ntp-config.yaml" +) + type ntpMonitoringAction struct { action.BaseAction } @@ -32,16 +39,40 @@ func (i ntpMonitoringAction) Name() string { } func (i ntpMonitoringAction) CanHandle(ctx context.Context, instance *rhtasv1alpha1.TimestampAuthority) bool { - c := meta.FindStatusCondition(instance.GetConditions(), constants.Ready) - return (c.Reason == constants.Creating || c.Reason == constants.Ready) && instance.Spec.NTPMonitoring.Enabled && instance.Spec.NTPMonitoring.Config != nil && (instance.Status.NTPMonitoring == nil || !equality.Semantic.DeepDerivative(instance.Spec.NTPMonitoring, *instance.Status.NTPMonitoring)) + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + + switch { + case c == nil: + return false + case c.Reason != constants.Creating && c.Reason != constants.Ready: + return false + case instance.Spec.NTPMonitoring.Config != nil: + return !equality.Semantic.DeepEqual(instance.Spec.NTPMonitoring, instance.Status.NTPMonitoring) + case c.Reason == constants.Ready: + return instance.Generation != c.ObservedGeneration + default: + return false + } } func (i ntpMonitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.TimestampAuthority) *action.Result { - if meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Reason != constants.Creating { + + var newStatus *rhtasv1alpha1.NTPMonitoring + if instance.Status.NTPMonitoring == nil { + newStatus = instance.Spec.NTPMonitoring.DeepCopy() + } else { + newStatus = instance.Status.NTPMonitoring + } + + if instance.Spec.NTPMonitoring.Config.NtpConfigRef != nil { + i.alignStatusFields(instance, newStatus, cmName) + instance.Status.NTPMonitoring = newStatus meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Creating, + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "NTP monitoring configured", + ObservedGeneration: instance.Generation, }) return i.StatusUpdate(ctx, instance) } @@ -54,53 +85,89 @@ func (i ntpMonitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1 ntpConfig, err := i.handleNTPMonitoring(instance) if err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) return i.FailedWithStatusUpdate(ctx, err, instance) } - if ntpConfig != nil { - labels := constants.LabelsFor(ComponentName, DeploymentName, instance.Name) - configMap := kubernetes.CreateImmutableConfigmap(NtpCMName, instance.Namespace, labels, map[string]string{"ntp-config.yaml": string(ntpConfig)}) - if err = controllerutil.SetControllerReference(instance, configMap, i.Client.Scheme()); err != nil { - return i.Failed(fmt.Errorf("could not set controller reference for ConfigMap: %w", err)) - } + labels := constants.LabelsFor(ComponentName, DeploymentName, instance.Name) + labels[constants.LabelResource] = ntpConfigLabel - if err = i.Client.DeleteAllOf(ctx, &v1.ConfigMap{}, client.InNamespace(instance.Namespace), client.MatchingLabels(labels)); err != nil { - return i.Failed(err) + if newStatus.Config.NtpConfigRef != nil { + cfg, err := kubernetes.GetConfigMap(ctx, i.Client, instance.Namespace, newStatus.Config.NtpConfigRef.Name) + if client.IgnoreNotFound(err) != nil { + return i.Failed(fmt.Errorf("NTPConfig: %w", err)) + } + if cfg != nil { + if reflect.DeepEqual(cfg.Data[ntpConfigName], string(ntpConfig)) { + return i.Continue() + } else { + i.Logger.Info("Remove invalid ConfigMap with NTP configuration", "Name", cfg.Name) + _ = i.Client.Delete(ctx, cfg) + } } + } + newStatus.Config.NtpConfigRef = nil - _, err = i.Ensure(ctx, configMap) + partialConfigs, err := kubernetes.ListConfigMaps(ctx, i.Client, instance.Namespace, labels2.SelectorFromSet(labels).String()) + if err != nil { + i.Logger.Error(err, "problem with finding configmap", "namespace", instance.Namespace) + } + for _, partialSecret := range partialConfigs.Items { + cm, err := kubernetes.GetConfigMap(ctx, i.Client, partialSecret.Namespace, partialSecret.Name) if err != nil { + return i.Failed(fmt.Errorf("can't load configMap data %w", err)) + } + if reflect.DeepEqual(cm.Data[ntpConfigName], string(ntpConfig)) && newStatus.Config.NtpConfigRef == nil { + i.Recorder.Eventf(instance, v1.EventTypeNormal, "NTPConfigDiscovered", "Existing ConfigMap with NTP configuration discovered: %s", cm.Name) + i.alignStatusFields(instance, newStatus, cm.Name) + instance.Status.NTPMonitoring = newStatus meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "NTP config discovered", + ObservedGeneration: instance.Generation, }) - return i.FailedWithStatusUpdate(ctx, err, instance) + } else { + i.Logger.Info("Remove invalid ConfigMap with NTP configuration", "Name", cm.Name) + _ = i.Client.Delete(ctx, cm) } - cmName = configMap.Name } - - if instance.Status.NTPMonitoring == nil { - instance.Status.NTPMonitoring = new(v1alpha1.NTPMonitoring) + if newStatus.Config.NtpConfigRef != nil { + return i.StatusUpdate(ctx, instance) } - instance.Spec.NTPMonitoring.DeepCopyInto(instance.Status.NTPMonitoring) - if instance.Spec.NTPMonitoring.Config.NtpConfigRef == nil { - instance.Status.NTPMonitoring.Config.NtpConfigRef = &rhtasv1alpha1.LocalObjectReference{Name: cmName} + configMap := kubernetes.CreateImmutableConfigmap(NtpCMName, instance.Namespace, labels, map[string]string{ntpConfigName: string(ntpConfig)}) + if err = controllerutil.SetControllerReference(instance, configMap, i.Client.Scheme()); err != nil { + return i.Failed(fmt.Errorf("could not set controller reference for ConfigMap: %w", err)) + } + _, err = i.Ensure(ctx, configMap) + if err != nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, + }) + return i.FailedWithStatusUpdate(ctx, err, instance) } + cmName = configMap.Name + i.alignStatusFields(instance, newStatus, cmName) + instance.Status.NTPMonitoring = newStatus meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Creating, - Message: "NTP monitoring configured"}, - ) + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "NTP monitoring configured", + ObservedGeneration: instance.Generation, + }) return i.StatusUpdate(ctx, instance) } @@ -110,10 +177,6 @@ func (i ntpMonitoringAction) handleNTPMonitoring(instance *rhtasv1alpha1.Timesta config []byte ) - if instance.Spec.NTPMonitoring.Config.NtpConfigRef != nil { - return nil, nil - } - ntpConfig := tsaUtils.NtpConfig{ RequestAttempts: instance.Spec.NTPMonitoring.Config.RequestAttempts, RequestTimeout: instance.Spec.NTPMonitoring.Config.RequestTimeout, @@ -129,3 +192,16 @@ func (i ntpMonitoringAction) handleNTPMonitoring(instance *rhtasv1alpha1.Timesta } return config, nil } + +func (i ntpMonitoringAction) alignStatusFields(instance *rhtasv1alpha1.TimestampAuthority, newStatus *rhtasv1alpha1.NTPMonitoring, cmName string) { + if newStatus == nil { + newStatus = new(rhtasv1alpha1.NTPMonitoring) + } + instance.Spec.NTPMonitoring.DeepCopyInto(newStatus) + + if instance.Spec.NTPMonitoring.Config.NtpConfigRef != nil { + newStatus.Config.NtpConfigRef = instance.Spec.NTPMonitoring.Config.NtpConfigRef + } else if cmName != "" { + newStatus.Config.NtpConfigRef = &rhtasv1alpha1.LocalObjectReference{Name: cmName} + } +} diff --git a/internal/controller/tsa/actions/ntpMonitoring_test.go b/internal/controller/tsa/actions/ntpMonitoring_test.go index c5c0c37cf..d34213d17 100644 --- a/internal/controller/tsa/actions/ntpMonitoring_test.go +++ b/internal/controller/tsa/actions/ntpMonitoring_test.go @@ -79,12 +79,6 @@ func Test_NTPCanHandle(t *testing.T) { name: "NTPMonitoring is disabled", testCase: func(instance *rhtasv1alpha1.TimestampAuthority) { instance.Spec.NTPMonitoring.Enabled = false - }, - expected: false, - }, - { - name: "NTPMonitoring config is nil", - testCase: func(instance *rhtasv1alpha1.TimestampAuthority) { instance.Spec.NTPMonitoring.Config = nil }, expected: false, @@ -114,22 +108,17 @@ func Test_NTPHandle(t *testing.T) { return common.TsaTestSetup(instance, t, nil, NewNtpMonitoringAction(), []client.Object{}...) }, testCase: func(g Gomega, _ action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { - if !g.Expect(instance.Status.NTPMonitoring).ToNot(BeNil()) { - t.Error("Status NTP Monitoring Config should not be nil") - return false - } + g.Expect(instance.Status.NTPMonitoring).NotTo(BeNil(), "Status NTP Monitoring Config should not be nil") + cm := &corev1.ConfigMap{} - if err := client.Get(context.TODO(), types.NamespacedName{Name: instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, Namespace: instance.GetNamespace()}, cm); err != nil { - t.Errorf("Unable to find config map: %s", err) - return false - } + err := client.Get(context.TODO(), types.NamespacedName{Name: instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, Namespace: instance.GetNamespace()}, cm) + g.Expect(err).NotTo(HaveOccurred(), "Unable to find config map") - if !g.Expect(instance.Status.NTPMonitoring.Config.NtpConfigRef.Name).To(Equal(cm.Name)) { - t.Errorf("Config Map name mismatch: expected %v, got %v", instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, cm.Name) - return false - } + g.Expect(instance.Status.NTPMonitoring.Config.NtpConfigRef.Name).To(Equal(cm.Name), "Config Map name mismatch") + + g.Expect(meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Message).To(Equal("NTP monitoring configured")) - return g.Expect(meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Message).To(Equal("NTP monitoring configured")) + return true }, }, { @@ -153,27 +142,21 @@ func Test_NTPHandle(t *testing.T) { Data: map[string]string{"ntp-config.yaml": ""}, } - obj := []client.Object{} - obj = append(obj, config) + obj := []client.Object{config} return common.TsaTestSetup(instance, t, nil, NewNtpMonitoringAction(), obj...) }, testCase: func(g Gomega, _ action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { - if !g.Expect(instance.Status.NTPMonitoring).ToNot(BeNil()) { - t.Error("Status NTP Monitoring Config should not be nil") - return false - } + g.Expect(instance.Status.NTPMonitoring).NotTo(BeNil(), "Status NTP Monitoring Config should not be nil") - if !g.Expect(instance.Status.NTPMonitoring.Config.NtpConfigRef.Name).To(Equal(instance.Spec.NTPMonitoring.Config.NtpConfigRef.Name)) { - t.Errorf("Config Map mismatch: expected %v, got %v", instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, instance.Spec.NTPMonitoring.Config.NtpConfigRef.Name) - return false - } + g.Expect(instance.Status.NTPMonitoring.Config.NtpConfigRef.Name).To(Equal(instance.Spec.NTPMonitoring.Config.NtpConfigRef.Name), "Config Map mismatch") cm := &corev1.ConfigMap{} - if err := client.Get(context.TODO(), types.NamespacedName{Name: instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, Namespace: instance.GetNamespace()}, cm); err != nil { - t.Errorf("Unable to find config map: %s", err) - return false - } - return g.Expect(meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Message).To(Equal("NTP monitoring configured")) + err := client.Get(context.TODO(), types.NamespacedName{Name: instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, Namespace: instance.GetNamespace()}, cm) + g.Expect(err).NotTo(HaveOccurred(), "Unable to find config map") + + g.Expect(meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Message).To(Equal("NTP monitoring configured")) + + return true }, }, { @@ -183,39 +166,28 @@ func Test_NTPHandle(t *testing.T) { return common.TsaTestSetup(instance, t, nil, NewNtpMonitoringAction(), []client.Object{}...) }, testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { - if !g.Expect(instance.Status.NTPMonitoring).ToNot(BeNil()) { - t.Error("Status NTP Monitoring Config should not be nil") - return false - } + g.Expect(instance.Status.NTPMonitoring).NotTo(BeNil(), "Status NTP Monitoring Config should not be nil") + cm := &corev1.ConfigMap{} - if err := client.Get(context.TODO(), types.NamespacedName{Name: instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, Namespace: instance.GetNamespace()}, cm); err != nil { - t.Errorf("Unable to find config map: %s", err) - return false - } + err := client.Get(context.TODO(), types.NamespacedName{Name: instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, Namespace: instance.GetNamespace()}, cm) + g.Expect(err).NotTo(HaveOccurred(), "Unable to find config map") - if !g.Expect(instance.Status.NTPMonitoring.Config.NtpConfigRef.Name).To(Equal(cm.Name)) { - t.Errorf("Config Map name mismatch: expected %v, got %v", instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, cm.Name) - return false - } + g.Expect(instance.Status.NTPMonitoring.Config.NtpConfigRef.Name).To(Equal(cm.Name), "Config Map name mismatch") instance.Spec.NTPMonitoring.Config.NumServers = 2 - if err := client.Update(context.TODO(), instance); err != nil { - t.Errorf("Error updating instance: %s", err) - return false - } + err = client.Update(context.TODO(), instance) + g.Expect(err).NotTo(HaveOccurred(), "Error updating instance") + _ = a.Handle(context.TODO(), instance) - if err := client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, instance); err != nil { - t.Errorf("Error re-fetching instance: %s", err) - return false - } + err = client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, instance) + g.Expect(err).NotTo(HaveOccurred(), "Error re-fetching instance") - if !g.Expect(instance.Spec.NTPMonitoring.Config.NumServers).To(Equal(2)) { - t.Errorf("NumServers mismatch: expected %v, got %v", 2, instance.Spec.NTPMonitoring.Config.NumServers) - return false - } + g.Expect(instance.Spec.NTPMonitoring.Config.NumServers).To(Equal(2), "NumServers mismatch") + + g.Expect(meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Message).To(Equal("NTP monitoring configured")) - return g.Expect(meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Message).To(Equal("NTP monitoring configured")) + return true }, }, { @@ -225,39 +197,29 @@ func Test_NTPHandle(t *testing.T) { return common.TsaTestSetup(instance, t, nil, NewNtpMonitoringAction(), []client.Object{}...) }, testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { - if !g.Expect(instance.Status.NTPMonitoring).ToNot(BeNil()) { - t.Error("Status NTP Monitoring Config should not be nil") - return false - } + g.Expect(instance.Status.NTPMonitoring).NotTo(BeNil(), "Status NTP Monitoring Config should not be nil") cm := &corev1.ConfigMap{} - if err := client.Get(context.TODO(), types.NamespacedName{Name: instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, Namespace: instance.GetNamespace()}, cm); err != nil { - t.Errorf("Unable to find config map: %s", err) - return false - } + err := client.Get(context.TODO(), types.NamespacedName{Name: instance.Status.NTPMonitoring.Config.NtpConfigRef.Name, Namespace: instance.GetNamespace()}, cm) + g.Expect(err).NotTo(HaveOccurred(), "Unable to find config map") oldConfigMapName := instance.Status.NTPMonitoring.Config.NtpConfigRef.Name instance.Spec.NTPMonitoring.Config.NumServers = 2 - if err := client.Update(context.TODO(), instance); err != nil { - t.Errorf("Error updating instance: %s", err) - return false - } + err = client.Update(context.TODO(), instance) + g.Expect(err).NotTo(HaveOccurred(), "Error updating instance") + _ = a.Handle(context.TODO(), instance) newConfigMapName := instance.Status.NTPMonitoring.Config.NtpConfigRef.Name - if !g.Expect(newConfigMapName).ToNot(Equal(oldConfigMapName)) { - t.Error("New ConfigMap should have a different name from the old ConfigMap") - return false - } + g.Expect(newConfigMapName).NotTo(Equal(oldConfigMapName), "New ConfigMap should have a different name from the old ConfigMap") - err := client.Get(context.TODO(), types.NamespacedName{Name: oldConfigMapName, Namespace: instance.GetNamespace()}, &corev1.ConfigMap{}) - if !errors.IsNotFound(err) { - t.Error("Old ConfigMap should be deleted") - return false - } + err = client.Get(context.TODO(), types.NamespacedName{Name: oldConfigMapName, Namespace: instance.GetNamespace()}, &corev1.ConfigMap{}) + g.Expect(errors.IsNotFound(err)).To(BeTrue(), "Old ConfigMap should be deleted") + + g.Expect(meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Message).To(Equal("NTP monitoring configured")) - return g.Expect(meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Message).To(Equal("NTP monitoring configured")) + return true }, }, } @@ -266,8 +228,8 @@ func Test_NTPHandle(t *testing.T) { g := NewWithT(t) instance := common.GenerateTSAInstance() client, action := tt.setup(instance) - g.Expect(client).NotTo(BeNil()) - g.Expect(action).NotTo(BeNil()) + g.Expect(client).NotTo(BeNil(), "Client should not be nil") + g.Expect(action).NotTo(BeNil(), "Action should not be nil") g.Expect(tt.testCase(g, action, client, instance)).To(BeTrue()) }) } diff --git a/internal/controller/tsa/actions/rbac.go b/internal/controller/tsa/actions/rbac.go index 1dc196f73..53e7b2391 100644 --- a/internal/controller/tsa/actions/rbac.go +++ b/internal/controller/tsa/actions/rbac.go @@ -50,10 +50,11 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Timestam _, err = i.Ensure(ctx, sa) if err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create SA: %w", err), instance) } diff --git a/internal/controller/tsa/actions/service.go b/internal/controller/tsa/actions/service.go index 3fe5c2f81..800b39bdf 100644 --- a/internal/controller/tsa/actions/service.go +++ b/internal/controller/tsa/actions/service.go @@ -56,26 +56,29 @@ func (i serviceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Times } if updated, err = i.Ensure(ctx, svc); err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSAServerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: TSAServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: constants.Ready, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + ObservedGeneration: instance.Generation, }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create service: %w", err), instance) } if updated { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: TSAServerCondition, - Status: metav1.ConditionFalse, - Reason: constants.Creating, - Message: "Service created", + Type: TSAServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Service created", + ObservedGeneration: instance.Generation, }) return i.StatusUpdate(ctx, instance) } else { diff --git a/internal/controller/tsa/timestampauthority_controller_test.go b/internal/controller/tsa/timestampauthority_controller_test.go index 6bec2889d..a264fc3c8 100644 --- a/internal/controller/tsa/timestampauthority_controller_test.go +++ b/internal/controller/tsa/timestampauthority_controller_test.go @@ -103,15 +103,15 @@ var _ = Describe("TimestampAuthority Controller", func() { Monitoring: rhtasv1alpha1.MonitoringConfig{Enabled: false}, Signer: rhtasv1alpha1.TimestampAuthoritySigner{ CertificateChain: rhtasv1alpha1.CertificateChain{ - RootCA: rhtasv1alpha1.TsaCertificateAuthority{ + RootCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "Red Hat", }, - IntermediateCA: []rhtasv1alpha1.TsaCertificateAuthority{ + IntermediateCA: []*rhtasv1alpha1.TsaCertificateAuthority{ { OrganizationName: "Red Hat", }, }, - LeafCA: rhtasv1alpha1.TsaCertificateAuthority{ + LeafCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "Red Hat", }, }, diff --git a/internal/controller/tsa/tsa_hot_update_test.go b/internal/controller/tsa/tsa_hot_update_test.go index 7237b14a9..f7c3ec72b 100644 --- a/internal/controller/tsa/tsa_hot_update_test.go +++ b/internal/controller/tsa/tsa_hot_update_test.go @@ -99,15 +99,15 @@ var _ = Describe("Timestamp Authority hot update", func() { Monitoring: rhtasv1alpha1.MonitoringConfig{Enabled: false}, Signer: rhtasv1alpha1.TimestampAuthoritySigner{ CertificateChain: rhtasv1alpha1.CertificateChain{ - RootCA: rhtasv1alpha1.TsaCertificateAuthority{ + RootCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "Red Hat", }, - IntermediateCA: []rhtasv1alpha1.TsaCertificateAuthority{ + IntermediateCA: []*rhtasv1alpha1.TsaCertificateAuthority{ { OrganizationName: "Red Hat", }, }, - LeafCA: rhtasv1alpha1.TsaCertificateAuthority{ + LeafCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "Red Hat", }, }, @@ -230,9 +230,14 @@ var _ = Describe("Timestamp Authority hot update", func() { Expect(k8sClient.Get(ctx, types.NamespacedName{Name: found.Status.NTPMonitoring.Config.NtpConfigRef.Name, Namespace: Namespace}, &corev1.ConfigMap{})).Should(Succeed()) By("Update NTP Config") - Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - found.Spec.NTPMonitoring.Config.NumServers = 2 - Expect(k8sClient.Update(ctx, found)).To(Succeed()) + Eventually(func(g Gomega) error { + err := k8sClient.Get(ctx, typeNamespaceName, found) + if err != nil { + return err + } + found.Spec.NTPMonitoring.Config.NumServers = 2 + return k8sClient.Update(ctx, found) + }).WithTimeout(1 * time.Second).Should(Succeed()) By("NTP monitoring should be resolved") Eventually(func(g Gomega) string { diff --git a/internal/controller/tsa/utils/tsa_cert_chain.go b/internal/controller/tsa/utils/tsa_cert_chain.go index f17ba5765..4873f9feb 100644 --- a/internal/controller/tsa/utils/tsa_cert_chain.go +++ b/internal/controller/tsa/utils/tsa_cert_chain.go @@ -90,7 +90,7 @@ func CreatePrivateKey(key *ecdsa.PrivateKey, password []byte) ([]byte, error) { func CreateTSACertChain(ctx context.Context, instance *rhtasv1alpha1.TimestampAuthority, deploymentName string, client client.Client, config *TsaCertChainConfig) ([]byte, error) { var err error - rootIssuer, err := CreateCAIssuer(instance, &instance.Spec.Signer.CertificateChain.RootCA, ctx, deploymentName, client) + rootIssuer, err := CreateCAIssuer(instance, instance.Spec.Signer.CertificateChain.RootCA, ctx, deploymentName, client) if err != nil { return nil, err } @@ -129,7 +129,7 @@ func CreateTSACertChain(ctx context.Context, instance *rhtasv1alpha1.TimestampAu var intermediateCerts []byte for index, intermediateKey := range instance.Spec.Signer.CertificateChain.IntermediateCA { - intermediateIssuer, err := CreateCAIssuer(instance, &intermediateKey, ctx, deploymentName, client) + intermediateIssuer, err := CreateCAIssuer(instance, intermediateKey, ctx, deploymentName, client) if err != nil { return nil, err } @@ -157,7 +157,7 @@ func CreateTSACertChain(ctx context.Context, instance *rhtasv1alpha1.TimestampAu intermediateCerts = append(intermediateCerts, intermediatePEM...) } - leafIssuer, err := CreateCAIssuer(instance, &instance.Spec.Signer.CertificateChain.LeafCA, ctx, deploymentName, client) + leafIssuer, err := CreateCAIssuer(instance, instance.Spec.Signer.CertificateChain.LeafCA, ctx, deploymentName, client) if err != nil { return nil, err } diff --git a/internal/testing/common/tsa/tsa.go b/internal/testing/common/tsa/tsa.go index ca1d2f755..f76e541a8 100644 --- a/internal/testing/common/tsa/tsa.go +++ b/internal/testing/common/tsa/tsa.go @@ -32,15 +32,15 @@ func GenerateTSAInstance() *rhtasv1alpha1.TimestampAuthority { Spec: rhtasv1alpha1.TimestampAuthoritySpec{ Signer: rhtasv1alpha1.TimestampAuthoritySigner{ CertificateChain: rhtasv1alpha1.CertificateChain{ - RootCA: rhtasv1alpha1.TsaCertificateAuthority{ + RootCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "Red Hat", }, - IntermediateCA: []rhtasv1alpha1.TsaCertificateAuthority{ + IntermediateCA: []*rhtasv1alpha1.TsaCertificateAuthority{ { OrganizationName: "Red Hat", }, }, - LeafCA: rhtasv1alpha1.TsaCertificateAuthority{ + LeafCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "Red Hat", }, }, @@ -63,7 +63,7 @@ func GenerateTSAInstance() *rhtasv1alpha1.TimestampAuthority { func TsaTestSetup(instance *rhtasv1alpha1.TimestampAuthority, t *testing.T, client client.WithWatch, action action.Action[*rhtasv1alpha1.TimestampAuthority], initObjs ...client.Object) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) { if client == nil { - client = testAction.FakeClientBuilder().WithObjects(instance).Build() + client = testAction.FakeClientBuilder().WithObjects(instance).WithStatusSubresource(instance).Build() } if err := client.Get(context.TODO(), types.NamespacedName{Name: instance.GetName(), Namespace: instance.GetNamespace()}, instance); err != nil { t.Error(err) diff --git a/test/e2e/byodb_test.go b/test/e2e/byodb_test.go index 279062d86..0c6d00da4 100644 --- a/test/e2e/byodb_test.go +++ b/test/e2e/byodb_test.go @@ -92,19 +92,19 @@ var _ = Describe("Securesign install with byodb", Ordered, func() { }, Signer: v1alpha1.TimestampAuthoritySigner{ CertificateChain: v1alpha1.CertificateChain{ - RootCA: v1alpha1.TsaCertificateAuthority{ + RootCA: &v1alpha1.TsaCertificateAuthority{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", }, - IntermediateCA: []v1alpha1.TsaCertificateAuthority{ + IntermediateCA: []*v1alpha1.TsaCertificateAuthority{ { OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", }, }, - LeafCA: v1alpha1.TsaCertificateAuthority{ + LeafCA: &v1alpha1.TsaCertificateAuthority{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", diff --git a/test/e2e/common_install_test.go b/test/e2e/common_install_test.go index 1ca1f5398..4d6f2485b 100644 --- a/test/e2e/common_install_test.go +++ b/test/e2e/common_install_test.go @@ -99,19 +99,19 @@ var _ = Describe("Securesign install with certificate generation", Ordered, func }, Signer: v1alpha1.TimestampAuthoritySigner{ CertificateChain: v1alpha1.CertificateChain{ - RootCA: v1alpha1.TsaCertificateAuthority{ + RootCA: &v1alpha1.TsaCertificateAuthority{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", }, - IntermediateCA: []v1alpha1.TsaCertificateAuthority{ + IntermediateCA: []*v1alpha1.TsaCertificateAuthority{ { OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", }, }, - LeafCA: v1alpha1.TsaCertificateAuthority{ + LeafCA: &v1alpha1.TsaCertificateAuthority{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", diff --git a/test/e2e/custom_install/proxy.go b/test/e2e/custom_install/proxy.go index 8112c65ea..bf30acee8 100644 --- a/test/e2e/custom_install/proxy.go +++ b/test/e2e/custom_install/proxy.go @@ -119,19 +119,19 @@ var _ = Describe("Securesign install in proxy-env", Ordered, func() { }, Signer: v1alpha1.TimestampAuthoritySigner{ CertificateChain: v1alpha1.CertificateChain{ - RootCA: v1alpha1.TsaCertificateAuthority{ + RootCA: &v1alpha1.TsaCertificateAuthority{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", }, - IntermediateCA: []v1alpha1.TsaCertificateAuthority{ + IntermediateCA: []*v1alpha1.TsaCertificateAuthority{ { OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", }, }, - LeafCA: v1alpha1.TsaCertificateAuthority{ + LeafCA: &v1alpha1.TsaCertificateAuthority{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", diff --git a/test/e2e/update/suite_test.go b/test/e2e/update/suite_test.go index bc645b9b0..b8814b5d4 100644 --- a/test/e2e/update/suite_test.go +++ b/test/e2e/update/suite_test.go @@ -89,19 +89,19 @@ func securesignResource(namespace *v1.Namespace) *rhtasv1alpha1.Securesign { }, Signer: rhtasv1alpha1.TimestampAuthoritySigner{ CertificateChain: rhtasv1alpha1.CertificateChain{ - RootCA: rhtasv1alpha1.TsaCertificateAuthority{ + RootCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", }, - IntermediateCA: []rhtasv1alpha1.TsaCertificateAuthority{ + IntermediateCA: []*rhtasv1alpha1.TsaCertificateAuthority{ { OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", }, }, - LeafCA: rhtasv1alpha1.TsaCertificateAuthority{ + LeafCA: &rhtasv1alpha1.TsaCertificateAuthority{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", diff --git a/test/e2e/upgrade_test.go b/test/e2e/upgrade_test.go index 5f32f7801..60ce65d47 100644 --- a/test/e2e/upgrade_test.go +++ b/test/e2e/upgrade_test.go @@ -371,19 +371,19 @@ var _ = Describe("Operator upgrade", Ordered, func() { }, Signer: tasv1alpha.TimestampAuthoritySigner{ CertificateChain: tasv1alpha.CertificateChain{ - RootCA: tasv1alpha.TsaCertificateAuthority{ + RootCA: &tasv1alpha.TsaCertificateAuthority{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", }, - IntermediateCA: []tasv1alpha.TsaCertificateAuthority{ + IntermediateCA: []*tasv1alpha.TsaCertificateAuthority{ { OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname", }, }, - LeafCA: tasv1alpha.TsaCertificateAuthority{ + LeafCA: &tasv1alpha.TsaCertificateAuthority{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", CommonName: "tsa.hostname",