diff --git a/pkg/preflight/analyze.go b/pkg/preflight/analyze.go index 8e74eb64..78fb5de6 100644 --- a/pkg/preflight/analyze.go +++ b/pkg/preflight/analyze.go @@ -77,8 +77,10 @@ func doAnalyze(allCollectedData map[string][]byte, analyzers []*troubleshootv1be for _, analyzer := range analyzers { analyzeResult, err := analyze.Analyze(analyzer, getCollectedFileContents, getChildCollectedFileContents) if err != nil { + strict, _ := HasStrictAnalyzer(analyzer) analyzeResult = []*analyze.AnalyzeResult{ { + Strict: strict, IsFail: true, Title: "Analyzer Failed", Message: err.Error(), diff --git a/pkg/preflight/util.go b/pkg/preflight/util.go new file mode 100644 index 00000000..4b6909e6 --- /dev/null +++ b/pkg/preflight/util.go @@ -0,0 +1,80 @@ +package preflight + +import ( + "encoding/json" + + "github.com/pkg/errors" + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" +) + +// HasStrictAnalyzers - checks and returns true if a preflight's analyzer has strict:true, else false +func HasStrictAnalyzers(preflight *troubleshootv1beta2.Preflight) (bool, error) { + if preflight == nil { + return false, nil + } + + marshalledAnalyzers, err := json.Marshal(preflight.Spec.Analyzers) // marshall and remove nil Analyzers eg result: "[{\"clusterVersion\":{\"exclude\":\"\",\"strict\":\"false\",\"outcomes\":null}}]" + if err != nil { + return false, errors.Wrap(err, "failed to marshal analyzers") + } + + analyzersMap := []map[string]interface{}{} + err = json.Unmarshal(marshalledAnalyzers, &analyzersMap) // Unmarshall again so we can loop over non nil analyzers + if err != nil { + return false, errors.Wrap(err, "failed to unmarshal analyzers") + } + + // analyzerMap will ignore empty Analyzers and loop around Analyzer with data + for _, analyzers := range analyzersMap { // for each analyzer: map["clusterVersion": map[string]interface{} ["exclude": "", "strict": "true", "outcomes": nil] + return hasStrictAnalyzer(analyzers) + } + return false, nil +} + +// HasStrictAnalyzers - checks and returns true if a preflight's analyzer has strict:true, else false +func HasStrictAnalyzer(analyzer *troubleshootv1beta2.Analyze) (bool, error) { + marshalledAnalyzer, err := json.Marshal(analyzer) + if err != nil { + return false, errors.Wrap(err, "error while marshalling analyzer") + } + + analyzerMap := make(map[string]interface{}) + err = json.Unmarshal(marshalledAnalyzer, &analyzerMap) // Unmarshall again so we can loop over non nil analyzers + if err != nil { + return false, errors.Wrap(err, "failed to unmarshal analyzers") + } + return hasStrictAnalyzer(analyzerMap) +} + +func hasStrictAnalyzer(analyzerMap map[string]interface{}) (bool, error) { + for _, analyzer := range analyzerMap { // for each analyzerMeta: map[string]interface{} ["exclude": "", "strict": "true", "outcomes": nil] + marshalledAnalyzer, err := json.Marshal(analyzer) + if err != nil { + return false, errors.Wrap(err, "error while marshalling analyzer") + } + // return Analyzer.Strict which can be extracted from AnalyzeMeta + analyzeMeta := troubleshootv1beta2.AnalyzeMeta{} + err = json.Unmarshal(marshalledAnalyzer, &analyzeMeta) + if err != nil { + return false, errors.Wrap(err, "error while un-marshalling marshalledAnalyzers") + } + return analyzeMeta.Strict.BoolOrDefaultFalse(), nil + } + return false, nil +} + +// HasStrictAnalyzersFailed - checks if preflight analyzer's result is strict:true and isFail:true, then returns true else false +func HasStrictAnalyzersFailed(preflightResult *UploadPreflightResults) bool { + hasStrictAnalyzersFailed := false + // if results are empty, treat as failure + if preflightResult == nil || len(preflightResult.Results) == 0 { + hasStrictAnalyzersFailed = true + } else { + for _, result := range preflightResult.Results { + if result.IsFail && result.Strict { + hasStrictAnalyzersFailed = true + } + } + } + return hasStrictAnalyzersFailed +} diff --git a/pkg/preflight/util_test.go b/pkg/preflight/util_test.go new file mode 100644 index 00000000..09cde6fd --- /dev/null +++ b/pkg/preflight/util_test.go @@ -0,0 +1,328 @@ +package preflight + +import ( + "testing" + + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + "github.com/replicatedhq/troubleshoot/pkg/multitype" +) + +var ( + analyzeMetaStrictFalseStr = troubleshootv1beta2.AnalyzeMeta{ + Strict: multitype.BoolOrString{ + StrVal: "false", + }, + } + analyzeMetaStrictInvalidStr = troubleshootv1beta2.AnalyzeMeta{ + Strict: multitype.BoolOrString{ + StrVal: "invalid", + }, + } + analyzeMetaStrictTrueStr = troubleshootv1beta2.AnalyzeMeta{ + Strict: multitype.BoolOrString{ + StrVal: "true", + }, + } + analyzeMetaStrictFalseBool = troubleshootv1beta2.AnalyzeMeta{ + Strict: multitype.BoolOrString{ + Type: multitype.Bool, + BoolVal: false, + }, + } + analyzeMetaStrictTrueBool = troubleshootv1beta2.AnalyzeMeta{ + Strict: multitype.BoolOrString{ + Type: multitype.Bool, + BoolVal: true, + }, + } + analyzeMetaStrictFalseInt = troubleshootv1beta2.AnalyzeMeta{ + Strict: multitype.BoolOrString{ + StrVal: "0", + }, + } + analyzeMetaStrictTrueInt = troubleshootv1beta2.AnalyzeMeta{ + Strict: multitype.BoolOrString{ + Type: multitype.String, + StrVal: "1", + }, + } +) + +func TestHasStrictAnalyzers(t *testing.T) { + + tests := []struct { + name string + preflight *troubleshootv1beta2.Preflight + want bool + wantErr bool + }{ + { + name: "expect false when preflight is nil", + preflight: nil, + want: false, + wantErr: false, + }, { + name: "expect false when preflight spec is empty", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{}, + }, + want: false, + wantErr: false, + }, { + name: "expect false when preflight spec's analyzers is nil", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{ + Analyzers: nil, + }, + }, + want: false, + wantErr: false, + }, { + name: "expect false when preflight spec's analyzers is empty", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{ + Analyzers: []*troubleshootv1beta2.Analyze{}, + }, + }, + want: false, + wantErr: false, + }, { + name: "expect false when preflight spec's analyzer has nil analyzer", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{ + Analyzers: []*troubleshootv1beta2.Analyze{ + { + ClusterVersion: nil, + }, + }, + }, + }, + want: false, + wantErr: false, + }, { + name: "expect false when preflight spec's analyzer has analyzer with strict str: false", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{ + Analyzers: []*troubleshootv1beta2.Analyze{ + { + ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseStr}, + }, + }, + }, + }, + want: false, + wantErr: false, + }, { + name: "expect false when preflight spec's analyzer has analyzer with strict bool: false", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{ + Analyzers: []*troubleshootv1beta2.Analyze{ + { + ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseBool}, + }, + }, + }, + }, + want: false, + wantErr: false, + }, { + name: "expect false when preflight spec's analyzer has analyzer with strict str: invalid", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{ + Analyzers: []*troubleshootv1beta2.Analyze{ + { + ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictInvalidStr}, + }, + }, + }, + }, + want: false, + wantErr: false, + }, { + name: "expect true when preflight spec's analyzer has analyzer with strict str: true", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{ + Analyzers: []*troubleshootv1beta2.Analyze{ + { + ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictTrueStr}, + }, + }, + }, + }, + want: true, + wantErr: false, + }, { + name: "expect true when preflight spec's analyzer has analyzer with strict bool: true", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{ + Analyzers: []*troubleshootv1beta2.Analyze{ + { + ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictTrueBool}, + }, + }, + }, + }, + want: true, + wantErr: false, + }, { + name: "expect true when preflight spec's analyzer has analyzer with strict int: 1", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{ + Analyzers: []*troubleshootv1beta2.Analyze{ + { + ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictTrueInt}, + }, + }, + }, + }, + want: true, + wantErr: false, + }, { + name: "expect false when preflight spec's analyzer has analyzer with strict int: 0", + preflight: &troubleshootv1beta2.Preflight{ + Spec: troubleshootv1beta2.PreflightSpec{ + Analyzers: []*troubleshootv1beta2.Analyze{ + { + ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseInt}, + }, + }, + }, + }, + want: false, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := HasStrictAnalyzers(tt.preflight) + if (err != nil) != tt.wantErr { + t.Errorf("HasStrictAnalyzers error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("HasStrictAnalyzers() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestHasStrictAnalyzerFailed(t *testing.T) { + tests := []struct { + name string + preflightResult *UploadPreflightResults + want bool + }{ + { + name: "expect true when preflightResult is nil", + preflightResult: nil, + want: true, + }, { + name: "expect true when preflightResult.Results is nil", + preflightResult: &UploadPreflightResults{ + Results: nil, + }, + want: true, + }, { + name: "expect true when preflightResult.Results is empty", + preflightResult: &UploadPreflightResults{ + Results: []*UploadPreflightResult{}, + }, + want: true, + }, { + name: "expect false when preflightResult.Results has result with strict false, IsFail false", + preflightResult: &UploadPreflightResults{ + Results: []*UploadPreflightResult{ + {Strict: false, IsFail: false}, + }, + }, + want: false, + }, { + name: "expect false when preflightResult.Results has result with strict false, IsFail true", + preflightResult: &UploadPreflightResults{ + Results: []*UploadPreflightResult{ + {Strict: false, IsFail: true}, + }, + }, + want: false, + }, { + name: "expect true when preflightResult.Results has result with strict true, IsFail true", + preflightResult: &UploadPreflightResults{ + Results: []*UploadPreflightResult{ + {Strict: true, IsFail: true}, + }, + }, + want: true, + }, { + name: "expect true when preflightResult.Results has multiple results where atleast result has strict true, IsFail true", + preflightResult: &UploadPreflightResults{ + Results: []*UploadPreflightResult{ + {Strict: true, IsFail: true}, + {Strict: false, IsFail: true}, + {Strict: true, IsFail: false}, + }, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := HasStrictAnalyzersFailed(tt.preflightResult); got != tt.want { + t.Errorf("HasStrictAnalyzersFailed() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestHasStrictAnalyzer(t *testing.T) { + tests := []struct { + name string + analyzer *troubleshootv1beta2.Analyze + want bool + wantErr bool + }{ + { + name: "expect strict=false, err=nil when analyzer is nil", + analyzer: nil, + want: false, + wantErr: false, + }, { + name: "expect strict=false, err=nil when analyzer is empty", + analyzer: &troubleshootv1beta2.Analyze{}, + want: false, + wantErr: false, + }, { + name: "expect strict=false, err=nil when ClusterVersion analyzer has strict=1", + analyzer: &troubleshootv1beta2.Analyze{ + ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseInt}, + }, + want: false, + wantErr: false, + }, { + name: "expect strict=false, err=nil when ClusterVersion analyzer has strict=true", + analyzer: &troubleshootv1beta2.Analyze{ + ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictTrueInt}, + }, + want: true, + wantErr: false, + }, { + name: "expect strict=false, err=nil when ClusterVersion analyzer is nil", + analyzer: &troubleshootv1beta2.Analyze{ + ClusterVersion: nil, + }, + want: false, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := HasStrictAnalyzer(tt.analyzer) + if (err != nil) != tt.wantErr { + t.Errorf("HasStrictAnalyzer() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("HasStrictAnalyzer() = %v, want %v", got, tt.want) + } + }) + } +}