Skip to content

Commit

Permalink
Merge pull request #74 from replicatedhq/analyze-spec
Browse files Browse the repository at this point in the history
Analyze spec
  • Loading branch information
marccampbell committed Oct 29, 2019
2 parents 25ec38b + 8a1bb8a commit 5830eb3
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 55 deletions.
22 changes: 10 additions & 12 deletions Makefile
Expand Up @@ -22,14 +22,14 @@ manager: generate fmt vet
support-bundle: generate fmt vet
go build -o bin/support-bundle github.com/replicatedhq/troubleshoot/cmd/troubleshoot

.PHONY: collector
collector: generate fmt vet
go build -o bin/collector github.com/replicatedhq/troubleshoot/cmd/collector

.PHONY: preflight
preflight: generate fmt vet
go build -o bin/preflight github.com/replicatedhq/troubleshoot/cmd/preflight

.PHONY: analyze
analyze: generate fmt vet
go build -o bin/analyze github.com/replicatedhq/troubleshoot/cmd/analyze

.PHONY: run
run: generate fmt vet
TROUBLESHOOT_EXTERNAL_MANAGER=1 go run ./cmd/manager/main.go
Expand Down Expand Up @@ -103,14 +103,12 @@ local-release:

.PHONY: run-preflight
run-preflight: preflight
./bin/preflight \
--image=localhost:32000/troubleshoot:alpha \
--pullpolicy=Always \
./examples/preflight/sample-preflight.yaml
./bin/preflight ./examples/preflight/sample-preflight.yaml

.PHONY: run-troubleshoot
run-troubleshoot: support-bundle
./bin/support-bundle \
--image=localhost:32000/troubleshoot:alpha \
--pullpolicy=Always \
./examples/troubleshoot/sample-troubleshoot.yaml
./bin/support-bundle ./examples/troubleshoot/sample-troubleshoot.yaml

.PHONY: run-analyze
run-analyze: analyze
./bin/analyze --analyzers ./examples/troubleshoot/sample-analyzers.yaml ./support-bundle.tar.gz
60 changes: 60 additions & 0 deletions cmd/analyze/cli/root.go
@@ -0,0 +1,60 @@
package cli

import (
"fmt"
"os"
"strings"

"github.com/replicatedhq/troubleshoot/pkg/logger"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/cli-runtime/pkg/genericclioptions"
)

var (
KubernetesConfigFlags *genericclioptions.ConfigFlags
)

func RootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "analyze [url]",
Args: cobra.MinimumNArgs(1),
Short: "Analyze a support bundle",
Long: `Run a series of analyzers on a support bundle archive`,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlags(cmd.Flags())
},
RunE: func(cmd *cobra.Command, args []string) error {
v := viper.GetViper()

logger.SetQuiet(v.GetBool("quiet"))

return runAnalyzers(v, args[0])
},
}

cobra.OnInitialize(initConfig)

cmd.Flags().String("analyzers", "", "filename or url of the analyzers to use")

viper.BindPFlags(cmd.Flags())

viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))

KubernetesConfigFlags = genericclioptions.NewConfigFlags(false)
KubernetesConfigFlags.AddFlags(cmd.Flags())

return cmd
}

func InitAndExecute() {
if err := RootCmd().Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func initConfig() {
viper.SetEnvPrefix("TROUBLESHOOT")
viper.AutomaticEnv()
}
75 changes: 75 additions & 0 deletions cmd/analyze/cli/run.go
@@ -0,0 +1,75 @@
package cli

import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"

"github.com/pkg/errors"
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
"github.com/spf13/viper"
)

func runAnalyzers(v *viper.Viper, bundlePath string) error {
specPath := v.GetString("analyzers")

specContent := ""
if !isURL(specPath) {
if _, err := os.Stat(specPath); os.IsNotExist(err) {
return fmt.Errorf("%s was not found", specPath)
}

b, err := ioutil.ReadFile(specPath)
if err != nil {
return err
}

specContent = string(b)
} else {
req, err := http.NewRequest("GET", specPath, nil)
if err != nil {
return err
}
req.Header.Set("User-Agent", "Replicated_Analyzer/v1beta1")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}

specContent = string(body)
}

