Files
troubleshoot/pkg/supportbundle/collect.go
Xav Paice 3513eeca19 Ensure clusterResources is added prior to other collectors (#768)
This change ensures that the clusterResources collector runs prior to any others
in order to not collect info on pods that collectors run during collection.

Additionally centralizes functions that are common to all collection to make future
maintenance simpler.

Fixes: #767
2022-11-01 12:16:01 +13:00

206 lines
6.3 KiB
Go

package supportbundle
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
"github.com/pkg/errors"
analyze "github.com/replicatedhq/troubleshoot/pkg/analyze"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/convert"
"github.com/replicatedhq/troubleshoot/pkg/version"
"gopkg.in/yaml.v2"
"k8s.io/client-go/kubernetes"
)
func runHostCollectors(hostCollectors []*troubleshootv1beta2.HostCollect, additionalRedactors *troubleshootv1beta2.Redactor, bundlePath string, opts SupportBundleCreateOpts) (collect.CollectorResult, error) {
collectSpecs := make([]*troubleshootv1beta2.HostCollect, 0, 0)
collectSpecs = append(collectSpecs, hostCollectors...)
allCollectedData := make(map[string][]byte)
var collectors []collect.HostCollector
for _, desiredCollector := range collectSpecs {
collector, ok := collect.GetHostCollector(desiredCollector, bundlePath)
if ok {
collectors = append(collectors, collector)
}
}
for _, collector := range collectors {
isExcluded, _ := collector.IsExcluded()
if isExcluded {
continue
}
opts.ProgressChan <- fmt.Sprintf("[%s] Running host collector...", collector.Title())
result, err := collector.Collect(opts.ProgressChan)
if err != nil {
opts.ProgressChan <- errors.Errorf("failed to run host collector: %s: %v", collector.Title(), err)
}
for k, v := range result {
allCollectedData[k] = v
}
}
collectResult := allCollectedData
globalRedactors := []*troubleshootv1beta2.Redact{}
if additionalRedactors != nil {
globalRedactors = additionalRedactors.Spec.Redactors
}
if opts.Redact {
err := collect.RedactResult(bundlePath, collectResult, globalRedactors)
if err != nil {
err = errors.Wrap(err, "failed to redact")
return collectResult, err
}
}
return collectResult, nil
}
func runCollectors(collectors []*troubleshootv1beta2.Collect, additionalRedactors *troubleshootv1beta2.Redactor, bundlePath string, opts SupportBundleCreateOpts) (collect.CollectorResult, error) {
collectSpecs := make([]*troubleshootv1beta2.Collect, 0)
collectSpecs = append(collectSpecs, collectors...)
collectSpecs = collect.EnsureCollectorInList(collectSpecs, troubleshootv1beta2.Collect{ClusterInfo: &troubleshootv1beta2.ClusterInfo{}})
collectSpecs = collect.EnsureCollectorInList(collectSpecs, troubleshootv1beta2.Collect{ClusterResources: &troubleshootv1beta2.ClusterResources{}})
collectSpecs = collect.EnsureClusterResourcesFirst(collectSpecs)
var allCollectors []collect.Collector
allCollectedData := make(map[string][]byte)
k8sClient, err := kubernetes.NewForConfig(opts.KubernetesRestConfig)
if err != nil {
return nil, errors.Wrap(err, "failed to instantiate Kubernetes client")
}
for _, desiredCollector := range collectSpecs {
if collectorInterface, ok := collect.GetCollector(desiredCollector, bundlePath, opts.Namespace, opts.KubernetesRestConfig, k8sClient, opts.SinceTime); ok {
if collector, ok := collectorInterface.(collect.Collector); ok {
err := collector.CheckRBAC(context.Background(), collector, desiredCollector, opts.KubernetesRestConfig, opts.Namespace)
if err != nil {
return nil, errors.Wrap(err, "failed to check RBAC for collectors")
}
if mergeCollector, ok := collectorInterface.(collect.MergeableCollector); ok {
allCollectors, err = mergeCollector.Merge(allCollectors)
if err != nil {
msg := fmt.Sprintf("failed to merge collector: %s: %s", collector.Title(), err)
opts.CollectorProgressCallback(opts.ProgressChan, msg)
}
} else {
allCollectors = append(allCollectors, collector)
}
}
}
}
foundForbidden := false
for _, c := range allCollectors {
for _, e := range c.GetRBACErrors() {
foundForbidden = true
opts.ProgressChan <- e
}
}
if foundForbidden && !opts.CollectWithoutPermissions {
return nil, errors.New("insufficient permissions to run all collectors")
}
for _, collector := range allCollectors {
isExcluded, _ := collector.IsExcluded()
if isExcluded {
continue
}
// skip collectors with RBAC errors unless its the ClusterResources collector
if collector.HasRBACErrors() {
if _, ok := collector.(*collect.CollectClusterResources); !ok {
msg := fmt.Sprintf("skipping collector %s with insufficient RBAC permissions", collector.Title())
opts.CollectorProgressCallback(opts.ProgressChan, msg)
continue
}
}
opts.CollectorProgressCallback(opts.ProgressChan, collector.Title())
result, err := collector.Collect(opts.ProgressChan)
if err != nil {
opts.ProgressChan <- errors.Errorf("failed to run collector: %s: %v", collector.Title(), err)
}
for k, v := range result {
allCollectedData[k] = v
}
}
collectResult := allCollectedData
globalRedactors := []*troubleshootv1beta2.Redact{}
if additionalRedactors != nil {
globalRedactors = additionalRedactors.Spec.Redactors
}
if opts.Redact {
err := collect.RedactResult(bundlePath, collectResult, globalRedactors)
if err != nil {
err = errors.Wrap(err, "failed to redact")
return collectResult, err
}
}
return collectResult, nil
}
func findFileName(basename, extension string) (string, error) {
n := 1
name := basename
for {
filename := name + "." + extension
if _, err := os.Stat(filename); os.IsNotExist(err) {
return filename, nil
} else if err != nil {
return "", errors.Wrap(err, "check file exists")
}
name = fmt.Sprintf("%s (%d)", basename, n)
n = n + 1
}
}
const VersionFilename = "version.yaml"
func getVersionFile() (io.Reader, error) {
version := troubleshootv1beta2.SupportBundleVersion{
ApiVersion: "troubleshoot.sh/v1beta2",
Kind: "SupportBundle",
Spec: troubleshootv1beta2.SupportBundleVersionSpec{
VersionNumber: version.Version(),
},
}
b, err := yaml.Marshal(version)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal version data")
}
return bytes.NewBuffer(b), nil
}
const AnalysisFilename = "analysis.json"
func getAnalysisFile(analyzeResults []*analyze.AnalyzeResult) (io.Reader, error) {
data := convert.FromAnalyzerResult(analyzeResults)
analysis, err := json.MarshalIndent(data, "", " ")
if err != nil {
return nil, errors.Wrap(err, "failed to marshal analysis")
}
return bytes.NewBuffer(analysis), nil
}