From 5b58a6dcfde6a952af6b6f0341db983fc04b245f Mon Sep 17 00:00:00 2001 From: Jan Bouska Date: Thu, 15 Feb 2024 19:50:09 +0100 Subject: [PATCH] Migrate from phases to conditions (#188) * Migrate from phases to conditions * Add status to securesign resource * Rename conditions * Minor fixes --- api/v1alpha1/common.go | 12 -- api/v1alpha1/ctlog_types.go | 3 +- api/v1alpha1/fulcio_types.go | 5 +- api/v1alpha1/rekor_types.go | 3 +- api/v1alpha1/securesign_types.go | 32 ++++- api/v1alpha1/trillian_types.go | 3 +- api/v1alpha1/tuf_types.go | 5 +- api/v1alpha1/zz_generated.deepcopy.go | 57 +++++++- .../rhtas-operator.clusterserviceversion.yaml | 14 +- bundle/manifests/rhtas.redhat.com_ctlogs.yaml | 10 +- .../manifests/rhtas.redhat.com_fulcios.yaml | 8 +- bundle/manifests/rhtas.redhat.com_rekors.yaml | 20 ++- .../rhtas.redhat.com_securesigns.yaml | 130 +++++++++++++++--- .../manifests/rhtas.redhat.com_trillians.yaml | 10 +- bundle/manifests/rhtas.redhat.com_tufs.yaml | 10 +- config/crd/bases/rhtas.redhat.com_ctlogs.yaml | 10 +- .../crd/bases/rhtas.redhat.com_fulcios.yaml | 8 +- config/crd/bases/rhtas.redhat.com_rekors.yaml | 8 +- .../bases/rhtas.redhat.com_securesigns.yaml | 118 +++++++++++++--- .../crd/bases/rhtas.redhat.com_trillians.yaml | 10 +- config/crd/bases/rhtas.redhat.com_tufs.yaml | 10 +- controllers/constants/constants.go | 7 + controllers/ctlog/actions/constants.go | 3 +- controllers/ctlog/actions/createTree.go | 20 ++- controllers/ctlog/actions/deployment.go | 17 ++- controllers/ctlog/actions/generate_keys.go | 8 +- .../ctlog/actions/handle_fulcio_root.go | 25 +++- controllers/ctlog/actions/initialize.go | 23 ++-- controllers/ctlog/actions/pending.go | 34 ++++- controllers/ctlog/actions/rbac.go | 24 ++-- controllers/ctlog/actions/serverConfig.go | 26 +++- controllers/ctlog/actions/service.go | 12 +- .../ctlog/actions/toInitializePhase.go | 10 +- controllers/ctlog/ctlog_controller_test.go | 34 +++-- controllers/fulcio/actions/constants.go | 2 + controllers/fulcio/actions/deployment.go | 15 +- controllers/fulcio/actions/generate_cert.go | 52 +++---- controllers/fulcio/actions/ingress.go | 16 ++- controllers/fulcio/actions/initialize.go | 18 ++- controllers/fulcio/actions/monitoring.go | 18 ++- controllers/fulcio/actions/rbac.go | 18 ++- controllers/fulcio/actions/service.go | 12 +- controllers/fulcio/actions/servrConfig.go | 17 +-- .../fulcio/actions/to_initialize_phase.go | 10 +- .../fulcio/actions/to_pending_phase.go | 39 ++++++ controllers/fulcio/fulcio_controller.go | 1 + controllers/fulcio/fulcio_controller_test.go | 20 ++- .../backfillRedis/backfill_redis_cronjob.go | 26 +++- controllers/rekor/actions/constants.go | 3 + controllers/rekor/actions/initialize.go | 26 ++-- controllers/rekor/actions/pending.go | 45 +++++- controllers/rekor/actions/rbac.go | 18 ++- controllers/rekor/actions/redis/deployment.go | 26 +++- controllers/rekor/actions/redis/initialize.go | 16 ++- controllers/rekor/actions/redis/svc.go | 26 +++- .../rekor/actions/server/createTree.go | 20 ++- .../rekor/actions/server/deployment.go | 26 +++- .../rekor/actions/server/deploymentReady.go | 45 ------ .../rekor/actions/server/generate_signer.go | 27 +++- controllers/rekor/actions/server/ingress.go | 27 +++- .../rekor/actions/server/initialize.go | 78 +++++++++++ .../rekor/actions/server/initializeUrl.go | 57 -------- .../rekor/actions/server/monitoring.go | 36 +++-- controllers/rekor/actions/server/pvc.go | 18 ++- .../rekor/actions/server/resolve_pub_key.go | 15 +- .../rekor/actions/server/servrConfig.go | 22 ++- controllers/rekor/actions/server/svc.go | 26 +++- .../rekor/actions/to_creating_phase.go | 35 ----- .../rekor/actions/to_initialize_phase.go | 9 +- controllers/rekor/actions/ui/deployment.go | 26 +++- controllers/rekor/actions/ui/ingress.go | 25 +++- controllers/rekor/actions/ui/initialize.go | 19 ++- controllers/rekor/actions/ui/svc.go | 26 +++- controllers/rekor/rekor_controller.go | 6 +- controllers/rekor/rekor_controller_test.go | 31 +++-- controllers/securesign/actions/constants.go | 9 ++ .../securesign/actions/ensure_ctlog.go | 48 ++++++- .../securesign/actions/ensure_fulcio.go | 51 ++++++- .../securesign/actions/ensure_rekor.go | 47 ++++++- .../securesign/actions/ensure_trillian.go | 47 ++++++- controllers/securesign/actions/ensure_tuf.go | 51 ++++++- .../securesign/actions/initialize_status.go | 38 +++++ .../securesign/actions/update_status.go | 52 +++++++ .../securesign/securesign_controller.go | 9 ++ controllers/trillian/actions/constants.go | 10 +- .../trillian/actions/db/create_secret.go | 21 ++- controllers/trillian/actions/db/deployment.go | 32 ++++- controllers/trillian/actions/db/initialize.go | 63 +++++++++ controllers/trillian/actions/db/pvc.go | 20 ++- controllers/trillian/actions/db/svc.go | 28 +++- controllers/trillian/actions/initialize.go | 28 ++-- .../trillian/actions/logserver/deployment.go | 34 ++++- .../trillian/actions/logserver/initialize.go | 52 +++++++ .../trillian/actions/logserver/service.go | 32 ++++- .../trillian/actions/logsigner/deployment.go | 34 ++++- .../trillian/actions/logsigner/initialize.go | 51 +++++++ controllers/trillian/actions/rbac.go | 33 ++--- .../trillian/actions/to_create_phase.go | 33 +++++ .../trillian/actions/to_initialize_phase.go | 14 +- .../trillian/actions/to_pending_phase.go | 42 ++++++ controllers/trillian/trillian_controller.go | 6 + .../trillian/trillian_controller_test.go | 27 ++-- controllers/tuf/actions/constants.go | 3 +- controllers/tuf/actions/deployment.go | 15 +- controllers/tuf/actions/generate_cert.go | 68 ++++----- controllers/tuf/actions/ingress.go | 15 +- controllers/tuf/actions/initialize.go | 17 ++- controllers/tuf/actions/rbac.go | 18 ++- controllers/tuf/actions/servise.go | 12 +- .../tuf/actions/to_initialize_phase.go | 12 +- controllers/tuf/actions/to_pending_phase.go | 43 ++++++ controllers/tuf/tuf_controller.go | 19 +-- controllers/tuf/tuf_controller_test.go | 43 +++--- e2e/byodb_test.go | 3 +- e2e/common_install_test.go | 3 +- e2e/key_autodiscovery_test.go | 3 +- e2e/provided_certs_test.go | 3 +- e2e/support/tas/ctlog.go | 8 +- e2e/support/tas/fulcio.go | 8 +- e2e/support/tas/rekor.go | 8 +- e2e/support/tas/securesign.go | 30 ++++ e2e/support/tas/trillian.go | 20 ++- e2e/support/tas/tuf.go | 8 +- 123 files changed, 2200 insertions(+), 798 deletions(-) create mode 100644 controllers/fulcio/actions/to_pending_phase.go delete mode 100644 controllers/rekor/actions/server/deploymentReady.go create mode 100644 controllers/rekor/actions/server/initialize.go delete mode 100644 controllers/rekor/actions/server/initializeUrl.go delete mode 100644 controllers/rekor/actions/to_creating_phase.go create mode 100644 controllers/securesign/actions/constants.go create mode 100644 controllers/securesign/actions/initialize_status.go create mode 100644 controllers/securesign/actions/update_status.go create mode 100644 controllers/trillian/actions/db/initialize.go create mode 100644 controllers/trillian/actions/logserver/initialize.go create mode 100644 controllers/trillian/actions/logsigner/initialize.go create mode 100644 controllers/trillian/actions/to_create_phase.go create mode 100644 controllers/trillian/actions/to_pending_phase.go create mode 100644 controllers/tuf/actions/to_pending_phase.go create mode 100644 e2e/support/tas/securesign.go diff --git a/api/v1alpha1/common.go b/api/v1alpha1/common.go index 049ff650f..ff582d159 100644 --- a/api/v1alpha1/common.go +++ b/api/v1alpha1/common.go @@ -2,18 +2,6 @@ package v1alpha1 import v1 "k8s.io/api/core/v1" -type Phase string - -const ( - PhaseNone Phase = "" - PhaseCreating Phase = "Creating" - - PhaseInitialize Phase = "Initialization" - PhaseReady Phase = "Ready" - PhasePending Phase = "Pending" - PhaseError Phase = "Error" -) - type ExternalAccess struct { // If set to true, the Operator will create an Ingress or a Route resource. //For the plain Ingress there is no TLS configuration provided Route object uses "edge" termination by default. diff --git a/api/v1alpha1/ctlog_types.go b/api/v1alpha1/ctlog_types.go index 5d52167c2..e3327a165 100644 --- a/api/v1alpha1/ctlog_types.go +++ b/api/v1alpha1/ctlog_types.go @@ -35,7 +35,6 @@ type CTlogSpec struct { // CTlogStatus defines the observed state of CTlog component type CTlogStatus struct { - Phase Phase `json:"phase"` // +listType=map // +listMapKey=type // +patchStrategy=merge @@ -46,7 +45,7 @@ type CTlogStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`,description="The component phase" +//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="The component status" // CTlog is the Schema for the ctlogs API type CTlog struct { diff --git a/api/v1alpha1/fulcio_types.go b/api/v1alpha1/fulcio_types.go index 2bd26913d..b0cc66e60 100644 --- a/api/v1alpha1/fulcio_types.go +++ b/api/v1alpha1/fulcio_types.go @@ -77,8 +77,7 @@ type OIDCIssuer struct { // FulcioStatus defines the observed state of Fulcio type FulcioStatus struct { - Url string `json:"url,omitempty"` - Phase Phase `json:"phase,omitempty"` + Url string `json:"url,omitempty"` // +listType=map // +listMapKey=type // +patchStrategy=merge @@ -89,7 +88,7 @@ type FulcioStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`,description="The component phase" +//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="The component status" //+kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.status.url`,description="The component url" // Fulcio is the Schema for the fulcios API diff --git a/api/v1alpha1/rekor_types.go b/api/v1alpha1/rekor_types.go index bb10dccbd..df7553486 100644 --- a/api/v1alpha1/rekor_types.go +++ b/api/v1alpha1/rekor_types.go @@ -55,7 +55,6 @@ type BackFillRedis struct { // RekorStatus defines the observed state of Rekor type RekorStatus struct { Url string `json:"url,omitempty"` - Phase Phase `json:"phase,omitempty"` RekorSearchUIUrl string `json:"rekorSearchUIUrl,omitempty"` // +listType=map // +listMapKey=type @@ -67,7 +66,7 @@ type RekorStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`,description="The component phase" +//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="The component status" //+kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.status.url`,description="The component url" // Rekor is the Schema for the rekors API diff --git a/api/v1alpha1/securesign_types.go b/api/v1alpha1/securesign_types.go index 3054ff7d2..5e6997061 100644 --- a/api/v1alpha1/securesign_types.go +++ b/api/v1alpha1/securesign_types.go @@ -35,17 +35,35 @@ type SecuresignSpec struct { // SecuresignStatus defines the observed state of Securesign type SecuresignStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - Trillian string `json:"trillian"` - Fulcio string `json:"fulcio"` - Tuf string `json:"tuf"` - CTlog string `json:"ctlog"` - Rekor string `json:"rekor"` + // +listType=map + // +listMapKey=type + // +patchStrategy=merge + // +patchMergeKey=type + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` + RekorStatus SecuresignRekorStatus `json:"rekor,omitempty"` + FulcioStatus SecuresignFulcioStatus `json:"fulcio,omitempty"` + TufStatus SecuresignTufStatus `json:"tuf,omitempty"` +} + +type SecuresignRekorStatus struct { + Url string `json:"url,omitempty"` +} + +type SecuresignFulcioStatus struct { + Url string `json:"url,omitempty"` +} + +type SecuresignTufStatus struct { + Url string `json:"url,omitempty"` } //+kubebuilder:object:root=true //+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="The Deployment status" +//+kubebuilder:printcolumn:name="Rekor URL",type=string,JSONPath=`.status.rekor.url`,description="The rekor url" +//+kubebuilder:printcolumn:name="Fulcio URL",type=string,JSONPath=`.status.fulcio.url`,description="The fulcio url" +//+kubebuilder:printcolumn:name="Tuf URL",type=string,JSONPath=`.status.tuf.url`,description="The tuf url" // Securesign is the Schema for the securesigns API type Securesign struct { diff --git a/api/v1alpha1/trillian_types.go b/api/v1alpha1/trillian_types.go index 3f065ccf9..4aab06e8a 100644 --- a/api/v1alpha1/trillian_types.go +++ b/api/v1alpha1/trillian_types.go @@ -39,7 +39,6 @@ type TrillianDB struct { // TrillianStatus defines the observed state of Trillian type TrillianStatus struct { - Phase Phase `json:"phase"` // +listType=map // +listMapKey=type // +patchStrategy=merge @@ -50,7 +49,7 @@ type TrillianStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`,description="The component phase" +//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="The component status" // Trillian is the Schema for the trillians API type Trillian struct { diff --git a/api/v1alpha1/tuf_types.go b/api/v1alpha1/tuf_types.go index 0565aef1b..12af41180 100644 --- a/api/v1alpha1/tuf_types.go +++ b/api/v1alpha1/tuf_types.go @@ -26,8 +26,7 @@ type TufKey struct { // TufStatus defines the observed state of Tuf type TufStatus struct { - Url string `json:"url,omitempty"` - Phase Phase `json:"phase"` + Url string `json:"url,omitempty"` // +listType=map // +listMapKey=type // +patchStrategy=merge @@ -38,7 +37,7 @@ type TufStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`,description="The component phase" +//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="The component status" //+kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.status.url`,description="The component url" // Tuf is the Schema for the tufs API diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index b33f9731e..dcd687a92 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -535,7 +535,7 @@ func (in *Securesign) DeepCopyInto(out *Securesign) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Securesign. @@ -556,6 +556,21 @@ func (in *Securesign) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecuresignFulcioStatus) DeepCopyInto(out *SecuresignFulcioStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecuresignFulcioStatus. +func (in *SecuresignFulcioStatus) DeepCopy() *SecuresignFulcioStatus { + if in == nil { + return nil + } + out := new(SecuresignFulcioStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecuresignList) DeepCopyInto(out *SecuresignList) { *out = *in @@ -588,6 +603,21 @@ func (in *SecuresignList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecuresignRekorStatus) DeepCopyInto(out *SecuresignRekorStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecuresignRekorStatus. +func (in *SecuresignRekorStatus) DeepCopy() *SecuresignRekorStatus { + if in == nil { + return nil + } + out := new(SecuresignRekorStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecuresignSpec) DeepCopyInto(out *SecuresignSpec) { *out = *in @@ -611,6 +641,16 @@ func (in *SecuresignSpec) DeepCopy() *SecuresignSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecuresignStatus) DeepCopyInto(out *SecuresignStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.RekorStatus = in.RekorStatus + out.FulcioStatus = in.FulcioStatus + out.TufStatus = in.TufStatus } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecuresignStatus. @@ -623,6 +663,21 @@ func (in *SecuresignStatus) DeepCopy() *SecuresignStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecuresignTufStatus) DeepCopyInto(out *SecuresignTufStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecuresignTufStatus. +func (in *SecuresignTufStatus) DeepCopy() *SecuresignTufStatus { + if in == nil { + return nil + } + out := new(SecuresignTufStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Trillian) DeepCopyInto(out *Trillian) { *out = *in diff --git a/bundle/manifests/rhtas-operator.clusterserviceversion.yaml b/bundle/manifests/rhtas-operator.clusterserviceversion.yaml index 27eadeeea..8d3f1ca85 100644 --- a/bundle/manifests/rhtas-operator.clusterserviceversion.yaml +++ b/bundle/manifests/rhtas-operator.clusterserviceversion.yaml @@ -176,7 +176,7 @@ metadata: } ] capabilities: Basic Install - createdAt: "2024-02-12T13:29:51Z" + createdAt: "2024-02-15T11:34:50Z" operators.operatorframework.io/builder: operator-sdk-v1.32.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 name: rhtas-operator.v0.0.1 @@ -275,6 +275,18 @@ spec: - patch - update - watch + - apiGroups: + - batch + resources: + - cronjobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - batch resources: diff --git a/bundle/manifests/rhtas.redhat.com_ctlogs.yaml b/bundle/manifests/rhtas.redhat.com_ctlogs.yaml index 302a0d13b..2fd2bc227 100644 --- a/bundle/manifests/rhtas.redhat.com_ctlogs.yaml +++ b/bundle/manifests/rhtas.redhat.com_ctlogs.yaml @@ -15,9 +15,9 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The component phase - jsonPath: .status.phase - name: Phase + - description: The component status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status type: string name: v1alpha1 schema: @@ -186,10 +186,6 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - phase: - type: string - required: - - phase type: object type: object served: true diff --git a/bundle/manifests/rhtas.redhat.com_fulcios.yaml b/bundle/manifests/rhtas.redhat.com_fulcios.yaml index f1f126d29..eac078dc5 100644 --- a/bundle/manifests/rhtas.redhat.com_fulcios.yaml +++ b/bundle/manifests/rhtas.redhat.com_fulcios.yaml @@ -15,9 +15,9 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The component phase - jsonPath: .status.phase - name: Phase + - description: The component status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status type: string - description: The component url jsonPath: .status.url @@ -274,8 +274,6 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - phase: - type: string url: type: string type: object diff --git a/bundle/manifests/rhtas.redhat.com_rekors.yaml b/bundle/manifests/rhtas.redhat.com_rekors.yaml index 08947ad14..81a354ef9 100644 --- a/bundle/manifests/rhtas.redhat.com_rekors.yaml +++ b/bundle/manifests/rhtas.redhat.com_rekors.yaml @@ -15,9 +15,9 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The component phase - jsonPath: .status.phase - name: Phase + - description: The component status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status type: string - description: The component url jsonPath: .status.url @@ -43,6 +43,18 @@ spec: spec: description: RekorSpec defines the desired state of Rekor properties: + backFillRedis: + description: BackFillRedis CronJob Configuration + properties: + enabled: + default: true + description: Enable the BackFillRedis CronJob + type: boolean + schedule: + default: 0 0 * * * + description: Schedule for the BackFillRedis CronJob + type: string + type: object externalAccess: description: Define whether you want to export service or not properties: @@ -189,8 +201,6 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - phase: - type: string rekorSearchUIUrl: type: string url: diff --git a/bundle/manifests/rhtas.redhat.com_securesigns.yaml b/bundle/manifests/rhtas.redhat.com_securesigns.yaml index eb8c4f80c..8ea15bb6d 100644 --- a/bundle/manifests/rhtas.redhat.com_securesigns.yaml +++ b/bundle/manifests/rhtas.redhat.com_securesigns.yaml @@ -14,7 +14,24 @@ spec: singular: securesign scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - description: The Deployment status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - description: The rekor url + jsonPath: .status.rekor.url + name: Rekor URL + type: string + - description: The fulcio url + jsonPath: .status.fulcio.url + name: Fulcio URL + type: string + - description: The tuf url + jsonPath: .status.tuf.url + name: Tuf URL + type: string + name: v1alpha1 schema: openAPIV3Schema: description: Securesign is the Schema for the securesigns API @@ -276,6 +293,18 @@ spec: rekor: description: RekorSpec defines the desired state of Rekor properties: + backFillRedis: + description: BackFillRedis CronJob Configuration + properties: + enabled: + default: true + description: Enable the BackFillRedis CronJob + type: boolean + schedule: + default: 0 0 * * * + description: Schedule for the BackFillRedis CronJob + type: string + type: object externalAccess: description: Define whether you want to export service or not properties: @@ -437,25 +466,92 @@ spec: status: description: SecuresignStatus defines the observed state of Securesign properties: - ctlog: - type: string + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map fulcio: - type: string + properties: + url: + type: string + type: object rekor: - type: string - trillian: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' - type: string + properties: + url: + type: string + type: object tuf: - type: string - required: - - ctlog - - fulcio - - rekor - - trillian - - tuf + properties: + url: + type: string + type: object type: object type: object served: true diff --git a/bundle/manifests/rhtas.redhat.com_trillians.yaml b/bundle/manifests/rhtas.redhat.com_trillians.yaml index 30f6f9ce4..2250657d1 100644 --- a/bundle/manifests/rhtas.redhat.com_trillians.yaml +++ b/bundle/manifests/rhtas.redhat.com_trillians.yaml @@ -15,9 +15,9 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The component phase - jsonPath: .status.phase - name: Phase + - description: The component status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status type: string name: v1alpha1 schema: @@ -136,10 +136,6 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - phase: - type: string - required: - - phase type: object type: object served: true diff --git a/bundle/manifests/rhtas.redhat.com_tufs.yaml b/bundle/manifests/rhtas.redhat.com_tufs.yaml index 3d9ad552a..1c73f0240 100644 --- a/bundle/manifests/rhtas.redhat.com_tufs.yaml +++ b/bundle/manifests/rhtas.redhat.com_tufs.yaml @@ -15,9 +15,9 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The component phase - jsonPath: .status.phase - name: Phase + - description: The component status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status type: string - description: The component url jsonPath: .status.url @@ -162,12 +162,8 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - phase: - type: string url: type: string - required: - - phase type: object type: object served: true diff --git a/config/crd/bases/rhtas.redhat.com_ctlogs.yaml b/config/crd/bases/rhtas.redhat.com_ctlogs.yaml index 4d34d8dab..f7f0c9c08 100644 --- a/config/crd/bases/rhtas.redhat.com_ctlogs.yaml +++ b/config/crd/bases/rhtas.redhat.com_ctlogs.yaml @@ -16,9 +16,9 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The component phase - jsonPath: .status.phase - name: Phase + - description: The component status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status type: string name: v1alpha1 schema: @@ -187,10 +187,6 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - phase: - type: string - required: - - phase type: object type: object served: true diff --git a/config/crd/bases/rhtas.redhat.com_fulcios.yaml b/config/crd/bases/rhtas.redhat.com_fulcios.yaml index 92aa42bb3..2da98b3e8 100644 --- a/config/crd/bases/rhtas.redhat.com_fulcios.yaml +++ b/config/crd/bases/rhtas.redhat.com_fulcios.yaml @@ -16,9 +16,9 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The component phase - jsonPath: .status.phase - name: Phase + - description: The component status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status type: string - description: The component url jsonPath: .status.url @@ -275,8 +275,6 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - phase: - type: string url: type: string type: object diff --git a/config/crd/bases/rhtas.redhat.com_rekors.yaml b/config/crd/bases/rhtas.redhat.com_rekors.yaml index b23713f5f..4367ffe21 100644 --- a/config/crd/bases/rhtas.redhat.com_rekors.yaml +++ b/config/crd/bases/rhtas.redhat.com_rekors.yaml @@ -16,9 +16,9 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The component phase - jsonPath: .status.phase - name: Phase + - description: The component status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status type: string - description: The component url jsonPath: .status.url @@ -202,8 +202,6 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - phase: - type: string rekorSearchUIUrl: type: string url: diff --git a/config/crd/bases/rhtas.redhat.com_securesigns.yaml b/config/crd/bases/rhtas.redhat.com_securesigns.yaml index 08b32c595..b8e36b472 100644 --- a/config/crd/bases/rhtas.redhat.com_securesigns.yaml +++ b/config/crd/bases/rhtas.redhat.com_securesigns.yaml @@ -15,7 +15,24 @@ spec: singular: securesign scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - description: The Deployment status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - description: The rekor url + jsonPath: .status.rekor.url + name: Rekor URL + type: string + - description: The fulcio url + jsonPath: .status.fulcio.url + name: Fulcio URL + type: string + - description: The tuf url + jsonPath: .status.tuf.url + name: Tuf URL + type: string + name: v1alpha1 schema: openAPIV3Schema: description: Securesign is the Schema for the securesigns API @@ -450,25 +467,92 @@ spec: status: description: SecuresignStatus defines the observed state of Securesign properties: - ctlog: - type: string + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map fulcio: - type: string + properties: + url: + type: string + type: object rekor: - type: string - trillian: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' - type: string + properties: + url: + type: string + type: object tuf: - type: string - required: - - ctlog - - fulcio - - rekor - - trillian - - tuf + properties: + url: + type: string + type: object type: object type: object served: true diff --git a/config/crd/bases/rhtas.redhat.com_trillians.yaml b/config/crd/bases/rhtas.redhat.com_trillians.yaml index b260d39b8..40db4f73a 100644 --- a/config/crd/bases/rhtas.redhat.com_trillians.yaml +++ b/config/crd/bases/rhtas.redhat.com_trillians.yaml @@ -16,9 +16,9 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The component phase - jsonPath: .status.phase - name: Phase + - description: The component status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status type: string name: v1alpha1 schema: @@ -137,10 +137,6 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - phase: - type: string - required: - - phase type: object type: object served: true diff --git a/config/crd/bases/rhtas.redhat.com_tufs.yaml b/config/crd/bases/rhtas.redhat.com_tufs.yaml index ac01269cb..9a617a112 100644 --- a/config/crd/bases/rhtas.redhat.com_tufs.yaml +++ b/config/crd/bases/rhtas.redhat.com_tufs.yaml @@ -16,9 +16,9 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The component phase - jsonPath: .status.phase - name: Phase + - description: The component status + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status type: string - description: The component url jsonPath: .status.url @@ -163,12 +163,8 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - phase: - type: string url: type: string - required: - - phase type: object type: object served: true diff --git a/controllers/constants/constants.go b/controllers/constants/constants.go index ad0794e90..bf05286a3 100644 --- a/controllers/constants/constants.go +++ b/controllers/constants/constants.go @@ -2,4 +2,11 @@ package constants const ( AppName = "trusted-artifact-signer" + + // conditions + Ready = "Ready" + Pending = "Pending" + Creating = "Creating" + Initialize = "Initialize" + Failure = "Failure" ) diff --git a/controllers/ctlog/actions/constants.go b/controllers/ctlog/actions/constants.go index 7f17534c2..4e9175d39 100644 --- a/controllers/ctlog/actions/constants.go +++ b/controllers/ctlog/actions/constants.go @@ -3,6 +3,7 @@ package actions const ( DeploymentName = "ctlog" ComponentName = "ctlog" + RBACName = "ctlog" - RBACName = "ctlog" + CertCondition = "FulcioCertAvailable" ) diff --git a/controllers/ctlog/actions/createTree.go b/controllers/ctlog/actions/createTree.go index 1ad7074e6..2c2f4eadf 100644 --- a/controllers/ctlog/actions/createTree.go +++ b/controllers/ctlog/actions/createTree.go @@ -8,8 +8,11 @@ import ( "github.com/securesign/operator/controllers/common" "github.com/securesign/operator/controllers/common/action" utils "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" trillian "github.com/securesign/operator/controllers/trillian/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func NewCreateTrillianTreeAction() action.Action[rhtasv1alpha1.CTlog] { @@ -25,7 +28,8 @@ func (i createTrillianTreeAction) Name() string { } func (i createTrillianTreeAction) CanHandle(instance *rhtasv1alpha1.CTlog) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating && (instance.Spec.TreeID == nil || *instance.Spec.TreeID == int64(0)) + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating && (instance.Spec.TreeID == nil || *instance.Spec.TreeID == int64(0)) } func (i createTrillianTreeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) *action.Result { @@ -33,12 +37,22 @@ func (i createTrillianTreeAction) Handle(ctx context.Context, instance *rhtasv1a trillUrl, err := utils.GetInternalUrl(ctx, i.Client, instance.Namespace, trillian.LogserverDeploymentName) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not find trillian instance: %w", err), instance) } tree, err := common.CreateTrillianTree(ctx, "ctlog-tree", trillUrl+":8091") if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create trillian tree: %w", err), instance) } i.Recorder.Event(instance, v1.EventTypeNormal, "TreeID", "New Trillian tree created") diff --git a/controllers/ctlog/actions/deployment.go b/controllers/ctlog/actions/deployment.go index 3faf92f63..5962a6924 100644 --- a/controllers/ctlog/actions/deployment.go +++ b/controllers/ctlog/actions/deployment.go @@ -8,6 +8,8 @@ import ( "github.com/securesign/operator/controllers/common/action" "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/ctlog/utils" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -23,8 +25,9 @@ func (i deployAction) Name() string { return "deploy" } -func (i deployAction) CanHandle(tuf *rhtasv1alpha1.CTlog) bool { - return tuf.Status.Phase == rhtasv1alpha1.PhaseCreating || tuf.Status.Phase == rhtasv1alpha1.PhaseReady +func (i deployAction) CanHandle(instance *rhtasv1alpha1.CTlog) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) *action.Result { @@ -42,11 +45,19 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) } if updated, err = i.Ensure(ctx, dp); err != nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create CTlog: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Service created"}) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/ctlog/actions/generate_keys.go b/controllers/ctlog/actions/generate_keys.go index 59d8d4d38..065a54744 100644 --- a/controllers/ctlog/actions/generate_keys.go +++ b/controllers/ctlog/actions/generate_keys.go @@ -30,7 +30,8 @@ func (g generateKeys) Name() string { } func (g generateKeys) CanHandle(instance *v1alpha1.CTlog) bool { - return instance.Status.Phase == v1alpha1.PhaseCreating && instance.Spec.PrivateKeyRef == nil + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating && instance.Spec.PrivateKeyRef == nil } func (g generateKeys) Handle(ctx context.Context, instance *v1alpha1.CTlog) *action.Result { @@ -54,11 +55,10 @@ func (g generateKeys) Handle(ctx context.Context, instance *v1alpha1.CTlog) *act return g.Failed(fmt.Errorf("could not set controller reference for Secret: %w", err)) } if _, err = g.Ensure(ctx, secret); err != nil { - instance.Status.Phase = v1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(v1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return g.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Secret: %w", err), instance) diff --git a/controllers/ctlog/actions/handle_fulcio_root.go b/controllers/ctlog/actions/handle_fulcio_root.go index 723e0aa3c..51da03674 100644 --- a/controllers/ctlog/actions/handle_fulcio_root.go +++ b/controllers/ctlog/actions/handle_fulcio_root.go @@ -7,8 +7,11 @@ import ( "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" k8sutils "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/fulcio/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func NewHandleFulcioCertAction() action.Action[v1alpha1.CTlog] { @@ -24,7 +27,8 @@ func (g handleFulcioCert) Name() string { } func (g handleFulcioCert) CanHandle(instance *v1alpha1.CTlog) bool { - return instance.Status.Phase == v1alpha1.PhaseCreating && len(instance.Spec.RootCertificates) == 0 + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating && len(instance.Spec.RootCertificates) == 0 } func (g handleFulcioCert) Handle(ctx context.Context, instance *v1alpha1.CTlog) *action.Result { @@ -34,9 +38,24 @@ func (g handleFulcioCert) Handle(ctx context.Context, instance *v1alpha1.CTlog) return g.Failed(err) } if scr == nil { - //TODO: add status condition - waiting for fulcio - return g.Requeue() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: CertCondition, + Status: metav1.ConditionFalse, + Reason: "Cert not found", + }) + return g.StatusUpdate(ctx, instance) + } else { + if !meta.IsStatusConditionTrue(instance.Status.Conditions, CertCondition) { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: CertCondition, + Status: metav1.ConditionTrue, + Reason: "Resolved", + }, + ) + return g.StatusUpdate(ctx, instance) + } } + if scr.Data[scr.Labels[actions.FulcioCALabel]] == nil { return g.Failed(fmt.Errorf("can't find fulcio certificate in provided secret")) } diff --git a/controllers/ctlog/actions/initialize.go b/controllers/ctlog/actions/initialize.go index aea4eef07..b53197148 100644 --- a/controllers/ctlog/actions/initialize.go +++ b/controllers/ctlog/actions/initialize.go @@ -24,7 +24,8 @@ func (i initializeAction) Name() string { } func (i initializeAction) CanHandle(instance *rhtasv1alpha1.CTlog) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseInitialize + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize } func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) *action.Result { @@ -39,13 +40,19 @@ func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CT } if !ok { i.Logger.Info("Waiting for deployment") - // deployment is watched - no need to requeue - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + }) + return i.StatusUpdate(ctx, instance) } - - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseReady)}) - - instance.Status.Phase = rhtasv1alpha1.PhaseReady + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionTrue, + Reason: constants.Ready, + Message: "Deployment ready", + }) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/ctlog/actions/pending.go b/controllers/ctlog/actions/pending.go index 68e28e2fc..a3754baeb 100644 --- a/controllers/ctlog/actions/pending.go +++ b/controllers/ctlog/actions/pending.go @@ -6,7 +6,10 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" utils "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" trillian "github.com/securesign/operator/controllers/trillian/actions" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func NewPendingAction() action.Action[rhtasv1alpha1.CTlog] { @@ -22,23 +25,44 @@ func (i pendingAction) Name() string { } func (i pendingAction) CanHandle(instance *rhtasv1alpha1.CTlog) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseNone || instance.Status.Phase == rhtasv1alpha1.PhasePending + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c == nil || c.Reason == constants.Pending } func (i pendingAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) *action.Result { - if instance.Status.Phase == rhtasv1alpha1.PhaseNone { - instance.Status.Phase = rhtasv1alpha1.PhasePending + if meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) == nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Pending, + }) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: CertCondition, + Status: metav1.ConditionUnknown, + Reason: constants.Pending, + }) return i.StatusUpdate(ctx, instance) } var err error _, err = utils.GetInternalUrl(ctx, i.Client, instance.Namespace, trillian.LogserverDeploymentName) if err != nil { - //TODO: add status condition - waiting for trillian + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Pending, + Message: "Waiting for Trillian Logserver service", + }) + // update will throw requeue only with first update + i.StatusUpdate(ctx, instance) return i.Requeue() } - instance.Status.Phase = rhtasv1alpha1.PhaseCreating + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + }) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/ctlog/actions/rbac.go b/controllers/ctlog/actions/rbac.go index 50fd5feec..082f9f130 100644 --- a/controllers/ctlog/actions/rbac.go +++ b/controllers/ctlog/actions/rbac.go @@ -28,7 +28,8 @@ func (i rbacAction) Name() string { } func (i rbacAction) CanHandle(instance *rhtasv1alpha1.CTlog) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) *action.Result { @@ -56,12 +57,11 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) * // don't re-enqueue for RBAC in any case (except failure) _, err = i.Ensure(ctx, sa) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: err.Error(), - Message: "Fail to create ServiceAccount", + Reason: constants.Failure, + Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create SA: %w", err), instance) } @@ -83,12 +83,11 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) * } _, err = i.Ensure(ctx, role) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: err.Error(), - Message: "Fail to create Role", + Reason: constants.Failure, + Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Role: %w", err), instance) } @@ -106,12 +105,11 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) * } _, err = i.Ensure(ctx, rb) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: err.Error(), - Message: "Fail to create RoleBinding", + Reason: constants.Failure, + Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create RoleBinding: %w", err), instance) } diff --git a/controllers/ctlog/actions/serverConfig.go b/controllers/ctlog/actions/serverConfig.go index f2340ff6c..dbf635609 100644 --- a/controllers/ctlog/actions/serverConfig.go +++ b/controllers/ctlog/actions/serverConfig.go @@ -33,7 +33,8 @@ func (i serverConfig) Name() string { } func (i serverConfig) CanHandle(instance *rhtasv1alpha1.CTlog) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i serverConfig) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) *action.Result { @@ -55,7 +56,12 @@ func (i serverConfig) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) certConfig, err := i.handlePrivateKey(instance) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) return i.FailedWithStatusUpdate(ctx, err, instance) } @@ -67,7 +73,12 @@ func (i serverConfig) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) //TODO: the config is generated in every reconcile loop rotation - it can cause performance issues if config, err = ctlogUtils.CreateCtlogConfig(ctx, instance.Namespace, trillUrl+":8091", *instance.Spec.TreeID, rootCerts, secretLabels, certConfig); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create CTLog configuration: %w", err), instance) } // patch secret name @@ -77,18 +88,19 @@ func (i serverConfig) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) return i.Failed(fmt.Errorf("could not set controller reference for Secret: %w", err)) } if _, err = i.Ensure(ctx, config); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Secret: %w", err), instance) } if updated { - return i.Requeue() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Server config created"}) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/ctlog/actions/service.go b/controllers/ctlog/actions/service.go index 31a81531b..693749189 100644 --- a/controllers/ctlog/actions/service.go +++ b/controllers/ctlog/actions/service.go @@ -28,7 +28,8 @@ func (i serviceAction) Name() string { } func (i serviceAction) CanHandle(instance *rhtasv1alpha1.CTlog) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i serviceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) *action.Result { @@ -50,18 +51,19 @@ func (i serviceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog return i.Failed(fmt.Errorf("could not set controller reference for Service: %w", err)) } if updated, err = i.Ensure(ctx, svc); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create service: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Service created"}) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/ctlog/actions/toInitializePhase.go b/controllers/ctlog/actions/toInitializePhase.go index 717b17023..8628854e9 100644 --- a/controllers/ctlog/actions/toInitializePhase.go +++ b/controllers/ctlog/actions/toInitializePhase.go @@ -5,6 +5,7 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -22,14 +23,13 @@ func (i toInitializeAction) Name() string { } func (i toInitializeAction) CanHandle(instance *rhtasv1alpha1.CTlog) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating } func (i toInitializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) *action.Result { - instance.Status.Phase = rhtasv1alpha1.PhaseInitialize - - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseInitialize)}) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionTrue, Reason: constants.Initialize}) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/ctlog/ctlog_controller_test.go b/controllers/ctlog/ctlog_controller_test.go index fda286dd6..90eba6420 100644 --- a/controllers/ctlog/ctlog_controller_test.go +++ b/controllers/ctlog/ctlog_controller_test.go @@ -28,6 +28,7 @@ import ( fulcio "github.com/securesign/operator/controllers/fulcio/actions" trillian "github.com/securesign/operator/controllers/trillian/actions" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -108,20 +109,27 @@ var _ = Describe("CTlog controller", func() { return k8sClient.Get(ctx, typeNamespaceName, found) }, time.Minute, time.Second).Should(Succeed()) + By("Status conditions are initialized") + Eventually(func() bool { + found := &v1alpha1.CTlog{} + Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) + return meta.IsStatusConditionPresentAndEqual(found.Status.Conditions, constants.Ready, metav1.ConditionFalse) + }, time.Minute, time.Second).Should(BeTrue()) + By("Pending phase until Trillian svc is created") - Eventually(func() v1alpha1.Phase { + Eventually(func() string { found := &v1alpha1.CTlog{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhasePending)) + return meta.FindStatusCondition(found.Status.Conditions, constants.Ready).Reason + }, time.Minute, time.Second).Should(Equal(constants.Pending)) By("Creating trillian service") - Expect(k8sClient.Create(ctx, kubernetes.CreateService(Namespace, trillian.LogserverDeploymentName, 8091, constants.LabelsForComponent(trillian.ComponentName, instance.Name)))).To(Succeed()) - Eventually(func() v1alpha1.Phase { + Expect(k8sClient.Create(ctx, kubernetes.CreateService(Namespace, trillian.LogserverDeploymentName, 8091, constants.LabelsForComponent(trillian.LogServerComponentName, instance.Name)))).To(Succeed()) + Eventually(func() string { found := &v1alpha1.CTlog{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseCreating)) + return meta.FindStatusCondition(found.Status.Conditions, constants.Ready).Reason + }, time.Minute, time.Second).Should(Equal(constants.Creating)) By("Creating fulcio root cert") Expect(k8sClient.Create(ctx, kubernetes.CreateSecret("test", Namespace, @@ -129,11 +137,11 @@ var _ = Describe("CTlog controller", func() { map[string]string{fulcio.FulcioCALabel: "cert"}, ))).To(Succeed()) - Eventually(func() v1alpha1.Phase { + Eventually(func() string { found := &v1alpha1.CTlog{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseCreating)) + return meta.FindStatusCondition(found.Status.Conditions, constants.Ready).Reason + }, time.Minute, time.Second).Should(Equal(constants.Creating)) By("Key Secret is created") found := &v1alpha1.CTlog{} @@ -171,11 +179,11 @@ var _ = Describe("CTlog controller", func() { Expect(k8sClient.Status().Update(ctx, deployment)).Should(Succeed()) By("Waiting until CTlog instance is Ready") - Eventually(func() v1alpha1.Phase { + Eventually(func() bool { found := &v1alpha1.CTlog{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseReady)) + return meta.IsStatusConditionTrue(found.Status.Conditions, constants.Ready) + }, time.Minute, time.Second).Should(BeTrue()) By("Checking if controller will return deployment to desired state") deployment = &appsv1.Deployment{} diff --git a/controllers/fulcio/actions/constants.go b/controllers/fulcio/actions/constants.go index 036c3c93e..6dad1edb2 100644 --- a/controllers/fulcio/actions/constants.go +++ b/controllers/fulcio/actions/constants.go @@ -6,4 +6,6 @@ const ( MonitoringRoleName = "prometheus-k8s-fulcio" ServiceMonitorName = "fulcio-metrics" RBACName = "fulcio" + + CertCondition = "FulcioCertAvailable" ) diff --git a/controllers/fulcio/actions/deployment.go b/controllers/fulcio/actions/deployment.go index ebab0ae54..26ab71baa 100644 --- a/controllers/fulcio/actions/deployment.go +++ b/controllers/fulcio/actions/deployment.go @@ -8,6 +8,8 @@ import ( "github.com/securesign/operator/controllers/common/action" "github.com/securesign/operator/controllers/constants" futils "github.com/securesign/operator/controllers/fulcio/utils" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -24,7 +26,8 @@ func (i deployAction) Name() string { } func (i deployAction) CanHandle(tuf *rhtasv1alpha1.Fulcio) bool { - return tuf.Status.Phase == rhtasv1alpha1.PhaseCreating || tuf.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) *action.Result { @@ -42,11 +45,19 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio } if updated, err = i.Ensure(ctx, dp); err != nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Fulcio: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Deployment created"}) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/fulcio/actions/generate_cert.go b/controllers/fulcio/actions/generate_cert.go index a9a8787eb..e37d0aa12 100644 --- a/controllers/fulcio/actions/generate_cert.go +++ b/controllers/fulcio/actions/generate_cert.go @@ -36,29 +36,26 @@ func (g generateCert) Name() string { } func (g generateCert) CanHandle(instance *v1alpha1.Fulcio) bool { - return instance.Status.Phase == v1alpha1.PhaseNone || instance.Status.Phase == v1alpha1.PhasePending + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Pending } func (g generateCert) Handle(ctx context.Context, instance *v1alpha1.Fulcio) *action.Result { - if instance.Status.Phase == v1alpha1.PhaseNone { - instance.Status.Phase = v1alpha1.PhasePending + if instance.Spec.Certificate.PrivateKeyRef == nil && instance.Spec.Certificate.CARef != nil { + err := fmt.Errorf("missing private key for CA certificate") meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: "FulcioCert", - Status: metav1.ConditionUnknown, - Reason: "Resolving", + Type: CertCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), }) - return g.StatusUpdate(ctx, instance) - } - - if instance.Spec.Certificate.PrivateKeyRef == nil && instance.Spec.Certificate.CARef != nil { - instance.Status.Phase = v1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(v1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", - Message: "There is set CARef field but there is no PrivateKeyRef secret attached.", + Reason: constants.Failure, + Message: err.Error(), }) - return g.FailedWithStatusUpdate(ctx, fmt.Errorf("missing private key for CA certificate"), instance) + return g.FailedWithStatusUpdate(ctx, err, instance) } secretName := fmt.Sprintf(SecretNameFormat, instance.Name) @@ -73,11 +70,11 @@ func (g generateCert) Handle(ctx context.Context, instance *v1alpha1.Fulcio) *ac config, err := g.setupCert(instance) if err != nil { - if !meta.IsStatusConditionFalse(instance.Status.Conditions, "FulcioCert") { + if !meta.IsStatusConditionFalse(instance.Status.Conditions, CertCondition) { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: "FulcioCert", + Type: CertCondition, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return g.StatusUpdate(ctx, instance) @@ -92,11 +89,16 @@ func (g generateCert) Handle(ctx context.Context, instance *v1alpha1.Fulcio) *ac return g.Failed(fmt.Errorf("could not set controller reference for Secret: %w", err)) } if updated, err = g.Ensure(ctx, secret); err != nil { - instance.Status.Phase = v1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(v1alpha1.PhaseReady), + Type: CertCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return g.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create secret: %w", err), instance) @@ -135,13 +137,13 @@ func (g generateCert) Handle(ctx context.Context, instance *v1alpha1.Fulcio) *ac return g.Update(ctx, instance) } meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: "FulcioCert", + Type: CertCondition, Status: metav1.ConditionTrue, Reason: "Resolved", }) - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(v1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(v1alpha1.PhaseCreating)}) - instance.Status.Phase = v1alpha1.PhaseCreating + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating}) + return g.StatusUpdate(ctx, instance) } diff --git a/controllers/fulcio/actions/ingress.go b/controllers/fulcio/actions/ingress.go index 33a9c23c2..0f3b584f6 100644 --- a/controllers/fulcio/actions/ingress.go +++ b/controllers/fulcio/actions/ingress.go @@ -9,6 +9,8 @@ import ( "github.com/securesign/operator/controllers/common/utils/kubernetes" "github.com/securesign/operator/controllers/constants" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -26,7 +28,8 @@ func (i ingressAction) Name() string { } func (i ingressAction) CanHandle(instance *rhtasv1alpha1.Fulcio) bool { - return (instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady) && + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready && instance.Spec.ExternalAccess.Enabled } @@ -50,11 +53,18 @@ func (i ingressAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulci } if updated, err = i.Ensure(ctx, ingress); err != nil { - return i.Failed(fmt.Errorf("could not create Ingress: %w", err)) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Ingress created"}) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/fulcio/actions/initialize.go b/controllers/fulcio/actions/initialize.go index 19a098c6d..3ff0af755 100644 --- a/controllers/fulcio/actions/initialize.go +++ b/controllers/fulcio/actions/initialize.go @@ -28,7 +28,8 @@ func (i initializeAction) Name() string { } func (i initializeAction) CanHandle(instance *rhtasv1alpha1.Fulcio) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseInitialize + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize } func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) *action.Result { @@ -43,8 +44,13 @@ func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fu } if !ok { i.Logger.Info("Waiting for deployment") - // deployment is watched - no need to requeue - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + }) + return i.StatusUpdate(ctx, instance) } if instance.Spec.ExternalAccess.Enabled { @@ -62,9 +68,7 @@ func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fu instance.Status.Url = fmt.Sprintf("http://%s.%s.svc", DeploymentName, instance.Namespace) } - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseReady)}) - - instance.Status.Phase = rhtasv1alpha1.PhaseReady + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionTrue, Reason: constants.Ready}) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/fulcio/actions/monitoring.go b/controllers/fulcio/actions/monitoring.go index 0826fa0c1..71a8ded8c 100644 --- a/controllers/fulcio/actions/monitoring.go +++ b/controllers/fulcio/actions/monitoring.go @@ -28,7 +28,8 @@ func (i monitoringAction) Name() string { } func (i monitoringAction) CanHandle(instance *rhtasv1alpha1.Fulcio) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating && instance.Spec.Monitoring.Enabled + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return (c.Reason == constants.Creating || c.Reason == constants.Ready) && instance.Spec.Monitoring.Enabled } func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) *action.Result { @@ -56,11 +57,10 @@ func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fu } if _, err = i.Ensure(ctx, role); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create monitoring role: %w", err), instance) @@ -84,11 +84,10 @@ func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fu } if _, err = i.Ensure(ctx, roleBinding); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create monitoring RoleBinding: %w", err), instance) @@ -113,11 +112,10 @@ func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fu } if _, err = i.Ensure(ctx, serviceMonitor); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create serviceMonitor: %w", err), instance) diff --git a/controllers/fulcio/actions/rbac.go b/controllers/fulcio/actions/rbac.go index c21894667..1fdf37d36 100644 --- a/controllers/fulcio/actions/rbac.go +++ b/controllers/fulcio/actions/rbac.go @@ -28,7 +28,8 @@ func (i rbacAction) Name() string { } func (i rbacAction) CanHandle(instance *rhtasv1alpha1.Fulcio) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) *action.Result { @@ -56,11 +57,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) // don't re-enqueue for RBAC in any case (except failure) _, err = i.Ensure(ctx, sa) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create SA: %w", err), instance) @@ -83,11 +83,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) } _, err = i.Ensure(ctx, role) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Role: %w", err), instance) @@ -106,11 +105,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) } _, err = i.Ensure(ctx, rb) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create RoleBinding: %w", err), instance) diff --git a/controllers/fulcio/actions/service.go b/controllers/fulcio/actions/service.go index d166370a9..b267c09bd 100644 --- a/controllers/fulcio/actions/service.go +++ b/controllers/fulcio/actions/service.go @@ -28,7 +28,8 @@ func (i serviceAction) Name() string { } func (i serviceAction) CanHandle(instance *rhtasv1alpha1.Fulcio) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i serviceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) *action.Result { @@ -56,18 +57,19 @@ func (i serviceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulci return i.Failed(fmt.Errorf("could not set controller reference for Service: %w", err)) } if updated, err = i.Ensure(ctx, svc); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create service: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Service created"}) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/fulcio/actions/servrConfig.go b/controllers/fulcio/actions/servrConfig.go index 7af9f92f2..c29a50b4d 100644 --- a/controllers/fulcio/actions/servrConfig.go +++ b/controllers/fulcio/actions/servrConfig.go @@ -31,15 +31,11 @@ func (i serverConfig) Name() string { } func (i serverConfig) CanHandle(instance *rhtasv1alpha1.Fulcio) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i serverConfig) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) *action.Result { - if instance.Status.Phase != rhtasv1alpha1.PhaseCreating { - instance.Status.Phase = rhtasv1alpha1.PhaseCreating - return i.Update(ctx, instance) - } - var ( err error updated bool @@ -57,18 +53,19 @@ func (i serverConfig) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio return i.Failed(fmt.Errorf("could not set controller reference for ConfigMap: %w", err)) } if updated, err = i.Ensure(ctx, cm); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create service: %w", err), instance) } if updated { - return i.Requeue() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Server config created"}) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/fulcio/actions/to_initialize_phase.go b/controllers/fulcio/actions/to_initialize_phase.go index 3a5b5fc2e..9767f1591 100644 --- a/controllers/fulcio/actions/to_initialize_phase.go +++ b/controllers/fulcio/actions/to_initialize_phase.go @@ -5,6 +5,7 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -22,14 +23,13 @@ func (i toInitialize) Name() string { } func (i toInitialize) CanHandle(instance *rhtasv1alpha1.Fulcio) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating } func (i toInitialize) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) *action.Result { - instance.Status.Phase = rhtasv1alpha1.PhaseInitialize - - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseInitialize)}) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Initialize}) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/fulcio/actions/to_pending_phase.go b/controllers/fulcio/actions/to_pending_phase.go new file mode 100644 index 000000000..ea3b83944 --- /dev/null +++ b/controllers/fulcio/actions/to_pending_phase.go @@ -0,0 +1,39 @@ +package actions + +import ( + "context" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func NewToPendingPhaseAction() action.Action[rhtasv1alpha1.Fulcio] { + return &toPending{} +} + +type toPending struct { + action.BaseAction +} + +func (i toPending) Name() string { + return "move to pending phase" +} + +func (i toPending) CanHandle(tuf *rhtasv1alpha1.Fulcio) bool { + return meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready) == nil +} + +func (i toPending) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) *action.Result { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Pending}) + + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: CertCondition, + Status: metav1.ConditionUnknown, + Reason: constants.Pending, + }) + return i.StatusUpdate(ctx, instance) +} diff --git a/controllers/fulcio/fulcio_controller.go b/controllers/fulcio/fulcio_controller.go index d5def0c48..d8fd16a6e 100644 --- a/controllers/fulcio/fulcio_controller.go +++ b/controllers/fulcio/fulcio_controller.go @@ -85,6 +85,7 @@ func (r *FulcioReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr target := instance.DeepCopy() acs := []action.Action[rhtasv1alpha1.Fulcio]{ + actions.NewToPendingPhaseAction(), actions.NewGenerateCertAction(), actions.NewRBACAction(), actions.NewServerConfigAction(), diff --git a/controllers/fulcio/fulcio_controller_test.go b/controllers/fulcio/fulcio_controller_test.go index 682f759eb..91760a1ea 100644 --- a/controllers/fulcio/fulcio_controller_test.go +++ b/controllers/fulcio/fulcio_controller_test.go @@ -27,6 +27,7 @@ import ( "github.com/securesign/operator/controllers/fulcio/actions" v1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -128,12 +129,19 @@ var _ = Describe("Fulcio controller", func() { return k8sClient.Get(ctx, typeNamespaceName, found) }, time.Minute, time.Second).Should(Succeed()) + By("Status conditions are initialized") + Eventually(func() bool { + found := &v1alpha1.Fulcio{} + Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) + return meta.IsStatusConditionPresentAndEqual(found.Status.Conditions, constants.Ready, metav1.ConditionFalse) + }, time.Minute, time.Second).Should(BeTrue()) + By("Pending phase until password key is resolved") - Eventually(func() v1alpha1.Phase { + Eventually(func() string { found := &v1alpha1.Fulcio{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhasePending)) + return meta.FindStatusCondition(found.Status.Conditions, constants.Ready).Reason + }, time.Minute, time.Second).Should(Equal(constants.Pending)) By("Creating password secret with cert password") Expect(k8sClient.Create(ctx, kubernetes.CreateSecret("password-secret", typeNamespaceName.Namespace, map[string][]byte{ @@ -184,11 +192,11 @@ var _ = Describe("Fulcio controller", func() { Expect(k8sClient.Status().Update(ctx, deployment)).Should(Succeed()) By("Waiting until Fulcio instance is Ready") - Eventually(func() v1alpha1.Phase { + Eventually(func() bool { found := &v1alpha1.Fulcio{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseReady)) + return meta.IsStatusConditionTrue(found.Status.Conditions, constants.Ready) + }, time.Minute, time.Second).Should(BeTrue()) By("Checking if Service was successfully created in the reconciliation") service := &corev1.Service{} diff --git a/controllers/rekor/actions/backfillRedis/backfill_redis_cronjob.go b/controllers/rekor/actions/backfillRedis/backfill_redis_cronjob.go index 4d32fac02..8e290ab5f 100644 --- a/controllers/rekor/actions/backfillRedis/backfill_redis_cronjob.go +++ b/controllers/rekor/actions/backfillRedis/backfill_redis_cronjob.go @@ -8,6 +8,7 @@ import ( "github.com/securesign/operator/controllers/rekor/actions" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -31,7 +32,8 @@ func (i backfillRedisCronJob) Name() string { } func (i backfillRedisCronJob) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return (instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady) && instance.Spec.BackFillRedis.Enabled + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return (c.Reason == constants.Creating || c.Reason == constants.Ready) && instance.Spec.BackFillRedis.Enabled } func (i backfillRedisCronJob) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -41,7 +43,6 @@ func (i backfillRedisCronJob) Handle(ctx context.Context, instance *rhtasv1alpha ) if _, err := cron.ParseStandard(instance.Spec.BackFillRedis.Schedule); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError return i.Failed(fmt.Errorf("could not create backfill redis cron job: %w", err)) } @@ -82,12 +83,29 @@ func (i backfillRedisCronJob) Handle(ctx context.Context, instance *rhtasv1alpha } if updated, err = i.Ensure(ctx, backfillRedisCronJob); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.RedisCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create backfill redis cron job: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Backfill redis job created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/rekor/actions/constants.go b/controllers/rekor/actions/constants.go index 264414dfc..6e1d93a1d 100644 --- a/controllers/rekor/actions/constants.go +++ b/controllers/rekor/actions/constants.go @@ -10,4 +10,7 @@ const ( RedisComponentName = "rekor-redis" UIComponentName = "rekor-ui" BackfillRedisCronJobName = "backfill-redis" + UICondition = "UiAvailable" + ServerCondition = "ServerAvailable" + RedisCondition = "RedisAvailable" ) diff --git a/controllers/rekor/actions/initialize.go b/controllers/rekor/actions/initialize.go index 8259ce870..568276b4c 100644 --- a/controllers/rekor/actions/initialize.go +++ b/controllers/rekor/actions/initialize.go @@ -5,6 +5,7 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -22,24 +23,19 @@ func (i initializeAction) Name() string { } func (i initializeAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseInitialize + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize } func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { - components := []string{ServerComponentName, RedisComponentName} - if instance.Spec.RekorSearchUI.Enabled { - components = append(components, UIComponentName) - } - for _, c := range components { - if !meta.IsStatusConditionTrue(instance.Status.Conditions, c+"Ready") { - // deployment is watched - no need to requeue - return i.Return() - } - } + if (!instance.Spec.RekorSearchUI.Enabled || meta.IsStatusConditionTrue(instance.Status.Conditions, UICondition)) && + meta.IsStatusConditionTrue(instance.Status.Conditions, RedisCondition) && + meta.IsStatusConditionTrue(instance.Status.Conditions, ServerCondition) { - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseReady)}) - instance.Status.Phase = rhtasv1alpha1.PhaseReady - return i.StatusUpdate(ctx, instance) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionTrue, Reason: constants.Ready}) + return i.StatusUpdate(ctx, instance) + } + return i.Requeue() } diff --git a/controllers/rekor/actions/pending.go b/controllers/rekor/actions/pending.go index facb5d20d..2d029e21c 100644 --- a/controllers/rekor/actions/pending.go +++ b/controllers/rekor/actions/pending.go @@ -6,7 +6,10 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" utils "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" trillian "github.com/securesign/operator/controllers/trillian/actions" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func NewPendingAction() action.Action[rhtasv1alpha1.Rekor] { @@ -22,23 +25,55 @@ func (i pendingAction) Name() string { } func (i pendingAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseNone || instance.Status.Phase == rhtasv1alpha1.PhasePending + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c == nil || c.Reason == constants.Pending } func (i pendingAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { - if instance.Status.Phase == rhtasv1alpha1.PhaseNone { - instance.Status.Phase = rhtasv1alpha1.PhasePending + if meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) == nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Pending, + }) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: ServerCondition, + Status: metav1.ConditionUnknown, + Reason: constants.Pending, + }) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: RedisCondition, + Status: metav1.ConditionUnknown, + Reason: constants.Pending, + }) + if instance.Spec.RekorSearchUI.Enabled { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: UICondition, + Status: metav1.ConditionUnknown, + Reason: constants.Pending, + }) + } return i.StatusUpdate(ctx, instance) } var err error _, err = utils.GetInternalUrl(ctx, i.Client, instance.Namespace, trillian.LogserverDeploymentName) if err != nil { - //TODO: add status condition - waiting for trillian + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Pending, + Message: "Waiting for Trillian Logserver service", + }) + i.StatusUpdate(ctx, instance) return i.Requeue() } - instance.Status.Phase = rhtasv1alpha1.PhaseCreating + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + }) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/rekor/actions/rbac.go b/controllers/rekor/actions/rbac.go index f3b80b148..90405e3ec 100644 --- a/controllers/rekor/actions/rbac.go +++ b/controllers/rekor/actions/rbac.go @@ -28,7 +28,8 @@ func (i rbacAction) Name() string { } func (i rbacAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -56,11 +57,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) * // don't re-enqueue for RBAC in any case (except failure) _, err = i.Ensure(ctx, sa) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create SA: %w", err), instance) @@ -83,11 +83,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) * } _, err = i.Ensure(ctx, role) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Role: %w", err), instance) @@ -106,11 +105,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) * } _, err = i.Ensure(ctx, rb) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create RoleBinding: %w", err), instance) diff --git a/controllers/rekor/actions/redis/deployment.go b/controllers/rekor/actions/redis/deployment.go index 35df9fd47..eec01b628 100644 --- a/controllers/rekor/actions/redis/deployment.go +++ b/controllers/rekor/actions/redis/deployment.go @@ -8,6 +8,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/rekor/actions" "github.com/securesign/operator/controllers/rekor/utils" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -26,7 +28,8 @@ func (i deployAction) Name() string { } func (i deployAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -41,12 +44,29 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) } if updated, err = i.Ensure(ctx, dp); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.RedisCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Rekor redis: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.RedisCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Redis created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/rekor/actions/redis/initialize.go b/controllers/rekor/actions/redis/initialize.go index 9010f06ee..736b08860 100644 --- a/controllers/rekor/actions/redis/initialize.go +++ b/controllers/rekor/actions/redis/initialize.go @@ -25,7 +25,8 @@ func (i initializeAction) Name() string { } func (i initializeAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseInitialize && !meta.IsStatusConditionTrue(instance.Status.Conditions, actions.RedisComponentName+"Ready") + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize && !meta.IsStatusConditionTrue(instance.Status.Conditions, actions.RedisCondition) } func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -40,12 +41,17 @@ func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Re } if !ok { i.Logger.Info("Waiting for deployment") - // deployment is watched - no need to requeue - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.RedisCondition, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + }) + return i.StatusUpdate(ctx, instance) } - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: actions.RedisComponentName + "Ready", - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseReady)}) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: actions.RedisCondition, + Status: metav1.ConditionTrue, Reason: constants.Ready}) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/rekor/actions/redis/svc.go b/controllers/rekor/actions/redis/svc.go index d11053f57..5d45abd47 100644 --- a/controllers/rekor/actions/redis/svc.go +++ b/controllers/rekor/actions/redis/svc.go @@ -8,6 +8,8 @@ import ( k8sutils "github.com/securesign/operator/controllers/common/utils/kubernetes" "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/rekor/actions" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -26,7 +28,8 @@ func (i createServiceAction) Name() string { } func (i createServiceAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -44,12 +47,29 @@ func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1 } if updated, err = i.Ensure(ctx, svc); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.RedisCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create service: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.RedisCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Service created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/rekor/actions/server/createTree.go b/controllers/rekor/actions/server/createTree.go index b9c223e90..c37cb0ac7 100644 --- a/controllers/rekor/actions/server/createTree.go +++ b/controllers/rekor/actions/server/createTree.go @@ -8,8 +8,12 @@ import ( "github.com/securesign/operator/controllers/common" "github.com/securesign/operator/controllers/common/action" k8sutils "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" + "github.com/securesign/operator/controllers/rekor/actions" trillian "github.com/securesign/operator/controllers/trillian/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func NewCreateTrillianTreeAction() action.Action[rhtasv1alpha1.Rekor] { @@ -25,7 +29,8 @@ func (i createTrillianTreeAction) Name() string { } func (i createTrillianTreeAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating && (instance.Spec.TreeID == nil || *instance.Spec.TreeID == int64(0)) + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating && (instance.Spec.TreeID == nil || *instance.Spec.TreeID == int64(0)) } func (i createTrillianTreeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -37,7 +42,18 @@ func (i createTrillianTreeAction) Handle(ctx context.Context, instance *rhtasv1a } tree, err := common.CreateTrillianTree(ctx, "rekor-tree", trillUrl+":8091") if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create trillian tree: %w", err), instance) } i.Recorder.Event(instance, v1.EventTypeNormal, "TreeID", "New Trillian tree created") diff --git a/controllers/rekor/actions/server/deployment.go b/controllers/rekor/actions/server/deployment.go index 50ad1efbd..020ef8314 100644 --- a/controllers/rekor/actions/server/deployment.go +++ b/controllers/rekor/actions/server/deployment.go @@ -8,6 +8,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/rekor/actions" "github.com/securesign/operator/controllers/rekor/utils" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -26,7 +28,8 @@ func (i deployAction) Name() string { } func (i deployAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return (c.Reason == constants.Creating || c.Reason == constants.Ready) } func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -41,12 +44,29 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) } if updated, err = i.Ensure(ctx, dp); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Rekor server: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Deployment created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/rekor/actions/server/deploymentReady.go b/controllers/rekor/actions/server/deploymentReady.go deleted file mode 100644 index 22d04a261..000000000 --- a/controllers/rekor/actions/server/deploymentReady.go +++ /dev/null @@ -1,45 +0,0 @@ -package server - -import ( - "context" - - rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" - "github.com/securesign/operator/controllers/common/action" - commonUtils "github.com/securesign/operator/controllers/common/utils/kubernetes" - "github.com/securesign/operator/controllers/constants" - "github.com/securesign/operator/controllers/rekor/actions" -) - -func NewDeploymentReadyAction() action.Action[rhtasv1alpha1.Rekor] { - return &deploymentReadyAction{} -} - -type deploymentReadyAction struct { - action.BaseAction -} - -func (i deploymentReadyAction) Name() string { - return "deployment ready" -} - -func (i deploymentReadyAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseInitialize -} - -func (i deploymentReadyAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { - var ( - ok bool - err error - ) - labels := constants.LabelsForComponent(actions.ServerComponentName, instance.Name) - ok, err = commonUtils.DeploymentIsRunning(ctx, i.Client, instance.Namespace, labels) - if err != nil { - return i.Failed(err) - } - if !ok { - i.Logger.Info("Waiting for deployment") - // deployment is watched - no need to requeue - return i.Return() - } - return i.Continue() -} diff --git a/controllers/rekor/actions/server/generate_signer.go b/controllers/rekor/actions/server/generate_signer.go index e6813eb8e..3b93d1e22 100644 --- a/controllers/rekor/actions/server/generate_signer.go +++ b/controllers/rekor/actions/server/generate_signer.go @@ -34,7 +34,8 @@ func (g generateSigner) Name() string { } func (g generateSigner) CanHandle(instance *v1alpha1.Rekor) bool { - return (instance.Status.Phase == v1alpha1.PhaseCreating) && + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating && (instance.Spec.Signer.KMS == "secret" || instance.Spec.Signer.KMS == "") && instance.Spec.Signer.KeyRef == nil } @@ -49,7 +50,18 @@ func (g generateSigner) Handle(ctx context.Context, instance *v1alpha1.Rekor) *a certConfig, err := utils.CreateRekorKey() if err != nil { - instance.Status.Phase = v1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + 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) } @@ -69,11 +81,16 @@ func (g generateSigner) Handle(ctx context.Context, instance *v1alpha1.Rekor) *a return g.Failed(fmt.Errorf("could not set controller reference for Secret: %w", err)) } if updated, err = g.Ensure(ctx, secret); err != nil { - instance.Status.Phase = v1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(v1alpha1.PhaseReady), + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return g.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create secret: %w", err), instance) diff --git a/controllers/rekor/actions/server/ingress.go b/controllers/rekor/actions/server/ingress.go index 33c99a136..cdb47082f 100644 --- a/controllers/rekor/actions/server/ingress.go +++ b/controllers/rekor/actions/server/ingress.go @@ -10,6 +10,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/rekor/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -27,7 +29,8 @@ func (i ingressAction) Name() string { } func (i ingressAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return (instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady) && + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return (c.Reason == constants.Creating || c.Reason == constants.Ready) && instance.Spec.ExternalAccess.Enabled } @@ -51,11 +54,29 @@ func (i ingressAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor } if updated, err = i.Ensure(ctx, ingress); err != nil { - return i.Failed(fmt.Errorf("could not create Ingress: %w", err)) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + 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 i.FailedWithStatusUpdate(ctx, err, instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Ingress created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/rekor/actions/server/initialize.go b/controllers/rekor/actions/server/initialize.go new file mode 100644 index 000000000..8fba9ecaf --- /dev/null +++ b/controllers/rekor/actions/server/initialize.go @@ -0,0 +1,78 @@ +package server + +import ( + "context" + "fmt" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/common/action" + commonUtils "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" + "github.com/securesign/operator/controllers/rekor/actions" + v12 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func NewInitializeAction() action.Action[rhtasv1alpha1.Rekor] { + return &initializeAction{} +} + +type initializeAction struct { + action.BaseAction +} + +func (i initializeAction) Name() string { + return "initialize" +} + +func (i initializeAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize && !meta.IsStatusConditionTrue(instance.Status.Conditions, actions.ServerCondition) +} + +func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { + var ( + ok bool + err error + ) + labels := constants.LabelsForComponent(actions.ServerComponentName, instance.Name) + ok, err = commonUtils.DeploymentIsRunning(ctx, i.Client, instance.Namespace, labels) + if err != nil { + return i.Failed(err) + } + if !ok { + i.Logger.Info("Waiting for deployment") + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + }) + return i.StatusUpdate(ctx, instance) + } + + if instance.Spec.ExternalAccess.Enabled { + protocol := "http://" + ingress := &v12.Ingress{} + err = i.Client.Get(ctx, types.NamespacedName{Name: actions.ServerDeploymentName, Namespace: instance.Namespace}, ingress) + if err != nil { + return i.Failed(err) + } + if len(ingress.Spec.TLS) > 0 { + protocol = "https://" + } + instance.Status.Url = protocol + ingress.Spec.Rules[0].Host + } else { + instance.Status.Url = fmt.Sprintf("http://%s.%s.svc", actions.ServerDeploymentName, instance.Namespace) + } + + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + Status: metav1.ConditionTrue, + Reason: constants.Ready, + Message: "Deployment ready", + }) + return i.Continue() +} diff --git a/controllers/rekor/actions/server/initializeUrl.go b/controllers/rekor/actions/server/initializeUrl.go deleted file mode 100644 index 01c963673..000000000 --- a/controllers/rekor/actions/server/initializeUrl.go +++ /dev/null @@ -1,57 +0,0 @@ -package server - -import ( - "context" - "fmt" - - "github.com/securesign/operator/controllers/common/action" - "github.com/securesign/operator/controllers/rekor/actions" - v12 "k8s.io/api/networking/v1" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - - rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" -) - -func NewInitializeUrlAction() action.Action[rhtasv1alpha1.Rekor] { - return &initializeUrlAction{} -} - -type initializeUrlAction struct { - action.BaseAction -} - -func (i initializeUrlAction) Name() string { - return "initialize url" -} - -func (i initializeUrlAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseInitialize && !meta.IsStatusConditionTrue(instance.Status.Conditions, actions.ServerComponentName+"Ready") -} - -func (i initializeUrlAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { - var ( - err error - ) - - if instance.Spec.ExternalAccess.Enabled { - protocol := "http://" - ingress := &v12.Ingress{} - err = i.Client.Get(ctx, types.NamespacedName{Name: actions.ServerDeploymentName, Namespace: instance.Namespace}, ingress) - if err != nil { - return i.Failed(err) - } - if len(ingress.Spec.TLS) > 0 { - protocol = "https://" - } - instance.Status.Url = protocol + ingress.Spec.Rules[0].Host - } else { - instance.Status.Url = fmt.Sprintf("http://%s.%s.svc", actions.ServerDeploymentName, instance.Namespace) - } - - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: actions.ServerComponentName + "Ready", - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseReady)}) - - return i.StatusUpdate(ctx, instance) -} diff --git a/controllers/rekor/actions/server/monitoring.go b/controllers/rekor/actions/server/monitoring.go index 8ff6b6411..a063321ee 100644 --- a/controllers/rekor/actions/server/monitoring.go +++ b/controllers/rekor/actions/server/monitoring.go @@ -29,7 +29,8 @@ func (i monitoringAction) Name() string { } func (i monitoringAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return (instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady) && instance.Spec.Monitoring.Enabled + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return (c.Reason == constants.Creating || c.Reason == constants.Ready) && instance.Spec.Monitoring.Enabled } func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -57,11 +58,16 @@ func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Re } if _, err = i.Ensure(ctx, role); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: actions.ServerCondition, Status: metav1.ConditionFalse, - Reason: "Failure", + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create monitoring role: %w", err), instance) @@ -85,11 +91,16 @@ func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Re } if _, err = i.Ensure(ctx, roleBinding); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create monitoring RoleBinding: %w", err), instance) @@ -114,11 +125,16 @@ func (i monitoringAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Re } if _, err = i.Ensure(ctx, serviceMonitor); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create serviceMonitor: %w", err), instance) diff --git a/controllers/rekor/actions/server/pvc.go b/controllers/rekor/actions/server/pvc.go index c53a62689..d645cf961 100644 --- a/controllers/rekor/actions/server/pvc.go +++ b/controllers/rekor/actions/server/pvc.go @@ -9,6 +9,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/rekor/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -29,7 +31,8 @@ func (i createPvcAction) Name() string { } func (i createPvcAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating && instance.Spec.PvcName == "" + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating && instance.Spec.PvcName == "" } func (i createPvcAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -44,7 +47,18 @@ func (i createPvcAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rek } if _, err = i.Ensure(ctx, pvc); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create DB PVC: %w", err), instance) } diff --git a/controllers/rekor/actions/server/resolve_pub_key.go b/controllers/rekor/actions/server/resolve_pub_key.go index 32c8ec271..c711476ad 100644 --- a/controllers/rekor/actions/server/resolve_pub_key.go +++ b/controllers/rekor/actions/server/resolve_pub_key.go @@ -34,7 +34,8 @@ func (i resolvePubKeyAction) Name() string { } func (i resolvePubKeyAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseInitialize + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize && meta.IsStatusConditionTrue(instance.Status.Conditions, actions.ServerCondition) } func (i resolvePubKeyAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -68,11 +69,16 @@ func (i resolvePubKeyAction) Handle(ctx context.Context, instance *rhtasv1alpha1 return i.Failed(fmt.Errorf("could not set controller reference for Secret: %w", err)) } if updated, err = i.Ensure(ctx, scr); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: actions.ServerCondition, Status: metav1.ConditionFalse, - Reason: "Failure", + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create secret: %w", err), instance) @@ -98,7 +104,6 @@ func (i resolvePubKeyAction) resolvePubKey(instance rhtasv1alpha1.Rekor) ([]byte } if err != nil || pubKeyResponse.StatusCode != http.StatusOK { - instance.Status.Phase = rhtasv1alpha1.PhaseError return nil, err } return io.ReadAll(pubKeyResponse.Body) diff --git a/controllers/rekor/actions/server/servrConfig.go b/controllers/rekor/actions/server/servrConfig.go index 0ae9b67e3..267a2bcf9 100644 --- a/controllers/rekor/actions/server/servrConfig.go +++ b/controllers/rekor/actions/server/servrConfig.go @@ -31,7 +31,8 @@ func (i serverConfig) Name() string { } func (i serverConfig) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i serverConfig) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -49,18 +50,29 @@ func (i serverConfig) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) return i.Failed(fmt.Errorf("could not set controller reference for ConfigMap: %w", err)) } if updated, err = i.Ensure(ctx, cm); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: actions.ServerCondition, Status: metav1.ConditionFalse, - Reason: "Failure", + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create CondigMap: %w", err), instance) } if updated { - return i.Requeue() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Server config created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/rekor/actions/server/svc.go b/controllers/rekor/actions/server/svc.go index a09cc98b6..54a745b67 100644 --- a/controllers/rekor/actions/server/svc.go +++ b/controllers/rekor/actions/server/svc.go @@ -9,6 +9,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/rekor/actions" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -28,7 +30,8 @@ func (i createServiceAction) Name() string { } func (i createServiceAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -52,12 +55,29 @@ func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1 } if updated, err = i.Ensure(ctx, svc); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create service: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Service created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/rekor/actions/to_creating_phase.go b/controllers/rekor/actions/to_creating_phase.go deleted file mode 100644 index 806139304..000000000 --- a/controllers/rekor/actions/to_creating_phase.go +++ /dev/null @@ -1,35 +0,0 @@ -package actions - -import ( - "context" - - rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" - "github.com/securesign/operator/controllers/common/action" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func NewToCreatingAction() action.Action[rhtasv1alpha1.Rekor] { - return &toCreatingAction{} -} - -type toCreatingAction struct { - action.BaseAction -} - -func (i toCreatingAction) Name() string { - return "move to creating phase" -} - -func (i toCreatingAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhasePending -} - -func (i toCreatingAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { - instance.Status.Phase = rhtasv1alpha1.PhaseCreating - - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseCreating)}) - - return i.StatusUpdate(ctx, instance) -} diff --git a/controllers/rekor/actions/to_initialize_phase.go b/controllers/rekor/actions/to_initialize_phase.go index ee0b5d7ab..b2a7b77a8 100644 --- a/controllers/rekor/actions/to_initialize_phase.go +++ b/controllers/rekor/actions/to_initialize_phase.go @@ -5,6 +5,7 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -22,14 +23,14 @@ func (i toInitializeAction) Name() string { } func (i toInitializeAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating } func (i toInitializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { - instance.Status.Phase = rhtasv1alpha1.PhaseInitialize - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseInitialize)}) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Initialize}) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/rekor/actions/ui/deployment.go b/controllers/rekor/actions/ui/deployment.go index cebb83d7a..2d9cf557b 100644 --- a/controllers/rekor/actions/ui/deployment.go +++ b/controllers/rekor/actions/ui/deployment.go @@ -8,6 +8,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/rekor/actions" "github.com/securesign/operator/controllers/rekor/utils" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -26,7 +28,8 @@ func (i deployAction) Name() string { } func (i deployAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return (instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady) && instance.Spec.RekorSearchUI.Enabled + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return (c.Reason == constants.Creating || c.Reason == constants.Ready) && instance.Spec.RekorSearchUI.Enabled } func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -41,12 +44,29 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) } if updated, err = i.Ensure(ctx, dp); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.UICondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Rekor search UI: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.UICondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Deployment created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/rekor/actions/ui/ingress.go b/controllers/rekor/actions/ui/ingress.go index 97aefa9c8..f6f696556 100644 --- a/controllers/rekor/actions/ui/ingress.go +++ b/controllers/rekor/actions/ui/ingress.go @@ -10,6 +10,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/rekor/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -27,7 +29,8 @@ func (i ingressAction) Name() string { } func (i ingressAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return (instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady) && + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return (c.Reason == constants.Creating || c.Reason == constants.Ready) && instance.Spec.RekorSearchUI.Enabled } @@ -51,11 +54,29 @@ func (i ingressAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor } if updated, err = i.Ensure(ctx, ingress); err != nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.UICondition, + 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 i.Failed(fmt.Errorf("could not create Ingress: %w", err)) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.UICondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Ingress created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/rekor/actions/ui/initialize.go b/controllers/rekor/actions/ui/initialize.go index 1d9e70518..c274fa898 100644 --- a/controllers/rekor/actions/ui/initialize.go +++ b/controllers/rekor/actions/ui/initialize.go @@ -28,8 +28,10 @@ func (i initializeAction) Name() string { } func (i initializeAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseInitialize && instance.Spec.RekorSearchUI.Enabled && - !meta.IsStatusConditionTrue(instance.Status.Conditions, actions.UIComponentName+"Ready") + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize && + !meta.IsStatusConditionTrue(instance.Status.Conditions, actions.UICondition) && + instance.Spec.RekorSearchUI.Enabled } func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -44,8 +46,13 @@ func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Re } if !ok { i.Logger.Info("Waiting for deployment") - // deployment is watched - no need to requeue - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.UICondition, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + }) + return i.StatusUpdate(ctx, instance) } protocol := "http://" @@ -60,8 +67,8 @@ func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Re } instance.Status.RekorSearchUIUrl = protocol + ingress.Spec.Rules[0].Host - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: actions.UIComponentName + "Ready", - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseReady)}) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: actions.UICondition, + Status: metav1.ConditionTrue, Reason: constants.Ready}) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/rekor/actions/ui/svc.go b/controllers/rekor/actions/ui/svc.go index 8eceed63d..4d3ba0c59 100644 --- a/controllers/rekor/actions/ui/svc.go +++ b/controllers/rekor/actions/ui/svc.go @@ -8,6 +8,8 @@ import ( k8sutils "github.com/securesign/operator/controllers/common/utils/kubernetes" "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/rekor/actions" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -26,7 +28,8 @@ func (i createServiceAction) Name() string { } func (i createServiceAction) CanHandle(instance *rhtasv1alpha1.Rekor) bool { - return (instance.Status.Phase == rhtasv1alpha1.PhaseCreating || instance.Status.Phase == rhtasv1alpha1.PhaseReady) && instance.Spec.RekorSearchUI.Enabled + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return (c.Reason == constants.Creating || c.Reason == constants.Ready) && instance.Spec.RekorSearchUI.Enabled } func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) *action.Result { @@ -45,12 +48,29 @@ func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1 } if updated, err = i.Ensure(ctx, svc); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.UICondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create service: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.UICondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Service created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/rekor/rekor_controller.go b/controllers/rekor/rekor_controller.go index 943f5fb83..dbdd23512 100644 --- a/controllers/rekor/rekor_controller.go +++ b/controllers/rekor/rekor_controller.go @@ -88,9 +88,6 @@ func (r *RekorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl actions2.NewPendingAction(), server.NewGenerateSignerAction(), - - // PENDING -> CREATING - actions2.NewToCreatingAction(), actions2.NewRBACAction(), server.NewServerConfigAction(), server.NewCreatePvcAction(), @@ -112,9 +109,8 @@ func (r *RekorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl // CREATE -> INITIALIZE actions2.NewToInitializeAction(), - server.NewDeploymentReadyAction(), + server.NewInitializeAction(), server.NewResolvePubKeyAction(), - server.NewInitializeUrlAction(), ui.NewInitializeAction(), redis.NewInitializeAction(), diff --git a/controllers/rekor/rekor_controller_test.go b/controllers/rekor/rekor_controller_test.go index efa468490..dab639316 100644 --- a/controllers/rekor/rekor_controller_test.go +++ b/controllers/rekor/rekor_controller_test.go @@ -29,6 +29,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -118,19 +119,21 @@ var _ = Describe("Rekor controller", func() { return k8sClient.Get(ctx, typeNamespaceName, found) }, time.Minute, time.Second).Should(Succeed()) - Eventually(func() v1alpha1.Phase { + By("Status conditions are initialized") + Eventually(func() bool { found := &v1alpha1.Rekor{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhasePending)) + return meta.IsStatusConditionPresentAndEqual(found.Status.Conditions, constants.Ready, metav1.ConditionFalse) + }, time.Minute, time.Second).Should(BeTrue()) - By("Move to CreatingPhase by creating trillian service") - Expect(k8sClient.Create(ctx, kubernetes.CreateService(Namespace, trillian.LogserverDeploymentName, 8091, constants.LabelsForComponent(trillian.ComponentName, instance.Name)))).To(Succeed()) - Eventually(func() v1alpha1.Phase { + Eventually(func() string { found := &v1alpha1.Rekor{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseCreating)) + return meta.FindStatusCondition(found.Status.Conditions, constants.Ready).Reason + }, time.Minute, time.Second).Should(Equal(constants.Pending)) + + By("Move to CreatingPhase by creating trillian service") + Expect(k8sClient.Create(ctx, kubernetes.CreateService(Namespace, trillian.LogserverDeploymentName, 8091, constants.LabelsForComponent(trillian.LogServerComponentName, instance.Name)))).To(Succeed()) By("Rekor signer created") found := &v1alpha1.Rekor{} @@ -190,11 +193,11 @@ var _ = Describe("Rekor controller", func() { }, time.Minute, time.Second).Should(Succeed()) By("Waiting until Rekor instance is Initialization") - Eventually(func() v1alpha1.Phase { + Eventually(func() string { found := &v1alpha1.Rekor{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseInitialize)) + return meta.FindStatusCondition(found.Status.Conditions, constants.Ready).Reason + }, time.Minute, time.Second).Should(Equal(constants.Initialize)) deployments := &appsv1.DeploymentList{} Expect(k8sClient.List(ctx, deployments, runtimeClient.InNamespace(Namespace))).To(Succeed()) @@ -207,11 +210,11 @@ var _ = Describe("Rekor controller", func() { // Workaround to succeed condition for Ready phase By("Waiting until Rekor instance is Ready") - Eventually(func() v1alpha1.Phase { + Eventually(func() bool { found := &v1alpha1.Rekor{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseReady)) + return meta.IsStatusConditionTrue(found.Status.Conditions, constants.Ready) + }, time.Minute, time.Second).Should(BeTrue()) By("Checking if controller will return deployment to desired state") deployment := &appsv1.Deployment{} diff --git a/controllers/securesign/actions/constants.go b/controllers/securesign/actions/constants.go new file mode 100644 index 000000000..7c3c1aed4 --- /dev/null +++ b/controllers/securesign/actions/constants.go @@ -0,0 +1,9 @@ +package actions + +const ( + TufCondition = "TufAvailable" + FulcioCondition = "FulcioAvailable" + RekorCondition = "RekorAvailable" + TrillianCondition = "TrillianAvailable" + CTlogCondition = "CTlogAvailable" +) diff --git a/controllers/securesign/actions/ensure_ctlog.go b/controllers/securesign/actions/ensure_ctlog.go index 4adcfc516..e1dab300e 100644 --- a/controllers/securesign/actions/ensure_ctlog.go +++ b/controllers/securesign/actions/ensure_ctlog.go @@ -6,7 +6,10 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" "github.com/securesign/operator/controllers/constants" - actions2 "github.com/securesign/operator/controllers/ctlog/actions" + "github.com/securesign/operator/controllers/ctlog/actions" + "k8s.io/apimachinery/pkg/api/meta" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -22,8 +25,8 @@ func (i ctlogAction) Name() string { return "create ctlog" } -func (i ctlogAction) CanHandle(instance *rhtasv1alpha1.Securesign) bool { - return instance.Status.CTlog == "" +func (i ctlogAction) CanHandle(*rhtasv1alpha1.Securesign) bool { + return true } func (i ctlogAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { @@ -35,7 +38,8 @@ func (i ctlogAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secures ctlog.Name = instance.Name ctlog.Namespace = instance.Namespace - ctlog.Labels = constants.LabelsFor(actions2.ComponentName, ctlog.Name, instance.Name) + ctlog.Labels = constants.LabelsFor(actions.ComponentName, ctlog.Name, instance.Name) + ctlog.Spec = instance.Spec.Ctlog if err = controllerutil.SetControllerReference(instance, ctlog, i.Client.Scheme()); err != nil { @@ -43,13 +47,45 @@ func (i ctlogAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secures } if updated, err = i.Ensure(ctx, ctlog); err != nil { - return i.Failed(err) + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: CTlogCondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + return i.FailedWithStatusUpdate(ctx, err, instance) } if updated { - instance.Status.CTlog = ctlog.Name + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: CTlogCondition, + Status: v1.ConditionFalse, + Reason: constants.Creating, + Message: "CTLog resource updated " + ctlog.Name, + }) return i.StatusUpdate(ctx, instance) } + return i.CopyStatus(ctx, client.ObjectKeyFromObject(ctlog), instance) +} + +func (i ctlogAction) CopyStatus(ctx context.Context, ok client.ObjectKey, instance *rhtasv1alpha1.Securesign) *action.Result { + ctl := &rhtasv1alpha1.CTlog{} + if err := i.Client.Get(ctx, ok, ctl); err != nil { + return i.Failed(err) + } + objectStatus := meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready) + if objectStatus == nil { + // not initialized yet, wait for update + return i.Continue() + } + if !meta.IsStatusConditionPresentAndEqual(instance.Status.Conditions, CTlogCondition, objectStatus.Status) { + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: CTlogCondition, + Status: objectStatus.Status, + Reason: objectStatus.Reason, + }) + return i.StatusUpdate(ctx, instance) + } return i.Continue() } diff --git a/controllers/securesign/actions/ensure_fulcio.go b/controllers/securesign/actions/ensure_fulcio.go index 39eab4bb6..7fce67aa5 100644 --- a/controllers/securesign/actions/ensure_fulcio.go +++ b/controllers/securesign/actions/ensure_fulcio.go @@ -6,7 +6,10 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" "github.com/securesign/operator/controllers/constants" - actions2 "github.com/securesign/operator/controllers/fulcio/actions" + "github.com/securesign/operator/controllers/fulcio/actions" + "k8s.io/apimachinery/pkg/api/meta" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -22,8 +25,8 @@ func (i fulcioAction) Name() string { return "create fulcio" } -func (i fulcioAction) CanHandle(instance *rhtasv1alpha1.Securesign) bool { - return instance.Status.Fulcio == "" +func (i fulcioAction) CanHandle(*rhtasv1alpha1.Securesign) bool { + return true } func (i fulcioAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { @@ -35,7 +38,8 @@ func (i fulcioAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secure fulcio.Name = instance.Name fulcio.Namespace = instance.Namespace - fulcio.Labels = constants.LabelsFor(actions2.ComponentName, fulcio.Name, instance.Name) + fulcio.Labels = constants.LabelsFor(actions.ComponentName, fulcio.Name, instance.Name) + fulcio.Spec = instance.Spec.Fulcio if err = controllerutil.SetControllerReference(instance, fulcio, i.Client.Scheme()); err != nil { @@ -43,13 +47,48 @@ func (i fulcioAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secure } if updated, err = i.Ensure(ctx, fulcio); err != nil { - return i.Failed(err) + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: FulcioCondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + return i.FailedWithStatusUpdate(ctx, err, instance) } if updated { - instance.Status.Fulcio = fulcio.Name + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: FulcioCondition, + Status: v1.ConditionFalse, + Reason: constants.Creating, + Message: "Fulcio resource created " + fulcio.Name, + }) return i.StatusUpdate(ctx, instance) } + return i.CopyStatus(ctx, client.ObjectKeyFromObject(fulcio), instance) +} + +func (i fulcioAction) CopyStatus(ctx context.Context, ok client.ObjectKey, instance *rhtasv1alpha1.Securesign) *action.Result { + object := &rhtasv1alpha1.Fulcio{} + if err := i.Client.Get(ctx, ok, object); err != nil { + return i.Failed(err) + } + objectStatus := meta.FindStatusCondition(object.Status.Conditions, constants.Ready) + if objectStatus == nil { + // not initialized yet, wait for update + return i.Continue() + } + if !meta.IsStatusConditionPresentAndEqual(instance.Status.Conditions, FulcioCondition, objectStatus.Status) { + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: FulcioCondition, + Status: objectStatus.Status, + Reason: objectStatus.Reason, + }) + if objectStatus.Status == v1.ConditionTrue { + instance.Status.FulcioStatus.Url = object.Status.Url + } + return i.StatusUpdate(ctx, instance) + } return i.Continue() } diff --git a/controllers/securesign/actions/ensure_rekor.go b/controllers/securesign/actions/ensure_rekor.go index 8b5516814..80aa0839c 100644 --- a/controllers/securesign/actions/ensure_rekor.go +++ b/controllers/securesign/actions/ensure_rekor.go @@ -6,6 +6,9 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" "github.com/securesign/operator/controllers/constants" + "k8s.io/apimachinery/pkg/api/meta" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -21,8 +24,8 @@ func (i rekorAction) Name() string { return "create rekor" } -func (i rekorAction) CanHandle(instance *rhtasv1alpha1.Securesign) bool { - return instance.Status.Rekor == "" +func (i rekorAction) CanHandle(*rhtasv1alpha1.Securesign) bool { + return true } func (i rekorAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { @@ -35,6 +38,7 @@ func (i rekorAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secures rekor.Name = instance.Name rekor.Namespace = instance.Namespace rekor.Labels = constants.LabelsFor("rekor", rekor.Name, instance.Name) + rekor.Spec = instance.Spec.Rekor if err = controllerutil.SetControllerReference(instance, rekor, i.Client.Scheme()); err != nil { @@ -42,13 +46,48 @@ func (i rekorAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secures } if updated, err = i.Ensure(ctx, rekor); err != nil { - return i.Failed(err) + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: RekorCondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + return i.FailedWithStatusUpdate(ctx, err, instance) } if updated { - instance.Status.Rekor = rekor.Name + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: RekorCondition, + Status: v1.ConditionFalse, + Reason: constants.Creating, + Message: "Rekor resource created " + rekor.Name, + }) return i.StatusUpdate(ctx, instance) } + return i.CopyStatus(ctx, client.ObjectKeyFromObject(rekor), instance) +} + +func (i rekorAction) CopyStatus(ctx context.Context, ok client.ObjectKey, instance *rhtasv1alpha1.Securesign) *action.Result { + object := &rhtasv1alpha1.Rekor{} + if err := i.Client.Get(ctx, ok, object); err != nil { + return i.Failed(err) + } + objectStatus := meta.FindStatusCondition(object.Status.Conditions, constants.Ready) + if objectStatus == nil { + // not initialized yet, wait for update + return i.Continue() + } + if !meta.IsStatusConditionPresentAndEqual(instance.Status.Conditions, RekorCondition, objectStatus.Status) { + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: RekorCondition, + Status: objectStatus.Status, + Reason: objectStatus.Reason, + }) + if objectStatus.Status == v1.ConditionTrue { + instance.Status.RekorStatus.Url = object.Status.Url + } + return i.StatusUpdate(ctx, instance) + } return i.Continue() } diff --git a/controllers/securesign/actions/ensure_trillian.go b/controllers/securesign/actions/ensure_trillian.go index 01914e52d..86aa98823 100644 --- a/controllers/securesign/actions/ensure_trillian.go +++ b/controllers/securesign/actions/ensure_trillian.go @@ -6,7 +6,9 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" "github.com/securesign/operator/controllers/constants" - actions2 "github.com/securesign/operator/controllers/trillian/actions" + "k8s.io/apimachinery/pkg/api/meta" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -22,8 +24,8 @@ func (i trillianAction) Name() string { return "create trillian" } -func (i trillianAction) CanHandle(instance *rhtasv1alpha1.Securesign) bool { - return instance.Status.Trillian == "" +func (i trillianAction) CanHandle(*rhtasv1alpha1.Securesign) bool { + return true } func (i trillianAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { @@ -35,7 +37,8 @@ func (i trillianAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secu trillian.Name = instance.Name trillian.Namespace = instance.Namespace - trillian.Labels = constants.LabelsFor(actions2.ComponentName, trillian.Name, instance.Name) + trillian.Labels = constants.LabelsFor("trillian", trillian.Name, instance.Name) + trillian.Spec = instance.Spec.Trillian if err = controllerutil.SetControllerReference(instance, trillian, i.Client.Scheme()); err != nil { @@ -43,13 +46,45 @@ func (i trillianAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secu } if updated, err = i.Ensure(ctx, trillian); err != nil { - return i.Failed(err) + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: TrillianCondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + return i.FailedWithStatusUpdate(ctx, err, instance) } if updated { - instance.Status.Trillian = trillian.Name + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: TrillianCondition, + Status: v1.ConditionFalse, + Reason: constants.Creating, + Message: "Trillian resource created " + trillian.Name, + }) return i.StatusUpdate(ctx, instance) } + return i.CopyStatus(ctx, client.ObjectKeyFromObject(trillian), instance) +} + +func (i trillianAction) CopyStatus(ctx context.Context, ok client.ObjectKey, instance *rhtasv1alpha1.Securesign) *action.Result { + object := &rhtasv1alpha1.Trillian{} + if err := i.Client.Get(ctx, ok, object); err != nil { + return i.Failed(err) + } + objectStatus := meta.FindStatusCondition(object.Status.Conditions, constants.Ready) + if objectStatus == nil { + // not initialized yet, wait for update + return i.Continue() + } + if meta.FindStatusCondition(instance.Status.Conditions, TrillianCondition).Reason != objectStatus.Reason { + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: TrillianCondition, + Status: objectStatus.Status, + Reason: objectStatus.Reason, + }) + return i.StatusUpdate(ctx, instance) + } return i.Continue() } diff --git a/controllers/securesign/actions/ensure_tuf.go b/controllers/securesign/actions/ensure_tuf.go index 710924a2e..0059a9b7e 100644 --- a/controllers/securesign/actions/ensure_tuf.go +++ b/controllers/securesign/actions/ensure_tuf.go @@ -6,7 +6,10 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" "github.com/securesign/operator/controllers/constants" - actions2 "github.com/securesign/operator/controllers/tuf/actions" + "github.com/securesign/operator/controllers/tuf/actions" + "k8s.io/apimachinery/pkg/api/meta" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -22,8 +25,8 @@ func (i tufAction) Name() string { return "create tuf" } -func (i tufAction) CanHandle(instance *rhtasv1alpha1.Securesign) bool { - return instance.Status.Tuf == "" +func (i tufAction) CanHandle(*rhtasv1alpha1.Securesign) bool { + return true } func (i tufAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { @@ -35,7 +38,8 @@ func (i tufAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesig tuf.Name = instance.Name tuf.Namespace = instance.Namespace - tuf.Labels = constants.LabelsFor(actions2.ComponentName, tuf.Name, instance.Name) + tuf.Labels = constants.LabelsFor(actions.ComponentName, tuf.Name, instance.Name) + tuf.Spec = instance.Spec.Tuf if err = controllerutil.SetControllerReference(instance, tuf, i.Client.Scheme()); err != nil { @@ -43,13 +47,48 @@ func (i tufAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesig } if updated, err = i.Ensure(ctx, tuf); err != nil { - return i.Failed(err) + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: TufCondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + return i.FailedWithStatusUpdate(ctx, err, instance) } if updated { - instance.Status.Tuf = tuf.Name + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: TufCondition, + Status: v1.ConditionFalse, + Reason: constants.Creating, + Message: "Tuf resource created " + tuf.Name, + }) return i.StatusUpdate(ctx, instance) } + return i.CopyStatus(ctx, client.ObjectKeyFromObject(tuf), instance) +} + +func (i tufAction) CopyStatus(ctx context.Context, ok client.ObjectKey, instance *rhtasv1alpha1.Securesign) *action.Result { + object := &rhtasv1alpha1.Tuf{} + if err := i.Client.Get(ctx, ok, object); err != nil { + return i.Failed(err) + } + objectStatus := meta.FindStatusCondition(object.Status.Conditions, constants.Ready) + if objectStatus == nil { + // not initialized yet, wait for update + return i.Continue() + } + if !meta.IsStatusConditionPresentAndEqual(instance.Status.Conditions, TufCondition, objectStatus.Status) { + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: TufCondition, + Status: objectStatus.Status, + Reason: objectStatus.Reason, + }) + if objectStatus.Status == v1.ConditionTrue { + instance.Status.TufStatus.Url = object.Status.Url + } + return i.StatusUpdate(ctx, instance) + } return i.Continue() } diff --git a/controllers/securesign/actions/initialize_status.go b/controllers/securesign/actions/initialize_status.go new file mode 100644 index 000000000..436f17418 --- /dev/null +++ b/controllers/securesign/actions/initialize_status.go @@ -0,0 +1,38 @@ +package actions + +import ( + "context" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" + "k8s.io/apimachinery/pkg/api/meta" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func NewInitializeStatusAction() action.Action[rhtasv1alpha1.Securesign] { + return &initializeStatus{} +} + +type initializeStatus struct { + action.BaseAction +} + +func (i initializeStatus) Name() string { + return "initialize status" +} + +func (i initializeStatus) CanHandle(instance *rhtasv1alpha1.Securesign) bool { + return meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) == nil +} + +func (i initializeStatus) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { + for _, conditionType := range []string{constants.Ready, TrillianCondition, FulcioCondition, RekorCondition, CTlogCondition, TufCondition} { + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: conditionType, + Status: v1.ConditionUnknown, + Reason: constants.Pending, + }) + } + return i.StatusUpdate(ctx, instance) +} diff --git a/controllers/securesign/actions/update_status.go b/controllers/securesign/actions/update_status.go new file mode 100644 index 000000000..08c66770b --- /dev/null +++ b/controllers/securesign/actions/update_status.go @@ -0,0 +1,52 @@ +package actions + +import ( + "context" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" + "k8s.io/apimachinery/pkg/api/meta" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func NewUpdateStatusAction() action.Action[rhtasv1alpha1.Securesign] { + return &updateStatusAction{} +} + +type updateStatusAction struct { + action.BaseAction +} + +func (i updateStatusAction) Name() string { + return "update status" +} + +func (i updateStatusAction) CanHandle(instance *rhtasv1alpha1.Securesign) bool { + return meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) != nil +} + +func (i updateStatusAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { + for _, conditionType := range []string{TrillianCondition, FulcioCondition, RekorCondition, CTlogCondition, TufCondition} { + c := meta.FindStatusCondition(instance.Status.Conditions, conditionType) + if c.Status != v1.ConditionTrue { + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: constants.Ready, + Status: c.Status, + Reason: c.Reason, + Message: c.Message, + }) + return i.StatusUpdate(ctx, instance) + } + } + + if !meta.IsStatusConditionTrue(instance.Status.Conditions, constants.Ready) { + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: constants.Ready, + Status: v1.ConditionTrue, + Reason: constants.Ready, + }) + return i.StatusUpdate(ctx, instance) + } + return i.Continue() +} diff --git a/controllers/securesign/securesign_controller.go b/controllers/securesign/securesign_controller.go index 93a095785..2b3299198 100644 --- a/controllers/securesign/securesign_controller.go +++ b/controllers/securesign/securesign_controller.go @@ -94,11 +94,15 @@ func (r *SecuresignReconciler) Reconcile(ctx context.Context, req ctrl.Request) } acs := []action.Action[rhtasv1alpha1.Securesign]{ + actions.NewInitializeStatusAction(), + actions.NewTrillianAction(), actions.NewFulcioAction(), actions.NewRekorAction(), actions.NewCtlogAction(), actions.NewTufAction(), + + actions.NewUpdateStatusAction(), } for _, a := range acs { @@ -119,5 +123,10 @@ func (r *SecuresignReconciler) Reconcile(ctx context.Context, req ctrl.Request) func (r *SecuresignReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&rhtasv1alpha1.Securesign{}). + Owns(&rhtasv1alpha1.Fulcio{}). + Owns(&rhtasv1alpha1.Rekor{}). + Owns(&rhtasv1alpha1.Tuf{}). + Owns(&rhtasv1alpha1.Trillian{}). + Owns(&rhtasv1alpha1.CTlog{}). Complete(r) } diff --git a/controllers/trillian/actions/constants.go b/controllers/trillian/actions/constants.go index 2305e1b5f..e7f4ea24a 100644 --- a/controllers/trillian/actions/constants.go +++ b/controllers/trillian/actions/constants.go @@ -1,4 +1,4 @@ -package actions2 +package actions const ( DbDeploymentName = "trillian-db" @@ -6,7 +6,13 @@ const ( LogserverDeploymentName = "trillian-logserver" LogsignerDeploymentName = "trillian-logsigner" - ComponentName = "trillian" + DbComponentName = "trillian-db" + LogServerComponentName = "trillian-logserver" + LogSignerComponentName = "trillian-logsigner" RBACName = "trillian" + + DbCondition = "DBAvailable" + ServerCondition = "LogServerAvailable" + SignerCondition = "LogSignerAvailable" ) diff --git a/controllers/trillian/actions/db/create_secret.go b/controllers/trillian/actions/db/create_secret.go index f82be891b..1a13d2dc1 100644 --- a/controllers/trillian/actions/db/create_secret.go +++ b/controllers/trillian/actions/db/create_secret.go @@ -8,7 +8,8 @@ import ( "github.com/securesign/operator/controllers/common" "github.com/securesign/operator/controllers/common/action" "github.com/securesign/operator/controllers/constants" - "github.com/securesign/operator/controllers/trillian/actions" + actions2 "github.com/securesign/operator/controllers/trillian/actions" + "k8s.io/apimachinery/pkg/api/meta" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -34,7 +35,8 @@ func (i createSecretAction) Name() string { } func (i createSecretAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating && instance.Spec.Db.Create && instance.Spec.Db.DatabaseSecretRef == nil + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating && instance.Spec.Db.Create && instance.Spec.Db.DatabaseSecretRef == nil } func (i createSecretAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { @@ -42,7 +44,7 @@ func (i createSecretAction) Handle(ctx context.Context, instance *rhtasv1alpha1. var ( err error ) - dbLabels := constants.LabelsFor(actions2.ComponentName, actions2.DbDeploymentName, instance.Name) + dbLabels := constants.LabelsFor(actions2.DbComponentName, actions2.DbDeploymentName, instance.Name) dbSecret := i.createDbSecret(instance.Namespace, dbLabels) if err = controllerutil.SetControllerReference(instance, dbSecret, i.Client.Scheme()); err != nil { @@ -51,7 +53,18 @@ func (i createSecretAction) Handle(ctx context.Context, instance *rhtasv1alpha1. // no watch on secret - continue if no error if _, err = i.Ensure(ctx, dbSecret); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions2.DbCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create DB secret: %w", err), instance) } diff --git a/controllers/trillian/actions/db/deployment.go b/controllers/trillian/actions/db/deployment.go index a17f5b21b..86d4187d8 100644 --- a/controllers/trillian/actions/db/deployment.go +++ b/controllers/trillian/actions/db/deployment.go @@ -8,6 +8,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/trillian/actions" trillianUtils "github.com/securesign/operator/controllers/trillian/utils" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -26,7 +28,8 @@ func (i deployAction) Name() string { } func (i deployAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating && instance.Spec.Db.Create + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return (c.Reason == constants.Ready || c.Reason == constants.Creating) && instance.Spec.Db.Create } func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { @@ -34,10 +37,10 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trilli err error updated bool ) - labels := constants.LabelsFor(actions2.ComponentName, actions2.DbDeploymentName, instance.Name) + labels := constants.LabelsFor(actions.DbComponentName, actions.DbDeploymentName, instance.Name) db := trillianUtils.CreateTrillDb(instance.Namespace, constants.TrillianDbImage, - actions2.DbDeploymentName, - actions2.RBACName, + actions.DbDeploymentName, + actions.RBACName, instance.Spec.Db.PvcName, *instance.Spec.Db.DatabaseSecretRef, labels) @@ -46,12 +49,29 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trilli } if updated, err = i.Ensure(ctx, db); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.DbCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Trillian DB: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.DbCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Database deployment created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/trillian/actions/db/initialize.go b/controllers/trillian/actions/db/initialize.go new file mode 100644 index 000000000..7aec975e5 --- /dev/null +++ b/controllers/trillian/actions/db/initialize.go @@ -0,0 +1,63 @@ +package db + +import ( + "context" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/common/action" + commonUtils "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" + "github.com/securesign/operator/controllers/trillian/actions" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func NewInitializeAction() action.Action[rhtasv1alpha1.Trillian] { + return &initializeAction{} +} + +type initializeAction struct { + action.BaseAction +} + +func (i initializeAction) Name() string { + return "db initialize" +} + +func (i initializeAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize && !meta.IsStatusConditionTrue(instance.Status.Conditions, actions.DbCondition) +} + +func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { + if !instance.Spec.Db.Create { + if instance.Spec.Db.DatabaseSecretRef != nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: actions.DbCondition, + Status: metav1.ConditionTrue, Reason: constants.Ready, Message: "Working with external DB"}) + } else { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: actions.DbCondition, + Status: metav1.ConditionFalse, Reason: constants.Failure, Message: "Expecting external DB configuration"}) + } + return i.StatusUpdate(ctx, instance) + } + + labels := constants.LabelsForComponent(actions.DbComponentName, instance.Name) + ok, err := commonUtils.DeploymentIsRunning(ctx, i.Client, instance.Namespace, labels) + if err != nil { + return i.Failed(err) + } + if !ok { + i.Logger.Info("Waiting for deployment") + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.DbCondition, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + }) + return i.StatusUpdate(ctx, instance) + } + + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: actions.DbCondition, + Status: metav1.ConditionTrue, Reason: constants.Ready, Message: "Db ready"}) + return i.StatusUpdate(ctx, instance) +} diff --git a/controllers/trillian/actions/db/pvc.go b/controllers/trillian/actions/db/pvc.go index 7b3bee3c9..9e5ef53e3 100644 --- a/controllers/trillian/actions/db/pvc.go +++ b/controllers/trillian/actions/db/pvc.go @@ -9,6 +9,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/trillian/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -27,7 +29,8 @@ func (i createPvcAction) Name() string { } func (i createPvcAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating && instance.Spec.Db.Create && instance.Spec.Db.PvcName == "" + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating && instance.Spec.Db.Create && instance.Spec.Db.PvcName == "" } func (i createPvcAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { @@ -36,13 +39,24 @@ func (i createPvcAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tri // PVC does not exist, create a new one i.Logger.V(1).Info("Creating new PVC") i.Recorder.Event(instance, v1.EventTypeNormal, "PersistentVolumeCreated", "New PersistentVolume created") - pvc := k8sutils.CreatePVC(instance.Namespace, actions2.DbPvcName, "5Gi", constants.LabelsFor(actions2.ComponentName, actions2.DbDeploymentName, instance.Name)) + pvc := k8sutils.CreatePVC(instance.Namespace, actions.DbPvcName, "5Gi", constants.LabelsFor(actions.DbComponentName, actions.DbDeploymentName, instance.Name)) if err = controllerutil.SetControllerReference(instance, pvc, i.Client.Scheme()); err != nil { return i.Failed(fmt.Errorf("could not set controller reference for PVC: %w", err)) } if _, err = i.Ensure(ctx, pvc); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.DbCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create DB PVC: %w", err), instance) } diff --git a/controllers/trillian/actions/db/svc.go b/controllers/trillian/actions/db/svc.go index 6b5e43691..f7dc86acc 100644 --- a/controllers/trillian/actions/db/svc.go +++ b/controllers/trillian/actions/db/svc.go @@ -8,6 +8,8 @@ import ( k8sutils "github.com/securesign/operator/controllers/common/utils/kubernetes" "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/trillian/actions" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -26,7 +28,8 @@ func (i createServiceAction) Name() string { } func (i createServiceAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating && instance.Spec.Db.Create + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return (c.Reason == constants.Creating || c.Reason == constants.Ready) && instance.Spec.Db.Create } func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { @@ -36,7 +39,7 @@ func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1 updated bool ) - labels := constants.LabelsFor(actions2.ComponentName, actions2.DbDeploymentName, instance.Name) + labels := constants.LabelsFor(actions.DbComponentName, actions.DbDeploymentName, instance.Name) mysql := k8sutils.CreateService(instance.Namespace, host, port, labels) if err = controllerutil.SetControllerReference(instance, mysql, i.Client.Scheme()); err != nil { @@ -44,12 +47,29 @@ func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1 } if updated, err = i.Ensure(ctx, mysql); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.DbCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Trillian DB service: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.DbCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Service created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/trillian/actions/initialize.go b/controllers/trillian/actions/initialize.go index c33d696db..4fc620097 100644 --- a/controllers/trillian/actions/initialize.go +++ b/controllers/trillian/actions/initialize.go @@ -1,12 +1,13 @@ -package actions2 +package actions import ( "context" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" - commonUtils "github.com/securesign/operator/controllers/common/utils/kubernetes" "github.com/securesign/operator/controllers/constants" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func NewInitializeAction() action.Action[rhtasv1alpha1.Trillian] { @@ -21,22 +22,19 @@ func (i initializeAction) Name() string { return "initialize" } -func (i initializeAction) CanHandle(trillian *rhtasv1alpha1.Trillian) bool { - return trillian.Status.Phase == rhtasv1alpha1.PhaseInitialize +func (i initializeAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize } func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { - labels := constants.LabelsForComponent(ComponentName, instance.Name) - ok, err := commonUtils.DeploymentIsRunning(ctx, i.Client, instance.Namespace, labels) - if err != nil { - return i.Failed(err) - } - if !ok { - i.Logger.Info("Waiting for deployment") - // deployment is watched - no need to requeue - return i.Return() + if meta.IsStatusConditionTrue(instance.Status.Conditions, DbCondition) && + meta.IsStatusConditionTrue(instance.Status.Conditions, SignerCondition) && + meta.IsStatusConditionTrue(instance.Status.Conditions, ServerCondition) { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionTrue, Reason: constants.Ready}) + return i.StatusUpdate(ctx, instance) } + return i.Requeue() - instance.Status.Phase = rhtasv1alpha1.PhaseReady - return i.StatusUpdate(ctx, instance) } diff --git a/controllers/trillian/actions/logserver/deployment.go b/controllers/trillian/actions/logserver/deployment.go index af2818bd0..3287b681a 100644 --- a/controllers/trillian/actions/logserver/deployment.go +++ b/controllers/trillian/actions/logserver/deployment.go @@ -8,6 +8,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/trillian/actions" trillianUtils "github.com/securesign/operator/controllers/trillian/utils" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -28,8 +30,9 @@ func (i deployAction) Name() string { return "deploy" } -func (i deployAction) CanHandle(trillian *rhtasv1alpha1.Trillian) bool { - return trillian.Status.Phase == rhtasv1alpha1.PhaseCreating +func (i deployAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { @@ -38,10 +41,10 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trilli updated bool ) - labels := constants.LabelsFor(actions2.ComponentName, actions2.LogserverDeploymentName, instance.Name) + labels := constants.LabelsFor(actions.LogServerComponentName, actions.LogserverDeploymentName, instance.Name) server := trillianUtils.CreateTrillDeployment(instance.Namespace, constants.TrillianServerImage, - actions2.LogserverDeploymentName, - actions2.RBACName, + actions.LogserverDeploymentName, + actions.RBACName, *instance.Spec.Db.DatabaseSecretRef, labels) server.Spec.Template.Spec.Containers[0].Ports = append(server.Spec.Template.Spec.Containers[0].Ports, corev1.ContainerPort{ @@ -54,12 +57,29 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trilli } if updated, err = i.Ensure(ctx, server); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Trillian server: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Deployment created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/trillian/actions/logserver/initialize.go b/controllers/trillian/actions/logserver/initialize.go new file mode 100644 index 000000000..abcb7a642 --- /dev/null +++ b/controllers/trillian/actions/logserver/initialize.go @@ -0,0 +1,52 @@ +package logserver + +import ( + "context" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/common/action" + commonUtils "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" + "github.com/securesign/operator/controllers/trillian/actions" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func NewInitializeAction() action.Action[rhtasv1alpha1.Trillian] { + return &initializeAction{} +} + +type initializeAction struct { + action.BaseAction +} + +func (i initializeAction) Name() string { + return "server initialize" +} + +func (i initializeAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize && !meta.IsStatusConditionTrue(instance.Status.Conditions, actions.ServerCondition) +} + +func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { + labels := constants.LabelsForComponent(actions.LogServerComponentName, instance.Name) + ok, err := commonUtils.DeploymentIsRunning(ctx, i.Client, instance.Namespace, labels) + if err != nil { + return i.Failed(err) + } + if !ok { + i.Logger.Info("Waiting for deployment") + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + }) + return i.StatusUpdate(ctx, instance) + } + + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: actions.ServerCondition, + Status: metav1.ConditionTrue, Reason: constants.Ready, Message: "Logserver is ready"}) + return i.StatusUpdate(ctx, instance) +} diff --git a/controllers/trillian/actions/logserver/service.go b/controllers/trillian/actions/logserver/service.go index 30452969d..15a8da059 100644 --- a/controllers/trillian/actions/logserver/service.go +++ b/controllers/trillian/actions/logserver/service.go @@ -8,6 +8,8 @@ import ( k8sutils "github.com/securesign/operator/controllers/common/utils/kubernetes" "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/trillian/actions" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -25,8 +27,9 @@ func (i createServiceAction) Name() string { return "create service" } -func (i createServiceAction) CanHandle(trillian *rhtasv1alpha1.Trillian) bool { - return trillian.Status.Phase == rhtasv1alpha1.PhaseCreating || trillian.Status.Phase == rhtasv1alpha1.PhaseReady +func (i createServiceAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { @@ -36,20 +39,37 @@ func (i createServiceAction) Handle(ctx context.Context, instance *rhtasv1alpha1 updated bool ) - labels := constants.LabelsFor(actions2.ComponentName, actions2.LogserverDeploymentName, instance.Name) - logserverService := k8sutils.CreateService(instance.Namespace, actions2.LogserverDeploymentName, serverPort, labels) + labels := constants.LabelsFor(actions.LogServerComponentName, actions.LogserverDeploymentName, instance.Name) + logserverService := k8sutils.CreateService(instance.Namespace, actions.LogserverDeploymentName, serverPort, labels) if err = controllerutil.SetControllerReference(instance, logserverService, i.Client.Scheme()); err != nil { return i.Failed(fmt.Errorf("could not set controller reference for logserver Service: %w", err)) } if updated, err = i.Ensure(ctx, logserverService); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create logserver Service: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.ServerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Service created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/trillian/actions/logsigner/deployment.go b/controllers/trillian/actions/logsigner/deployment.go index 0c7073089..323dcca9e 100644 --- a/controllers/trillian/actions/logsigner/deployment.go +++ b/controllers/trillian/actions/logsigner/deployment.go @@ -8,6 +8,8 @@ import ( "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/trillian/actions" trillianUtils "github.com/securesign/operator/controllers/trillian/utils" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" @@ -25,8 +27,9 @@ func (i deployAction) Name() string { return "deploy" } -func (i deployAction) CanHandle(trillian *rhtasv1alpha1.Trillian) bool { - return trillian.Status.Phase == rhtasv1alpha1.PhaseCreating +func (i deployAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { @@ -35,10 +38,10 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trilli updated bool ) - labels := constants.LabelsFor(actions2.ComponentName, actions2.LogsignerDeploymentName, instance.Name) + labels := constants.LabelsFor(actions.LogSignerComponentName, actions.LogsignerDeploymentName, instance.Name) signer := trillianUtils.CreateTrillDeployment(instance.Namespace, constants.TrillianLogSignerImage, - actions2.LogsignerDeploymentName, - actions2.RBACName, + actions.LogsignerDeploymentName, + actions.RBACName, *instance.Spec.Db.DatabaseSecretRef, labels) signer.Spec.Template.Spec.Containers[0].Args = append(signer.Spec.Template.Spec.Containers[0].Args, "--force_master=true") @@ -48,12 +51,29 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trilli } if updated, err = i.Ensure(ctx, signer); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.SignerCondition, + 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 i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Trillian LogSigner deployment: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.SignerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: "Deployment created", + }) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/trillian/actions/logsigner/initialize.go b/controllers/trillian/actions/logsigner/initialize.go new file mode 100644 index 000000000..d8b3260bb --- /dev/null +++ b/controllers/trillian/actions/logsigner/initialize.go @@ -0,0 +1,51 @@ +package logsigner + +import ( + "context" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/common/action" + commonUtils "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" + "github.com/securesign/operator/controllers/trillian/actions" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func NewInitializeAction() action.Action[rhtasv1alpha1.Trillian] { + return &initializeAction{} +} + +type initializeAction struct { + action.BaseAction +} + +func (i initializeAction) Name() string { + return "server initialize" +} + +func (i initializeAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize && !meta.IsStatusConditionTrue(instance.Status.Conditions, actions.SignerCondition) +} + +func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { + labels := constants.LabelsForComponent(actions.LogSignerComponentName, instance.Name) + ok, err := commonUtils.DeploymentIsRunning(ctx, i.Client, instance.Namespace, labels) + if err != nil { + return i.Failed(err) + } + if !ok { + i.Logger.Info("Waiting for deployment") + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: actions.SignerCondition, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + }) + return i.StatusUpdate(ctx, instance) + } + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: actions.SignerCondition, + Status: metav1.ConditionTrue, Reason: constants.Ready, Message: "Logsigner is ready"}) + return i.StatusUpdate(ctx, instance) +} diff --git a/controllers/trillian/actions/rbac.go b/controllers/trillian/actions/rbac.go index aa4d727ee..1730fefa4 100644 --- a/controllers/trillian/actions/rbac.go +++ b/controllers/trillian/actions/rbac.go @@ -1,4 +1,4 @@ -package actions2 +package actions import ( "context" @@ -28,25 +28,15 @@ func (i rbacAction) Name() string { } func (i rbacAction) CanHandle(instance *rhtasv1alpha1.Trillian) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseNone || - instance.Status.Phase == rhtasv1alpha1.PhaseCreating || - instance.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { - if instance.Status.Phase == rhtasv1alpha1.PhaseNone { - instance.Status.Phase = rhtasv1alpha1.PhaseCreating - - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseCreating)}) - - return i.StatusUpdate(ctx, instance) - } - var ( err error ) - labels := constants.LabelsFor(ComponentName, RBACName, instance.Name) + labels := constants.LabelsFor(LogServerComponentName, RBACName, instance.Name) sa := &v1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ @@ -67,11 +57,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian // don't re-enqueue for RBAC in any case (except failure) _, err = i.Ensure(ctx, sa) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create SA: %w", err), instance) @@ -94,11 +83,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian } _, err = i.Ensure(ctx, role) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Role: %w", err), instance) @@ -117,11 +105,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian } _, err = i.Ensure(ctx, rb) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create RoleBinding: %w", err), instance) diff --git a/controllers/trillian/actions/to_create_phase.go b/controllers/trillian/actions/to_create_phase.go new file mode 100644 index 000000000..951a41741 --- /dev/null +++ b/controllers/trillian/actions/to_create_phase.go @@ -0,0 +1,33 @@ +package actions + +import ( + "context" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func NewToCreatePhaseAction() action.Action[rhtasv1alpha1.Trillian] { + return &toCreate{} +} + +type toCreate struct { + action.BaseAction +} + +func (i toCreate) Name() string { + return "move to create phase" +} + +func (i toCreate) CanHandle(instance *rhtasv1alpha1.Trillian) bool { + return meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Reason == constants.Pending +} + +func (i toCreate) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating}) + return i.StatusUpdate(ctx, instance) +} diff --git a/controllers/trillian/actions/to_initialize_phase.go b/controllers/trillian/actions/to_initialize_phase.go index 96d63a41b..6ca4c5f89 100644 --- a/controllers/trillian/actions/to_initialize_phase.go +++ b/controllers/trillian/actions/to_initialize_phase.go @@ -1,10 +1,11 @@ -package actions2 +package actions import ( "context" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -18,18 +19,17 @@ type toInitialize struct { } func (i toInitialize) Name() string { - return "move to initialize" + return "to initialize" } func (i toInitialize) CanHandle(instance *rhtasv1alpha1.Trillian) bool { - return instance.Status.Phase == rhtasv1alpha1.PhaseCreating + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating } func (i toInitialize) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { - instance.Status.Phase = rhtasv1alpha1.PhaseInitialize - - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseInitialize)}) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Initialize}) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/trillian/actions/to_pending_phase.go b/controllers/trillian/actions/to_pending_phase.go new file mode 100644 index 000000000..a1b078949 --- /dev/null +++ b/controllers/trillian/actions/to_pending_phase.go @@ -0,0 +1,42 @@ +package actions + +import ( + "context" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func NewToPendingPhaseAction() action.Action[rhtasv1alpha1.Trillian] { + return &toPending{} +} + +type toPending struct { + action.BaseAction +} + +func (i toPending) Name() string { + return "move to pending phase" +} + +func (i toPending) CanHandle(instance *rhtasv1alpha1.Trillian) bool { + return meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) == nil +} + +func (i toPending) Handle(ctx context.Context, instance *rhtasv1alpha1.Trillian) *action.Result { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Pending}) + + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: DbCondition, + Status: metav1.ConditionUnknown, Reason: constants.Pending}) + + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: ServerCondition, + Status: metav1.ConditionUnknown, Reason: constants.Pending}) + + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: SignerCondition, + Status: metav1.ConditionUnknown, Reason: constants.Pending}) + return i.StatusUpdate(ctx, instance) +} diff --git a/controllers/trillian/trillian_controller.go b/controllers/trillian/trillian_controller.go index fec1fdafc..59243d7b4 100644 --- a/controllers/trillian/trillian_controller.go +++ b/controllers/trillian/trillian_controller.go @@ -76,6 +76,8 @@ func (r *TrillianReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } target := instance.DeepCopy() actions := []action.Action[rhtasv1alpha1.Trillian]{ + actions2.NewToPendingPhaseAction(), + actions2.NewToCreatePhaseAction(), actions2.NewRBACAction(), db.NewCreateSecretAction(), @@ -87,8 +89,12 @@ func (r *TrillianReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c logserver.NewCreateServiceAction(), logsigner.NewDeployAction(), + actions2.NewToInitializePhaseAction(), + db.NewInitializeAction(), + logserver.NewInitializeAction(), + logsigner.NewInitializeAction(), actions2.NewInitializeAction(), } diff --git a/controllers/trillian/trillian_controller_test.go b/controllers/trillian/trillian_controller_test.go index 5b606cfd6..1fd2b43f4 100644 --- a/controllers/trillian/trillian_controller_test.go +++ b/controllers/trillian/trillian_controller_test.go @@ -21,9 +21,10 @@ import ( "time" "github.com/securesign/operator/api/v1alpha1" - "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" actions "github.com/securesign/operator/controllers/trillian/actions" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -104,13 +105,19 @@ var _ = Describe("Trillian controller", func() { return k8sClient.Get(ctx, typeNamespaceName, found) }, time.Minute, time.Second).Should(Succeed()) - By("Database secret created") + By("Status conditions are initialized") + Eventually(func() bool { + found := &v1alpha1.Trillian{} + Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) + return meta.IsStatusConditionPresentAndEqual(found.Status.Conditions, constants.Ready, metav1.ConditionFalse) + }, time.Minute, time.Second).Should(BeTrue()) found := &v1alpha1.Trillian{} + + By("Database secret created") Eventually(func() *corev1.LocalObjectReference { Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) return found.Spec.Db.DatabaseSecretRef }, time.Minute, time.Second).Should(Not(BeNil())) - Expect(k8sClient.Get(ctx, types.NamespacedName{Name: found.Spec.Db.DatabaseSecretRef.Name, Namespace: Namespace}, &corev1.Secret{})).Should(Succeed()) By("Database PVC created") @@ -147,14 +154,14 @@ var _ = Describe("Trillian controller", func() { }, time.Minute, time.Second).Should(Succeed()) By("Waiting until Trillian instance is Initialization") - Eventually(func() v1alpha1.Phase { + Eventually(func() string { found := &v1alpha1.Trillian{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseInitialize)) + return meta.FindStatusCondition(found.Status.Conditions, constants.Ready).Reason + }, time.Minute, time.Second).Should(Equal(constants.Initialize)) deployments := &appsv1.DeploymentList{} - Expect(k8sClient.List(ctx, deployments, runtimeClient.InNamespace(Namespace), runtimeClient.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName})).To(Succeed()) + Expect(k8sClient.List(ctx, deployments, runtimeClient.InNamespace(Namespace))).To(Succeed()) By("Move to Ready phase") for _, d := range deployments.Items { d.Status.Replicas = *d.Spec.Replicas @@ -164,11 +171,11 @@ var _ = Describe("Trillian controller", func() { // Workaround to succeed condition for Ready phase By("Waiting until Trillian instance is Ready") - Eventually(func() v1alpha1.Phase { + Eventually(func() bool { found := &v1alpha1.Trillian{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseReady)) + return meta.IsStatusConditionTrue(found.Status.Conditions, constants.Ready) + }, time.Minute, time.Second).Should(BeTrue()) By("Checking if controller will return deployment to desired state") deployment := &appsv1.Deployment{} diff --git a/controllers/tuf/actions/constants.go b/controllers/tuf/actions/constants.go index 66ce936a6..a6498b201 100644 --- a/controllers/tuf/actions/constants.go +++ b/controllers/tuf/actions/constants.go @@ -3,6 +3,5 @@ package actions const ( ComponentName = "tuf" DeploymentName = "tuf" - - RBACName = "tuf" + RBACName = "tuf" ) diff --git a/controllers/tuf/actions/deployment.go b/controllers/tuf/actions/deployment.go index 748146a8e..1a665f6d8 100644 --- a/controllers/tuf/actions/deployment.go +++ b/controllers/tuf/actions/deployment.go @@ -8,6 +8,8 @@ import ( "github.com/securesign/operator/controllers/common/action" "github.com/securesign/operator/controllers/constants" tufutils "github.com/securesign/operator/controllers/tuf/utils" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -24,7 +26,8 @@ func (i deployAction) Name() string { } func (i deployAction) CanHandle(tuf *rhtasv1alpha1.Tuf) bool { - return tuf.Status.Phase == rhtasv1alpha1.PhaseCreating || tuf.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *action.Result { @@ -42,11 +45,19 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) * } if updated, err = i.Ensure(ctx, dp); err != nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create TUF: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Deployment created"}) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/tuf/actions/generate_cert.go b/controllers/tuf/actions/generate_cert.go index 8ec555707..3a6d3d083 100644 --- a/controllers/tuf/actions/generate_cert.go +++ b/controllers/tuf/actions/generate_cert.go @@ -14,58 +14,37 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func NewPendingAction() action.Action[rhtasv1alpha1.Tuf] { - return &pendingAction{} +func NewResolveKeysAction() action.Action[rhtasv1alpha1.Tuf] { + return &resolveKeysAction{} } -type pendingAction struct { +type resolveKeysAction struct { action.BaseAction } -func (i pendingAction) Name() string { - return "pending" +func (i resolveKeysAction) Name() string { + return "resolve keys" } -func (i pendingAction) CanHandle(tuf *rhtasv1alpha1.Tuf) bool { - return tuf.Status.Phase == rhtasv1alpha1.PhaseNone || tuf.Status.Phase == rhtasv1alpha1.PhasePending +func (i resolveKeysAction) CanHandle(tuf *rhtasv1alpha1.Tuf) bool { + c := meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready) + return c.Reason == constants.Pending } -func (i pendingAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *action.Result { - if instance.Status.Phase == rhtasv1alpha1.PhaseNone { - instance.Status.Phase = rhtasv1alpha1.PhasePending - meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), - Status: v1.ConditionFalse, - Reason: (string)(rhtasv1alpha1.PhasePending), - Message: "Resolving keys", - }) - return i.StatusUpdate(ctx, instance) - } - +func (i resolveKeysAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *action.Result { for index, key := range instance.Spec.Keys { - if meta.FindStatusCondition(instance.Status.Conditions, key.Name) == nil { - meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ - Type: key.Name, - Status: v1.ConditionUnknown, - Reason: "Resolving", - }) - return i.StatusUpdate(ctx, instance) - } - if !meta.IsStatusConditionTrue(instance.Status.Conditions, key.Name) { updated, err := i.handleKey(ctx, instance, &instance.Spec.Keys[index]) if err != nil { - if !meta.IsStatusConditionFalse(instance.Status.Conditions, key.Name) { - meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ - Type: key.Name, - Status: v1.ConditionFalse, - Reason: "Failure", - Message: err.Error(), - }) - return i.StatusUpdate(ctx, instance) - } - - // swallow error and retry + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{Type: constants.Ready, + Status: v1.ConditionFalse, Reason: constants.Pending, Message: "Resolving keys"}) + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ + Type: key.Name, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + i.StatusUpdate(ctx, instance) return i.Requeue() } if updated { @@ -75,19 +54,18 @@ func (i pendingAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ Type: key.Name, Status: v1.ConditionTrue, - Reason: "Ready", + Reason: constants.Ready, }) return i.StatusUpdate(ctx, instance) } } - meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: v1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseCreating)}) - instance.Status.Phase = rhtasv1alpha1.PhaseCreating + meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{Type: constants.Ready, + Status: v1.ConditionFalse, Reason: constants.Creating, Message: "Keys resolved"}) return i.StatusUpdate(ctx, instance) } -func (i pendingAction) handleKey(ctx context.Context, instance *rhtasv1alpha1.Tuf, key *rhtasv1alpha1.TufKey) (bool, error) { +func (i resolveKeysAction) handleKey(ctx context.Context, instance *rhtasv1alpha1.Tuf, key *rhtasv1alpha1.TufKey) (bool, error) { switch { case key.SecretRef == nil: sks, err := i.discoverSecret(ctx, instance.Namespace, key) @@ -103,7 +81,7 @@ func (i pendingAction) handleKey(ctx context.Context, instance *rhtasv1alpha1.Tu } } -func (i pendingAction) discoverSecret(ctx context.Context, namespace string, key *rhtasv1alpha1.TufKey) (*rhtasv1alpha1.SecretKeySelector, error) { +func (i resolveKeysAction) discoverSecret(ctx context.Context, namespace string, key *rhtasv1alpha1.TufKey) (*rhtasv1alpha1.SecretKeySelector, error) { labelName := constants.LabelNamespace + "/" + key.Name s, err := k8sutils.FindSecret(ctx, i.Client, namespace, labelName) if err != nil { diff --git a/controllers/tuf/actions/ingress.go b/controllers/tuf/actions/ingress.go index 13d9cb6b7..92b10f5a2 100644 --- a/controllers/tuf/actions/ingress.go +++ b/controllers/tuf/actions/ingress.go @@ -9,6 +9,8 @@ import ( "github.com/securesign/operator/controllers/common/utils/kubernetes" "github.com/securesign/operator/controllers/constants" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -26,7 +28,8 @@ func (i ingressAction) Name() string { } func (i ingressAction) CanHandle(tuf *rhtasv1alpha1.Tuf) bool { - return (tuf.Status.Phase == rhtasv1alpha1.PhaseCreating || tuf.Status.Phase == rhtasv1alpha1.PhaseReady) && + c := meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready) + return (c.Reason == constants.Creating || c.Reason == constants.Ready) && tuf.Spec.ExternalAccess.Enabled } @@ -50,11 +53,19 @@ func (i ingressAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) } if updated, err = i.Ensure(ctx, ingress); err != nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) return i.Failed(fmt.Errorf("could not create Ingress: %w", err)) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Ingress created"}) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/tuf/actions/initialize.go b/controllers/tuf/actions/initialize.go index b2d7ea0a0..d33b2c303 100644 --- a/controllers/tuf/actions/initialize.go +++ b/controllers/tuf/actions/initialize.go @@ -28,7 +28,8 @@ func (i initializeAction) Name() string { } func (i initializeAction) CanHandle(tuf *rhtasv1alpha1.Tuf) bool { - return tuf.Status.Phase == rhtasv1alpha1.PhaseInitialize + c := meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready) + return c.Reason == constants.Initialize } func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *action.Result { @@ -43,8 +44,13 @@ func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tu } if !ok { i.Logger.Info("Waiting for deployment") - // deployment is watched - no need to requeue - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Initialize, + Message: "Waiting for deployment to be ready", + }) + return i.StatusUpdate(ctx, instance) } if instance.Spec.ExternalAccess.Enabled { @@ -62,9 +68,8 @@ func (i initializeAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tu instance.Status.Url = fmt.Sprintf("http://%s.%s.svc", DeploymentName, instance.Namespace) } - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseReady)}) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionTrue, Reason: constants.Ready}) - instance.Status.Phase = rhtasv1alpha1.PhaseReady return i.StatusUpdate(ctx, instance) } diff --git a/controllers/tuf/actions/rbac.go b/controllers/tuf/actions/rbac.go index 69a5e2c83..260f71620 100644 --- a/controllers/tuf/actions/rbac.go +++ b/controllers/tuf/actions/rbac.go @@ -28,7 +28,8 @@ func (i rbacAction) Name() string { } func (i rbacAction) CanHandle(tuf *rhtasv1alpha1.Tuf) bool { - return tuf.Status.Phase == rhtasv1alpha1.PhaseCreating || tuf.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *action.Result { @@ -56,11 +57,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *ac // don't re-enqueue for RBAC in any case (except failure) _, err = i.Ensure(ctx, sa) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create SA: %w", err), instance) @@ -83,11 +83,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *ac } _, err = i.Ensure(ctx, role) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create Role: %w", err), instance) @@ -106,11 +105,10 @@ func (i rbacAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *ac } _, err = i.Ensure(ctx, rb) if err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create RoleBinding: %w", err), instance) diff --git a/controllers/tuf/actions/servise.go b/controllers/tuf/actions/servise.go index 484de55e3..f518b1f08 100644 --- a/controllers/tuf/actions/servise.go +++ b/controllers/tuf/actions/servise.go @@ -26,7 +26,8 @@ func (i serviceAction) Name() string { } func (i serviceAction) CanHandle(tuf *rhtasv1alpha1.Tuf) bool { - return tuf.Status.Phase == rhtasv1alpha1.PhaseCreating || tuf.Status.Phase == rhtasv1alpha1.PhaseReady + c := meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready) + return c.Reason == constants.Creating || c.Reason == constants.Ready } func (i serviceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *action.Result { @@ -44,18 +45,19 @@ func (i serviceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) return i.Failed(fmt.Errorf("could not set controller reference for Service: %w", err)) } if updated, err = i.Ensure(ctx, svc); err != nil { - instance.Status.Phase = rhtasv1alpha1.PhaseError meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), + Type: constants.Ready, Status: metav1.ConditionFalse, - Reason: "Failure", + Reason: constants.Failure, Message: err.Error(), }) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create service: %w", err), instance) } if updated { - return i.Return() + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Service created"}) + return i.StatusUpdate(ctx, instance) } else { return i.Continue() } diff --git a/controllers/tuf/actions/to_initialize_phase.go b/controllers/tuf/actions/to_initialize_phase.go index c5986dde0..fda7a7d35 100644 --- a/controllers/tuf/actions/to_initialize_phase.go +++ b/controllers/tuf/actions/to_initialize_phase.go @@ -5,6 +5,7 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -18,18 +19,17 @@ type toInitialize struct { } func (i toInitialize) Name() string { - return "create action done" + return "move to initialize" } func (i toInitialize) CanHandle(tuf *rhtasv1alpha1.Tuf) bool { - return tuf.Status.Phase == rhtasv1alpha1.PhaseCreating + c := meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready) + return c.Status == metav1.ConditionFalse && (c.Reason == constants.Creating) } func (i toInitialize) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *action.Result { - instance.Status.Phase = rhtasv1alpha1.PhaseInitialize - - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionTrue, Reason: string(rhtasv1alpha1.PhaseInitialize)}) + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Initialize, Message: "Move to initialize phase"}) return i.StatusUpdate(ctx, instance) } diff --git a/controllers/tuf/actions/to_pending_phase.go b/controllers/tuf/actions/to_pending_phase.go new file mode 100644 index 000000000..d458cfb9a --- /dev/null +++ b/controllers/tuf/actions/to_pending_phase.go @@ -0,0 +1,43 @@ +package actions + +import ( + "context" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/common/action" + "github.com/securesign/operator/controllers/constants" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func NewToPendingPhaseAction() action.Action[rhtasv1alpha1.Tuf] { + return &toPending{} +} + +type toPending struct { + action.BaseAction +} + +func (i toPending) Name() string { + return "move to pending phase" +} + +func (i toPending) CanHandle(tuf *rhtasv1alpha1.Tuf) bool { + return meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready) == nil +} + +func (i toPending) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) *action.Result { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Pending}) + + for _, key := range instance.Spec.Keys { + if meta.FindStatusCondition(instance.Status.Conditions, key.Name) == nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: key.Name, + Status: metav1.ConditionUnknown, + Reason: constants.Pending, + }) + } + } + return i.StatusUpdate(ctx, instance) +} diff --git a/controllers/tuf/tuf_controller.go b/controllers/tuf/tuf_controller.go index d11a5c2a5..b8979c7b7 100644 --- a/controllers/tuf/tuf_controller.go +++ b/controllers/tuf/tuf_controller.go @@ -26,8 +26,6 @@ import ( v12 "k8s.io/api/core/v1" v13 "k8s.io/api/networking/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" @@ -77,25 +75,16 @@ func (r *TufReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R } } - if instance.Status.Conditions == nil || len(instance.Status.Conditions) == 0 { - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: string(rhtasv1alpha1.PhaseReady), - Status: metav1.ConditionUnknown, - Reason: string(rhtasv1alpha1.PhasePending)}) - if err := r.Status().Update(ctx, instance); err != nil { - rlog.Error(err, "Failed to update TUF status") - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - target := instance.DeepCopy() acs := []action.Action[rhtasv1alpha1.Tuf]{ - actions.NewPendingAction(), + actions.NewToPendingPhaseAction(), + + actions.NewResolveKeysAction(), actions.NewRBACAction(), actions.NewDeployAction(), actions.NewServiceAction(), actions.NewIngressAction(), + actions.NewToInitializePhaseAction(), actions.NewInitializeAction(), diff --git a/controllers/tuf/tuf_controller_test.go b/controllers/tuf/tuf_controller_test.go index fac041c36..51f641cfe 100644 --- a/controllers/tuf/tuf_controller_test.go +++ b/controllers/tuf/tuf_controller_test.go @@ -28,6 +28,7 @@ import ( "github.com/securesign/operator/controllers/tuf/actions" v1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -132,12 +133,19 @@ var _ = Describe("TUF controller", func() { return k8sClient.Get(ctx, typeNamespaceName, found) }, time.Minute, time.Second).Should(Succeed()) - By("Pending phase untill ctlog public key is resolved") - Eventually(func() v1alpha1.Phase { + By("Status conditions are initialized") + Eventually(func() bool { found := &v1alpha1.Tuf{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhasePending)) + return meta.IsStatusConditionPresentAndEqual(found.Status.Conditions, constants.Ready, metav1.ConditionFalse) + }, time.Minute, time.Second).Should(BeTrue()) + + By("Pending phase until ctlog public key is resolved") + Eventually(func() string { + found := &v1alpha1.Tuf{} + Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) + return meta.FindStatusCondition(found.Status.Conditions, constants.Ready).Reason + }, time.Minute, time.Second).Should(Equal(constants.Pending)) By("Creating ctlog secret with public key") secretLabels := map[string]string{ @@ -149,11 +157,11 @@ var _ = Describe("TUF controller", func() { }, secretLabels)) By("Waiting until Tuf instance is Initialization") - Eventually(func() v1alpha1.Phase { + Eventually(func() string { found := &v1alpha1.Tuf{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseInitialize)) + return meta.FindStatusCondition(found.Status.Conditions, constants.Ready).Reason + }, time.Minute, time.Second).Should(Equal(constants.Initialize)) deployment := &appsv1.Deployment{} By("Checking if Deployment was successfully created in the reconciliation") @@ -168,11 +176,11 @@ var _ = Describe("TUF controller", func() { Expect(k8sClient.Status().Update(ctx, deployment)).Should(Succeed()) By("Waiting until Tuf instance is Ready") - Eventually(func() v1alpha1.Phase { + Eventually(func() bool { found := &v1alpha1.Tuf{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - return found.Status.Phase - }, time.Minute, time.Second).Should(Equal(v1alpha1.PhaseReady)) + return meta.IsStatusConditionTrue(found.Status.Conditions, constants.Ready) + }, time.Minute, time.Second).Should(BeTrue()) By("Checking if Service was successfully created in the reconciliation") service := &corev1.Service{} @@ -194,11 +202,11 @@ var _ = Describe("TUF controller", func() { Eventually(func() error { found := &v1alpha1.Tuf{} Expect(k8sClient.Get(ctx, typeNamespaceName, found)).Should(Succeed()) - rekorCondition := findCondition(found.Status.Conditions, "rekor.pub") + rekorCondition := meta.FindStatusCondition(found.Status.Conditions, "rekor.pub") Expect(rekorCondition).Should(Not(BeNil())) Expect(rekorCondition.Status).Should(Equal(metav1.ConditionTrue)) Expect(rekorCondition.Reason).Should(Equal("Ready")) - ctlogCondition := findCondition(found.Status.Conditions, "ctfe.pub") + ctlogCondition := meta.FindStatusCondition(found.Status.Conditions, "ctfe.pub") Expect(ctlogCondition).Should(Not(BeNil())) Expect(ctlogCondition.Status).Should(Equal(metav1.ConditionTrue)) Expect(ctlogCondition.Reason).Should(Equal("Ready")) @@ -221,14 +229,3 @@ var _ = Describe("TUF controller", func() { }) }) }) - -func findCondition(conditions []metav1.Condition, ctype string) *metav1.Condition { - if conditions != nil && len(conditions) != 0 { - for _, c := range conditions { - if c.Type == ctype { - return &c - } - } - } - return nil -} diff --git a/e2e/byodb_test.go b/e2e/byodb_test.go index 850476280..8abf105c3 100644 --- a/e2e/byodb_test.go +++ b/e2e/byodb_test.go @@ -96,7 +96,8 @@ var _ = Describe("Securesign install with byodb", Ordered, func() { }) It("All components are running", func() { - tas.VerifyTrillian(ctx, cli, namespace.Name, securesign.Name) + tas.VerifySecuresign(ctx, cli, namespace.Name, securesign.Name) + tas.VerifyTrillian(ctx, cli, namespace.Name, securesign.Name, false) tas.VerifyFulcio(ctx, cli, namespace.Name, securesign.Name) tas.VerifyRekor(ctx, cli, namespace.Name, securesign.Name) tas.VerifyCTLog(ctx, cli, namespace.Name, securesign.Name) diff --git a/e2e/common_install_test.go b/e2e/common_install_test.go index 7867758b7..55338db90 100644 --- a/e2e/common_install_test.go +++ b/e2e/common_install_test.go @@ -170,7 +170,8 @@ var _ = Describe("Securesign install with certificate generation", Ordered, func }) It("All other components are running", func() { - tas.VerifyTrillian(ctx, cli, namespace.Name, securesign.Name) + tas.VerifySecuresign(ctx, cli, namespace.Name, securesign.Name) + tas.VerifyTrillian(ctx, cli, namespace.Name, securesign.Name, true) tas.VerifyCTLog(ctx, cli, namespace.Name, securesign.Name) tas.VerifyTuf(ctx, cli, namespace.Name, securesign.Name) tas.VerifyRekorSearchUI(ctx, cli, namespace.Name, securesign.Name) diff --git a/e2e/key_autodiscovery_test.go b/e2e/key_autodiscovery_test.go index 106f3e17d..52a012158 100644 --- a/e2e/key_autodiscovery_test.go +++ b/e2e/key_autodiscovery_test.go @@ -132,10 +132,11 @@ var _ = Describe("Securesign key autodiscovery test", Ordered, func() { }) It("All components are running", func() { + tas.VerifySecuresign(ctx, cli, namespace.Name, securesign.Name) tas.VerifyRekor(ctx, cli, namespace.Name, securesign.Name) tas.VerifyFulcio(ctx, cli, namespace.Name, securesign.Name) tas.VerifyCTLog(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyTrillian(ctx, cli, namespace.Name, securesign.Name) + tas.VerifyTrillian(ctx, cli, namespace.Name, securesign.Name, true) tas.VerifyTuf(ctx, cli, namespace.Name, securesign.Name) }) diff --git a/e2e/provided_certs_test.go b/e2e/provided_certs_test.go index a87133b94..968fadb03 100644 --- a/e2e/provided_certs_test.go +++ b/e2e/provided_certs_test.go @@ -208,8 +208,9 @@ var _ = Describe("Securesign install with provided certs", Ordered, func() { }) It("All other components are running", func() { + tas.VerifySecuresign(ctx, cli, namespace.Name, securesign.Name) tas.VerifyCTLog(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyTrillian(ctx, cli, namespace.Name, securesign.Name) + tas.VerifyTrillian(ctx, cli, namespace.Name, securesign.Name, true) tas.VerifyTuf(ctx, cli, namespace.Name, securesign.Name) }) diff --git a/e2e/support/tas/ctlog.go b/e2e/support/tas/ctlog.go index 8f79df645..b5a018200 100644 --- a/e2e/support/tas/ctlog.go +++ b/e2e/support/tas/ctlog.go @@ -6,21 +6,23 @@ import ( . "github.com/onsi/gomega" "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/ctlog/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ) func VerifyCTLog(ctx context.Context, cli client.Client, namespace string, name string) { - Eventually(func() v1alpha1.Phase { + Eventually(func() bool { instance := &v1alpha1.CTlog{} cli.Get(ctx, types.NamespacedName{ Namespace: namespace, Name: name, }, instance) - return instance.Status.Phase - }).Should(Equal(v1alpha1.PhaseReady)) + return meta.IsStatusConditionTrue(instance.Status.Conditions, constants.Ready) + }).Should(BeTrue()) list := &v1.PodList{} cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName}) diff --git a/e2e/support/tas/fulcio.go b/e2e/support/tas/fulcio.go index 3c0f67b1e..ecfbf11c6 100644 --- a/e2e/support/tas/fulcio.go +++ b/e2e/support/tas/fulcio.go @@ -6,17 +6,19 @@ import ( . "github.com/onsi/gomega" "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/fulcio/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ) func VerifyFulcio(ctx context.Context, cli client.Client, namespace string, name string) { Eventually(GetFulcio(ctx, cli, namespace, name)).Should( - WithTransform(func(f *v1alpha1.Fulcio) v1alpha1.Phase { - return f.Status.Phase - }, Equal(v1alpha1.PhaseReady))) + WithTransform(func(f *v1alpha1.Fulcio) bool { + return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) + }, BeTrue())) list := &v1.PodList{} cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName}) diff --git a/e2e/support/tas/rekor.go b/e2e/support/tas/rekor.go index 64f780b63..6a33857b3 100644 --- a/e2e/support/tas/rekor.go +++ b/e2e/support/tas/rekor.go @@ -6,17 +6,19 @@ import ( . "github.com/onsi/gomega" "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/rekor/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ) func VerifyRekor(ctx context.Context, cli client.Client, namespace string, name string) { Eventually(GetRekor(ctx, cli, namespace, name)).Should( - WithTransform(func(f *v1alpha1.Rekor) v1alpha1.Phase { - return f.Status.Phase - }, Equal(v1alpha1.PhaseReady))) + WithTransform(func(f *v1alpha1.Rekor) bool { + return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) + }, BeTrue())) list := &v1.PodList{} diff --git a/e2e/support/tas/securesign.go b/e2e/support/tas/securesign.go new file mode 100644 index 000000000..9282776ab --- /dev/null +++ b/e2e/support/tas/securesign.go @@ -0,0 +1,30 @@ +package tas + +import ( + "context" + + . "github.com/onsi/gomega" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/controllers/constants" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func VerifySecuresign(ctx context.Context, cli client.Client, namespace string, name string) { + Eventually(GetSecuresign(ctx, cli, namespace, name)).Should( + WithTransform(func(f *v1alpha1.Securesign) bool { + return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) + }, BeTrue())) +} + +func GetSecuresign(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.Securesign { + return func() *v1alpha1.Securesign { + instance := &v1alpha1.Securesign{} + cli.Get(ctx, types.NamespacedName{ + Namespace: ns, + Name: name, + }, instance) + return instance + } +} diff --git a/e2e/support/tas/trillian.go b/e2e/support/tas/trillian.go index 70c6fcbc1..27dd98ba8 100644 --- a/e2e/support/tas/trillian.go +++ b/e2e/support/tas/trillian.go @@ -6,23 +6,33 @@ import ( . "github.com/onsi/gomega" "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/trillian/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ) -func VerifyTrillian(ctx context.Context, cli client.Client, namespace string, name string) { - Eventually(func() v1alpha1.Phase { +func VerifyTrillian(ctx context.Context, cli client.Client, namespace string, name string, dbPresent bool) { + Eventually(func() bool { instance := &v1alpha1.Trillian{} cli.Get(ctx, types.NamespacedName{ Namespace: namespace, Name: name, }, instance) - return instance.Status.Phase - }).Should(Equal(v1alpha1.PhaseReady)) + return meta.IsStatusConditionTrue(instance.Status.Conditions, constants.Ready) + }).Should(BeTrue()) list := &v1.PodList{} - cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions2.ComponentName}) + if dbPresent { + cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.DbComponentName}) + Expect(list.Items).To(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) + } + + cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.LogServerComponentName}) + Expect(list.Items).To(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) + + cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.LogSignerComponentName}) Expect(list.Items).To(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) } diff --git a/e2e/support/tas/tuf.go b/e2e/support/tas/tuf.go index d6fe176bb..a588e1e1a 100644 --- a/e2e/support/tas/tuf.go +++ b/e2e/support/tas/tuf.go @@ -6,17 +6,19 @@ import ( . "github.com/onsi/gomega" "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/controllers/common/utils/kubernetes" + "github.com/securesign/operator/controllers/constants" "github.com/securesign/operator/controllers/tuf/actions" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ) func VerifyTuf(ctx context.Context, cli client.Client, namespace string, name string) { Eventually(GetTuf(ctx, cli, namespace, name)).Should( - WithTransform(func(f *v1alpha1.Tuf) v1alpha1.Phase { - return f.Status.Phase - }, Equal(v1alpha1.PhaseReady))) + WithTransform(func(f *v1alpha1.Tuf) string { + return meta.FindStatusCondition(f.Status.Conditions, constants.Ready).Reason + }, Equal(constants.Ready))) list := &v1.PodList{} cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName})