From b0781e09916ed92829fa2b95386c760412d85069 Mon Sep 17 00:00:00 2001 From: Jan Bouska Date: Thu, 6 Feb 2025 13:39:03 +0100 Subject: [PATCH] Migrate segment jobs and securesign resources --- .../controller/annotations/annotations.go | 14 +- .../annotations/annotations_test.go | 78 ------ .../controller/common/action/base_action.go | 215 +-------------- .../utils/kubernetes/ensure/deployment.go | 4 +- .../kubernetes/ensure/deployment_test.go | 3 +- .../{ => kubernetes/ensure}/set_proxy_test.go | 8 +- .../common/utils/kubernetes/ensure/utils.go | 30 +++ .../common/utils/kubernetes/job/job.go | 36 --- internal/controller/common/utils/set_proxy.go | 32 --- .../controller/common/utils/set_trusted_ca.go | 69 ----- .../common/utils/set_trusted_ca_test.go | 249 ------------------ .../controller/ctlog/actions/deployment.go | 2 +- .../controller/fulcio/actions/deployment.go | 2 +- .../fulcio/actions/fulcio_deployment_test.go | 5 +- .../rekor/actions/server/deployment.go | 2 +- .../securesign/actions/ensure_ctlog.go | 65 ++--- .../securesign/actions/ensure_fulcio.go | 66 ++--- .../securesign/actions/ensure_rekor.go | 63 ++--- .../securesign/actions/ensure_trillian.go | 63 ++--- .../securesign/actions/ensure_tsa.go | 72 +++-- .../securesign/actions/ensure_tuf.go | 63 ++--- .../actions/segment_backup_cronjob.go | 131 +++++---- .../securesign/actions/segment_backup_job.go | 107 ++++---- .../trillian/actions/db/handle_secret.go | 75 +++--- .../trillian/actions/logserver/deployment.go | 3 +- .../trillian/actions/logsigner/deployment.go | 3 +- internal/controller/tsa/actions/deployment.go | 2 +- .../controller/tuf/actions/tuf_init_job.go | 6 +- internal/controller/tuf/utils/tuf_init_job.go | 15 +- test/e2e/support/tas/tuf/tuf.go | 2 +- 30 files changed, 404 insertions(+), 1081 deletions(-) delete mode 100644 internal/controller/annotations/annotations_test.go rename internal/controller/common/utils/{ => kubernetes/ensure}/set_proxy_test.go (92%) create mode 100644 internal/controller/common/utils/kubernetes/ensure/utils.go delete mode 100644 internal/controller/common/utils/set_proxy.go delete mode 100644 internal/controller/common/utils/set_trusted_ca.go delete mode 100644 internal/controller/common/utils/set_trusted_ca_test.go diff --git a/internal/controller/annotations/annotations.go b/internal/controller/annotations/annotations.go index 8482ba971..82ceecacd 100644 --- a/internal/controller/annotations/annotations.go +++ b/internal/controller/annotations/annotations.go @@ -110,18 +110,6 @@ const ( TLS = "service.beta.openshift.io/serving-cert-secret-name" ) -var inheritable = []string{ +var InheritableAnnotations = []string{ TrustedCA, LogType, } - -func FilterInheritable(annotations map[string]string) map[string]string { - result := make(map[string]string, 0) - for key, value := range annotations { - for _, ia := range inheritable { - if key == ia { - result[key] = value - } - } - } - return result -} diff --git a/internal/controller/annotations/annotations_test.go b/internal/controller/annotations/annotations_test.go deleted file mode 100644 index 5828ad0f2..000000000 --- a/internal/controller/annotations/annotations_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package annotations - -import ( - "maps" - "reflect" - "strconv" - "testing" -) - -func TestFilterInheritable(t *testing.T) { - allIn := make(map[string]string) - allCopy := make(map[string]string) - for i, a := range inheritable { - allIn[a] = strconv.FormatInt(int64(i), 10) - } - maps.Copy(allCopy, allIn) - - type args struct { - annotations map[string]string - } - tests := []struct { - name string - args args - want map[string]string - }{ - { - name: "empty", - args: args{ - annotations: make(map[string]string), - }, - want: map[string]string{}, - }, - { - name: "nil", - args: args{ - annotations: nil, - }, - want: map[string]string{}, - }, - { - name: "no inheritable", - args: args{ - annotations: map[string]string{ - "name1": "value", - "name2": "value", - }, - }, - want: map[string]string{}, - }, - { - name: "all inheritable", - args: args{ - annotations: allIn, - }, - want: allCopy, - }, - { - name: "one inheritable", - args: args{ - annotations: map[string]string{ - "name1": "value", - "name2": "value", - TrustedCA: "ca", - }, - }, - want: map[string]string{ - TrustedCA: "ca", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := FilterInheritable(tt.args.annotations); !reflect.DeepEqual(got, tt.want) { - t.Errorf("FilterInheritable() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/controller/common/action/base_action.go b/internal/controller/common/action/base_action.go index 5b6916ac7..4753dfb7d 100644 --- a/internal/controller/common/action/base_action.go +++ b/internal/controller/common/action/base_action.go @@ -3,24 +3,13 @@ package action import ( "context" "errors" - "fmt" - "maps" - "reflect" - "strconv" "strings" "time" + "github.com/go-logr/logr" "github.com/securesign/operator/internal/apis" - "github.com/securesign/operator/internal/controller/annotations" "github.com/securesign/operator/internal/controller/constants" - "k8s.io/apimachinery/pkg/api/equality" - apiErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/util/retry" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/go-logr/logr" - rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" client2 "sigs.k8s.io/controller-runtime/pkg/client" @@ -30,8 +19,6 @@ import ( // OptimisticLockErrorMsg - ignore update error: https://github.com/kubernetes/kubernetes/issues/28149 const OptimisticLockErrorMsg = "the object has been modified; please apply your changes to the latest version and try again" -type EnsureOption func(current client.Object, expected client.Object) error - type BaseAction struct { Client client.Client Recorder record.EventRecorder @@ -126,203 +113,3 @@ func (action *BaseAction) Requeue() *Result { Err: nil, } } - -// Deprecated: Use kubernetes.CreateOrUpdate function -func (action *BaseAction) Ensure(ctx context.Context, obj client2.Object, opts ...EnsureOption) (bool, error) { - var ( - expected client2.Object - ok bool - result controllerutil.OperationResult - ) - - if len(opts) == 0 { - opts = []EnsureOption{ - EnsureSpec(), - } - } - - if expected, ok = obj.DeepCopyObject().(client2.Object); !ok { - return false, errors.New("can't create DeepCopy object") - } - - err := retry.OnError(retry.DefaultRetry, func(err error) bool { - return apiErrors.IsConflict(err) || apiErrors.IsAlreadyExists(err) - }, func() error { - var createUpdateError error - result, createUpdateError = controllerutil.CreateOrUpdate(ctx, action.Client, obj, func() error { - annoStr, find := obj.GetAnnotations()[annotations.PausedReconciliation] - if find { - annoBool, _ := strconv.ParseBool(annoStr) - if annoBool { - return nil - } - } - - for _, opt := range opts { - optError := opt(obj, expected) - if optError != nil { - return optError - } - } - - return nil - }) - return createUpdateError - }) - - if err != nil { - return false, err - } - - return result != controllerutil.OperationResultNone, nil -} - -func EnsureSpec() EnsureOption { - return func(current client.Object, expected client.Object) error { - currentSpec := reflect.ValueOf(current).Elem().FieldByName("Spec") - expectedSpec := reflect.ValueOf(expected).Elem().FieldByName("Spec") - if currentSpec == reflect.ValueOf(nil) { - // object without spec - // return without update - return nil - } - if !expectedSpec.IsValid() || !currentSpec.IsValid() { - return errors.New("spec is not valid") - } - if !currentSpec.CanSet() { - return errors.New("can't set expected spec to current object") - } - - // WORKAROUND: CreateOrUpdate uses DeepEqual to compare - // DeepEqual does not honor default values - if !equality.Semantic.DeepDerivative(expectedSpec.Interface(), currentSpec.Interface()) { - currentSpec.Set(expectedSpec) - } - return nil - } -} - -func EnsureRouteSelectorLabels(managedLabels ...string) EnsureOption { - return func(current client.Object, expected client.Object) error { - if current == nil || expected == nil { - return fmt.Errorf("nil object passed") - } - - currentSpec := reflect.ValueOf(current).Elem().FieldByName("Spec") - expectedSpec := reflect.ValueOf(expected).Elem().FieldByName("Spec") - if !currentSpec.IsValid() || !expectedSpec.IsValid() { - return nil - } - - //Current workaround for DeepEqual vs DeepDerivative, more info here https://issues.redhat.com/browse/SECURESIGN-1393 - currentRouteSelectorLabels, expectedRouteSelectorLabels := getRouteSelectorLabels(currentSpec, expectedSpec) - if currentRouteSelectorLabels.CanSet() && - !equality.Semantic.DeepEqual(currentRouteSelectorLabels.Interface(), expectedRouteSelectorLabels.Interface()) { - currentRouteSelectorLabels.Set(expectedRouteSelectorLabels) - } - - gvk := current.GetObjectKind().GroupVersionKind() - if gvk.Kind == "Ingress" || gvk.Kind == "Route" { - if !reflect.DeepEqual(current.GetLabels(), expected.GetLabels()) { - if err := EnsureLabels(managedLabels...)(current, expected); err != nil { - return err - } - } - } - return nil - } -} - -func EnsureLabels(managedLabels ...string) EnsureOption { - return func(current client.Object, expected client.Object) error { - expectedLabels := expected.GetLabels() - if expectedLabels == nil { - expectedLabels = map[string]string{} - } - currentLabels := current.GetLabels() - if currentLabels == nil { - currentLabels = map[string]string{} - } - mergedLabels := make(map[string]string) - maps.Copy(mergedLabels, currentLabels) - - maps.DeleteFunc(mergedLabels, func(k, v string) bool { - _, existsInExpected := expectedLabels[k] - return !existsInExpected - }) - - for _, managedLabel := range managedLabels { - if val, exists := expectedLabels[managedLabel]; exists { - mergedLabels[managedLabel] = val - } - } - current.SetLabels(mergedLabels) - return nil - } -} - -func EnsureAnnotations(managedAnnotations ...string) EnsureOption { - return func(current client.Object, expected client.Object) error { - expectedAnno := expected.GetAnnotations() - if expectedAnno == nil { - expectedAnno = map[string]string{} - } - currentAnno := current.GetAnnotations() - if currentAnno == nil { - currentAnno = map[string]string{} - } - mergedAnnotations := make(map[string]string) - maps.Copy(mergedAnnotations, currentAnno) - - for _, managedAnno := range managedAnnotations { - if val, exists := expectedAnno[managedAnno]; exists { - mergedAnnotations[managedAnno] = val - } else { - delete(mergedAnnotations, managedAnno) - } - } - current.SetAnnotations(mergedAnnotations) - return nil - } -} - -func EnsureNTPConfig() EnsureOption { - return func(current client.Object, expected client.Object) error { - currentTSA, ok1 := current.(*rhtasv1alpha1.TimestampAuthority) - expectedTSA, ok2 := expected.(*rhtasv1alpha1.TimestampAuthority) - if !ok1 || !ok2 { - return fmt.Errorf("EnsureNTPConfig: objects are not of type *rhtasv1alpha1.TimestampAuthority") - } - currentTSA.Spec.NTPMonitoring = expectedTSA.Spec.NTPMonitoring - return nil - } -} - -func getRouteSelectorLabels(currentSpec, expectedSpec reflect.Value) (reflect.Value, reflect.Value) { - var currentRouteSelectorLabels, expectedRouteSelectorLabels reflect.Value - getRouteSelectorLabels := func(spec reflect.Value, fieldName string) reflect.Value { - if field := spec.FieldByName(fieldName); field.IsValid() { - if routeSelectorLabels := field.FieldByName("RouteSelectorLabels"); routeSelectorLabels.IsValid() { - return routeSelectorLabels - } - } - return reflect.Value{} - } - - // Handle Rekor and rekor search ui - currentRekorLabels := getRouteSelectorLabels(currentSpec, "RekorSearchUI") - expectedRekorLabels := getRouteSelectorLabels(expectedSpec, "RekorSearchUI") - if currentRekorLabels.IsValid() && expectedRekorLabels.IsValid() { - if !equality.Semantic.DeepEqual(currentRekorLabels.Interface(), expectedRekorLabels.Interface()) { - currentRouteSelectorLabels = currentRekorLabels - expectedRouteSelectorLabels = expectedRekorLabels - } - } - - //Handle the rest - if !currentRouteSelectorLabels.IsValid() && !expectedRouteSelectorLabels.IsValid() { - currentRouteSelectorLabels = getRouteSelectorLabels(currentSpec, "ExternalAccess") - expectedRouteSelectorLabels = getRouteSelectorLabels(expectedSpec, "ExternalAccess") - } - return currentRouteSelectorLabels, expectedRouteSelectorLabels -} diff --git a/internal/controller/common/utils/kubernetes/ensure/deployment.go b/internal/controller/common/utils/kubernetes/ensure/deployment.go index 4cb19a159..b0379d97a 100644 --- a/internal/controller/common/utils/kubernetes/ensure/deployment.go +++ b/internal/controller/common/utils/kubernetes/ensure/deployment.go @@ -4,7 +4,6 @@ import ( "slices" "github.com/securesign/operator/api/v1alpha1" - "github.com/securesign/operator/internal/controller/common/utils" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -23,7 +22,7 @@ const ( func Proxy() func(*v1.Deployment) error { return func(dp *v1.Deployment) error { - utils.SetProxyEnvs(dp) + SetProxyEnvs(dp.Spec.Template.Spec.Containers) return nil } } @@ -31,7 +30,6 @@ func Proxy() func(*v1.Deployment) error { // TrustedCA mount config map with trusted CA bundle to all deployment's containers. func TrustedCA(lor *v1alpha1.LocalObjectReference) func(dp *v1.Deployment) error { return func(dp *v1.Deployment) error { - template := &dp.Spec.Template for i := range template.Spec.Containers { env := kubernetes.FindEnvByNameOrCreate(&template.Spec.Containers[i], "SSL_CERT_DIR") diff --git a/internal/controller/common/utils/kubernetes/ensure/deployment_test.go b/internal/controller/common/utils/kubernetes/ensure/deployment_test.go index b1ff02f64..598300d45 100644 --- a/internal/controller/common/utils/kubernetes/ensure/deployment_test.go +++ b/internal/controller/common/utils/kubernetes/ensure/deployment_test.go @@ -7,7 +7,6 @@ import ( "github.com/onsi/gomega" "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/annotations" - "github.com/securesign/operator/internal/controller/common/utils" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" testAction "github.com/securesign/operator/internal/testing/action" v1 "k8s.io/api/apps/v1" @@ -41,7 +40,7 @@ func TestEnsureTrustedCAFromAnnotations(t *testing.T) { result, err := kubernetes.CreateOrUpdate(ctx, c, &v1.Deployment{ObjectMeta: v2.ObjectMeta{Name: name, Namespace: "default"}}, - TrustedCA(utils.TrustedCAAnnotationToReference(map[string]string{annotations.TrustedCA: "test"})), + TrustedCA(TrustedCAAnnotationToReference(map[string]string{annotations.TrustedCA: "test"})), ) gomega.Expect(err).ToNot(gomega.HaveOccurred()) diff --git a/internal/controller/common/utils/set_proxy_test.go b/internal/controller/common/utils/kubernetes/ensure/set_proxy_test.go similarity index 92% rename from internal/controller/common/utils/set_proxy_test.go rename to internal/controller/common/utils/kubernetes/ensure/set_proxy_test.go index d29f5ad07..3307fed4d 100644 --- a/internal/controller/common/utils/set_proxy_test.go +++ b/internal/controller/common/utils/kubernetes/ensure/set_proxy_test.go @@ -1,4 +1,4 @@ -package utils +package ensure import ( "testing" @@ -49,7 +49,7 @@ func TestSetProxyEnvs(t *testing.T) { }, } - SetProxyEnvs(dep) + SetProxyEnvs(dep.Spec.Template.Spec.Containers) g.Expect(dep.Spec.Template.Spec.Containers).ShouldNot(BeNil()) g.Expect(dep.Spec.Template.Spec.Containers[0].Env).Should(HaveLen(2)) @@ -59,7 +59,7 @@ func TestSetProxyEnvs(t *testing.T) { t.Setenv(e.Name, e.Value) } - SetProxyEnvs(dep) + SetProxyEnvs(dep.Spec.Template.Spec.Containers) expectedEnvVars := append(mockReadProxyVarsFromEnv(), corev1.EnvVar{ Name: "answer", @@ -71,7 +71,7 @@ func TestSetProxyEnvs(t *testing.T) { g.Expect(dep.Spec.Template.Spec.Containers[0].Env).Should(ConsistOf(expectedEnvVars)) // ensure no duplicates - SetProxyEnvs(dep) + SetProxyEnvs(dep.Spec.Template.Spec.Containers) g.Expect(dep.Spec.Template.Spec.Containers).ShouldNot(BeNil()) g.Expect(dep.Spec.Template.Spec.Containers[0].Env).Should(HaveLen(7)) } diff --git a/internal/controller/common/utils/kubernetes/ensure/utils.go b/internal/controller/common/utils/kubernetes/ensure/utils.go new file mode 100644 index 000000000..8eb1595dc --- /dev/null +++ b/internal/controller/common/utils/kubernetes/ensure/utils.go @@ -0,0 +1,30 @@ +package ensure + +import ( + "github.com/operator-framework/operator-lib/proxy" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/annotations" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + v1 "k8s.io/api/core/v1" +) + +// SetProxyEnvs set the standard environment variables for proxys "HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY" +func SetProxyEnvs(containers []v1.Container) { + proxyEnvs := proxy.ReadProxyVarsFromEnv() + for i := range containers { + for _, e := range proxyEnvs { + env := kubernetes.FindEnvByNameOrCreate(&containers[i], e.Name) + env.Value = e.Value + + } + } +} + +func TrustedCAAnnotationToReference(anns map[string]string) *v1alpha1.LocalObjectReference { + if v, ok := anns[annotations.TrustedCA]; ok { + return &v1alpha1.LocalObjectReference{ + Name: v, + } + } + return nil +} diff --git a/internal/controller/common/utils/kubernetes/job/job.go b/internal/controller/common/utils/kubernetes/job/job.go index b02cf3efb..2cd88b335 100644 --- a/internal/controller/common/utils/kubernetes/job/job.go +++ b/internal/controller/common/utils/kubernetes/job/job.go @@ -2,48 +2,12 @@ package job import ( "context" - "fmt" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) -const ( - jobNameTemplate = "%s-" -) - -func CreateJob(namespace string, name string, labels map[string]string, image string, serviceAccountName string, parallelism int32, completions int32, activeDeadlineSeconds int64, backoffLimit int32, command []string, env []corev1.EnvVar) *batchv1.Job { - return &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: fmt.Sprintf(jobNameTemplate, name), - Namespace: namespace, - Labels: labels, - }, - Spec: batchv1.JobSpec{ - Parallelism: ¶llelism, - Completions: &completions, - ActiveDeadlineSeconds: &activeDeadlineSeconds, - BackoffLimit: &backoffLimit, - Template: corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - ServiceAccountName: serviceAccountName, - RestartPolicy: "OnFailure", - Containers: []corev1.Container{ - { - Name: name, - Image: image, - Command: command, - Env: env, - }, - }, - }, - }, - }, - } -} - func GetJob(ctx context.Context, c client.Client, namespace, jobName string) (*batchv1.Job, error) { job := &batchv1.Job{} err := c.Get(ctx, client.ObjectKey{Namespace: namespace, Name: jobName}, job) diff --git a/internal/controller/common/utils/set_proxy.go b/internal/controller/common/utils/set_proxy.go deleted file mode 100644 index a76300de8..000000000 --- a/internal/controller/common/utils/set_proxy.go +++ /dev/null @@ -1,32 +0,0 @@ -package utils - -import ( - "reflect" - "slices" - - "github.com/operator-framework/operator-lib/proxy" - appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" -) - -// SetProxyEnvs set the standard environment variables for proxys "HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY" -func SetProxyEnvs(dep *appsv1.Deployment) { - proxyEnvs := proxy.ReadProxyVarsFromEnv() - for i, container := range dep.Spec.Template.Spec.Containers { - for _, e := range proxyEnvs { - if index := slices.IndexFunc(container.Env, - func(envVar v1.EnvVar) bool { return e.Name == envVar.Name }, - ); index > -1 { - if reflect.DeepEqual(e, container.Env[index]) { - // variable already present - continue - } else { - // overwrite - dep.Spec.Template.Spec.Containers[i].Env[index] = e - } - } else { - dep.Spec.Template.Spec.Containers[i].Env = append(dep.Spec.Template.Spec.Containers[i].Env, e) - } - } - } -} diff --git a/internal/controller/common/utils/set_trusted_ca.go b/internal/controller/common/utils/set_trusted_ca.go deleted file mode 100644 index 9fb86e3e7..000000000 --- a/internal/controller/common/utils/set_trusted_ca.go +++ /dev/null @@ -1,69 +0,0 @@ -package utils - -import ( - "errors" - - "github.com/securesign/operator/api/v1alpha1" - "github.com/securesign/operator/internal/controller/annotations" - corev1 "k8s.io/api/core/v1" -) - -// SetTrustedCA mount config map with trusted CA bundle to all deployment's containers. -func SetTrustedCA(template *corev1.PodTemplateSpec, lor *v1alpha1.LocalObjectReference) error { - if template == nil { - return errors.New("SetTrustedCA: PodTemplateSpec is not set") - } - - for i, container := range template.Spec.Containers { - if template.Spec.Containers[i].Env == nil { - template.Spec.Containers[i].Env = make([]corev1.EnvVar, 0) - } - template.Spec.Containers[i].Env = append(container.Env, corev1.EnvVar{ - Name: "SSL_CERT_DIR", - Value: "/var/run/configs/tas/ca-trust:/var/run/secrets/kubernetes.io/serviceaccount", - }) - - if template.Spec.Containers[i].VolumeMounts == nil { - template.Spec.Containers[i].VolumeMounts = make([]corev1.VolumeMount, 0) - } - template.Spec.Containers[i].VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ - Name: "ca-trust", - MountPath: "/var/run/configs/tas/ca-trust", - ReadOnly: true, - }) - } - - projections := make([]corev1.VolumeProjection, 0) - if lor != nil { - projections = append(projections, corev1.VolumeProjection{ - ConfigMap: &corev1.ConfigMapProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: lor.Name, - }, - }, - }) - } - - if template.Spec.Volumes == nil { - template.Spec.Volumes = make([]corev1.Volume, 0) - } - template.Spec.Volumes = append(template.Spec.Volumes, corev1.Volume{ - Name: "ca-trust", - VolumeSource: corev1.VolumeSource{ - Projected: &corev1.ProjectedVolumeSource{ - Sources: projections, - DefaultMode: Pointer(int32(420)), - }, - }, - }) - return nil -} - -func TrustedCAAnnotationToReference(anns map[string]string) *v1alpha1.LocalObjectReference { - if v, ok := anns[annotations.TrustedCA]; ok { - return &v1alpha1.LocalObjectReference{ - Name: v, - } - } - return nil -} diff --git a/internal/controller/common/utils/set_trusted_ca_test.go b/internal/controller/common/utils/set_trusted_ca_test.go deleted file mode 100644 index 5a07d6f56..000000000 --- a/internal/controller/common/utils/set_trusted_ca_test.go +++ /dev/null @@ -1,249 +0,0 @@ -package utils - -import ( - "reflect" - "testing" - - . "github.com/onsi/gomega" - "github.com/onsi/gomega/gstruct" - "github.com/securesign/operator/api/v1alpha1" - "github.com/securesign/operator/internal/controller/annotations" - corev1 "k8s.io/api/core/v1" -) - -func TestSetTrustedCA(t *testing.T) { - g := NewWithT(t) - deployment := func() *corev1.PodTemplateSpec { - return &corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "empty", - }, - { - Name: "env", - Env: []corev1.EnvVar{ - { - Name: "NAME", - Value: "VALUE", - }, - }, - }, - { - Name: "volume", - VolumeMounts: []corev1.VolumeMount{ - { - Name: "mount", - MountPath: "/mount/path/", - }, - }, - }, - }, - Volumes: []corev1.Volume{ - { - Name: "mount", - }, - }, - }, - } - } - - type asserts func(*corev1.PodTemplateSpec, error) - type args struct { - dep *corev1.PodTemplateSpec - lor *v1alpha1.LocalObjectReference - } - tests := []struct { - name string - args args - error bool - want asserts - }{ - { - name: "nil LocalObjectReference", - args: args{ - dep: deployment(), - lor: nil, - }, - want: func(spec *corev1.PodTemplateSpec, _ error) { - - g.Expect(spec.Spec.Containers).ShouldNot(BeNil()) - g.Expect(spec.Spec.Containers).Should(HaveLen(3)) - g.Expect(spec.Spec.Containers[0].Name).Should(BeEquivalentTo("empty")) - g.Expect(spec.Spec.Containers[0].Env).Should(HaveLen(1)) - g.Expect(spec.Spec.Containers[0].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("SSL_CERT_DIR"), - }))) - g.Expect(spec.Spec.Containers[0].VolumeMounts).Should(HaveLen(1)) - g.Expect(spec.Spec.Containers[0].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("ca-trust"), - }))) - - g.Expect(spec.Spec.Containers[1].Name).Should(BeEquivalentTo("env")) - g.Expect(spec.Spec.Containers[1].Env).Should(HaveLen(2)) - g.Expect(spec.Spec.Containers[1].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("NAME"), - }))) - g.Expect(spec.Spec.Containers[1].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("SSL_CERT_DIR"), - }))) - g.Expect(spec.Spec.Containers[1].VolumeMounts).Should(HaveLen(1)) - g.Expect(spec.Spec.Containers[1].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("ca-trust"), - }))) - - g.Expect(spec.Spec.Containers[2].Name).Should(BeEquivalentTo("volume")) - g.Expect(spec.Spec.Containers[2].Env).Should(HaveLen(1)) - g.Expect(spec.Spec.Containers[2].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("SSL_CERT_DIR"), - }))) - g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(HaveLen(2)) - g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("mount"), - }))) - g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("ca-trust"), - }))) - - g.Expect(spec.Spec.Volumes).Should(HaveLen(2)) - g.Expect(spec.Spec.Volumes).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("mount"), - }))) - g.Expect(spec.Spec.Volumes).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("ca-trust"), - }))) - g.Expect(spec.Spec.Volumes[1].VolumeSource.Projected.Sources).Should(BeEmpty()) - }, - }, - { - name: "mount config map", - args: args{ - dep: deployment(), - lor: &v1alpha1.LocalObjectReference{Name: "trusted"}, - }, - want: func(spec *corev1.PodTemplateSpec, _ error) { - - g.Expect(spec.Spec.Containers).ShouldNot(BeNil()) - g.Expect(spec.Spec.Containers).Should(HaveLen(3)) - g.Expect(spec.Spec.Containers[0].Name).Should(BeEquivalentTo("empty")) - g.Expect(spec.Spec.Containers[0].Env).Should(HaveLen(1)) - g.Expect(spec.Spec.Containers[0].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("SSL_CERT_DIR"), - }))) - g.Expect(spec.Spec.Containers[0].VolumeMounts).Should(HaveLen(1)) - g.Expect(spec.Spec.Containers[0].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("ca-trust"), - }))) - - g.Expect(spec.Spec.Containers[1].Name).Should(BeEquivalentTo("env")) - g.Expect(spec.Spec.Containers[1].Env).Should(HaveLen(2)) - g.Expect(spec.Spec.Containers[1].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("NAME"), - }))) - g.Expect(spec.Spec.Containers[1].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("SSL_CERT_DIR"), - }))) - g.Expect(spec.Spec.Containers[1].VolumeMounts).Should(HaveLen(1)) - g.Expect(spec.Spec.Containers[1].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("ca-trust"), - }))) - - g.Expect(spec.Spec.Containers[2].Name).Should(BeEquivalentTo("volume")) - g.Expect(spec.Spec.Containers[2].Env).Should(HaveLen(1)) - g.Expect(spec.Spec.Containers[2].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("SSL_CERT_DIR"), - }))) - g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(HaveLen(2)) - g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("mount"), - }))) - g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("ca-trust"), - }))) - - g.Expect(spec.Spec.Volumes).Should(HaveLen(2)) - g.Expect(spec.Spec.Volumes).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("mount"), - }))) - g.Expect(spec.Spec.Volumes).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal("ca-trust"), - }))) - g.Expect(spec.Spec.Volumes[1].VolumeSource.Projected.Sources).Should(HaveLen(1)) - g.Expect(spec.Spec.Volumes[1].VolumeSource.Projected.Sources[0].ConfigMap.LocalObjectReference.Name).Should(Equal("trusted")) - }, - }, - { - name: "nil Deployment", - args: args{ - dep: nil, - lor: nil, - }, - error: true, - want: func(d *corev1.PodTemplateSpec, err error) { - g.Expect(d).Should(BeNil()) - g.Expect(err).Should(MatchError(ContainSubstring("PodTemplateSpec is not set"))) - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := SetTrustedCA(tt.args.dep, tt.args.lor) - if tt.error { - g.Expect(err).Should(HaveOccurred()) - } else { - g.Expect(err).ShouldNot(HaveOccurred()) - } - tt.want(tt.args.dep, err) - }) - } -} - -func TestTrustedCAAnnotationToReference(t *testing.T) { - type args struct { - anns map[string]string - } - tests := []struct { - name string - args args - want *v1alpha1.LocalObjectReference - }{ - { - name: "nil", - args: args{ - anns: nil, - }, - want: nil, - }, - { - name: "empty", - args: args{ - anns: make(map[string]string, 0), - }, - want: nil, - }, - { - name: "not existing", - args: args{ - anns: map[string]string{ - "annotation": "value", - }, - }, - want: nil, - }, - { - name: "existing", - args: args{map[string]string{ - "annotation": "value", - annotations.TrustedCA: "trusted", - }}, - want: &v1alpha1.LocalObjectReference{Name: "trusted"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := TrustedCAAnnotationToReference(tt.args.anns); !reflect.DeepEqual(got, tt.want) { - t.Errorf("TrustedCAAnnotationToReference() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/controller/ctlog/actions/deployment.go b/internal/controller/ctlog/actions/deployment.go index fae808bde..a6dd67c25 100644 --- a/internal/controller/ctlog/actions/deployment.go +++ b/internal/controller/ctlog/actions/deployment.go @@ -69,7 +69,7 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) ensure.ControllerReference[*v1.Deployment](instance, i.Client), ensure.Labels[*v1.Deployment](maps.Keys(labels), labels), ensure.Proxy(), - ensure.TrustedCA(cutils.TrustedCAAnnotationToReference(instance.Annotations)), + ensure.TrustedCA(ensure.TrustedCAAnnotationToReference(instance.Annotations)), ); err != nil { return i.Error(ctx, fmt.Errorf("could not create ctlog server deployment: %w", err), instance) } diff --git a/internal/controller/fulcio/actions/deployment.go b/internal/controller/fulcio/actions/deployment.go index e729379ac..cf31b23b3 100644 --- a/internal/controller/fulcio/actions/deployment.go +++ b/internal/controller/fulcio/actions/deployment.go @@ -59,7 +59,7 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio caRef := instance.Spec.TrustedCA if caRef == nil { // override if spec.trustedCA is not defined - caRef = cutils.TrustedCAAnnotationToReference(instance.Annotations) + caRef = ensure.TrustedCAAnnotationToReference(instance.Annotations) } if result, err = kubernetes.CreateOrUpdate(ctx, i.Client, diff --git a/internal/controller/fulcio/actions/fulcio_deployment_test.go b/internal/controller/fulcio/actions/fulcio_deployment_test.go index 7cbf001b3..bde88b4d9 100644 --- a/internal/controller/fulcio/actions/fulcio_deployment_test.go +++ b/internal/controller/fulcio/actions/fulcio_deployment_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/securesign/operator/internal/controller/annotations" - cutils "github.com/securesign/operator/internal/controller/common/utils" "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" "github.com/securesign/operator/internal/controller/fulcio/utils" "github.com/securesign/operator/internal/controller/labels" @@ -231,7 +230,7 @@ func createDeployment(instance *v1alpha1.Fulcio, labels map[string]string) (*v13 caRef := instance.Spec.TrustedCA if caRef == nil { // override if spec.trustedCA is not defined - caRef = cutils.TrustedCAAnnotationToReference(instance.Annotations) + caRef = ensure.TrustedCAAnnotationToReference(instance.Annotations) } d := &v13.Deployment{ ObjectMeta: v1.ObjectMeta{ @@ -241,7 +240,7 @@ func createDeployment(instance *v1alpha1.Fulcio, labels map[string]string) (*v13 } if caRef == nil { // override if spec.trustedCA is not defined - cutils.TrustedCAAnnotationToReference(instance.Annotations) + ensure.TrustedCAAnnotationToReference(instance.Annotations) } ensures := []func(*v13.Deployment) error{ diff --git a/internal/controller/rekor/actions/server/deployment.go b/internal/controller/rekor/actions/server/deployment.go index bf948f636..9739cfcb0 100644 --- a/internal/controller/rekor/actions/server/deployment.go +++ b/internal/controller/rekor/actions/server/deployment.go @@ -67,7 +67,7 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) ensure.ControllerReference[*v2.Deployment](instance, i.Client), ensure.Labels[*v2.Deployment](maps.Keys(labels), labels), ensure.Proxy(), - ensure.TrustedCA(cutils.TrustedCAAnnotationToReference(instance.Annotations)), + ensure.TrustedCA(ensure.TrustedCAAnnotationToReference(instance.Annotations)), ); err != nil { return i.Error(ctx, fmt.Errorf("could create server Deployment: %w", err), instance) } diff --git a/internal/controller/securesign/actions/ensure_ctlog.go b/internal/controller/securesign/actions/ensure_ctlog.go index ed245dc16..37a6be28c 100644 --- a/internal/controller/securesign/actions/ensure_ctlog.go +++ b/internal/controller/securesign/actions/ensure_ctlog.go @@ -2,8 +2,12 @@ package actions import ( "context" + "fmt" "github.com/securesign/operator/internal/controller/annotations" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" + "golang.org/x/exp/maps" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -12,7 +16,6 @@ import ( "github.com/securesign/operator/internal/controller/labels" "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" ) @@ -34,50 +37,50 @@ func (i ctlogAction) CanHandle(context.Context, *rhtasv1alpha1.Securesign) bool func (i ctlogAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { var ( - err error - updated bool + err error + result controllerutil.OperationResult + l = labels.For(actions.ComponentName, instance.Name, instance.Name) + ctl = &rhtasv1alpha1.CTlog{ + ObjectMeta: v1.ObjectMeta{ + Name: instance.Name, + Namespace: instance.Namespace, + }, + } ) - ctlog := &rhtasv1alpha1.CTlog{} - ctlog.Name = instance.Name - ctlog.Namespace = instance.Namespace - ctlog.Labels = labels.For(actions.ComponentName, ctlog.Name, instance.Name) - ctlog.Annotations = annotations.FilterInheritable(instance.Annotations) - - ctlog.Spec = instance.Spec.Ctlog - - if err = controllerutil.SetControllerReference(instance, ctlog, i.Client.Scheme()); err != nil { - return i.Failed(err) + if result, err = kubernetes.CreateOrUpdate(ctx, i.Client, + ctl, + ensure.ControllerReference[*rhtasv1alpha1.CTlog](instance, i.Client), + ensure.Labels[*rhtasv1alpha1.CTlog](maps.Keys(l), l), + ensure.Annotations[*rhtasv1alpha1.CTlog](annotations.InheritableAnnotations, instance.Annotations), + func(object *rhtasv1alpha1.CTlog) error { + object.Spec = instance.Spec.Ctlog + return nil + }, + ); err != nil { + return i.Error(ctx, fmt.Errorf("could not create Ctlog: %w", err), instance, + v1.Condition{ + Type: CTlogCondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) } - if updated, err = i.Ensure(ctx, ctlog); err != nil { - 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 { + if result != controllerutil.OperationResultNone { meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ Type: CTlogCondition, Status: v1.ConditionFalse, Reason: constants.Creating, - Message: "CTLog resource updated " + ctlog.Name, + Message: "CTLog resource updated " + ctl.Name, }) return i.StatusUpdate(ctx, instance) } - return i.CopyStatus(ctx, client.ObjectKeyFromObject(ctlog), instance) + return i.CopyStatus(ctx, ctl, 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) - } +func (i ctlogAction) CopyStatus(ctx context.Context, ctl *rhtasv1alpha1.CTlog, instance *rhtasv1alpha1.Securesign) *action.Result { objectStatus := meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready) if objectStatus == nil { // not initialized yet, wait for update diff --git a/internal/controller/securesign/actions/ensure_fulcio.go b/internal/controller/securesign/actions/ensure_fulcio.go index 236e9dd12..3977452e8 100644 --- a/internal/controller/securesign/actions/ensure_fulcio.go +++ b/internal/controller/securesign/actions/ensure_fulcio.go @@ -2,17 +2,19 @@ package actions import ( "context" - - "github.com/securesign/operator/internal/controller/annotations" + "fmt" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/annotations" "github.com/securesign/operator/internal/controller/common/action" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" "github.com/securesign/operator/internal/controller/constants" "github.com/securesign/operator/internal/controller/fulcio/actions" "github.com/securesign/operator/internal/controller/labels" + "golang.org/x/exp/maps" "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" ) @@ -34,33 +36,37 @@ func (i fulcioAction) CanHandle(context.Context, *rhtasv1alpha1.Securesign) bool func (i fulcioAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { var ( - err error - updated bool + err error + result controllerutil.OperationResult + l = labels.For(actions.ComponentName, instance.Name, instance.Name) + fulcio = &rhtasv1alpha1.Fulcio{ + ObjectMeta: v1.ObjectMeta{ + Name: instance.Name, + Namespace: instance.Namespace, + }, + } ) - fulcio := &rhtasv1alpha1.Fulcio{} - - fulcio.Name = instance.Name - fulcio.Namespace = instance.Namespace - fulcio.Labels = labels.For(actions.ComponentName, fulcio.Name, instance.Name) - fulcio.Annotations = annotations.FilterInheritable(instance.Annotations) - - fulcio.Spec = instance.Spec.Fulcio - - if err = controllerutil.SetControllerReference(instance, fulcio, i.Client.Scheme()); err != nil { - return i.Failed(err) - } - if updated, err = i.Ensure(ctx, fulcio, action.EnsureSpec(), action.EnsureRouteSelectorLabels()); err != nil { - 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 result, err = kubernetes.CreateOrUpdate(ctx, i.Client, + fulcio, + ensure.ControllerReference[*rhtasv1alpha1.Fulcio](instance, i.Client), + ensure.Labels[*rhtasv1alpha1.Fulcio](maps.Keys(l), l), + ensure.Annotations[*rhtasv1alpha1.Fulcio](annotations.InheritableAnnotations, instance.Annotations), + func(object *rhtasv1alpha1.Fulcio) error { + object.Spec = instance.Spec.Fulcio + return nil + }, + ); err != nil { + return i.Error(ctx, fmt.Errorf("could not create Fulcio: %w", err), instance, + v1.Condition{ + Type: FulcioCondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) } - if updated { + if result != controllerutil.OperationResultNone { meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ Type: FulcioCondition, Status: v1.ConditionFalse, @@ -70,14 +76,10 @@ func (i fulcioAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secure return i.StatusUpdate(ctx, instance) } - return i.CopyStatus(ctx, client.ObjectKeyFromObject(fulcio), instance) + return i.CopyStatus(ctx, 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) - } +func (i fulcioAction) CopyStatus(ctx context.Context, object *rhtasv1alpha1.Fulcio, instance *rhtasv1alpha1.Securesign) *action.Result { objectStatus := meta.FindStatusCondition(object.Status.Conditions, constants.Ready) if objectStatus == nil { // not initialized yet, wait for update diff --git a/internal/controller/securesign/actions/ensure_rekor.go b/internal/controller/securesign/actions/ensure_rekor.go index d8b229199..72eca142d 100644 --- a/internal/controller/securesign/actions/ensure_rekor.go +++ b/internal/controller/securesign/actions/ensure_rekor.go @@ -2,8 +2,12 @@ package actions import ( "context" + "fmt" "github.com/securesign/operator/internal/controller/annotations" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" + "golang.org/x/exp/maps" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -11,7 +15,6 @@ import ( "github.com/securesign/operator/internal/controller/labels" "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" ) @@ -33,33 +36,37 @@ func (i rekorAction) CanHandle(context.Context, *rhtasv1alpha1.Securesign) bool func (i rekorAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { var ( - err error - updated bool + err error + result controllerutil.OperationResult + l = labels.For("rekor", instance.Name, instance.Name) + rekor = &rhtasv1alpha1.Rekor{ + ObjectMeta: v1.ObjectMeta{ + Name: instance.Name, + Namespace: instance.Namespace, + }, + } ) - rekor := &rhtasv1alpha1.Rekor{} - rekor.Name = instance.Name - rekor.Namespace = instance.Namespace - rekor.Labels = labels.For("rekor", rekor.Name, instance.Name) - rekor.Annotations = annotations.FilterInheritable(instance.Annotations) - - rekor.Spec = instance.Spec.Rekor - - if err = controllerutil.SetControllerReference(instance, rekor, i.Client.Scheme()); err != nil { - return i.Failed(err) + if result, err = kubernetes.CreateOrUpdate(ctx, i.Client, + rekor, + ensure.ControllerReference[*rhtasv1alpha1.Rekor](instance, i.Client), + ensure.Labels[*rhtasv1alpha1.Rekor](maps.Keys(l), l), + ensure.Annotations[*rhtasv1alpha1.Rekor](annotations.InheritableAnnotations, instance.Annotations), + func(object *rhtasv1alpha1.Rekor) error { + object.Spec = instance.Spec.Rekor + return nil + }, + ); err != nil { + return i.Error(ctx, fmt.Errorf("could not create Rekor: %w", err), instance, + v1.Condition{ + Type: RekorCondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) } - if updated, err = i.Ensure(ctx, rekor, action.EnsureSpec(), action.EnsureRouteSelectorLabels()); err != nil { - 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 { + if result != controllerutil.OperationResultNone { meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ Type: RekorCondition, Status: v1.ConditionFalse, @@ -69,14 +76,10 @@ func (i rekorAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secures return i.StatusUpdate(ctx, instance) } - return i.CopyStatus(ctx, client.ObjectKeyFromObject(rekor), instance) + return i.CopyStatus(ctx, 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) - } +func (i rekorAction) CopyStatus(ctx context.Context, object *rhtasv1alpha1.Rekor, instance *rhtasv1alpha1.Securesign) *action.Result { objectStatus := meta.FindStatusCondition(object.Status.Conditions, constants.Ready) if objectStatus == nil { // not initialized yet, wait for update diff --git a/internal/controller/securesign/actions/ensure_trillian.go b/internal/controller/securesign/actions/ensure_trillian.go index 2282348fb..ffa8c56ca 100644 --- a/internal/controller/securesign/actions/ensure_trillian.go +++ b/internal/controller/securesign/actions/ensure_trillian.go @@ -2,8 +2,12 @@ package actions import ( "context" + "fmt" "github.com/securesign/operator/internal/controller/annotations" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" + "golang.org/x/exp/maps" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -11,7 +15,6 @@ import ( "github.com/securesign/operator/internal/controller/labels" "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" ) @@ -33,33 +36,37 @@ func (i trillianAction) CanHandle(context.Context, *rhtasv1alpha1.Securesign) bo func (i trillianAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { var ( - err error - updated bool + err error + result controllerutil.OperationResult + l = labels.For("trillian", instance.Name, instance.Name) + trillian = &rhtasv1alpha1.Trillian{ + ObjectMeta: v1.ObjectMeta{ + Name: instance.Name, + Namespace: instance.Namespace, + }, + } ) - trillian := &rhtasv1alpha1.Trillian{} - trillian.Name = instance.Name - trillian.Namespace = instance.Namespace - trillian.Labels = labels.For("trillian", trillian.Name, instance.Name) - trillian.Annotations = annotations.FilterInheritable(instance.Annotations) - - trillian.Spec = instance.Spec.Trillian - - if err = controllerutil.SetControllerReference(instance, trillian, i.Client.Scheme()); err != nil { - return i.Failed(err) + if result, err = kubernetes.CreateOrUpdate(ctx, i.Client, + trillian, + ensure.ControllerReference[*rhtasv1alpha1.Trillian](instance, i.Client), + ensure.Labels[*rhtasv1alpha1.Trillian](maps.Keys(l), l), + ensure.Annotations[*rhtasv1alpha1.Trillian](annotations.InheritableAnnotations, instance.Annotations), + func(object *rhtasv1alpha1.Trillian) error { + object.Spec = instance.Spec.Trillian + return nil + }, + ); err != nil { + return i.Error(ctx, fmt.Errorf("could not create Trillian: %w", err), instance, + v1.Condition{ + Type: TrillianCondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) } - if updated, err = i.Ensure(ctx, trillian); err != nil { - 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 { + if result != controllerutil.OperationResultNone { meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ Type: TrillianCondition, Status: v1.ConditionFalse, @@ -69,14 +76,10 @@ func (i trillianAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secu return i.StatusUpdate(ctx, instance) } - return i.CopyStatus(ctx, client.ObjectKeyFromObject(trillian), instance) + return i.CopyStatus(ctx, 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) - } +func (i trillianAction) CopyStatus(ctx context.Context, object *rhtasv1alpha1.Trillian, instance *rhtasv1alpha1.Securesign) *action.Result { objectStatus := meta.FindStatusCondition(object.Status.Conditions, constants.Ready) if objectStatus == nil { // not initialized yet, wait for update diff --git a/internal/controller/securesign/actions/ensure_tsa.go b/internal/controller/securesign/actions/ensure_tsa.go index 600a6eee4..5ae71e58b 100644 --- a/internal/controller/securesign/actions/ensure_tsa.go +++ b/internal/controller/securesign/actions/ensure_tsa.go @@ -2,16 +2,19 @@ package actions import ( "context" - "reflect" + "fmt" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/annotations" "github.com/securesign/operator/internal/controller/common/action" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" "github.com/securesign/operator/internal/controller/constants" "github.com/securesign/operator/internal/controller/labels" "github.com/securesign/operator/internal/controller/tsa/actions" + "golang.org/x/exp/maps" "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" ) @@ -33,40 +36,37 @@ func (i tsaAction) CanHandle(context.Context, *rhtasv1alpha1.Securesign) bool { func (i tsaAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { var ( - err error - updated bool + err error + result controllerutil.OperationResult + l = labels.For(actions.ComponentName, instance.Name, instance.Name) + tsa = &rhtasv1alpha1.TimestampAuthority{ + ObjectMeta: v1.ObjectMeta{ + Name: instance.Name, + Namespace: instance.Namespace, + }, + } ) - tsa := &rhtasv1alpha1.TimestampAuthority{} - tsa.Name = instance.Name - tsa.Namespace = instance.Namespace - tsa.Labels = labels.For(actions.ComponentName, tsa.Name, instance.Name) - if reflect.ValueOf(instance.Spec.TimestampAuthority).IsZero() { - meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ - Type: TSACondition, - Status: v1.ConditionFalse, - Reason: constants.NotDefined, - Message: "TSA resource is undefined", - }) - return i.StatusUpdate(ctx, instance) - } - tsa.Spec = *instance.Spec.TimestampAuthority - - if err = controllerutil.SetControllerReference(instance, tsa, i.Client.Scheme()); err != nil { - return i.Failed(err) + if result, err = kubernetes.CreateOrUpdate(ctx, i.Client, + tsa, + ensure.ControllerReference[*rhtasv1alpha1.TimestampAuthority](instance, i.Client), + ensure.Labels[*rhtasv1alpha1.TimestampAuthority](maps.Keys(l), l), + ensure.Annotations[*rhtasv1alpha1.TimestampAuthority](annotations.InheritableAnnotations, instance.Annotations), + func(object *rhtasv1alpha1.TimestampAuthority) error { + object.Spec = *instance.Spec.TimestampAuthority + return nil + }, + ); err != nil { + return i.Error(ctx, fmt.Errorf("could not create TimestampAuthority: %w", err), instance, + v1.Condition{ + Type: TSACondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) } - if updated, err = i.Ensure(ctx, tsa, action.EnsureSpec(), action.EnsureRouteSelectorLabels(), action.EnsureNTPConfig()); err != nil { - meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ - Type: TSACondition, - Status: v1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), - }) - return i.FailedWithStatusUpdate(ctx, err, instance) - } - - if updated { + if result != controllerutil.OperationResultNone { meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ Type: TSACondition, Status: v1.ConditionFalse, @@ -76,14 +76,10 @@ func (i tsaAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesig return i.StatusUpdate(ctx, instance) } - return i.CopyStatus(ctx, client.ObjectKeyFromObject(tsa), instance) + return i.CopyStatus(ctx, tsa, instance) } -func (i tsaAction) CopyStatus(ctx context.Context, ok client.ObjectKey, instance *rhtasv1alpha1.Securesign) *action.Result { - object := &rhtasv1alpha1.TimestampAuthority{} - if err := i.Client.Get(ctx, ok, object); err != nil { - return i.Failed(err) - } +func (i tsaAction) CopyStatus(ctx context.Context, object *rhtasv1alpha1.TimestampAuthority, instance *rhtasv1alpha1.Securesign) *action.Result { objectStatus := meta.FindStatusCondition(object.Status.Conditions, constants.Ready) if objectStatus == nil { // not initialized yet, wait for update diff --git a/internal/controller/securesign/actions/ensure_tuf.go b/internal/controller/securesign/actions/ensure_tuf.go index 3a7b8e7b9..07bae2bc7 100644 --- a/internal/controller/securesign/actions/ensure_tuf.go +++ b/internal/controller/securesign/actions/ensure_tuf.go @@ -2,9 +2,13 @@ package actions import ( "context" + "fmt" "github.com/securesign/operator/internal/controller/annotations" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" tufConstants "github.com/securesign/operator/internal/controller/tuf/constants" + "golang.org/x/exp/maps" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -12,7 +16,6 @@ import ( "github.com/securesign/operator/internal/controller/labels" "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" ) @@ -34,33 +37,37 @@ func (i tufAction) CanHandle(context.Context, *rhtasv1alpha1.Securesign) bool { func (i tufAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { var ( - err error - updated bool + err error + result controllerutil.OperationResult + l = labels.For(tufConstants.ComponentName, instance.Name, instance.Name) + tuf = &rhtasv1alpha1.Tuf{ + ObjectMeta: v1.ObjectMeta{ + Name: instance.Name, + Namespace: instance.Namespace, + }, + } ) - tuf := &rhtasv1alpha1.Tuf{} - tuf.Name = instance.Name - tuf.Namespace = instance.Namespace - tuf.Labels = labels.For(tufConstants.ComponentName, tuf.Name, instance.Name) - tuf.Annotations = annotations.FilterInheritable(instance.Annotations) - - tuf.Spec = instance.Spec.Tuf - - if err = controllerutil.SetControllerReference(instance, tuf, i.Client.Scheme()); err != nil { - return i.Failed(err) + if result, err = kubernetes.CreateOrUpdate(ctx, i.Client, + tuf, + ensure.ControllerReference[*rhtasv1alpha1.Tuf](instance, i.Client), + ensure.Labels[*rhtasv1alpha1.Tuf](maps.Keys(l), l), + ensure.Annotations[*rhtasv1alpha1.Tuf](annotations.InheritableAnnotations, instance.Annotations), + func(object *rhtasv1alpha1.Tuf) error { + object.Spec = instance.Spec.Tuf + return nil + }, + ); err != nil { + return i.Error(ctx, fmt.Errorf("could not create Tuf: %w", err), instance, + v1.Condition{ + Type: TufCondition, + Status: v1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) } - if updated, err = i.Ensure(ctx, tuf, action.EnsureSpec(), action.EnsureRouteSelectorLabels()); err != nil { - 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 { + if result != controllerutil.OperationResultNone { meta.SetStatusCondition(&instance.Status.Conditions, v1.Condition{ Type: TufCondition, Status: v1.ConditionFalse, @@ -70,14 +77,10 @@ func (i tufAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesig return i.StatusUpdate(ctx, instance) } - return i.CopyStatus(ctx, client.ObjectKeyFromObject(tuf), instance) + return i.CopyStatus(ctx, 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) - } +func (i tufAction) CopyStatus(ctx context.Context, object *rhtasv1alpha1.Tuf, instance *rhtasv1alpha1.Securesign) *action.Result { objectStatus := meta.FindStatusCondition(object.Status.Conditions, constants.Ready) if objectStatus == nil { // not initialized yet, wait for update diff --git a/internal/controller/securesign/actions/segment_backup_cronjob.go b/internal/controller/securesign/actions/segment_backup_cronjob.go index 2157dadf7..461ff0da3 100644 --- a/internal/controller/securesign/actions/segment_backup_cronjob.go +++ b/internal/controller/securesign/actions/segment_backup_cronjob.go @@ -4,24 +4,23 @@ import ( "fmt" "strconv" - "github.com/securesign/operator/internal/controller/annotations" - "github.com/robfig/cron/v3" + "github.com/securesign/operator/internal/controller/annotations" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" "github.com/securesign/operator/internal/controller/constants" "github.com/securesign/operator/internal/controller/labels" + "golang.org/x/exp/maps" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + "context" 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" - - "context" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" - - "github.com/operator-framework/operator-lib/proxy" ) func NewSegmentBackupCronJobAction() action.Action[*rhtasv1alpha1.Securesign] { @@ -52,12 +51,12 @@ func (i segmentBackupCronJob) CanHandle(_ context.Context, instance *rhtasv1alph func (i segmentBackupCronJob) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { var ( - err error - updated bool + err error + result controllerutil.OperationResult ) if _, err := cron.ParseStandard(AnalyiticsCronSchedule); err != nil { - return i.Failed(fmt.Errorf("could not create segment backuup cron job due to errors with parsing the cron schedule: %w", err)) + return i.Error(ctx, fmt.Errorf("could not create segment backuup cron job due to errors with parsing the cron schedule: %w", err), instance) } labels := labels.For(SegmentBackupCronJobName, SegmentBackupCronJobName, instance.Name) @@ -68,76 +67,64 @@ func (i segmentBackupCronJob) Handle(ctx context.Context, instance *rhtasv1alpha Namespace: instance.Namespace, Labels: labels, }, - Spec: batchv1.CronJobSpec{ - Schedule: AnalyiticsCronSchedule, - JobTemplate: batchv1.JobTemplateSpec{ - Spec: batchv1.JobSpec{ - Template: corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - ServiceAccountName: SegmentRBACName, - RestartPolicy: "OnFailure", - Containers: []corev1.Container{ - { - Name: SegmentBackupCronJobName, - Image: constants.SegmentBackupImage, - Command: []string{"python3", "/opt/app-root/src/src/script.py"}, - Env: []corev1.EnvVar{ - { - Name: "RUN_TYPE", - Value: "nightly", - }, - { - Name: "REQUESTS_CA_BUNDLE", - Value: "/etc/pki/tls/certs/ca-bundle.crt", // Certificate used to verify requests externally i.e communication with segment - }, - { - Name: "REQUESTS_CA_BUNDLE_INTERNAL", - Value: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt", // Certificate used to verify requests internally i.e queries to thanos - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - // Adding proxy variables to operand - for i, container := range segmentBackupCronJob.Spec.JobTemplate.Spec.Template.Spec.Containers { - segmentBackupCronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[i].Env = append(container.Env, proxy.ReadProxyVarsFromEnv()...) - } - - if err = controllerutil.SetControllerReference(instance, segmentBackupCronJob, i.Client.Scheme()); err != nil { - return i.Failed(fmt.Errorf("could not set controller reference for segment backup cron job: %w", err)) } - if updated, err = i.Ensure(ctx, segmentBackupCronJob); err != nil { - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: MetricsCondition, - Status: metav1.ConditionFalse, - Reason: constants.Failure, - Message: err.Error(), - }) - return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create segment backup cron job: %w", err), instance) + if result, err = kubernetes.CreateOrUpdate(ctx, i.Client, + segmentBackupCronJob, + i.ensureSegmentBackupCronJob(), + ensure.ControllerReference[*batchv1.CronJob](instance, i.Client), + ensure.Labels[*batchv1.CronJob](maps.Keys(labels), labels), + func(object *batchv1.CronJob) error { + ensure.SetProxyEnvs(object.Spec.JobTemplate.Spec.Template.Spec.Containers) + return nil + }, + ); err != nil { + return i.Error(ctx, fmt.Errorf("could not create segment backup cron job: %w", err), instance, + metav1.Condition{ + Type: MetricsCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) } - if updated { + if result != controllerutil.OperationResultNone { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ Type: MetricsCondition, - Status: metav1.ConditionFalse, - Reason: constants.Creating, - Message: "Segment backup Cron Job creating", + Status: metav1.ConditionTrue, + Reason: constants.Ready, + Message: "Segment backup Cron Job created", }) return i.StatusUpdate(ctx, instance) } - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: MetricsCondition, - Status: metav1.ConditionTrue, - Reason: constants.Ready, - Message: "Segment backup Cron Job created", - }) return i.Continue() } + +func (i segmentBackupCronJob) ensureSegmentBackupCronJob() func(job *batchv1.CronJob) error { + return func(job *batchv1.CronJob) error { + { + spec := &job.Spec + spec.Schedule = AnalyiticsCronSchedule + + templateSpec := &spec.JobTemplate.Spec.Template.Spec + templateSpec.ServiceAccountName = SegmentRBACName + templateSpec.RestartPolicy = "OnFailure" + + container := kubernetes.FindContainerByNameOrCreate(templateSpec, SegmentBackupCronJobName) + container.Image = constants.SegmentBackupImage + container.Command = []string{"python3", "/opt/app-root/src/src/script.py"} + + runTypeEnv := kubernetes.FindEnvByNameOrCreate(container, "RUN_TYPE") + runTypeEnv.Value = "nightly" + + caBundleEnv := kubernetes.FindEnvByNameOrCreate(container, "REQUESTS_CA_BUNDLE") + caBundleEnv.Value = "/etc/pki/tls/certs/ca-bundle.crt" // Certificate used to verify requests externally i.e communication with segment + + internalCaBundleEnv := kubernetes.FindEnvByNameOrCreate(container, "REQUESTS_CA_BUNDLE_INTERNAL") + internalCaBundleEnv.Value = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" // Certificate used to verify requests internally i.e queries to thanos + + } + return nil + } +} diff --git a/internal/controller/securesign/actions/segment_backup_job.go b/internal/controller/securesign/actions/segment_backup_job.go index ce8eec778..790ca983d 100644 --- a/internal/controller/securesign/actions/segment_backup_job.go +++ b/internal/controller/securesign/actions/segment_backup_job.go @@ -4,24 +4,21 @@ import ( "fmt" "strconv" - "github.com/securesign/operator/internal/controller/common/utils/kubernetes/job" - "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/securesign/operator/internal/controller/annotations" + "github.com/securesign/operator/internal/controller/common/utils" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" "github.com/securesign/operator/internal/controller/labels" + "golang.org/x/exp/maps" + batchv1 "k8s.io/api/batch/v1" "context" + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" "github.com/securesign/operator/internal/controller/constants" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ctrl "sigs.k8s.io/controller-runtime" - - rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" - - "github.com/operator-framework/operator-lib/proxy" ) func NewSegmentBackupJobAction() action.Action[*rhtasv1alpha1.Securesign] { @@ -55,54 +52,62 @@ func (i segmentBackupJob) CanHandle(_ context.Context, instance *rhtasv1alpha1.S func (i segmentBackupJob) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesign) *action.Result { var ( err error + job = &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: SegmentBackupJobName + "-", + Namespace: instance.Namespace, + }, + } ) - labels := labels.For(SegmentBackupJobName, SegmentBackupJobName, instance.Name) - parallelism := int32(1) - completions := int32(1) - activeDeadlineSeconds := int64(600) - backoffLimit := int32(5) - command := []string{"python3", "/opt/app-root/src/src/script.py"} - env := []corev1.EnvVar{ - { - Name: "RUN_TYPE", - Value: "installation", - }, - { - Name: "REQUESTS_CA_BUNDLE", - Value: "/etc/pki/tls/certs/ca-bundle.crt", // Certificate used to verify requests externally i.e communication with segment - }, - { - Name: "REQUESTS_CA_BUNDLE_INTERNAL", - Value: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt", // Certificate used to verify requests internally i.e queries to thanos + l := labels.For(SegmentBackupJobName, SegmentBackupJobName, instance.Name) + if _, err = kubernetes.CreateOrUpdate(ctx, i.Client, + job, + i.ensureSegmentBackupJob(), + ensure.ControllerReference[*batchv1.Job](instance, i.Client), + ensure.Labels[*batchv1.Job](maps.Keys(l), l), + func(object *batchv1.Job) error { + ensure.SetProxyEnvs(object.Spec.Template.Spec.Containers) + return nil }, + ); err != nil { + return i.Error(ctx, fmt.Errorf("could not create segment backup job: %w", err), instance, + metav1.Condition{ + Type: MetricsCondition, + Status: metav1.ConditionFalse, + Reason: constants.Creating, + Message: err.Error(), + }) } - // Adding proxy variables to operand - env = append(env, proxy.ReadProxyVarsFromEnv()...) + return i.Continue() +} - // Logic to delete old SBJ to avoid SECURESIGN-1207, can be removed after next release - if sbj, err := job.GetJob(ctx, i.Client, instance.Namespace, SegmentBackupJobName); sbj != nil { - if err = i.Client.Delete(ctx, sbj); err != nil { - i.Logger.Error(err, "problem with removing SBJ resources", "namespace", instance.Namespace, "name", SegmentBackupJobName) - } - } else if client.IgnoreNotFound(err) != nil { - i.Logger.Error(err, "unable to retrieve SBJ resource", "namespace", instance.Namespace, "name", SegmentBackupJobName) - } +func (i segmentBackupJob) ensureSegmentBackupJob() func(*batchv1.Job) error { + return func(job *batchv1.Job) error { - job := job.CreateJob(instance.Namespace, SegmentBackupJobName, labels, constants.SegmentBackupImage, SegmentRBACName, parallelism, completions, activeDeadlineSeconds, backoffLimit, command, env) - if err = ctrl.SetControllerReference(instance, job, i.Client.Scheme()); err != nil { - return i.Failed(fmt.Errorf("could not set controller reference for Job: %w", err)) - } - _, err = i.Ensure(ctx, job) - if err != nil { - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: MetricsCondition, - Status: metav1.ConditionFalse, - Reason: constants.Creating, - Message: err.Error(), - }) - return i.StatusUpdate(ctx, instance) + spec := &job.Spec + spec.Parallelism = utils.Pointer[int32](1) + spec.Completions = utils.Pointer[int32](1) + spec.ActiveDeadlineSeconds = utils.Pointer[int64](600) + spec.BackoffLimit = utils.Pointer[int32](5) + + templateSpec := &spec.Template.Spec + templateSpec.ServiceAccountName = SegmentRBACName + templateSpec.RestartPolicy = "OnFailure" + + container := kubernetes.FindContainerByNameOrCreate(templateSpec, SegmentBackupJobName) + container.Image = constants.SegmentBackupImage + container.Command = []string{"python3", "/opt/app-root/src/src/script.py"} + + runTypeEnv := kubernetes.FindEnvByNameOrCreate(container, "RUN_TYPE") + runTypeEnv.Value = "installation" + + caBundleEnv := kubernetes.FindEnvByNameOrCreate(container, "REQUESTS_CA_BUNDLE") + caBundleEnv.Value = "/etc/pki/tls/certs/ca-bundle.crt" // Certificate used to verify requests externally i.e communication with segment + + internalCaBundleEnv := kubernetes.FindEnvByNameOrCreate(container, "REQUESTS_CA_BUNDLE_INTERNAL") + internalCaBundleEnv.Value = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" // Certificate used to verify requests internally i.e queries to thanos + return nil } - return i.Continue() } diff --git a/internal/controller/trillian/actions/db/handle_secret.go b/internal/controller/trillian/actions/db/handle_secret.go index 5d1b81160..7e3d1cc62 100644 --- a/internal/controller/trillian/actions/db/handle_secret.go +++ b/internal/controller/trillian/actions/db/handle_secret.go @@ -6,23 +6,22 @@ import ( "fmt" "strconv" + "github.com/securesign/operator/internal/controller/common/utils" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" + "golang.org/x/exp/maps" "k8s.io/apimachinery/pkg/api/equality" apierros "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/labels" - "github.com/securesign/operator/internal/controller/common/utils" - + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common" "github.com/securesign/operator/internal/controller/common/action" "github.com/securesign/operator/internal/controller/constants" labels2 "github.com/securesign/operator/internal/controller/labels" trillian "github.com/securesign/operator/internal/controller/trillian/actions" - "k8s.io/apimachinery/pkg/api/meta" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -40,6 +39,8 @@ const ( annotationHost = labels2.LabelNamespace + "/" + trillian.SecretHost ) +var managedAnnotations = []string{annotationDatabase, annotationUser, annotationPort, annotationHost} + var ErrMissingDBConfiguration = errors.New("expecting external DB configuration") func NewHandleSecretAction() action.Action[*rhtasv1alpha1.Trillian] { @@ -143,26 +144,25 @@ func (i handleSecretAction) Handle(ctx context.Context, instance *rhtasv1alpha1. return i.StatusUpdate(ctx, instance) } - dbSecret := i.createDbSecret(instance.Namespace, dbLabels) - if err = controllerutil.SetControllerReference(instance, dbSecret, i.Client.Scheme()); err != nil { - return i.Failed(fmt.Errorf("could not set controller reference for secret: %w", err)) + dbSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: dbConnectionSecretName, + Namespace: instance.Namespace, + }, } - - // no watch on secret - continue if no error - if _, err = i.Ensure(ctx, dbSecret); err != nil { - meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ - Type: trillian.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) + if _, err = kubernetes.CreateOrUpdate(ctx, i.Client, + dbSecret, + ensure.Labels[*corev1.Secret](maps.Keys(dbLabels), dbLabels), + ensure.Annotations[*corev1.Secret](managedAnnotations, i.secretAnnotations()), + kubernetes.EnsureSecretData(true, i.defaultDBData()), + ); err != nil { + return i.Error(ctx, fmt.Errorf("can't generate certificate secret: %w", err), instance, + metav1.Condition{ + Type: trillian.DbCondition, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) } instance.Status.Db.DatabaseSecretRef = &rhtasv1alpha1.LocalObjectReference{ @@ -170,28 +170,19 @@ func (i handleSecretAction) Handle(ctx context.Context, instance *rhtasv1alpha1. } return i.StatusUpdate(ctx, instance) } -func (i handleSecretAction) createDbSecret(namespace string, labels map[string]string) *corev1.Secret { +func (i handleSecretAction) defaultDBData() map[string][]byte { // Define a new Secret object var rootPass []byte var mysqlPass []byte rootPass = common.GeneratePassword(12) mysqlPass = common.GeneratePassword(12) - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: dbConnectionSecretName, - Namespace: namespace, - Labels: labels, - Annotations: i.secretAnnotations(), - }, - Type: "Opaque", - Data: map[string][]byte{ - trillian.SecretRootPassword: rootPass, - trillian.SecretPassword: mysqlPass, - trillian.SecretDatabaseName: []byte(databaseName), - trillian.SecretUser: []byte(user), - trillian.SecretPort: []byte(strconv.Itoa(port)), - trillian.SecretHost: []byte(host), - }, + return map[string][]byte{ + trillian.SecretRootPassword: rootPass, + trillian.SecretPassword: mysqlPass, + trillian.SecretDatabaseName: []byte(databaseName), + trillian.SecretUser: []byte(user), + trillian.SecretPort: []byte(strconv.Itoa(port)), + trillian.SecretHost: []byte(host), } } diff --git a/internal/controller/trillian/actions/logserver/deployment.go b/internal/controller/trillian/actions/logserver/deployment.go index 37f74549c..d9e129f95 100644 --- a/internal/controller/trillian/actions/logserver/deployment.go +++ b/internal/controller/trillian/actions/logserver/deployment.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/securesign/operator/internal/controller/common/action" - "github.com/securesign/operator/internal/controller/common/utils" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" "github.com/securesign/operator/internal/controller/constants" @@ -48,7 +47,7 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trilli insCopy := instance.DeepCopy() if insCopy.Spec.TrustedCA == nil { - insCopy.Spec.TrustedCA = utils.TrustedCAAnnotationToReference(instance.Annotations) + insCopy.Spec.TrustedCA = ensure.TrustedCAAnnotationToReference(instance.Annotations) } if result, err = kubernetes.CreateOrUpdate(ctx, i.Client, diff --git a/internal/controller/trillian/actions/logsigner/deployment.go b/internal/controller/trillian/actions/logsigner/deployment.go index fe173e046..5eb1f61fc 100644 --- a/internal/controller/trillian/actions/logsigner/deployment.go +++ b/internal/controller/trillian/actions/logsigner/deployment.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/securesign/operator/internal/controller/common/action" - "github.com/securesign/operator/internal/controller/common/utils" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure" "github.com/securesign/operator/internal/controller/constants" @@ -46,7 +45,7 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trilli labels := labels.For(actions.LogSignerComponentName, actions.LogsignerDeploymentName, instance.Name) - caTrustRef := utils.TrustedCAAnnotationToReference(instance.Annotations) + caTrustRef := ensure.TrustedCAAnnotationToReference(instance.Annotations) // override if spec.trustedCA is defined if instance.Spec.TrustedCA != nil { caTrustRef = instance.Spec.TrustedCA diff --git a/internal/controller/tsa/actions/deployment.go b/internal/controller/tsa/actions/deployment.go index c36018a58..120516721 100644 --- a/internal/controller/tsa/actions/deployment.go +++ b/internal/controller/tsa/actions/deployment.go @@ -68,7 +68,7 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Timest labels := labels.For(ComponentName, DeploymentName, instance.Name) - caRef := cutils.TrustedCAAnnotationToReference(instance.Annotations) + caRef := ensure.TrustedCAAnnotationToReference(instance.Annotations) // override if spec.trustedCA is defined if instance.Spec.TrustedCA != nil { caRef = instance.Spec.TrustedCA diff --git a/internal/controller/tuf/actions/tuf_init_job.go b/internal/controller/tuf/actions/tuf_init_job.go index 54120eca6..4964d5447 100644 --- a/internal/controller/tuf/actions/tuf_init_job.go +++ b/internal/controller/tuf/actions/tuf_init_job.go @@ -92,9 +92,13 @@ func (i initJobAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Tuf) Namespace: instance.Namespace, }, }, - utils.CreateTufInitJob(instance, tufConstants.RBACName, l), + utils.EnsureTufInitJob(instance, tufConstants.RBACName, l), ensure.ControllerReference[*v2.Job](pvc, i.Client), ensure.Labels[*v2.Job](maps.Keys(l), l), + func(object *v2.Job) error { + ensure.SetProxyEnvs(object.Spec.Template.Spec.Containers) + return nil + }, ); err != nil { return i.Error(ctx, fmt.Errorf("could not create TUF init job: %w", err), instance) } diff --git a/internal/controller/tuf/utils/tuf_init_job.go b/internal/controller/tuf/utils/tuf_init_job.go index 37c38faf9..db00900b7 100644 --- a/internal/controller/tuf/utils/tuf_init_job.go +++ b/internal/controller/tuf/utils/tuf_init_job.go @@ -3,7 +3,6 @@ package utils import ( "path/filepath" - "github.com/operator-framework/operator-lib/proxy" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/internal/controller/constants" @@ -18,18 +17,9 @@ const ( targetMonthPath = "/var/run/target" ) -func CreateTufInitJob(instance *rhtasv1alpha1.Tuf, sa string, labels map[string]string) func(*batchv1.Job) error { +func EnsureTufInitJob(instance *rhtasv1alpha1.Tuf, sa string, labels map[string]string) func(*batchv1.Job) error { return func(job *batchv1.Job) error { - // prepare env vars - env := []v1.EnvVar{ - { - Name: "NAMESPACE", - Value: instance.Namespace, - }, - } - env = append(env, proxy.ReadProxyVarsFromEnv()...) - // prepare args args := []string{"--export-keys", instance.Spec.RootKeySecretRef.Name} for _, key := range instance.Spec.Keys { @@ -71,7 +61,8 @@ func CreateTufInitJob(instance *rhtasv1alpha1.Tuf, sa string, labels map[string] // init containers container := kubernetes.FindContainerByNameOrCreate(templateSpec, "tuf-init") container.Image = constants.TufImage - container.Env = env + env := kubernetes.FindEnvByNameOrCreate(container, "NAMESPACE") + env.Value = instance.Namespace container.Args = args container.VolumeMounts = []v1.VolumeMount{ { diff --git a/test/e2e/support/tas/tuf/tuf.go b/test/e2e/support/tas/tuf/tuf.go index 3cdab4a91..f04b07c37 100644 --- a/test/e2e/support/tas/tuf/tuf.go +++ b/test/e2e/support/tas/tuf/tuf.go @@ -102,7 +102,7 @@ func refreshTufJob(instance *v1alpha1.Tuf) *v12.Job { } l := maps.Clone(instance.Labels) l[labels.LabelAppComponent] = "test" - Expect(utils2.CreateTufInitJob(instance, constants.RBACName, instance.Labels)(j)).To(Succeed()) + Expect(utils2.EnsureTufInitJob(instance, constants.RBACName, instance.Labels)(j)).To(Succeed()) c := kubernetes.FindContainerByNameOrCreate(&j.Spec.Template.Spec, "tuf-init") c.Command = []string{"/bin/sh", "-c"} args := c.Args