analyzeResults, err := analyzer.DownloadAndAnalyze(specContent, bundlePath)
if err != nil {
return errors.Wrap(err, "failed to download and analyze bundle")
}

for _, analyzeResult := range analyzeResults {
if analyzeResult.IsPass {
fmt.Printf("Pass: %s\n %s\n", analyzeResult.Title, analyzeResult.Message)
} else if analyzeResult.IsWarn {
fmt.Printf("Warn: %s\n %s\n", analyzeResult.Title, analyzeResult.Message)
} else if analyzeResult.IsFail {
fmt.Printf("Fail: %s\n %s\n", analyzeResult.Title, analyzeResult.Message)
}
}

return nil
}

func isURL(str string) bool {
parsed, err := url.ParseRequestURI(str)
if err != nil {
return false
}

return parsed.Scheme != ""
}
10 changes: 10 additions & 0 deletions cmd/analyze/main.go
@@ -0,0 +1,10 @@
package main

import (
"github.com/replicatedhq/troubleshoot/cmd/analyze/cli"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
)

func main() {
cli.InitAndExecute()
}
4 changes: 2 additions & 2 deletions cmd/preflight/cli/util.go
Expand Up @@ -17,12 +17,12 @@ func homeDir() string {
}

func isURL(str string) bool {
_, err := url.ParseRequestURI(str)
parsed, err := url.ParseRequestURI(str)
if err != nil {
return false
}

return true
return parsed.Scheme != ""
}

