Skip to content

Commit

Permalink
feat: reconcile helm resources with hooks + set a healthy state as a …
Browse files Browse the repository at this point in the history
…default for CRD without a condition block (#301)

* reconcile helm resources with hooks

* fix linter

* fetch default status for custom resources
  • Loading branch information
zreigz authored Oct 17, 2024
1 parent d70cf61 commit a3e941a
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 5 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
7 changes: 5 additions & 2 deletions pkg/common/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -866,5 +869,5 @@ func GetOtherHealthStatus(obj *unstructured.Unstructured) (*HealthStatus, error)
}
}

return nil, nil
return defaultReadyStatus, nil
}
87 changes: 87 additions & 0 deletions pkg/common/health_test.go
Original file line number Diff line number Diff line change
@@ -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",
}))
})

})
})
66 changes: 66 additions & 0 deletions pkg/common/suite_test.go
Original file line number Diff line number Diff line change
@@ -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())
})
12 changes: 12 additions & 0 deletions pkg/manifests/template/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
15 changes: 15 additions & 0 deletions pkg/manifests/template/helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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))
})

})
Expand Down
16 changes: 16 additions & 0 deletions test/helm/yet-another-cloudwatch-exporter/templates/job.yaml
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit a3e941a

Please sign in to comment.