Files
troubleshoot/pkg/collect/secret.go
Diamon Wiggins 989780af69 feat: allow secrets collector to retreive all key data if specified (#1801)
* allow secrets collector retreival all key data if specified

* add new line

* remove unneeded comments
2025-06-30 10:06:14 -04:00

164 lines
5.0 KiB
Go

package collect
import (
"bytes"
"context"
"encoding/json"
"fmt"
"path/filepath"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
corev1 "k8s.io/api/core/v1"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
type SecretOutput struct {
Namespace string `json:"namespace"`
Name string `json:"name"`
Key string `json:"key"`
SecretExists bool `json:"secretExists"`
KeyExists bool `json:"keyExists"`
Value string `json:"value,omitempty"`
Data map[string]string `json:"data,omitempty"`
}
type CollectSecret struct {
Collector *troubleshootv1beta2.Secret
BundlePath string
Namespace string
ClientConfig *rest.Config
Client kubernetes.Interface
Context context.Context
RBACErrors
}
func (c *CollectSecret) Title() string {
return getCollectorName(c)
}
func (c *CollectSecret) IsExcluded() (bool, error) {
return isExcluded(c.Collector.Exclude)
}
func (c *CollectSecret) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
output := NewResult()
secrets := []corev1.Secret{}
if c.Collector.Name != "" {
secret, err := c.Client.CoreV1().Secrets(c.Collector.Namespace).Get(c.Context, c.Collector.Name, metav1.GetOptions{})
if err != nil {
if kuberneteserrors.IsNotFound(err) {
filePath, encoded, err := secretToOutput(c.Collector, nil, c.Collector.Name)
if err != nil {
return output, errors.Wrapf(err, "collect secret %s", c.Collector.Name)
}
output.SaveResult(c.BundlePath, filePath, bytes.NewBuffer(encoded))
}
output.SaveResult(c.BundlePath, GetSecretErrorsFileName(c.Collector), marshalErrors([]string{err.Error()}))
return output, nil
}
secrets = append(secrets, *secret)
} else if len(c.Collector.Selector) > 0 {
ss, err := listSecretsForSelector(c.Context, c.Client, c.Collector.Namespace, c.Collector.Selector)
if err != nil {
output.SaveResult(c.BundlePath, GetSecretErrorsFileName(c.Collector), marshalErrors([]string{err.Error()}))
return output, nil
}
secrets = append(secrets, ss...)
} else {
return nil, errors.New("either name or selector must be specified")
}
for _, secret := range secrets {
filePath, encoded, err := secretToOutput(c.Collector, &secret, secret.Name)
if err != nil {
return output, errors.Wrapf(err, "collect secret %s", secret.Name)
}
output.SaveResult(c.BundlePath, filePath, bytes.NewBuffer(encoded))
}
return output, nil
}
func secretToOutput(secretCollector *troubleshootv1beta2.Secret, secret *corev1.Secret, secretName string) (string, []byte, error) {
foundSecret := SecretOutput{
Namespace: secretCollector.Namespace,
Name: secretName,
Key: secretCollector.Key,
}
if secret != nil {
foundSecret.SecretExists = true
if secretCollector.IncludeAllData {
foundSecret.Data = make(map[string]string)
for k, v := range secret.Data {
foundSecret.Data[k] = string(v)
}
} else if secretCollector.Key != "" {
if val, ok := secret.Data[secretCollector.Key]; ok {
foundSecret.KeyExists = true
if secretCollector.IncludeValue {
foundSecret.Value = string(val)
}
}
}
}
return marshalSecretOutput(secretCollector, foundSecret)
}
func listSecretsForSelector(ctx context.Context, client kubernetes.Interface, namespace string, selector []string) ([]corev1.Secret, error) {
serializedLabelSelector := strings.Join(selector, ",")
listOptions := metav1.ListOptions{
LabelSelector: serializedLabelSelector,
}
secrets, err := client.CoreV1().Secrets(namespace).List(ctx, listOptions)
if err != nil {
return nil, err
}
return secrets.Items, nil
}
func marshalSecretOutput(secretCollector *troubleshootv1beta2.Secret, secret SecretOutput) (string, []byte, error) {
path := GetSecretFileName(secretCollector, secret.Name)
b, err := json.MarshalIndent(secret, "", " ")
if err != nil {
return path, nil, err
}
return path, b, nil
}
func GetSecretFileName(secretCollector *troubleshootv1beta2.Secret, name string) string {
parts := []string{"secrets", secretCollector.Namespace, name}
// Only include key in filename when doing key-specific processing
if secretCollector.Key != "" && !secretCollector.IncludeAllData {
parts = append(parts, secretCollector.Key)
}
return fmt.Sprintf("%s.json", filepath.Join(parts...))
}
func GetSecretErrorsFileName(secretCollector *troubleshootv1beta2.Secret) string {
parts := []string{"secrets-errors", secretCollector.Namespace}
if secretCollector.Name != "" {
parts = append(parts, secretCollector.Name)
} else {
parts = append(parts, selectorToString(secretCollector.Selector))
}
// Only include key in filename when doing key-specific processing
if secretCollector.Key != "" && !secretCollector.IncludeAllData {
parts = append(parts, secretCollector.Key)
}
return fmt.Sprintf("%s.json", filepath.Join(parts...))
}