func createTroubleshootK8sClient(configFlags *genericclioptions.ConfigFlags) (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
Expand Down
3 changes: 1 addition & 2 deletions cmd/troubleshoot/cli/analyze.go
@@ -1,7 +1,6 @@
package cli

import (
"context"
"encoding/json"
"fmt"

Expand All @@ -28,7 +27,7 @@ func Analyze() *cobra.Command {

logger.SetQuiet(v.GetBool("quiet"))

result, err := analyzer.DownloadAndAnalyze(context.TODO(), v.GetString("url"))
result, err := analyzer.DownloadAndAnalyze("", v.GetString("url"))
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/troubleshoot/cli/util.go
Expand Up @@ -18,12 +18,12 @@ func homeDir() string {
}

func isURL(str string) bool {
_, err := url.ParseRequestURI(str)
parsed, err := url.ParseRequestURI(str)
if err != nil {
return false
}

return true
return parsed.Scheme != ""
}

func createTroubleshootK8sClient(configFlags *genericclioptions.ConfigFlags) (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
Expand Down
19 changes: 19 additions & 0 deletions examples/troubleshoot/sample-analyzers.yaml
@@ -0,0 +1,19 @@
apiVersion: troubleshoot.replicated.com/v1beta1
kind: Analyzer
metadata:
name: defaultAnalyzers
spec:
analyzers:
- clusterVersion:
outcomes:
- fail:
when: "< 1.13.0"
message: The application requires at Kubernetes 1.13.0 or later, and recommends 1.15.0.
uri: https://www.kubernetes.io
- warn:
when: "< 1.15.0"
message: Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.15.0 or later.
uri: https://kubernetes.io
- pass:
when: ">= 1.15.0"
message: Your cluster meets the recommended and required versions of Kubernetes.
4 changes: 2 additions & 2 deletions ffi/main.go
Expand Up @@ -15,10 +15,10 @@ import (
)

//export Analyze
func Analyze(bundleURL string, outputFormat string, compatibility string) *C.char {
func Analyze(bundleURL string, analyzers string, outputFormat string, compatibility string) *C.char {
logger.SetQuiet(true)

result, err := analyzer.DownloadAndAnalyze(context.TODO(), bundleURL)
result, err := analyzer.DownloadAndAnalyze(bundleURL, analyzers)
if err != nil {
fmt.Printf("error downloading and analyzing: %s\n", err.Error())
return C.CString("")
Expand Down
73 changes: 47 additions & 26 deletions pkg/analyze/download.go
Expand Up @@ -3,26 +3,24 @@ package analyzer
import (
"archive/tar"
"compress/gzip"
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"

getter "github.com/hashicorp/go-getter"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootscheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"gopkg.in/yaml.v2"
"k8s.io/client-go/kubernetes/scheme"
)

type fileContentProvider struct {
rootDir string
}

func DownloadAndAnalyze(ctx context.Context, bundleURL string) ([]*AnalyzeResult, error) {
func DownloadAndAnalyze(analyzersSpec string, bundleURL string) ([]*AnalyzeResult, error) {
tmpDir, err := ioutil.TempDir("", "troubleshoot-k8s")
if err != nil {
return nil, errors.Wrap(err, "failed to create temp dir")
Expand All @@ -38,9 +36,20 @@ func DownloadAndAnalyze(ctx context.Context, bundleURL string) ([]*AnalyzeResult
return nil, errors.Wrap(err, "failed to read version.yaml")
}

analyzers, err := getTroubleshootAnalyzers()
if err != nil {
return nil, errors.Wrap(err, "failed to get analyzers")
analyzers := []*troubleshootv1beta1.Analyze{}

if analyzersSpec == "" {
defaultAnalyzers, err := getDefaultAnalyzers()
if err != nil {
return nil, errors.Wrap(err, "failed to get default analyzers")
}
analyzers = defaultAnalyzers
} else {
parsedAnalyzers, err := parseAnalyzers(analyzersSpec)
if err != nil {
return nil, errors.Wrap(err, "failed to parse analyzers")
}
analyzers = parsedAnalyzers
}

fcp := fileContentProvider{rootDir: tmpDir}
Expand Down Expand Up @@ -138,29 +147,41 @@ func extractTroubleshootBundle(reader io.Reader, destDir string) error {
return nil
}

func getTroubleshootAnalyzers() ([]*troubleshootv1beta1.Analyze, error) {
specURL := `https://troubleshoot.replicated.com/`
resp, err := http.Get(specURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("could not download analyzer spec, status code: %v", resp.StatusCode)
}
func parseAnalyzers(spec string) ([]*troubleshootv1beta1.Analyze, error) {
troubleshootscheme.AddToScheme(scheme.Scheme)
decode := scheme.Codecs.UniversalDeserializer().Decode

spec, err := ioutil.ReadAll(resp.Body)
obj, _, err := decode([]byte(spec), nil, nil)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "failed to decode analyzers")
}

preflight := troubleshootv1beta1.Preflight{}
if err := yaml.Unmarshal([]byte(spec), &preflight); err != nil {
return nil, err
}
analyzer := obj.(*troubleshootv1beta1.Analyzer)
return analyzer.Spec.Analyzers, nil
}

return preflight.Spec.Analyzers, nil
func getDefaultAnalyzers() ([]*troubleshootv1beta1.Analyze, error) {
spec := `apiVersion: troubleshoot.replicated.com/v1beta1
kind: Analyzer
metadata:
name: defaultAnalyzers
spec:
analyzers:
- clusterVersion:
outcomes:
- fail:
when: "< 1.13.0"
message: The application requires at Kubernetes 1.13.0 or later, and recommends 1.15.0.
uri: https://www.kubernetes.io
- warn:
when: "< 1.15.0"
message: Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.15.0 or later.
uri: https://kubernetes.io
- pass:
when: ">= 1.15.0"
message: Your cluster meets the recommended and required versions of Kubernetes.`

return parseAnalyzers(spec)
}

func (f fileContentProvider) getFileContents(fileName string) ([]byte, error) {
Expand Down

0 comments on commit 5830eb3

Please sign in to comment.