diff --git a/go.mod b/go.mod index 0c0cf60d..a1e9b611 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/open-policy-agent/gatekeeper/v3 v3.17.1 github.com/orcaman/concurrent-map/v2 v2.0.1 github.com/pkg/errors v0.9.1 - github.com/pluralsh/console/go/client v1.21.2 + github.com/pluralsh/console/go/client v1.21.4 github.com/pluralsh/controller-reconcile-helper v0.1.0 github.com/pluralsh/gophoenix v0.1.3-0.20231201014135-dff1b4309e34 github.com/pluralsh/polly v0.1.10 diff --git a/go.sum b/go.sum index e9da5f9f..fd6709fc 100644 --- a/go.sum +++ b/go.sum @@ -666,8 +666,8 @@ github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rK github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pluralsh/console/go/client v1.21.2 h1:bYbgporkQx8yDrKARF2tsKh4o15fwtT4yDLTkyPmi9M= -github.com/pluralsh/console/go/client v1.21.2/go.mod h1:lpoWASYsM9keNePS3dpFiEisUHEfObIVlSL3tzpKn8k= +github.com/pluralsh/console/go/client v1.21.4 h1:LT0u/2b8HYTZtFUc/UBXqj08dD9y3R2FWU+zDHJxwMA= +github.com/pluralsh/console/go/client v1.21.4/go.mod h1:lpoWASYsM9keNePS3dpFiEisUHEfObIVlSL3tzpKn8k= github.com/pluralsh/controller-reconcile-helper v0.1.0 h1:BV3dYZFH5rn8ZvZjtpkACSv/GmLEtRftNQj/Y4ddHEo= github.com/pluralsh/controller-reconcile-helper v0.1.0/go.mod h1:RxAbvSB4/jkvx616krCdNQXPbpGJXW3J1L3rASxeFOA= github.com/pluralsh/gophoenix v0.1.3-0.20231201014135-dff1b4309e34 h1:ab2PN+6if/Aq3/sJM0AVdy1SYuMAnq4g20VaKhTm/Bw= diff --git a/pkg/common/health.go b/pkg/common/health.go index fb67a874..8a303aa2 100644 --- a/pkg/common/health.go +++ b/pkg/common/health.go @@ -846,13 +846,16 @@ func GetHealthCheckFuncByGroupVersionKind(gvk schema.GroupVersionKind) func(obj } func GetOtherHealthStatus(obj *unstructured.Unstructured) (*HealthStatus, error) { + defaultReadyStatus := &HealthStatus{ + Status: HealthStatusHealthy, + } sts := Status{} status, ok := obj.Object["status"] if ok { s, ok := status.(map[string]interface{}) if ok { if err := runtime.DefaultUnstructuredConverter.FromUnstructured(s, &sts); err != nil { - return nil, nil + return defaultReadyStatus, nil } if meta.FindStatusCondition(sts.Conditions, readyCondition) != nil { status := HealthStatusProgressing @@ -866,5 +869,5 @@ func GetOtherHealthStatus(obj *unstructured.Unstructured) (*HealthStatus, error) } } - return nil, nil + return defaultReadyStatus, nil } diff --git a/pkg/common/health_test.go b/pkg/common/health_test.go new file mode 100644 index 00000000..42de12b4 --- /dev/null +++ b/pkg/common/health_test.go @@ -0,0 +1,87 @@ +package common_test + +import ( + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + deploymentsv1alpha1 "github.com/pluralsh/deployment-operator/api/v1alpha1" + "github.com/pluralsh/deployment-operator/pkg/common" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +var _ = Describe("Health Test", Ordered, func() { + Context("Test health functions", func() { + customResource := &deploymentsv1alpha1.MetricsAggregate{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + } + + It("should get default status from CRD without condition block", func() { + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(customResource) + Expect(err).NotTo(HaveOccurred()) + status, err := common.GetResourceHealth(&unstructured.Unstructured{Object: obj}) + Expect(err).NotTo(HaveOccurred()) + Expect(status).To(Not(BeNil())) + Expect(*status).To(Equal(common.HealthStatus{ + Status: common.HealthStatusHealthy, + })) + }) + It("should get healthy status from CRD with condition block", func() { + customResource.Status = deploymentsv1alpha1.MetricsAggregateStatus{ + Conditions: []metav1.Condition{ + { + Type: "Ready", + Status: "True", + }, + }, + } + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(customResource) + Expect(err).NotTo(HaveOccurred()) + status, err := common.GetResourceHealth(&unstructured.Unstructured{Object: obj}) + Expect(err).NotTo(HaveOccurred()) + Expect(status).To(Not(BeNil())) + Expect(*status).To(Equal(common.HealthStatus{ + Status: common.HealthStatusHealthy, + })) + }) + + It("should get healthy status from CRD with condition block", func() { + customResource.Status = deploymentsv1alpha1.MetricsAggregateStatus{ + Conditions: []metav1.Condition{ + { + Type: "Ready", + Status: "False", + }, + }, + } + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(customResource) + Expect(err).NotTo(HaveOccurred()) + status, err := common.GetResourceHealth(&unstructured.Unstructured{Object: obj}) + Expect(err).NotTo(HaveOccurred()) + Expect(status).To(Not(BeNil())) + Expect(*status).To(Equal(common.HealthStatus{ + Status: common.HealthStatusProgressing, + })) + }) + + It("should get HealthStatusProgressing status during deletion", func() { + customResource.DeletionTimestamp = &metav1.Time{ + Time: time.Now(), + } + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(customResource) + Expect(err).NotTo(HaveOccurred()) + status, err := common.GetResourceHealth(&unstructured.Unstructured{Object: obj}) + Expect(err).NotTo(HaveOccurred()) + Expect(status).To(Not(BeNil())) + Expect(*status).To(Equal(common.HealthStatus{ + Status: common.HealthStatusProgressing, + Message: "Pending deletion", + })) + }) + + }) +}) diff --git a/pkg/common/suite_test.go b/pkg/common/suite_test.go new file mode 100644 index 00000000..d1afa4c2 --- /dev/null +++ b/pkg/common/suite_test.go @@ -0,0 +1,66 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package common_test + +import ( + "fmt" + "path/filepath" + "runtime" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. +var testEnv *envtest.Environment +var kClient client.Client + +func TestCommon(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Common Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", + fmt.Sprintf("1.28.3-%s-%s", runtime.GOOS, runtime.GOARCH)), + } + + cfg, err := testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + kClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(kClient).NotTo(BeNil()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/pkg/manifests/template/helm.go b/pkg/manifests/template/helm.go index 3efcbcff..ae25dcb0 100644 --- a/pkg/manifests/template/helm.go +++ b/pkg/manifests/template/helm.go @@ -110,6 +110,18 @@ func (h *helm) Render(svc *console.GetServiceDeploymentForAgent_ServiceDeploymen if err != nil { return nil, err } + if svc.Helm != nil && svc.Helm.IgnoreHooks != nil && !*svc.Helm.IgnoreHooks { + for _, h := range rel.Hooks { + _, err = fmt.Fprintln(&buffer, "---") + if err != nil { + return nil, err + } + _, err = fmt.Fprintln(&buffer, strings.TrimSpace(h.Manifest)) + if err != nil { + return nil, err + } + } + } r := bytes.NewReader(buffer.Bytes()) mapper, err := utilFactory.ToRESTMapper() diff --git a/pkg/manifests/template/helm_test.go b/pkg/manifests/template/helm_test.go index 103ad3a6..28f801cf 100644 --- a/pkg/manifests/template/helm_test.go +++ b/pkg/manifests/template/helm_test.go @@ -11,6 +11,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" console "github.com/pluralsh/console/go/client" + "github.com/samber/lo" ) var _ = Describe("Helm template", func() { @@ -65,6 +66,20 @@ var _ = Describe("Helm template", func() { resp, err := NewHelm(dir).Render(svc, utilFactory) Expect(err).NotTo(HaveOccurred()) Expect(len(resp)).To(Equal(1)) + + // Ignore hooks + svc.Helm = &console.GetServiceDeploymentForAgent_ServiceDeployment_Helm{ + IgnoreHooks: lo.ToPtr(true), + } + resp, err = NewHelm(dir).Render(svc, utilFactory) + Expect(err).NotTo(HaveOccurred()) + Expect(len(resp)).To(Equal(1)) + + // Reconcile hooks + svc.Helm.IgnoreHooks = lo.ToPtr(false) + resp, err = NewHelm(dir).Render(svc, utilFactory) + Expect(err).NotTo(HaveOccurred()) + Expect(len(resp)).To(Equal(2)) }) }) diff --git a/test/helm/yet-another-cloudwatch-exporter/templates/job.yaml b/test/helm/yet-another-cloudwatch-exporter/templates/job.yaml new file mode 100644 index 00000000..b6dbb1e3 --- /dev/null +++ b/test/helm/yet-another-cloudwatch-exporter/templates/job.yaml @@ -0,0 +1,16 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: pi + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-delete-policy": hook-succeeded, before-hook-creation +spec: + template: + spec: + containers: + - name: pi + image: perl:5.34.0 + command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + restartPolicy: Never + backoffLimit: 4 \ No newline at end of file