diff --git a/internal/dao/alias_test.go b/internal/dao/alias_test.go index f28b16db6f..315c06f7ac 100644 --- a/internal/dao/alias_test.go +++ b/internal/dao/alias_test.go @@ -12,11 +12,7 @@ import ( "github.com/derailed/k9s/internal/config" "github.com/derailed/k9s/internal/dao" "github.com/derailed/k9s/internal/render" - "github.com/derailed/k9s/internal/watch" "github.com/stretchr/testify/assert" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/informers" ) func TestAsGVR(t *testing.T) { @@ -88,32 +84,3 @@ func makeAliases() *dao.Alias { }, } } - -type testFactory struct{} - -func makeFactory() dao.Factory { - return testFactory{} -} - -var _ dao.Factory = testFactory{} - -func (f testFactory) Client() client.Connection { - return nil -} -func (f testFactory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime.Object, error) { - return nil, nil -} -func (f testFactory) List(gvr, ns string, wait bool, sel labels.Selector) ([]runtime.Object, error) { - return nil, nil -} -func (f testFactory) ForResource(ns, gvr string) (informers.GenericInformer, error) { - return nil, nil -} -func (f testFactory) CanForResource(ns, gvr string, verbs []string) (informers.GenericInformer, error) { - return nil, nil -} -func (f testFactory) WaitForCacheSync() {} -func (f testFactory) Forwarders() watch.Forwarders { - return nil -} -func (f testFactory) DeleteForwarder(string) {} diff --git a/internal/dao/container_test.go b/internal/dao/container_test.go index 25877dfd08..38a24dd815 100644 --- a/internal/dao/container_test.go +++ b/internal/dao/container_test.go @@ -73,7 +73,7 @@ func (c *conn) IsActiveNamespace(string) bool { return fal type podFactory struct{} -var _ dao.Factory = testFactory{} +var _ dao.Factory = &testFactory{} func (f podFactory) Client() client.Connection { return makeConn() diff --git a/internal/dao/secret.go b/internal/dao/secret.go index 9944a6c8a5..4160667734 100644 --- a/internal/dao/secret.go +++ b/internal/dao/secret.go @@ -31,6 +31,17 @@ func (s *Secret) Describe(path string) (string, error) { return encodedDescription, nil } + return s.Decode(encodedDescription, path) +} + +// SetDecode sets the decode flag. +func (s *Secret) SetDecode(flag bool) { + s.decode = flag +} + +// Decode removes the ecnode part from the secret's description and appends the +// secret's decoded data. +func (s *Secret) Decode(encodedDescription, path string) (string, error) { o, err := s.GetFactory().Get(s.GVR(), path, true, labels.Everything()) if err != nil { @@ -43,7 +54,7 @@ func (s *Secret) Describe(path string) (string, error) { return "", fmt.Errorf("unable to find data section in secret description") } - d, err := DecodeSecret(o.(*unstructured.Unstructured)) + d, err := ExtractSecrets(o.(*unstructured.Unstructured)) if err != nil { return "", err @@ -60,17 +71,12 @@ func (s *Secret) Describe(path string) (string, error) { return decodedDescription, nil } -// SetDecode sets the decode flag. -func (s *Secret) SetDecode(flag bool) { - s.decode = flag -} - -// DecodeSecret takes an unstructured object and attempts to convert it into a +// ExtractSecrets takes an unstructured object and attempts to convert it into a // Kubernetes Secret. // It returns a map where the keys are the secret data keys and the values are // the corresponding secret data values. // If the conversion fails, it returns an error. -func DecodeSecret(o *unstructured.Unstructured) (map[string]string, error) { +func ExtractSecrets(o *unstructured.Unstructured) (map[string]string, error) { var secret v1.Secret err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.Object, &secret) diff --git a/internal/dao/secret_test.go b/internal/dao/secret_test.go new file mode 100644 index 0000000000..a1e10f32fa --- /dev/null +++ b/internal/dao/secret_test.go @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of K9s + +package dao_test + +import ( + "context" + "testing" + + "github.com/derailed/k9s/internal/client" + "github.com/derailed/k9s/internal/dao" + "github.com/stretchr/testify/assert" +) + +func TestEncodedSecretDescribe(t *testing.T) { + s := dao.Secret{} + s.Init(makeFactory(), client.NewGVR("v1/secrets")) + + ctx := context.Background() + oo, err := s.List(ctx, "kube-system") + + assert.Nil(t, err) + assert.Equal(t, 1, len(oo)) + + encodedString := + ` +Name: bootstrap-token-abcdef +Namespace: kube-system +Labels: +Annotations: + +Type: generic + +Data +==== +token-secret: 24 bytes` + + expected := "\nName: bootstrap-token-abcdef\n" + + "Namespace: kube-system\n" + + "Labels: \n" + + "Annotations: \n" + + "\n" + + "Type: generic\n" + + "\n" + + "Data\n" + + "====\n" + + "token-secret:\t0123456789abcdef" + + decodedDescription, _ := s.Decode(encodedString, "kube-system/bootstrap-token-abcdef") + assert.Equal(t, expected, decodedDescription) +} diff --git a/internal/dao/testdata/secret.json b/internal/dao/testdata/secret.json new file mode 100644 index 0000000000..fa560690ae --- /dev/null +++ b/internal/dao/testdata/secret.json @@ -0,0 +1,15 @@ +{ + "apiVersion": "v1", + "data": { + "token-secret": "MDEyMzQ1Njc4OWFiY2RlZg==" + }, + "kind": "Secret", + "metadata": { + "creationTimestamp": "2024-01-15T18:19:00Z", + "name": "bootstrap-token-abcdef", + "namespace": "kube-system", + "resourceVersion": "243", + "uid": "6f5695d4-c0f4-4b65-890a-b1115ffd1f3b" + }, + "type": "bootstrap.kubernetes.io/token" +} diff --git a/internal/dao/utils_test.go b/internal/dao/utils_test.go new file mode 100644 index 0000000000..75da42e206 --- /dev/null +++ b/internal/dao/utils_test.go @@ -0,0 +1,77 @@ +package dao_test + +import ( + "encoding/json" + "fmt" + "os" + "path" + "strings" + + "github.com/derailed/k9s/internal/client" + "github.com/derailed/k9s/internal/dao" + "github.com/derailed/k9s/internal/watch" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" +) + +type testFactory struct { + inventory map[string]map[string][]runtime.Object +} + +func makeFactory() dao.Factory { + return &testFactory{ + inventory: map[string]map[string][]runtime.Object{ + "kube-system": { + "v1/secrets": { + load("secret"), + }, + }, + }, + } +} + +var _ dao.Factory = &testFactory{} + +func (f *testFactory) Client() client.Connection { + return nil +} +func (f *testFactory) Get(gvr, fqn string, wait bool, sel labels.Selector) (runtime.Object, error) { + ns, po := path.Split(fqn) + ns = strings.Trim(ns, "/") + + for _, o := range f.inventory[ns][gvr] { + if o.(*unstructured.Unstructured).GetName() == po { + return o, nil + } + } + + return nil, nil +} +func (f *testFactory) List(gvr, ns string, wait bool, sel labels.Selector) ([]runtime.Object, error) { + return f.inventory[ns][gvr], nil +} + +func (f *testFactory) ForResource(ns, gvr string) (informers.GenericInformer, error) { + return nil, nil +} +func (f *testFactory) CanForResource(ns, gvr string, verbs []string) (informers.GenericInformer, error) { + return nil, nil +} +func (f *testFactory) WaitForCacheSync() {} +func (f *testFactory) Forwarders() watch.Forwarders { + return nil +} +func (f *testFactory) DeleteForwarder(string) {} + +type testResource struct{} + +func load(n string) *unstructured.Unstructured { + raw, _ := os.ReadFile(fmt.Sprintf("testdata/%s.json", n)) + + var o unstructured.Unstructured + json.Unmarshal(raw, &o) + + return &o +} diff --git a/internal/view/secret.go b/internal/view/secret.go index 2be6d445df..e37cdb7687 100644 --- a/internal/view/secret.go +++ b/internal/view/secret.go @@ -51,7 +51,7 @@ func (s *Secret) decodeCmd(evt *tcell.EventKey) *tcell.EventKey { return nil } - d, err := dao.DecodeSecret(o.(*unstructured.Unstructured)) + d, err := dao.ExtractSecrets(o.(*unstructured.Unstructured)) if err != nil { s.App().Flash().Err(err) return nil