Skip to content

Commit ce1f4ef

Browse files
committed
test: add TestIsMinikubeKubernetes covering string-valued extension panic
Covers the cases for IsMinikubeKubernetes: - nil client / nil ClientConfig - context name match ("minikube") - structured extension with minikube provider - structured extension with a different provider - string-valued extension (e.g. written by Teleport) — must not panic
1 parent 79b593e commit ce1f4ef

1 file changed

Lines changed: 132 additions & 0 deletions

File tree

pkg/devspace/kubectl/util_test.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package kubectl
2+
3+
import (
4+
"context"
5+
"io"
6+
"testing"
7+
8+
"github.com/loft-sh/devspace/pkg/util/kubeconfig"
9+
"gotest.tools/assert"
10+
k8sv1 "k8s.io/api/core/v1"
11+
"k8s.io/apimachinery/pkg/runtime"
12+
"k8s.io/client-go/kubernetes"
13+
"k8s.io/client-go/rest"
14+
"k8s.io/client-go/tools/clientcmd"
15+
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
16+
)
17+
18+
// minimalClient implements kubectl.Client with only the two methods that
19+
// IsMinikubeKubernetes actually calls. All other methods panic if invoked.
20+
type minimalClient struct {
21+
context string
22+
clientConfig clientcmd.ClientConfig
23+
}
24+
25+
func (c *minimalClient) CurrentContext() string { return c.context }
26+
func (c *minimalClient) ClientConfig() clientcmd.ClientConfig { return c.clientConfig }
27+
func (c *minimalClient) KubeClient() kubernetes.Interface { panic("not implemented") }
28+
func (c *minimalClient) Namespace() string { panic("not implemented") }
29+
func (c *minimalClient) RestConfig() *rest.Config { panic("not implemented") }
30+
func (c *minimalClient) KubeConfigLoader() kubeconfig.Loader { panic("not implemented") }
31+
func (c *minimalClient) IsInCluster() bool { panic("not implemented") }
32+
func (c *minimalClient) CopyFromReader(_ context.Context, _ *k8sv1.Pod, _, _ string, _ io.Reader) error {
33+
panic("not implemented")
34+
}
35+
func (c *minimalClient) Copy(_ context.Context, _ *k8sv1.Pod, _, _, _ string, _ []string) error {
36+
panic("not implemented")
37+
}
38+
func (c *minimalClient) ExecStream(_ context.Context, _ *ExecStreamOptions) error {
39+
panic("not implemented")
40+
}
41+
func (c *minimalClient) ExecBuffered(_ context.Context, _ *k8sv1.Pod, _ string, _ []string, _ io.Reader) ([]byte, []byte, error) {
42+
panic("not implemented")
43+
}
44+
func (c *minimalClient) ExecBufferedCombined(_ context.Context, _ *k8sv1.Pod, _ string, _ []string, _ io.Reader) ([]byte, error) {
45+
panic("not implemented")
46+
}
47+
func (c *minimalClient) GenericRequest(_ context.Context, _ *GenericRequestOptions) (string, error) {
48+
panic("not implemented")
49+
}
50+
func (c *minimalClient) ReadLogs(_ context.Context, _, _, _ string, _ bool, _ *int64) (string, error) {
51+
panic("not implemented")
52+
}
53+
func (c *minimalClient) Logs(_ context.Context, _, _, _ string, _ bool, _ *int64, _ bool) (io.ReadCloser, error) {
54+
panic("not implemented")
55+
}
56+
func (c *minimalClient) EnsureNamespace(_ context.Context, _ string, _ interface{ Debug(args ...interface{}) }) error {
57+
panic("not implemented")
58+
}
59+
60+
// makeClient builds a test Client whose current context points at a cluster
61+
// with the given extensions map.
62+
func makeClient(contextName string, extensions map[string]runtime.Object) *minimalClient {
63+
apiCfg := clientcmdapi.NewConfig()
64+
apiCfg.Clusters[contextName] = &clientcmdapi.Cluster{
65+
Server: "https://example.test:6443",
66+
Extensions: extensions,
67+
}
68+
apiCfg.Contexts[contextName] = &clientcmdapi.Context{
69+
Cluster: contextName,
70+
}
71+
apiCfg.CurrentContext = contextName
72+
73+
cfg := clientcmd.NewNonInteractiveClientConfig(
74+
*apiCfg,
75+
contextName,
76+
&clientcmd.ConfigOverrides{},
77+
nil,
78+
)
79+
return &minimalClient{context: contextName, clientConfig: cfg}
80+
}
81+
82+
func TestIsMinikubeKubernetes(t *testing.T) {
83+
t.Run("nil client returns false", func(t *testing.T) {
84+
assert.Equal(t, false, IsMinikubeKubernetes(nil))
85+
})
86+
87+
t.Run("nil ClientConfig returns false", func(t *testing.T) {
88+
c := &minimalClient{context: "some-cluster", clientConfig: nil}
89+
assert.Equal(t, false, IsMinikubeKubernetes(c))
90+
})
91+
92+
t.Run("context named 'minikube' returns true", func(t *testing.T) {
93+
c := makeClient(minikubeContext, nil)
94+
assert.Equal(t, true, IsMinikubeKubernetes(c))
95+
})
96+
97+
t.Run("non-minikube context with no extensions returns false", func(t *testing.T) {
98+
c := makeClient("my-cluster", nil)
99+
assert.Equal(t, false, IsMinikubeKubernetes(c))
100+
})
101+
102+
t.Run("cluster extension with minikube provider returns true", func(t *testing.T) {
103+
ext := &runtime.Unknown{
104+
Raw: []byte(`{"provider":"minikube.sigs.k8s.io"}`),
105+
ContentType: runtime.ContentTypeJSON,
106+
}
107+
c := makeClient("my-cluster", map[string]runtime.Object{minikubeProvider: ext})
108+
assert.Equal(t, true, IsMinikubeKubernetes(c))
109+
})
110+
111+
t.Run("cluster extension with different provider returns false", func(t *testing.T) {
112+
ext := &runtime.Unknown{
113+
Raw: []byte(`{"provider":"some-other-provider"}`),
114+
ContentType: runtime.ContentTypeJSON,
115+
}
116+
c := makeClient("my-cluster", map[string]runtime.Object{"some-other-provider": ext})
117+
assert.Equal(t, false, IsMinikubeKubernetes(c))
118+
})
119+
120+
// Some tools (e.g. Teleport) serialise kubeconfig extensions as plain YAML
121+
// strings rather than structured objects. runtime.ToUnstructured panics on
122+
// these via reflection instead of returning an error. isMinikubeExtension
123+
// must recover gracefully and return false.
124+
t.Run("string-valued extension does not panic and returns false", func(t *testing.T) {
125+
ext := &runtime.Unknown{
126+
Raw: []byte(`"my-cluster-name"`), // bare JSON string, not an object
127+
ContentType: runtime.ContentTypeJSON,
128+
}
129+
c := makeClient("my-cluster", map[string]runtime.Object{"example.dev/cluster-name": ext})
130+
assert.Equal(t, false, IsMinikubeKubernetes(c))
131+
})
132+
}

0 commit comments

Comments
 (0)