Skip to content

Commit 7fcc6ec

Browse files
authored
Merge pull request #21847 from volatilemolotov/addons/helm_addon
Addons: helm based addons
2 parents bac7021 + fa4d670 commit 7fcc6ec

File tree

7 files changed

+330
-42
lines changed

7 files changed

+330
-42
lines changed

pkg/addons/addons.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,14 +450,29 @@ func enableOrDisableAddonInternal(cc *config.ClusterConfig, addon *assets.Addon,
450450
}
451451
}
452452

453+
if addon.HelmChart != nil {
454+
err := helmInstallBinary(addon, runner)
455+
if err != nil {
456+
return err
457+
}
458+
459+
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
460+
defer cancel()
461+
cmd := helmUninstallOrInstall(ctx, addon.HelmChart, enable)
462+
_, err = runner.RunCmd(cmd)
463+
return err
464+
}
465+
453466
// on the first attempt try without force, but on subsequent attempts use force
454467
force := false
455468

456469
// Retry, because sometimes we race against an apiserver restart
457470
apply := func() error {
458471
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
459472
defer cancel()
460-
_, err := runner.RunCmd(kubectlCommand(ctx, cc, deployFiles, enable, force))
473+
cmd := kubectlCommand(ctx, cc, deployFiles, enable, force)
474+
_, err := runner.RunCmd(cmd)
475+
461476
if err != nil {
462477
klog.Warningf("apply failed, will retry: %v", err)
463478
force = true

pkg/addons/helm.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package addons
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"os/exec"
23+
"path"
24+
25+
"github.com/pkg/errors"
26+
"k8s.io/minikube/pkg/minikube/assets"
27+
"k8s.io/minikube/pkg/minikube/command"
28+
"k8s.io/minikube/pkg/minikube/vmpath"
29+
)
30+
31+
// runs a helm install within the minikube vm or container based on the contents of chart *assets.HelmChart
32+
func installHelmChart(ctx context.Context, chart *assets.HelmChart) *exec.Cmd {
33+
args := []string{
34+
fmt.Sprintf("KUBECONFIG=%s", path.Join(vmpath.GuestPersistentDir, "kubeconfig")),
35+
"helm", "upgrade", "--install", chart.Name, chart.Repo, "--create-namespace",
36+
}
37+
if chart.Namespace != "" {
38+
args = append(args, "--namespace", chart.Namespace)
39+
}
40+
41+
if chart.Values != nil {
42+
for _, value := range chart.Values {
43+
args = append(args, "--set", value)
44+
}
45+
}
46+
47+
if chart.ValueFiles != nil {
48+
for _, value := range chart.ValueFiles {
49+
args = append(args, "--values", value)
50+
}
51+
}
52+
53+
return exec.CommandContext(ctx, "sudo", args...)
54+
}
55+
56+
// runs a helm uninstall based on the contents of chart *assets.HelmChart
57+
func uninstalllHelmChart(ctx context.Context, chart *assets.HelmChart) *exec.Cmd {
58+
args := []string{
59+
fmt.Sprintf("KUBECONFIG=%s", path.Join(vmpath.GuestPersistentDir, "kubeconfig")),
60+
"helm", "uninstall", chart.Name,
61+
}
62+
if chart.Namespace != "" {
63+
args = append(args, "--namespace", chart.Namespace)
64+
}
65+
return exec.CommandContext(ctx, "sudo", args...)
66+
}
67+
68+
// based on enable will execute installHelmChart or uninstallHelmChart
69+
func helmUninstallOrInstall(ctx context.Context, chart *assets.HelmChart, enable bool) *exec.Cmd {
70+
if enable {
71+
return installHelmChart(ctx, chart)
72+
}
73+
return uninstalllHelmChart(ctx, chart)
74+
}
75+
76+
func helmInstallBinary(addon *assets.Addon, runner command.Runner) error {
77+
_, err := runner.RunCmd(exec.Command("test", "-f", "/usr/bin/helm"))
78+
if err != nil {
79+
_, err = runner.RunCmd(exec.Command("test", "-d", "/usr/local/bin"))
80+
if err != nil {
81+
_, err = runner.RunCmd(exec.Command("sudo", "mkdir", "-p", "/usr/local/bin"))
82+
}
83+
84+
installCmd := fmt.Sprint("curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 && chmod 700 get_helm.sh && ./get_helm.sh")
85+
_, err = runner.RunCmd(exec.Command("sudo", "bash", "-c", installCmd))
86+
// we copy the binary from /usr/local/bin to /usr/bin because /usr/local/bin is not in PATH in both iso and kicbase
87+
_, err = runner.RunCmd(exec.Command("sudo", "mv", "/usr/local/bin/helm", "/usr/bin/helm"))
88+
if err != nil {
89+
return errors.Wrap(err, "installing helm")
90+
}
91+
}
92+
return err
93+
}

pkg/addons/helm_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package addons
18+
19+
import (
20+
"context"
21+
"strings"
22+
"testing"
23+
24+
"k8s.io/minikube/pkg/minikube/assets"
25+
)
26+
27+
func TestHelmCommand(t *testing.T) {
28+
tests := []struct {
29+
description string
30+
chart *assets.HelmChart
31+
enable bool
32+
expected string
33+
mode string
34+
}{
35+
{
36+
description: "enable an addon",
37+
chart: &assets.HelmChart{
38+
Name: "addon-name",
39+
Repo: "addon-repo/addon-chart",
40+
Namespace: "addon-namespace",
41+
Values: []string{"key=value"},
42+
ValueFiles: []string{"/etc/kubernetes/addons/values.yaml"},
43+
},
44+
enable: true,
45+
expected: "sudo KUBECONFIG=/var/lib/minikube/kubeconfig helm upgrade --install addon-name addon-repo/addon-chart --create-namespace --namespace addon-namespace --set key=value --values /etc/kubernetes/addons/values.yaml",
46+
},
47+
{
48+
description: "enable an addon without namespace",
49+
chart: &assets.HelmChart{
50+
Name: "addon-name",
51+
Repo: "addon-repo/addon-chart",
52+
Values: []string{"key=value"},
53+
ValueFiles: []string{"/etc/kubernetes/addons/values.yaml"},
54+
},
55+
enable: true,
56+
expected: "sudo KUBECONFIG=/var/lib/minikube/kubeconfig helm upgrade --install addon-name addon-repo/addon-chart --create-namespace --set key=value --values /etc/kubernetes/addons/values.yaml",
57+
},
58+
{
59+
description: "disable an addon",
60+
chart: &assets.HelmChart{
61+
Name: "addon-name",
62+
Namespace: "addon-namespace",
63+
},
64+
enable: false,
65+
expected: "sudo KUBECONFIG=/var/lib/minikube/kubeconfig helm uninstall addon-name --namespace addon-namespace",
66+
mode: "cpu",
67+
},
68+
}
69+
70+
for _, test := range tests {
71+
t.Run(test.description, func(t *testing.T) {
72+
command := helmUninstallOrInstall(context.Background(), test.chart, test.enable)
73+
actual := strings.Join(command.Args, " ")
74+
if actual != test.expected {
75+
t.Errorf("helm command mismatch:\nexpected: %s\nactual: %s", test.expected, actual)
76+
}
77+
})
78+
}
79+
}

0 commit comments

Comments
 (0)