k9s/internal/dao/secret.go

99 lines
2.6 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s
package dao
import (
"fmt"
"strings"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
// Secret represents a secret K8s resource.
type Secret struct {
Resource
decodeData bool
}
// Describe describes a secret that can be encoded or decoded.
func (s *Secret) Describe(path string) (string, error) {
encodedDescription, err := s.Generic.Describe(path)
if err != nil {
return "", err
}
if !s.decodeData {
return encodedDescription, nil
}
return s.Decode(encodedDescription, path)
}
// SetDecodeData toggles decode mode.
func (s *Secret) SetDecodeData(b bool) {
s.decodeData = b
}
// Decode removes the encoded 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 {
return "", err
}
dataEndIndex := strings.Index(encodedDescription, "====")
if dataEndIndex == -1 {
return "", fmt.Errorf("unable to find data section in secret description")
}
dataEndIndex += 4
if dataEndIndex >= len(encodedDescription) {
return "", fmt.Errorf("data section in secret description is invalid")
}
// Remove the encoded part from k8s's describe API
// More details about the reasoning of index: https://github.com/kubernetes/kubectl/blob/v0.29.0/pkg/describe/describe.go#L2542
body := encodedDescription[0:dataEndIndex]
data, err := ExtractSecrets(o)
if err != nil {
return "", err
}
decodedSecrets := make([]string, 0, len(data))
for k, v := range data {
line := fmt.Sprintf("%s: %s", k, v)
decodedSecrets = append(decodedSecrets, strings.TrimSpace(line))
}
return body + "\n" + strings.Join(decodedSecrets, "\n"), nil
}
// 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 ExtractSecrets(o runtime.Object) (map[string]string, error) {
u, ok := o.(*unstructured.Unstructured)
if !ok {
return nil, fmt.Errorf("expecting *unstructured.Unstructured but got %T", o)
}
var secret v1.Secret
err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &secret)
if err != nil {
return nil, err
}
secretData := make(map[string]string, len(secret.Data))
for k, val := range secret.Data {
secretData[k] = string(val)
}
return secretData, nil
}