Skip to content

Commit

Permalink
More updates to support bundle binary
Browse files Browse the repository at this point in the history
  • Loading branch information
banjoh committed Sep 21, 2023
1 parent a55d61e commit 1791b33
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 101 deletions.
2 changes: 1 addition & 1 deletion cmd/troubleshoot/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust

// `no-uri` references the `followURI` functionality where we can use an upstream spec when creating a support bundle
// This flag makes sure we can also disable this and fall back to the default spec.
cmd.Flags().Bool("no-uri", false, "When this flag is used, Troubleshoot does not attempt to retrieve the bundle referenced by the uri: field in the spec.`")
cmd.Flags().Bool("no-uri", false, "When this flag is used, Troubleshoot does not attempt to retrieve the spec referenced by the uri: field`")

k8sutil.AddFlags(cmd.Flags())

Expand Down
141 changes: 70 additions & 71 deletions cmd/troubleshoot/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"os"
"os/signal"
"path/filepath"
"strings"
"sync"
"time"

Expand All @@ -18,14 +17,12 @@ import (
"github.com/mattn/go-isatty"
"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/specs"
privSpecs "github.com/replicatedhq/troubleshoot/internal/specs"
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/convert"
"github.com/replicatedhq/troubleshoot/pkg/httputil"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/loader"
"github.com/replicatedhq/troubleshoot/pkg/supportbundle"
"github.com/replicatedhq/troubleshoot/pkg/types"
"github.com/spf13/viper"
Expand All @@ -48,7 +45,8 @@ func runTroubleshoot(v *viper.Viper, args []string) error {
return errors.Wrap(err, "failed to create kubernetes client")
}

kinds, err := specs.LoadFromCLIArgs(ctx, client, args, viper.GetViper())
argWithRedactors := append(args, v.GetStringSlice("redactors")...)
kinds, err := specs.LoadFromCLIArgs(ctx, client, argWithRedactors, viper.GetViper())
if err != nil {
return err
}
Expand Down Expand Up @@ -108,66 +106,67 @@ func runTroubleshoot(v *viper.Viper, args []string) error {
})
}

var mainBundle *troubleshootv1beta2.SupportBundle

additionalRedactors := &troubleshootv1beta2.Redactor{}

// Defining `v` below will render using `v` in reference to Viper unusable.
// Therefore refactoring `v` to `val` will make sure we can still use it.
for _, val := range args {

collectorContent, err := supportbundle.LoadSupportBundleSpec(val)
if err != nil {
return errors.Wrap(err, "failed to load support bundle spec")
}
multidocs := strings.Split(string(collectorContent), "\n---\n")
// Referencing `ParseSupportBundle with a secondary arg of `no-uri`
// Will make sure we can enable or disable the use of the `Spec.uri` field for an upstream spec.
// This change will not have an impact on KOTS' usage of `ParseSupportBundle`
// As Kots uses `load.go` directly.
supportBundle, err := supportbundle.ParseSupportBundle([]byte(multidocs[0]), !v.GetBool("no-uri"))
if err != nil {
return errors.Wrap(err, "failed to parse support bundle spec")
}

mainBundle = supportbundle.ConcatSpec(mainBundle, supportBundle)

parsedRedactors, err := supportbundle.ParseRedactorsFromDocs(multidocs)
if err != nil {
return errors.Wrap(err, "failed to parse redactors from doc")
}
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, parsedRedactors...)
}

if v.GetBool("load-cluster-specs") {
kinds, err := loadClusterSpecs(ctx, v)
if err != nil {
return err
}
if len(kinds.SupportBundlesV1Beta2) == 0 {
return errors.New("no support bundle specs found in cluster")
}
for _, sb := range kinds.SupportBundlesV1Beta2 {
sb := sb // Why? https://golang.org/doc/faq#closures_and_goroutines
mainBundle = supportbundle.ConcatSpec(mainBundle, &sb)
}

for _, redactor := range kinds.RedactorsV1Beta2 {
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactor.Spec.Redactors...)
}
mainBundle := &troubleshootv1beta2.SupportBundle{}
for _, sb := range kinds.SupportBundlesV1Beta2 {
sb := sb
mainBundle = supportbundle.ConcatSpec(mainBundle, &sb)
}

