Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Dry run flag to print preflight specs to std out #1240

Merged
merged 22 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cmd/analyze/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"os"
"strings"

"github.com/replicatedhq/troubleshoot/cmd/util"
"github.com/replicatedhq/troubleshoot/cmd/internal/util"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -43,6 +43,8 @@ func RootCmd() *cobra.Command {

cobra.OnInitialize(initConfig)

cmd.AddCommand(util.VersionCmd())

cmd.Flags().String("analyzers", "", "filename or url of the analyzers to use")
cmd.Flags().Bool("debug", false, "enable debug logging")

Expand Down
4 changes: 2 additions & 2 deletions cmd/collect/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"os"
"strings"

"github.com/replicatedhq/troubleshoot/cmd/util"
"github.com/replicatedhq/troubleshoot/cmd/internal/util"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -43,7 +43,7 @@ func RootCmd() *cobra.Command {

cobra.OnInitialize(initConfig)

cmd.AddCommand(VersionCmd())
cmd.AddCommand(util.VersionCmd())

cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use")
cmd.Flags().Bool("redact", true, "enable/disable default redactions")
Expand Down
22 changes: 0 additions & 22 deletions cmd/collect/cli/version.go

This file was deleted.

File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package cli
package util

import (
"fmt"
Expand Down
8 changes: 6 additions & 2 deletions cmd/preflight/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"os"
"strings"

"github.com/replicatedhq/troubleshoot/cmd/util"
"github.com/replicatedhq/troubleshoot/cmd/internal/util"
"github.com/replicatedhq/troubleshoot/internal/traces"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
Expand Down Expand Up @@ -64,10 +64,14 @@ that a cluster meets the requirements to run an application.`,

cobra.OnInitialize(initConfig)

cmd.AddCommand(VersionCmd())
cmd.AddCommand(util.VersionCmd())
cmd.AddCommand(OciFetchCmd())
preflight.AddFlags(cmd.PersistentFlags())

// Dry run flag should be in cmd.PersistentFlags() flags made available to all subcommands
// Adding here to avoid that
cmd.Flags().Bool("dry-run", false, "print the preflight spec without running preflight checks")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this added below in pkg/preflight/flags.go? Not sure I understand the comment.

Copy link
Member Author

@banjoh banjoh Sep 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this added below in pkg/preflight/flags.go? Not sure I understand the comment.

All flags in pkg/preflight/flags.go are also available to all subcommands e.g preflight completions.

[evans] $ preflight completion -h
Generate the autocompletion script for preflight for the specified shell.
See each sub-command's help for details on how to use the generated script.

Usage:
  preflight completion [command]

Available Commands:
  bash        Generate the autocompletion script for bash
  fish        Generate the autocompletion script for fish
  powershell  Generate the autocompletion script for powershell
  zsh         Generate the autocompletion script for zsh

Flags:
  -h, --help   help for completion

Global Flags:
      --collect-without-permissions   always run preflight checks even if some require permissions that preflight does not have (default true)
      --collector-image string        the full name of the collector image to use
      --collector-pullpolicy string   the pull policy of the collector image
      --cpuprofile string             File path to write cpu profiling data
      --debug                         enable debug logging
      --format string                 output format, one of human, json, yaml. only used when interactive is set to false (default "human")
      --interactive                   interactive preflights (default true)
      --memprofile string             File path to write memory profiling data
  -o, --output string                 specify the output file path for the preflight checks
      --selector string               selector (label query) to filter remote collection nodes on.
      --since string                  force pod logs collectors to return logs newer than a relative duration like 5s, 2m, or 3h.
      --since-time string             force pod logs collectors to return logs after a specific date (RFC3339)

Use "preflight completion [command] --help" for more information about a command.

Running preflight completions --dry-run or preflight completions --since 3h is redundant.

Copy link
Member Author

@banjoh banjoh Sep 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we could change this line to preflight.AddFlags(cmd.Flags())

Personally I prefer each subcommand to explicitly list all the flags it needs to expose like the support-bundle binary does. Thats for another PR though. I thought refactoring this part would have been scope creep.


k8sutil.AddFlags(cmd.Flags())

// Initialize klog flags
Expand Down
23 changes: 0 additions & 23 deletions cmd/preflight/cli/version.go

This file was deleted.

4 changes: 2 additions & 2 deletions cmd/troubleshoot/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"os"
"strings"

"github.com/replicatedhq/troubleshoot/cmd/util"
"github.com/replicatedhq/troubleshoot/cmd/internal/util"
"github.com/replicatedhq/troubleshoot/internal/traces"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/logger"
Expand Down Expand Up @@ -62,7 +62,7 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust

cmd.AddCommand(Analyze())
cmd.AddCommand(Redact())
cmd.AddCommand(VersionCmd())
cmd.AddCommand(util.VersionCmd())

cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use")
cmd.Flags().Bool("redact", true, "enable/disable default redactions")
Expand Down
3 changes: 2 additions & 1 deletion docs/preflight.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ preflight [url] [flags]
--cpuprofile string File path to write cpu profiling data
--debug enable debug logging
--disable-compression If true, opt-out of response compression for all requests to the server
--dry-run print the preflight spec without running preflight checks
--format string output format, one of human, json, yaml. only used when interactive is set to false (default "human")
-h, --help help for preflight
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
Expand All @@ -53,4 +54,4 @@ preflight [url] [flags]
* [preflight oci-fetch](preflight_oci-fetch.md) - Fetch a preflight from an OCI registry and print it to standard out
* [preflight version](preflight_version.md) - Print the current version and exit

###### Auto generated by spf13/cobra on 8-Jun-2023
###### Auto generated by spf13/cobra on 31-Aug-2023
2 changes: 1 addition & 1 deletion docs/preflight_oci-fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ preflight oci-fetch [URI] [flags]

* [preflight](preflight.md) - Run and retrieve preflight checks in a cluster

###### Auto generated by spf13/cobra on 8-Jun-2023
###### Auto generated by spf13/cobra on 31-Aug-2023
2 changes: 1 addition & 1 deletion docs/preflight_version.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ preflight version [flags]

* [preflight](preflight.md) - Run and retrieve preflight checks in a cluster

###### Auto generated by spf13/cobra on 3-Jan-2023
###### Auto generated by spf13/cobra on 31-Aug-2023
2 changes: 1 addition & 1 deletion docs/support-bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ support-bundle [urls...] [flags]
* [support-bundle redact](support-bundle_redact.md) - Redact information from a generated support bundle archive
* [support-bundle version](support-bundle_version.md) - Print the current version and exit

###### Auto generated by spf13/cobra on 8-Jun-2023
###### Auto generated by spf13/cobra on 31-Aug-2023
2 changes: 1 addition & 1 deletion docs/support-bundle_analyze.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ support-bundle analyze [url] [flags]

* [support-bundle](support-bundle.md) - Generate a support bundle

###### Auto generated by spf13/cobra on 3-Jan-2023
###### Auto generated by spf13/cobra on 31-Aug-2023
2 changes: 1 addition & 1 deletion docs/support-bundle_redact.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ support-bundle redact [urls...] [flags]

* [support-bundle](support-bundle.md) - Generate a support bundle

###### Auto generated by spf13/cobra on 3-Jan-2023
###### Auto generated by spf13/cobra on 31-Aug-2023
2 changes: 1 addition & 1 deletion docs/support-bundle_version.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ support-bundle version [flags]

* [support-bundle](support-bundle.md) - Generate a support bundle

###### Auto generated by spf13/cobra on 3-Jan-2023
###### Auto generated by spf13/cobra on 31-Aug-2023
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ require (
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
sigs.k8s.io/yaml v1.3.0
)

replace (
Expand Down
5 changes: 4 additions & 1 deletion internal/testutils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import (

func GetTestFixture(t *testing.T, path string) string {
t.Helper()
p := filepath.Join("../../testdata", path)
p := path
if !filepath.IsAbs(path) {
p = filepath.Join("../../testdata", path)
}
b, err := os.ReadFile(p)
require.NoError(t, err)
return string(b)
Expand Down
31 changes: 30 additions & 1 deletion pkg/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package loader

import (
"context"
"reflect"
"strings"

"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
Expand All @@ -10,10 +12,10 @@ import (
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/docrewrite"
"github.com/replicatedhq/troubleshoot/pkg/types"
"gopkg.in/yaml.v2"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog/v2"
"sigs.k8s.io/yaml"
)

var decoder runtime.Decoder
Expand Down Expand Up @@ -97,6 +99,33 @@ func (kinds *TroubleshootKinds) Add(other *TroubleshootKinds) {
kinds.SupportBundlesV1Beta2 = append(kinds.SupportBundlesV1Beta2, other.SupportBundlesV1Beta2...)
}

// ToYaml returns a yaml document/multi-doc of all the parsed specs
// This function utilises reflection to iterate over all the fields
// of the TroubleshootKinds object then marshals them to yaml.
func (kinds *TroubleshootKinds) ToYaml() (string, error) {
rawList := []string{}
obj := reflect.ValueOf(*kinds)

for i := 0; i < obj.NumField(); i++ {
field := obj.Field(i)
if field.Kind() != reflect.Slice {
continue
}

// skip empty slices to avoid empty yaml documents
for count := 0; count < field.Len(); count++ {
val := field.Index(count)
yamlOut, err := yaml.Marshal(val.Interface())
if err != nil {
return "", err
}
rawList = append(rawList, string(yamlOut))
}
}

return strings.Join(rawList, "---\n"), nil
}

func NewTroubleshootKinds() *TroubleshootKinds {
return &TroubleshootKinds{}
}
Expand Down
57 changes: 57 additions & 0 deletions pkg/loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,3 +522,60 @@ func TestAddingKinds(t *testing.T) {
}
assert.Equal(t, k2, k1)
}

func TestToYaml(t *testing.T) {
k := &TroubleshootKinds{
AnalyzersV1Beta2: []troubleshootv1beta2.Analyzer{
{
TypeMeta: metav1.TypeMeta{
Kind: "Analyzer",
APIVersion: "troubleshoot.sh/v1beta2",
},
Spec: troubleshootv1beta2.AnalyzerSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "Cluster is up to date",
},
},
},
},
},
},
},
},
},
SupportBundlesV1Beta2: []troubleshootv1beta2.SupportBundle{
{
TypeMeta: metav1.TypeMeta{
Kind: "SupportBundle",
APIVersion: "troubleshoot.sh/v1beta2",
},
Spec: troubleshootv1beta2.SupportBundleSpec{
Collectors: []*troubleshootv1beta2.Collect{
{
ClusterResources: &troubleshootv1beta2.ClusterResources{
IgnoreRBAC: true,
},
},
},
},
},
},
}

y, err := k.ToYaml()
require.NoError(t, err)
assert.Contains(t, string(y), `apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
creationTimestamp: null
spec:
collectors:
- clusterResources:
ignoreRBAC: true`)
assert.Contains(t, string(y), "message: Cluster is up to date")
}
1 change: 1 addition & 0 deletions pkg/preflight/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
flagSince = "since"
flagOutput = "output"
flagDebug = "debug"
flagDryRun = "dry-run"
)

type PreflightFlags struct {
Expand Down
37 changes: 23 additions & 14 deletions pkg/preflight/read_specs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,55 @@ import (
"github.com/replicatedhq/troubleshoot/internal/specs"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/loader"
"github.com/spf13/viper"
"k8s.io/client-go/kubernetes"
)

type PreflightSpecs struct {
PreflightSpec *troubleshootv1beta2.Preflight
HostPreflightSpec *troubleshootv1beta2.HostPreflight
UploadResultSpecs []*troubleshootv1beta2.Preflight
}

func (p *PreflightSpecs) Read(args []string) error {
func readSpecs(args []string) (*loader.TroubleshootKinds, error) {
config, err := k8sutil.GetRESTConfig()
if err != nil {
return errors.Wrap(err, "failed to convert kube flags to rest config")
return nil, errors.Wrap(err, "failed to convert kube flags to rest config")
}

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

ctx := context.Background()
kinds, err := specs.LoadFromCLIArgs(ctx, client, args, viper.GetViper())
if err != nil {
return err
return nil, err
}

ret := loader.NewTroubleshootKinds()

// Concatenate all preflight inclusterSpecs that don't have an upload destination
inclusterSpecs := []troubleshootv1beta2.Preflight{}
var concatenatedSpec *troubleshootv1beta2.Preflight
for _, v := range kinds.PreflightsV1Beta2 {
v := v // https://golang.org/doc/faq#closures_and_goroutines
if v.Spec.UploadResultsTo == "" {
p.PreflightSpec = ConcatPreflightSpec(p.PreflightSpec, &v)
concatenatedSpec = ConcatPreflightSpec(concatenatedSpec, &v)
} else {
p.UploadResultSpecs = append(p.UploadResultSpecs, &v)
inclusterSpecs = append(inclusterSpecs, v)
}
}

if concatenatedSpec != nil {
inclusterSpecs = append(inclusterSpecs, *concatenatedSpec)
}
ret.PreflightsV1Beta2 = inclusterSpecs

var hostSpec *troubleshootv1beta2.HostPreflight
for _, v := range kinds.HostPreflightsV1Beta2 {
v := v // https://golang.org/doc/faq#closures_and_goroutines
p.HostPreflightSpec = ConcatHostPreflightSpec(p.HostPreflightSpec, &v)
hostSpec = ConcatHostPreflightSpec(hostSpec, &v)
}
if hostSpec != nil {
ret.HostPreflightsV1Beta2 = []troubleshootv1beta2.HostPreflight{*hostSpec}
}

return nil
return ret, nil
}
Loading
Loading