From 88c5fe8648ba61ff1814d57dd51c3b02180d1f6e Mon Sep 17 00:00:00 2001 From: Ananya E P Date: Tue, 7 Jan 2025 10:26:59 +0530 Subject: [PATCH 1/2] Improve test coverage for --take-ownership flag in Helm commands --- pkg/kube/fake/fake.go | 2 + pkg/kube/fake/fake_test.go | 141 ++++++++++++++++++++++++++++++++++++ pkg/testutils/helm_test.go | 83 +++++++++++++++++++++ pkg/testutils/setup_test.go | 46 ++++++++++++ 4 files changed, 272 insertions(+) create mode 100644 pkg/kube/fake/fake_test.go create mode 100644 pkg/testutils/helm_test.go create mode 100644 pkg/testutils/setup_test.go diff --git a/pkg/kube/fake/fake.go b/pkg/kube/fake/fake.go index a60d9f34e..086afe6e4 100644 --- a/pkg/kube/fake/fake.go +++ b/pkg/kube/fake/fake.go @@ -158,3 +158,5 @@ func createDummyResourceList() kube.ResourceList { return resourceList } + + diff --git a/pkg/kube/fake/fake_test.go b/pkg/kube/fake/fake_test.go new file mode 100644 index 000000000..74a9123e0 --- /dev/null +++ b/pkg/kube/fake/fake_test.go @@ -0,0 +1,141 @@ +/* +Copyright The Helm Authors. + +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 fake + +import ( + "errors" + "os" + "testing" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/cli-runtime/pkg/resource" + + "helm.sh/helm/v4/pkg/kube" +) + +func TestFailingKubeClient_UpdateWithTakeOwnership(t *testing.T) { + // Creating a dummy resource with existing ownership + existingResource := &resource.Info{ + Name: "existing-resource", + Namespace: "default", + Object: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "existing-resource", + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "v1", + Kind: "ReplicaSet", + Name: "original-owner", + UID: "123", + }, + }, + }, + }, + } + + // Creating modified resource with new ownership + modifiedResource := &resource.Info{ + Name: "existing-resource", + Namespace: "default", + Object: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "existing-resource", + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "v1", + Kind: "Helm", + Name: "new-owner", + UID: "456", + }, + }, + }, + }, + } + + tests := []struct { + name string + takeOwnership bool + updateError error + wantError bool + }{ + { + name: "successful take ownership", + takeOwnership: true, + updateError: nil, + wantError: false, + }, + { + name: "ownership not taken when flag is false", + takeOwnership: false, + updateError: nil, + wantError: false, + }, + { + name: "update error with take ownership", + takeOwnership: true, + updateError: errors.New("update failed"), + wantError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + client := &FailingKubeClient{ + PrintingKubeClient: PrintingKubeClient{ + Out: os.Stdout, + }, + UpdateError: tt.updateError, + } + + existingList := kube.ResourceList{existingResource} + modifiedList := kube.ResourceList{modifiedResource} + result, err := client.Update(existingList, modifiedList, tt.takeOwnership) + + if tt.wantError && err == nil { + t.Error("expected error, got none") + } + if !tt.wantError && err != nil { + t.Errorf("unexpected error: %v", err) + } + + if err == nil { + if tt.takeOwnership { + modifiedPod := modifiedList[0].Object.(*v1.Pod) + if len(modifiedPod.OwnerReferences) != 1 { + t.Error("expected exactly one owner reference") + } + if ownerRef := modifiedPod.OwnerReferences[0]; ownerRef.Kind != "Helm" { + t.Errorf("expected owner kind to be Helm, got %s", ownerRef.Kind) + } + } + if !tt.takeOwnership { + originalPod := existingList[0].Object.(*v1.Pod) + if len(originalPod.OwnerReferences) != 1 { + t.Error("expected exactly one owner reference") + } + if ownerRef := originalPod.OwnerReferences[0]; ownerRef.Kind != "ReplicaSet" { + t.Errorf("expected owner kind to be ReplicaSet, got %s", ownerRef.Kind) + } + } + if result == nil { + t.Error("expected non-nil result") + } + } + }) + } +} diff --git a/pkg/testutils/helm_test.go b/pkg/testutils/helm_test.go new file mode 100644 index 000000000..a3bde2a17 --- /dev/null +++ b/pkg/testutils/helm_test.go @@ -0,0 +1,83 @@ +/* +Copyright The Helm Authors. + +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 testutils + +import ( + "context" + "os" + "os/exec" + "testing" + + v1 "k8s.io/api/core/v1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" +) + +func RunHelmCommand(t *testing.T, args []string) (string, error) { + t.Helper() + + kubeConfig := os.Getenv("KUBECONFIG") + if kubeConfig == "" { + t.Fatal("KUBECONFIG environment variable is not set") + } + + cmd := exec.Command("helm", args...) + cmd.Env = append(os.Environ(), "KUBECONFIG="+kubeConfig) + + output, err := cmd.CombinedOutput() + if err != nil { + t.Logf("Helm command failed: %s", string(output)) + return string(output), err + } + + return string(output), nil +} + +// Test for `helm upgrade --install --take-ownership` +func TestTakeOwnershipFlag(t *testing.T) { + ns, cleanup := SetupTestEnvironment(t) + defer cleanup() + client := fake.NewSimpleClientset() + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-pod", + Namespace: ns, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "v1", + Kind: "Helm", + Name: "test-release", + UID: "456", + }, + }, + }, + } + + _, err := client.CoreV1().Pods(ns).Create(context.Background(), pod, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("failed to create pod: %v", err) + } + retrievedPod, err := client.CoreV1().Pods(ns).Get(context.Background(), "example-pod", metav1.GetOptions{}) + if err != nil { + t.Fatalf("failed to retrieve pod: %v", err) + } + + if len(retrievedPod.OwnerReferences) != 1 || retrievedPod.OwnerReferences[0].Kind != "Helm" { + t.Errorf("expected owner kind to be Helm, got %v", retrievedPod.OwnerReferences[0].Kind) + } +} diff --git a/pkg/testutils/setup_test.go b/pkg/testutils/setup_test.go new file mode 100644 index 000000000..994763fef --- /dev/null +++ b/pkg/testutils/setup_test.go @@ -0,0 +1,46 @@ +/* +Copyright The Helm Authors. + +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 testutils +import ( + "context" + "testing" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" +) + +// SetupTestEnvironment creates a test namespace using a fake client and returns a cleanup function +func SetupTestEnvironment(t *testing.T) (string, func()) { + t.Helper() + client := fake.NewSimpleClientset() + ns := &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "helm-test-", + }, + } + + ns, err := client.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("failed to create namespace: %v", err) + } + + return ns.Name, func() { + if err := client.CoreV1().Namespaces().Delete(context.Background(), ns.Name, metav1.DeleteOptions{}); err != nil { + t.Logf("failed to clean up namespace: %v", err) + } + } +} From 80d22fa08b16ef0d29323a3ec05e18af039d666d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 21:18:26 +0000 Subject: [PATCH 2/2] Bump github.com/moby/term from 0.5.0 to 0.5.1 Bumps [github.com/moby/term](https://github.com/moby/term) from 0.5.0 to 0.5.1. - [Commits](https://github.com/moby/term/compare/v0.5.0...v0.5.1) --- updated-dependencies: - dependency-name: github.com/moby/term dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: Ananya E P --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 68b07c1a9..fafbda08e 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/lib/pq v1.10.9 github.com/mattn/go-shellwords v1.0.12 github.com/mitchellh/copystructure v1.2.0 - github.com/moby/term v0.5.0 + github.com/moby/term v0.5.1 github.com/opencontainers/image-spec v1.1.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 84c9ba0ec..7c903c9f7 100644 --- a/go.sum +++ b/go.sum @@ -263,8 +263,8 @@ github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vyg github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/term v0.5.1 h1:iy+9Sd2jzMs24S0frhx8fMvcw0/5C5LxVU0eEqOWoRs= +github.com/moby/term v0.5.1/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=