Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 78cf5bf

Browse files
committedMar 10, 2025·
🌱 Implement a smoke test for clusterctl plugins
1 parent bfb2649 commit 78cf5bf

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-1
lines changed
 

‎cmd/clusterctl/cmd/plugin_test.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
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 cmd
18+
19+
import (
20+
"bytes"
21+
"fmt"
22+
"os"
23+
"os/exec"
24+
"path/filepath"
25+
"strconv"
26+
"testing"
27+
28+
"github.com/Masterminds/goutils"
29+
. "github.com/onsi/gomega"
30+
)
31+
32+
var baseArgs = []string{"clusterctl", "foo"}
33+
34+
const (
35+
forkEnvVar = "FORK"
36+
pluginCommandEnvVar = "PLUGIN_COMMAND"
37+
)
38+
39+
func Test_plugin(t *testing.T) {
40+
tt := []struct {
41+
name string
42+
command string
43+
expected string
44+
}{
45+
{
46+
name: "base plugin command test",
47+
expected: "I am a plugin named clusterctl-foo",
48+
},
49+
{
50+
name: "plugin version command test",
51+
command: "version",
52+
expected: "1.0.0",
53+
},
54+
}
55+
56+
for _, tc := range tt {
57+
t.Run(tc.name, func(t *testing.T) {
58+
g := NewWithT(t)
59+
stdout, _, err := runForkTest("TestExecutePlugin", fmt.Sprintf("%s=%s", pluginCommandEnvVar, tc.command))
60+
g.Expect(err).NotTo(HaveOccurred())
61+
g.Expect(stdout).To(ContainSubstring(tc.expected))
62+
})
63+
}
64+
}
65+
66+
func TestExecutePlugin(t *testing.T) {
67+
g := NewWithT(t)
68+
if goutils.DefaultString(os.Getenv(forkEnvVar), "false") == strconv.FormatBool(false) {
69+
t.Skip("FORK environment variable isn't set. Skipping test")
70+
}
71+
72+
tmpDir := t.TempDir()
73+
path := filepath.Join(tmpDir, "clusterctl-foo")
74+
g.Expect(os.WriteFile(path, []byte(pluginCode), 0755)).To(Succeed()) //nolint:gosec
75+
g.Expect(os.Setenv("PATH", fmt.Sprintf("%s:%s", os.Getenv("PATH"), tmpDir))).ToNot(HaveOccurred())
76+
77+
os.Args = append(baseArgs, os.Getenv(pluginCommandEnvVar))
78+
Execute()
79+
}
80+
81+
const pluginCode = `
82+
#!/bin/bash
83+
84+
# optional argument handling
85+
if [[ "$1" == "version" ]]
86+
then
87+
echo "1.0.0"
88+
exit 0
89+
fi
90+
91+
echo "I am a plugin named clusterctl-foo"
92+
`
93+
94+
func runForkTest(testName string, option string) (string, string, error) {
95+
cmd := exec.Command(os.Args[0], fmt.Sprintf("-test.run=%v", testName)) //nolint:gosec
96+
cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%v", forkEnvVar, true), option)
97+
98+
var stdoutB, stderrB bytes.Buffer
99+
cmd.Stdout = &stdoutB
100+
cmd.Stderr = &stderrB
101+
102+
err := cmd.Run()
103+
104+
return stdoutB.String(), stderrB.String(), err
105+
}

‎cmd/clusterctl/cmd/root.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func handlePlugins() {
218218
case "help", cobra.ShellCompRequestCmd, cobra.ShellCompNoDescRequestCmd:
219219
// Don't search for a plugin
220220
default:
221-
if err := handlePluginCommand(pluginHandler, cmdPathPieces, 0); err != nil {
221+
if err = handlePluginCommand(pluginHandler, cmdPathPieces, 0); err != nil {
222222
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
223223
os.Exit(1)
224224
}

0 commit comments

Comments
 (0)
Please sign in to comment.