Skip to content

Commit cd9e06b

Browse files
feat: allow disabling add-ons (#42)
* feat: allow disabling add-ons to prepare helmvm for kots regression testing, we need to give users the ability to deactivate add-on installations. this feature will enable kots to bypass the add-on installation during regression tests. they can then install their current development branch over a clean helmvm setup to conduct their tests. * Apply suggestions from code review Co-authored-by: Ethan Mosbaugh <[email protected]> --------- Co-authored-by: Ethan Mosbaugh <[email protected]>
1 parent 1f79990 commit cd9e06b

File tree

12 files changed

+387
-47
lines changed

12 files changed

+387
-47
lines changed

.github/workflows/e2e.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ jobs:
1717
- TestSingleNodeInstallationCentos8Stream
1818
- TestVersion
1919
- TestMultiNodeInteractiveInstallation
20+
- TestInstallWithDisabledAddons
21+
- TestEmbedAddonsOnly
2022
steps:
2123
- name: Move Docker aside
2224
run: |

cmd/helmvm/install.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,10 @@ var installCommand = &cli.Command{
329329
Usage: "Do not prompt user when it is not necessary",
330330
Value: false,
331331
},
332+
&cli.StringSliceFlag{
333+
Name: "disable-addon",
334+
Usage: "Disable addon during install/upgrade",
335+
},
332336
},
333337
Action: func(c *cli.Context) error {
334338
if defaults.DecentralizedInstall() {
@@ -363,9 +367,14 @@ var installCommand = &cli.Command{
363367
ccfg := defaults.PathToConfig("k0sctl.yaml")
364368
kcfg := defaults.PathToConfig("kubeconfig")
365369
os.Setenv("KUBECONFIG", kcfg)
366-
if applier, err := addons.NewApplier(useprompt, true); err != nil {
367-
return fmt.Errorf("unable to create applier: %w", err)
368-
} else if err := applier.Apply(c.Context); err != nil {
370+
opts := []addons.Option{}
371+
if c.Bool("no-prompt") {
372+
opts = append(opts, addons.WithoutPrompt())
373+
}
374+
for _, addon := range c.StringSlice("disable-addon") {
375+
opts = append(opts, addons.WithoutAddon(addon))
376+
}
377+
if err := addons.NewApplier(opts...).Apply(c.Context); err != nil {
369378
return fmt.Errorf("unable to apply addons: %w", err)
370379
}
371380
if err := runPostApply(c.Context); err != nil {

cmd/helmvm/upgrade.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ var upgradeCommand = &cli.Command{
5151
Usage: "Do not prompt user when it is not necessary",
5252
Value: false,
5353
},
54+
&cli.StringSliceFlag{
55+
Name: "disable-addon",
56+
Usage: "Disable addon during upgrade",
57+
},
5458
},
5559
Action: func(c *cli.Context) error {
5660
if err := canRunUpgrade(c); err != nil {
@@ -81,9 +85,14 @@ var upgradeCommand = &cli.Command{
8185
}
8286
os.Setenv("KUBECONFIG", kcfg)
8387
logrus.Infof("Upgrading addons")
84-
if applier, err := addons.NewApplier(c.Bool("no-prompt"), true); err != nil {
85-
return fmt.Errorf("unable to create applier: %w", err)
86-
} else if err := applier.Apply(c.Context); err != nil {
88+
opts := []addons.Option{}
89+
if c.Bool("no-prompt") {
90+
opts = append(opts, addons.WithoutPrompt())
91+
}
92+
for _, addon := range c.StringSlice("disable-addon") {
93+
opts = append(opts, addons.WithoutAddon(addon))
94+
}
95+
if err := addons.NewApplier(opts...).Apply(c.Context); err != nil {
8796
return fmt.Errorf("unable to apply addons: %w", err)
8897
}
8998
logrus.Infof("Upgrade complete")

cmd/helmvm/version.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@ var versionCommand = &cli.Command{
1717
Name: "version",
1818
Usage: fmt.Sprintf("Shows the %s installer version", defaults.BinaryName()),
1919
Action: func(c *cli.Context) error {
20-
applier, err := addons.NewApplier(true, false)
21-
if err != nil {
22-
return fmt.Errorf("unable to create applier: %w", err)
23-
}
24-
versions, err := applier.Versions()
20+
opts := []addons.Option{addons.Quiet(), addons.WithoutPrompt()}
21+
versions, err := addons.NewApplier(opts...).Versions()
2522
if err != nil {
2623
return fmt.Errorf("unable to get versions: %w", err)
2724
}

e2e/embed_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,31 @@ func TestEmbedAndInstall(t *testing.T) {
3333
t.Fatalf("fail to create deployment with pvc: %v", err)
3434
}
3535
}
36+
37+
func TestEmbedAddonsOnly(t *testing.T) {
38+
t.Parallel()
39+
tc := cluster.NewTestCluster(&cluster.Input{
40+
T: t,
41+
Nodes: 1,
42+
Image: "ubuntu/jammy",
43+
SSHPublicKey: "../output/tmp/id_rsa.pub",
44+
SSHPrivateKey: "../output/tmp/id_rsa",
45+
HelmVMPath: "../output/bin/helmvm",
46+
})
47+
defer tc.Destroy()
48+
t.Log("installing ssh in node 0")
49+
line := []string{"apt", "install", "openssh-server", "-y"}
50+
if _, _, err := RunCommandOnNode(t, tc, 0, line); err != nil {
51+
t.Fatalf("fail to install ssh on node %s: %v", tc.Nodes[0], err)
52+
}
53+
t.Log("installing helmvm on node 0")
54+
line = []string{"single-node-install.sh"}
55+
if _, _, err := RunCommandOnNode(t, tc, 0, line); err != nil {
56+
t.Fatalf("fail to install helmvm on node %s: %v", tc.Nodes[0], err)
57+
}
58+
t.Log("testing --addons-only on node 0")
59+
line = []string{"addons-only.sh"}
60+
if _, _, err := RunCommandOnNode(t, tc, 0, line); err != nil {
61+
t.Fatalf("fail to install embedded ssh in node 0: %v", err)
62+
}
63+
}

e2e/install_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,26 @@ func TestMultiNodeInteractiveInstallation(t *testing.T) {
229229
t.Fatalf("nodes not reporting ready: %v", err)
230230
}
231231
}
232+
233+
func TestInstallWithDisabledAddons(t *testing.T) {
234+
t.Parallel()
235+
tc := cluster.NewTestCluster(&cluster.Input{
236+
T: t,
237+
Nodes: 1,
238+
Image: "ubuntu/jammy",
239+
SSHPublicKey: "../output/tmp/id_rsa.pub",
240+
SSHPrivateKey: "../output/tmp/id_rsa",
241+
HelmVMPath: "../output/bin/helmvm",
242+
})
243+
defer tc.Destroy()
244+
t.Log("installing ssh in node 0")
245+
line := []string{"apt", "install", "openssh-server", "-y"}
246+
if _, _, err := RunCommandOnNode(t, tc, 0, line); err != nil {
247+
t.Fatalf("fail to install ssh on node %s: %v", tc.Nodes[0], err)
248+
}
249+
t.Log("installling with disabled addons on node 0")
250+
line = []string{"install-with-disabled-addons.sh"}
251+
if _, _, err := RunCommandOnNode(t, tc, 0, line); err != nil {
252+
t.Fatalf("fail to install embedded ssh in node 0: %v", err)
253+
}
254+
}

e2e/scripts/addons-only.sh

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env bash
2+
# This script is used to test the embed capability of helmvm. It will pull the memcached helm chart and
3+
# embed it into the binary, issuing then a single node install. This script waits for the memcached pod
4+
# to be ready.
5+
set -euo pipefail
6+
7+
wait_for_healthy_node() {
8+
ready=$(kubectl get nodes | grep -v NotReady | grep -c Ready || true)
9+
counter=0
10+
while [ -z "$ready" ] || [ "$ready" -lt "1" ]; do
11+
if [ "$counter" -gt 36 ]; then
12+
return 1
13+
fi
14+
sleep 5
15+
counter=$((counter+1))
16+
echo "Waiting for node to be ready"
17+
ready=$(kubectl get nodes | grep -v NotReady | grep -c Ready || true)
18+
kubectl get nodes || true
19+
done
20+
return 0
21+
}
22+
23+
install_helm() {
24+
apt update -y
25+
if ! apt install -y curl ; then
26+
return 1
27+
fi
28+
if ! curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 ; then
29+
return 1
30+
fi
31+
chmod 700 get_helm.sh
32+
if ! ./get_helm.sh ; then
33+
return 1
34+
fi
35+
return 0
36+
}
37+
38+
pull_helm_chart() {
39+
mkdir chart
40+
if ! helm pull oci://registry-1.docker.io/bitnamicharts/memcached --destination chart; then
41+
return 1
42+
fi
43+
return 0
44+
}
45+
46+
embed_helm_chart() {
47+
fpath=$(ls -d chart/*)
48+
if ! helmvm embed --chart "$fpath" --output helmvm; then
49+
return 1
50+
fi
51+
mv helmvm /usr/local/bin
52+
return 0
53+
}
54+
55+
wait_for_memcached_pods() {
56+
ready=$(kubectl get pods -n helmvm | grep -v NotReady | grep Ready | grep -c memcached || true)
57+
counter=0
58+
while [ -z "$ready" ] || [ "$ready" -lt "1" ]; do
59+
if [ "$counter" -gt 36 ]; then
60+
return 1
61+
fi
62+
sleep 5
63+
counter=$((counter+1))
64+
echo "Waiting for memcached pods to be ready"
65+
ready=$(kubectl get pods -n helmvm | grep Running | grep -c memcached || true)
66+
kubectl get pods -n helmvm 2>&1 || true
67+
echo "$ready"
68+
done
69+
return 0
70+
}
71+
72+
main() {
73+
if ! install_helm ; then
74+
echo "Failed to install helm"
75+
exit 1
76+
fi
77+
if ! pull_helm_chart ; then
78+
echo "Failed to pull helm chart"
79+
exit 1
80+
fi
81+
if ! embed_helm_chart ; then
82+
echo "Failed to embed helm chart"
83+
exit 1
84+
fi
85+
if ! helmvm install --no-prompt --addons-only 2>&1 | tee /tmp/log ; then
86+
echo "Failed to install addons"
87+
exit 1
88+
fi
89+
if ! grep -q "You can now access your cluster" /tmp/log; then
90+
echo "Failed to install helmvm"
91+
exit 1
92+
fi
93+
echo "waiting for nodes" >> /tmp/log
94+
if ! wait_for_healthy_node; then
95+
echo "Nodes not reporting healthy"
96+
exit 1
97+
fi
98+
echo "waiting for memcached " >> /tmp/log
99+
if ! wait_for_memcached_pods; then
100+
echo "Memcached pods not reporting healthy"
101+
exit 1
102+
fi
103+
}
104+
105+
export KUBECONFIG=/root/.helmvm/etc/kubeconfig
106+
export PATH="$PATH:/root/.helmvm/bin"
107+
main
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
wait_for_healthy_node() {
5+
ready=$(kubectl get nodes | grep -v NotReady | grep -c Ready || true)
6+
counter=0
7+
while [ -z "$ready" ] || [ "$ready" -lt "1" ]; do
8+
if [ "$counter" -gt 36 ]; then
9+
return 1
10+
fi
11+
sleep 5
12+
counter=$((counter+1))
13+
echo "Waiting for node to be ready"
14+
ready=$(kubectl get nodes | grep -v NotReady | grep -c Ready || true)
15+
kubectl get nodes || true
16+
done
17+
return 0
18+
}
19+
20+
install_helm() {
21+
apt update -y
22+
if ! apt install -y curl ; then
23+
return 1
24+
fi
25+
if ! curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 ; then
26+
return 1
27+
fi
28+
chmod 700 get_helm.sh
29+
if ! ./get_helm.sh ; then
30+
return 1
31+
fi
32+
return 0
33+
}
34+
35+
pull_helm_chart() {
36+
mkdir chart
37+
if ! helm pull oci://registry-1.docker.io/bitnamicharts/memcached --destination chart; then
38+
return 1
39+
fi
40+
return 0
41+
}
42+
43+
embed_helm_chart() {
44+
fpath=$(ls -d chart/*)
45+
if ! helmvm embed --chart "$fpath" --output helmvm; then
46+
return 1
47+
fi
48+
mv helmvm /usr/local/bin
49+
return 0
50+
}
51+
52+
check_empty_helmvm_namespace() {
53+
pods=$(kubectl get pods -n helmvm | wc -l)
54+
if [ "$pods" -gt 0 ]; then
55+
kubectl get pods -n helmvm
56+
return 1
57+
fi
58+
}
59+
60+
main() {
61+
if ! install_helm ; then
62+
echo "Failed to install helm"
63+
exit 1
64+
fi
65+
if ! pull_helm_chart ; then
66+
echo "Failed to pull helm chart"
67+
exit 1
68+
fi
69+
if ! embed_helm_chart ; then
70+
echo "Failed to embed helm chart"
71+
exit 1
72+
fi
73+
if ! helmvm install --disable-addon openebs --disable-addon adminconsole --disable-addon memcached --no-prompt 2>&1 | tee /tmp/log ; then
74+
echo "Failed to install helmvm"
75+
exit 1
76+
fi
77+
if ! grep -q "You can now access your cluster" /tmp/log; then
78+
echo "Failed to install helmvm"
79+
exit 1
80+
fi
81+
echo "waiting for nodes" >> /tmp/log
82+
if ! wait_for_healthy_node; then
83+
echo "Nodes not reporting healthy"
84+
exit 1
85+
fi
86+
echo "check nothing exists on helmvm namespace " >> /tmp/log
87+
if ! check_empty_helmvm_namespace; then
88+
echo "Pods found on helmvm namespace"
89+
exit 1
90+
fi
91+
}
92+
93+
export KUBECONFIG=/root/.helmvm/etc/kubeconfig
94+
export PATH=$PATH:/root/.helmvm/bin
95+
main

0 commit comments

Comments
 (0)