Skip to content

Commit cc5e60c

Browse files
benmosherclaude
andcommitted
fix: replace recover() with type-check in isMinikubeExtension
Detect plain-string kubeconfig extensions upfront by type-asserting to *runtime.Unknown and using json.Unmarshal, which returns an error for non-object JSON instead of panicking. This avoids a try/catch pattern that could silently swallow unrelated panics from the k8s runtime library. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Ben Mosher <ben.mosher@dbtlabs.com>
1 parent cd266a5 commit cc5e60c

2 files changed

Lines changed: 11 additions & 24 deletions

File tree

pkg/devspace/kubectl/util.go

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package kubectl
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"net"
78
"net/http"
@@ -264,27 +265,13 @@ func IsMinikubeKubernetes(kubeClient Client) bool {
264265
return false
265266
}
266267

267-
// isMinikubeExtension safely checks whether a kubeconfig cluster extension
268-
// identifies the cluster as a minikube provider.
269-
//
270-
// Some tools (e.g. Teleport) write extension values as plain YAML strings
271-
// rather than structured objects. runtime.DefaultUnstructuredConverter.ToUnstructured
272-
// panics on such values via reflection ("reflect.Set: value of type string is
273-
// not assignable to type map[string]interface {}") rather than returning an
274-
// error, so we recover() and treat unparseable extensions as non-minikube.
275-
//
276-
// noinline is required: if the compiler inlines this function into its caller,
277-
// the deferred recover() loses its stack frame and cannot catch the panic.
278-
//
279-
//go:noinline
280-
func isMinikubeExtension(extension runtime.Object) (result bool) {
281-
defer func() {
282-
if recover() != nil {
283-
result = false
284-
}
285-
}()
286-
ext, err := runtime.DefaultUnstructuredConverter.ToUnstructured(extension)
287-
if err != nil {
268+
func isMinikubeExtension(extension runtime.Object) bool {
269+
unknown, ok := extension.(*runtime.Unknown)
270+
if !ok {
271+
return false
272+
}
273+
var ext map[string]interface{}
274+
if err := json.Unmarshal(unknown.Raw, &ext); err != nil {
288275
return false
289276
}
290277
provider, ok := ext["provider"].(string)

pkg/devspace/kubectl/util_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,9 @@ func TestIsMinikubeKubernetes(t *testing.T) {
118118
})
119119

120120
// 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.
121+
// strings rather than structured objects. isMinikubeExtension detects this
122+
// by type-asserting the extension to *runtime.Unknown and using json.Unmarshal,
123+
// which returns an error for non-object JSON (e.g. a bare string) rather than panicking.
124124
t.Run("string-valued extension does not panic and returns false", func(t *testing.T) {
125125
ext := &runtime.Unknown{
126126
Raw: []byte(`"my-cluster-name"`), // bare JSON string, not an object

0 commit comments

Comments
 (0)