From cfa7f993a343b191c08691f3666d6f5cd4e239d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciek=20Go=C5=82aszewski?= Date: Fri, 20 Sep 2024 13:27:29 +0200 Subject: [PATCH] Add unit tests for coredns (#684) * Add unit tests for coredns KU-1515 --- src/k8s/pkg/k8sd/features/coredns/chart.go | 6 +- src/k8s/pkg/k8sd/features/coredns/coredns.go | 18 +- .../pkg/k8sd/features/coredns/coredns_test.go | 198 ++++++++++++++++++ src/k8s/pkg/k8sd/features/coredns/register.go | 2 +- 4 files changed, 211 insertions(+), 13 deletions(-) create mode 100644 src/k8s/pkg/k8sd/features/coredns/coredns_test.go diff --git a/src/k8s/pkg/k8sd/features/coredns/chart.go b/src/k8s/pkg/k8sd/features/coredns/chart.go index 72cf75bfb..08d37d60e 100644 --- a/src/k8s/pkg/k8sd/features/coredns/chart.go +++ b/src/k8s/pkg/k8sd/features/coredns/chart.go @@ -8,7 +8,7 @@ import ( var ( // chartCoreDNS represents manifests to deploy CoreDNS. - chart = helm.InstallableChart{ + Chart = helm.InstallableChart{ Name: "ck-dns", Namespace: "kube-system", ManifestPath: filepath.Join("charts", "coredns-1.29.0.tgz"), @@ -17,6 +17,6 @@ var ( // imageRepo is the image to use for CoreDNS. imageRepo = "ghcr.io/canonical/coredns" - // imageTag is the tag to use for the CoreDNS image. - imageTag = "1.11.1-ck4" + // ImageTag is the tag to use for the CoreDNS image. + ImageTag = "1.11.1-ck4" ) diff --git a/src/k8s/pkg/k8sd/features/coredns/coredns.go b/src/k8s/pkg/k8sd/features/coredns/coredns.go index 28402e707..79e66d6e6 100644 --- a/src/k8s/pkg/k8sd/features/coredns/coredns.go +++ b/src/k8s/pkg/k8sd/features/coredns/coredns.go @@ -29,17 +29,17 @@ func ApplyDNS(ctx context.Context, snap snap.Snap, dns types.DNS, kubelet types. m := snap.HelmClient() if !dns.GetEnabled() { - if _, err := m.Apply(ctx, chart, helm.StateDeleted, nil); err != nil { + if _, err := m.Apply(ctx, Chart, helm.StateDeleted, nil); err != nil { err = fmt.Errorf("failed to uninstall coredns: %w", err) return types.FeatureStatus{ Enabled: false, - Version: imageTag, + Version: ImageTag, Message: fmt.Sprintf(deleteFailedMsgTmpl, err), }, "", err } return types.FeatureStatus{ Enabled: false, - Version: imageTag, + Version: ImageTag, Message: disabledMsg, }, "", nil } @@ -47,7 +47,7 @@ func ApplyDNS(ctx context.Context, snap snap.Snap, dns types.DNS, kubelet types. values := map[string]any{ "image": map[string]any{ "repository": imageRepo, - "tag": imageTag, + "tag": ImageTag, }, "service": map[string]any{ "name": "coredns", @@ -82,11 +82,11 @@ func ApplyDNS(ctx context.Context, snap snap.Snap, dns types.DNS, kubelet types. }, } - if _, err := m.Apply(ctx, chart, helm.StatePresent, values); err != nil { + if _, err := m.Apply(ctx, Chart, helm.StatePresent, values); err != nil { err = fmt.Errorf("failed to apply coredns: %w", err) return types.FeatureStatus{ Enabled: false, - Version: imageTag, + Version: ImageTag, Message: fmt.Sprintf(deployFailedMsgTmpl, err), }, "", err } @@ -96,7 +96,7 @@ func ApplyDNS(ctx context.Context, snap snap.Snap, dns types.DNS, kubelet types. err = fmt.Errorf("failed to create kubernetes client: %w", err) return types.FeatureStatus{ Enabled: false, - Version: imageTag, + Version: ImageTag, Message: fmt.Sprintf(deployFailedMsgTmpl, err), }, "", err } @@ -105,14 +105,14 @@ func ApplyDNS(ctx context.Context, snap snap.Snap, dns types.DNS, kubelet types. err = fmt.Errorf("failed to retrieve the coredns service: %w", err) return types.FeatureStatus{ Enabled: false, - Version: imageTag, + Version: ImageTag, Message: fmt.Sprintf(deployFailedMsgTmpl, err), }, "", err } return types.FeatureStatus{ Enabled: true, - Version: imageTag, + Version: ImageTag, Message: fmt.Sprintf(enabledMsgTmpl, dnsIP), }, dnsIP, err } diff --git a/src/k8s/pkg/k8sd/features/coredns/coredns_test.go b/src/k8s/pkg/k8sd/features/coredns/coredns_test.go new file mode 100644 index 000000000..b420aa15e --- /dev/null +++ b/src/k8s/pkg/k8sd/features/coredns/coredns_test.go @@ -0,0 +1,198 @@ +package coredns_test + +import ( + "context" + "errors" + "strings" + "testing" + + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/utils/ptr" + + "github.com/canonical/k8s/pkg/client/helm" + helmmock "github.com/canonical/k8s/pkg/client/helm/mock" + "github.com/canonical/k8s/pkg/client/kubernetes" + "github.com/canonical/k8s/pkg/k8sd/features/coredns" + "github.com/canonical/k8s/pkg/k8sd/types" + snapmock "github.com/canonical/k8s/pkg/snap/mock" +) + +func TestDisabled(t *testing.T) { + t.Run("HelmApplyFails", func(t *testing.T) { + g := NewWithT(t) + + applyErr := errors.New("failed to apply") + helmM := &helmmock.Mock{ + ApplyErr: applyErr, + } + snapM := &snapmock.Snap{ + Mock: snapmock.Mock{ + HelmClient: helmM, + }, + } + dns := types.DNS{ + Enabled: ptr.To(false), + } + kubelet := types.Kubelet{} + + status, str, err := coredns.ApplyDNS(context.Background(), snapM, dns, kubelet, nil) + + g.Expect(err).To(MatchError(ContainSubstring(applyErr.Error()))) + g.Expect(str).To(BeEmpty()) + g.Expect(status.Message).To(ContainSubstring(applyErr.Error())) + g.Expect(status.Message).To(ContainSubstring("failed to uninstall coredns")) + g.Expect(status.Enabled).To(BeFalse()) + g.Expect(status.Version).To(Equal(coredns.ImageTag)) + + callArgs := helmM.ApplyCalledWith[0] + g.Expect(callArgs.Chart).To(Equal(coredns.Chart)) + g.Expect(callArgs.State).To(Equal(helm.StateDeleted)) + g.Expect(callArgs.Values).To(BeNil()) + + }) + t.Run("Success", func(t *testing.T) { + g := NewWithT(t) + + helmM := &helmmock.Mock{} + snapM := &snapmock.Snap{ + Mock: snapmock.Mock{ + HelmClient: helmM, + }, + } + dns := types.DNS{ + Enabled: ptr.To(false), + } + kubelet := types.Kubelet{} + + status, str, err := coredns.ApplyDNS(context.Background(), snapM, dns, kubelet, nil) + + g.Expect(err).To(BeNil()) + g.Expect(str).To(BeEmpty()) + g.Expect(status.Message).To(Equal("disabled")) + g.Expect(status.Enabled).To(BeFalse()) + g.Expect(status.Version).To(Equal(coredns.ImageTag)) + g.Expect(helmM.ApplyCalledWith).To(HaveLen(1)) + + callArgs := helmM.ApplyCalledWith[0] + g.Expect(callArgs.Chart).To(Equal(coredns.Chart)) + g.Expect(callArgs.State).To(Equal(helm.StateDeleted)) + g.Expect(callArgs.Values).To(BeNil()) + }) +} + +func TestEnabled(t *testing.T) { + t.Run("HelmApplyFails", func(t *testing.T) { + g := NewWithT(t) + + applyErr := errors.New("failed to apply") + helmM := &helmmock.Mock{ + ApplyErr: applyErr, + } + snapM := &snapmock.Snap{ + Mock: snapmock.Mock{ + HelmClient: helmM, + }, + } + dns := types.DNS{ + Enabled: ptr.To(true), + } + kubelet := types.Kubelet{} + + status, str, err := coredns.ApplyDNS(context.Background(), snapM, dns, kubelet, nil) + + g.Expect(err).To(MatchError(ContainSubstring(applyErr.Error()))) + g.Expect(str).To(BeEmpty()) + g.Expect(status.Message).To(ContainSubstring(applyErr.Error())) + g.Expect(status.Message).To(ContainSubstring("failed to apply coredns")) + g.Expect(status.Enabled).To(BeFalse()) + g.Expect(status.Version).To(Equal(coredns.ImageTag)) + + callArgs := helmM.ApplyCalledWith[0] + g.Expect(callArgs.Chart).To(Equal(coredns.Chart)) + g.Expect(callArgs.State).To(Equal(helm.StatePresent)) + validateValues(g, callArgs.Values, dns, kubelet) + }) + t.Run("HelmApplySuccessServiceFails", func(t *testing.T) { + g := NewWithT(t) + + helmM := &helmmock.Mock{} + clientset := fake.NewSimpleClientset() + snapM := &snapmock.Snap{ + Mock: snapmock.Mock{ + HelmClient: helmM, + KubernetesClient: &kubernetes.Client{Interface: clientset}, + }, + } + dns := types.DNS{ + Enabled: ptr.To(true), + } + kubelet := types.Kubelet{} + + status, str, err := coredns.ApplyDNS(context.Background(), snapM, dns, kubelet, nil) + + g.Expect(err).To(MatchError(ContainSubstring("services \"coredns\" not found"))) + g.Expect(str).To(BeEmpty()) + g.Expect(status.Message).To(ContainSubstring("failed to retrieve the coredns service")) + g.Expect(status.Enabled).To(BeFalse()) + g.Expect(status.Version).To(Equal(coredns.ImageTag)) + g.Expect(helmM.ApplyCalledWith).To(HaveLen(1)) + + callArgs := helmM.ApplyCalledWith[0] + g.Expect(callArgs.Chart).To(Equal(coredns.Chart)) + g.Expect(callArgs.State).To(Equal(helm.StatePresent)) + validateValues(g, callArgs.Values, dns, kubelet) + }) + t.Run("Success", func(t *testing.T) { + g := NewWithT(t) + + helmM := &helmmock.Mock{} + clusterIp := "10.96.0.10" + corednsService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coredns", + Namespace: "kube-system", + }, + Spec: corev1.ServiceSpec{ + ClusterIP: clusterIp, + }, + } + clientset := fake.NewSimpleClientset(corednsService) + snapM := &snapmock.Snap{ + Mock: snapmock.Mock{ + HelmClient: helmM, + KubernetesClient: &kubernetes.Client{Interface: clientset}, + }, + } + dns := types.DNS{ + Enabled: ptr.To(true), + } + kubelet := types.Kubelet{} + + status, str, err := coredns.ApplyDNS(context.Background(), snapM, dns, kubelet, nil) + + g.Expect(err).To(BeNil()) + g.Expect(str).To(Equal(clusterIp)) + g.Expect(status.Message).To(ContainSubstring("enabled at " + clusterIp)) + g.Expect(status.Enabled).To(BeTrue()) + g.Expect(status.Version).To(Equal(coredns.ImageTag)) + g.Expect(helmM.ApplyCalledWith).To(HaveLen(1)) + + callArgs := helmM.ApplyCalledWith[0] + g.Expect(callArgs.Chart).To(Equal(coredns.Chart)) + g.Expect(callArgs.State).To(Equal(helm.StatePresent)) + validateValues(g, callArgs.Values, dns, kubelet) + }) +} + +func validateValues(g Gomega, values map[string]any, dns types.DNS, kubelet types.Kubelet) { + service := values["service"].(map[string]any) + g.Expect(service["clusterIP"]).To(Equal(kubelet.GetClusterDNS())) + + servers := values["servers"].([]map[string]any) + plugins := servers[0]["plugins"].([]map[string]any) + g.Expect(plugins[3]["parameters"]).To(ContainSubstring(kubelet.GetClusterDomain())) + g.Expect(plugins[5]["parameters"]).To(ContainSubstring(strings.Join(dns.GetUpstreamNameservers(), " "))) +} diff --git a/src/k8s/pkg/k8sd/features/coredns/register.go b/src/k8s/pkg/k8sd/features/coredns/register.go index 1dc54d943..566d31f09 100644 --- a/src/k8s/pkg/k8sd/features/coredns/register.go +++ b/src/k8s/pkg/k8sd/features/coredns/register.go @@ -8,6 +8,6 @@ import ( func init() { images.Register( - fmt.Sprintf("%s:%s", imageRepo, imageTag), + fmt.Sprintf("%s:%s", imageRepo, ImageTag), ) }