Skip to content

Commit

Permalink
feat: command to dump troubleshoot specs to std out
Browse files Browse the repository at this point in the history
  • Loading branch information
banjoh committed Jun 23, 2023
1 parent 2c6b186 commit 10b8943
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 0 deletions.
1 change: 1 addition & 0 deletions cmd/preflight/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ that a cluster meets the requirements to run an application.`,

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

k8sutil.AddFlags(cmd.Flags())
Expand Down
1 change: 1 addition & 0 deletions cmd/troubleshoot/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust

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

cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use")
Expand Down
134 changes: 134 additions & 0 deletions cmd/util/dump_spec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package util

import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"

"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/loader"
"github.com/replicatedhq/troubleshoot/pkg/oci"
"github.com/replicatedhq/troubleshoot/pkg/specs"
"github.com/replicatedhq/troubleshoot/pkg/types"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
)

func DumpSpecCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "dump [URI]",
Args: cobra.MinimumNArgs(1),
Short: "BLAH BLAH BLAH",
RunE: func(cmd *cobra.Command, args []string) error {
return printSpecs(args)
},
}
return cmd
}

func printSpecs(args []string) error {
var theSpec []byte
var err error
ctx := context.Background()

kinds := loader.NewTroubleshootKinds()

// TODO: Earmarked for cleanup in favour of loader.LoadFromArgs(args []string)
for _, v := range args {
if strings.HasPrefix(v, "secret/") {
// format secret/namespace-name/secret-name
pathParts := strings.Split(v, "/")
if len(pathParts) != 3 {
return types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Errorf("path %s must have 3 components", v))
}

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

theSpec = spec
} else if _, err = os.Stat(v); err == nil {
b, err := os.ReadFile(v)
if err != nil {
return types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
}

theSpec = b
} else if v == "-" {
b, err := io.ReadAll(os.Stdin)
if err != nil {
return types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, err)
}
theSpec = b
} else {
u, err := url.Parse(v)
if err != nil {
return types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
}

if u.Scheme == "oci" {
content, err := oci.PullPreflightFromOCI(v)
if err != nil {
if err == oci.ErrNoRelease {
return types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Errorf("no release found for %s.\nCheck the oci:// uri for errors or contact the application vendor for support.", v))
}

return types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
}

theSpec = content
} else {
if !util.IsURL(v) {
return types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, fmt.Errorf("%s is not a URL and was not found (err %s)", v, err))
}

req, err := http.NewRequest("GET", v, nil)
if err != nil {
// exit code: should this be catch all or spec issues...?
return types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, err)
}
req.Header.Set("User-Agent", "Replicated_Preflight/v1beta2")
resp, err := http.DefaultClient.Do(req)
if err != nil {
// exit code: should this be catch all or spec issues...?
return types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
}

theSpec = body
}
}

k, err := loader.LoadSpecs(ctx, loader.LoadOptions{
RawSpec: string(theSpec),
})
if err != nil {
return err
}

kinds.Add(k)
}

// TODO: Choose format i.e yaml or json
// TODO: Merge the specs first, maybe? or print them all out as a multi-doc yaml? How about JSON?
yamlOut, err := yaml.Marshal(kinds)
if err != nil {
return err
}

fmt.Println(string(yamlOut))

return nil
}
11 changes: 11 additions & 0 deletions pkg/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ func (kinds *TroubleshootKinds) IsEmpty() bool {
len(kinds.SupportBundlesV1Beta2) == 0
}

func (kinds *TroubleshootKinds) Add(other *TroubleshootKinds) {
kinds.AnalyzersV1Beta2 = append(kinds.AnalyzersV1Beta2, other.AnalyzersV1Beta2...)
kinds.CollectorsV1Beta2 = append(kinds.CollectorsV1Beta2, other.CollectorsV1Beta2...)
kinds.HostCollectorsV1Beta2 = append(kinds.HostCollectorsV1Beta2, other.HostCollectorsV1Beta2...)
kinds.HostPreflightsV1Beta2 = append(kinds.HostPreflightsV1Beta2, other.HostPreflightsV1Beta2...)
kinds.PreflightsV1Beta2 = append(kinds.PreflightsV1Beta2, other.PreflightsV1Beta2...)
kinds.RedactorsV1Beta2 = append(kinds.RedactorsV1Beta2, other.RedactorsV1Beta2...)
kinds.RemoteCollectorsV1Beta2 = append(kinds.RemoteCollectorsV1Beta2, other.RemoteCollectorsV1Beta2...)
kinds.SupportBundlesV1Beta2 = append(kinds.SupportBundlesV1Beta2, other.SupportBundlesV1Beta2...)
}

func NewTroubleshootKinds() *TroubleshootKinds {
return &TroubleshootKinds{}
}
Expand Down

0 comments on commit 10b8943

Please sign in to comment.