if mainBundle == nil {
return errors.New("no support bundle specs provided to run")
} else if mainBundle.Spec.Collectors == nil && mainBundle.Spec.HostCollectors == nil {
return errors.New("no collectors specified in support bundle")
additionalRedactors := &troubleshootv1beta2.Redactor{}
for _, r := range kinds.RedactorsV1Beta2 {
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, r.Spec.Redactors...)
}

redactors, err := supportbundle.GetRedactorsFromURIs(v.GetStringSlice("redactors"))
if err != nil {
return errors.Wrap(err, "failed to get redactors")
}
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactors...)
// // Defining `v` below will render using `v` in reference to Viper unusable.
// // Therefore refactoring `v` to `val` will make sure we can still use it.
// for _, val := range args {

// collectorContent, err := supportbundle.LoadSupportBundleSpec(val)
// if err != nil {
// return errors.Wrap(err, "failed to load support bundle spec")
// }
// multidocs := strings.Split(string(collectorContent), "\n---\n")
// // Referencing `ParseSupportBundle with a secondary arg of `no-uri`
// // Will make sure we can enable or disable the use of the `Spec.uri` field for an upstream spec.
// // This change will not have an impact on KOTS' usage of `ParseSupportBundle`
// // As Kots uses `load.go` directly.
// supportBundle, err := supportbundle.ParseSupportBundle([]byte(multidocs[0]), !v.GetBool("no-uri"))
// if err != nil {
// return errors.Wrap(err, "failed to parse support bundle spec")
// }

// mainBundle = supportbundle.ConcatSpec(mainBundle, supportBundle)

// parsedRedactors, err := supportbundle.ParseRedactorsFromDocs(multidocs)
// if err != nil {
// return errors.Wrap(err, "failed to parse redactors from doc")
// }
// additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, parsedRedactors...)
// }

// if v.GetBool("load-cluster-specs") {
// kinds, err := loadClusterSpecs(ctx, v)
// if err != nil {
// return err
// }
// if len(kinds.SupportBundlesV1Beta2) == 0 {
// return errors.New("no support bundle specs found in cluster")
// }
// for _, sb := range kinds.SupportBundlesV1Beta2 {
// sb := sb // Why? https://golang.org/doc/faq#closures_and_goroutines
// mainBundle = supportbundle.ConcatSpec(mainBundle, &sb)
// }

// for _, redactor := range kinds.RedactorsV1Beta2 {
// additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactor.Spec.Redactors...)
// }
// }

// redactors, err := supportbundle.GetRedactorsFromURIs(v.GetStringSlice("redactors"))
// if err != nil {
// return errors.Wrap(err, "failed to get redactors")
// }
// additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactors...)

var wg sync.WaitGroup
collectorCB := func(c chan interface{}, msg string) { c <- msg }
Expand Down Expand Up @@ -295,19 +294,19 @@ the %s Admin Console to begin analysis.`
return nil
}

func loadClusterSpecs(ctx context.Context, v *viper.Viper) (*loader.TroubleshootKinds, error) {
config, err := k8sutil.GetRESTConfig()
if err != nil {
return nil, errors.Wrap(err, "failed to convert kube flags to rest config")
}
// func loadClusterSpecs(ctx context.Context, v *viper.Viper) (*loader.TroubleshootKinds, error) {
// config, err := k8sutil.GetRESTConfig()
// if err != nil {
// return nil, errors.Wrap(err, "failed to convert kube flags to rest config")
// }

client, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, errors.Wrap(err, "failed to convert create k8s client")
}
// client, err := kubernetes.NewForConfig(config)
// if err != nil {
// return nil, errors.Wrap(err, "failed to convert create k8s client")
// }

return privSpecs.LoadFromCluster(ctx, client, v.GetStringSlice("selector"), v.GetString("namespace"))
}
// return privSpecs.LoadFromCluster(ctx, client, v.GetStringSlice("selector"), v.GetString("namespace"))
// }

func parseTimeFlags(v *viper.Viper) (*time.Time, error) {
var (
Expand Down
13 changes: 4 additions & 9 deletions internal/specs/configmaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,17 @@ import (
"k8s.io/klog/v2"
)

func LoadFromConfigMap(ctx context.Context, client kubernetes.Interface, ns string, name string, key string) ([]byte, error) {
func LoadFromConfigMap(ctx context.Context, client kubernetes.Interface, ns string, name string) (map[string]string, error) {
foundConfigMap, err := client.CoreV1().ConfigMaps(ns).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to get configmap")
}

spec, ok := foundConfigMap.Data[key]
if !ok {
return nil, errors.Errorf("spec not found in configmap %s", name)
}

klog.V(1).InfoS("Loaded spec from config map", "name",
foundConfigMap.Name, "namespace", foundConfigMap.Namespace, "data key", key,
klog.V(1).InfoS("Loaded data from config map", "name",
foundConfigMap.Name, "namespace", foundConfigMap.Namespace,
)

return []byte(spec), nil
return foundConfigMap.Data, nil
}

func LoadFromConfigMapMatchingLabel(ctx context.Context, client kubernetes.Interface, label string, ns string, key string) ([]string, error) {
Expand Down
12 changes: 2 additions & 10 deletions internal/specs/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,13 @@ import (
"k8s.io/klog/v2"
)

func LoadFromSecret(ctx context.Context, client kubernetes.Interface, ns string, name string, key string) ([]byte, error) {
func LoadFromSecret(ctx context.Context, client kubernetes.Interface, ns string, name string) (map[string][]byte, error) {
foundSecret, err := client.CoreV1().Secrets(ns).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to get secret")
}

spec, ok := foundSecret.Data[key]
if !ok {
return nil, errors.Errorf("spec not found in secret %s", name)
}

klog.V(1).InfoS("Loaded spec from secret", "name",
foundSecret.Name, "namespace", foundSecret.Namespace, "data key", key,
)
return spec, nil
return foundSecret.Data, nil
}

func LoadFromSecretMatchingLabel(ctx context.Context, client kubernetes.Interface, label string, ns string, key string) ([]string, error) {
Expand Down
28 changes: 24 additions & 4 deletions internal/specs/specs.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,34 @@ func LoadFromCLIArgs(ctx context.Context, client kubernetes.Interface, args []st
// format secret/namespace-name/secret-name
pathParts := strings.Split(v, "/")
if len(pathParts) != 3 {
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Errorf("path %s must have 3 components", v))
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Errorf("secret path %q must have 3 components", v))
}

spec, err := LoadFromSecret(ctx, client, pathParts[1], pathParts[2], "preflight-spec")
data, err := LoadFromSecret(ctx, client, pathParts[1], pathParts[2])
if err != nil {
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Wrap(err, "failed to get spec from secret"))
}

rawSpecs = append(rawSpecs, string(spec))
// Append all data in the secret. Some may not be specs, but that's ok. They will be ignored.
for _, spec := range data {
rawSpecs = append(rawSpecs, string(spec))
}
} else if strings.HasPrefix(v, "configmap/") {
// format configmap/namespace-name/configmap-name
pathParts := strings.Split(v, "/")
if len(pathParts) != 3 {
return nil, errors.Errorf("configmap path %q must have 3 components", v)
}

data, err := LoadFromConfigMap(ctx, client, pathParts[1], pathParts[2])
if err != nil {
return nil, errors.Wrap(err, "failed to get spec from configmap")
}

// Append all data in the configmap. Some may not be specs, but that's ok. They will be ignored.
for _, spec := range data {
rawSpecs = append(rawSpecs, spec)
}
} else if _, err := os.Stat(v); err == nil {
b, err := os.ReadFile(v)
if err != nil {
Expand Down Expand Up @@ -154,7 +173,8 @@ func LoadFromCLIArgs(ctx context.Context, client kubernetes.Interface, args []st
}

kinds, err := loader.LoadSpecs(ctx, loader.LoadOptions{
RawSpecs: rawSpecs,
RawSpecs: rawSpecs,
IgnoreUpdateDownloads: vp.GetBool("no-uri"),
})
if err != nil {
return nil, err
Expand Down
Loading

0 comments on commit 1791b33

Please sign in to comment.