Merge pull request #99 from ashwathishiva/custom_text_analyzer

Adding a text analyzer
This commit is contained in:
Marc Campbell
2019-12-23 12:45:32 -08:00
committed by GitHub
5 changed files with 303 additions and 0 deletions

View File

@@ -79,6 +79,9 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
}
return analyzeDistribution(analyzer.Distribution, getFile)
}
if analyzer.TextAnalyze != nil {
return analyzeTextAnalyze(analyzer.TextAnalyze, getFile)
}
return nil, errors.New("invalid analyzer")
}

View File

@@ -0,0 +1,47 @@
package analyzer
import (
"path"
"regexp"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
)
func analyzeTextAnalyze(analyzer *troubleshootv1beta1.TextAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
fullPath := path.Join(analyzer.CollectorName, analyzer.FileName)
collected, err := getCollectedFileContents(fullPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
}
re, err := regexp.Compile(analyzer.RegexPattern)
if err != nil {
return nil, errors.Wrapf(err, "failed to compile regex: %s", analyzer.RegexPattern)
}
var failOutcome *troubleshootv1beta1.Outcome
var passOutcome *troubleshootv1beta1.Outcome
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
failOutcome = outcome
} else if outcome.Pass != nil {
passOutcome = outcome
}
}
if re.MatchString(string(collected)) {
return &AnalyzeResult{
Title: analyzer.CheckName,
IsPass: true,
Message: passOutcome.Pass.Message,
URI: passOutcome.Pass.URI,
}, nil
}
return &AnalyzeResult{
Title: analyzer.CheckName,
IsFail: true,
Message: failOutcome.Fail.Message,
URI: failOutcome.Fail.URI,
}, nil
}

View File

@@ -0,0 +1,212 @@
package analyzer
import (
"fmt"
"testing"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_textAnalyze(t *testing.T) {
tests := []struct {
name string
analyzer troubleshootv1beta1.TextAnalyze
expectResult AnalyzeResult
files map[string][]byte
}{
{
name: "success case 1",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Message: "pass",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Message: "fail",
},
},
},
CollectorName: "text-collector-1",
FileName: "cfile-1.txt",
RegexPattern: "succeeded",
},
expectResult: AnalyzeResult{
IsPass: true,
IsWarn: false,
IsFail: false,
Message: "pass",
},
files: map[string][]byte{
"text-collector-1/cfile-1.txt": []byte("Yes it all succeeded"),
},
},
{
name: "failure case 1",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Message: "success",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Message: "fail",
},
},
},
CollectorName: "text-collector-2",
FileName: "cfile-2.txt",
RegexPattern: "succeeded",
},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: false,
IsFail: true,
Message: "fail",
},
files: map[string][]byte{
"text-collector-2/cfile-2.txt": []byte(""),
},
},
{
name: "success case 2",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Message: "success",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Message: "fail",
},
},
},
CollectorName: "text-collector-3",
FileName: "cfile-3.txt",
RegexPattern: "",
},
expectResult: AnalyzeResult{
IsPass: true,
IsWarn: false,
IsFail: false,
Message: "success",
},
files: map[string][]byte{
"text-collector-3/cfile-3.txt": []byte("Connection to service succeeded"),
},
},
{
name: "success case 3",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Message: "success",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Message: "fail",
},
},
},
CollectorName: "text-collector-5",
FileName: "cfile-5.txt",
RegexPattern: "([a-zA-Z0-9\\-_:*\\s])*succe([a-zA-Z0-9\\-_:*\\s!])*",
},
expectResult: AnalyzeResult{
IsPass: true,
IsWarn: false,
IsFail: false,
Message: "success",
},
files: map[string][]byte{
"text-collector-5/cfile-5.txt": []byte("Connection to service succeeded!"),
},
},
{
name: "failure case 3",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Message: "success",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Message: "fail",
},
},
},
CollectorName: "text-collector-4",
FileName: "cfile-4.txt",
RegexPattern: "succeeded",
},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: false,
IsFail: true,
Message: "fail",
},
files: map[string][]byte{
"text-collector-4/cfile-4.txt": []byte("A different message"),
},
},
{
name: "failure case 4",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Message: "success",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Message: "fail",
},
},
},
CollectorName: "text-collector-6",
FileName: "cfile-6.txt",
RegexPattern: "([a-zA-Z0-9\\-_:*\\s])*succe([a-zA-Z0-9\\-_:*\\s!])*",
},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: false,
IsFail: true,
Message: "fail",
},
files: map[string][]byte{
"text-collector-6/cfile-6.txt": []byte("A different message"),
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
getFiles := func(n string) ([]byte, error) {
val, ok := test.files[n]
if !ok {
return nil, fmt.Errorf("File not found: %s", n)
}
return val, nil
}
actual, err := analyzeTextAnalyze(&test.analyzer, getFiles)
req.NoError(err)
assert.Equal(t, &test.expectResult, actual)
})
}
}

View File

@@ -74,6 +74,14 @@ type Distribution struct {
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
}
type TextAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
FileName string `json:"fileName,omitempty" yaml:"fileName,omitempty"`
RegexPattern string `json:"regex,omitempty" yaml:"regex,omitempty"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
}
type AnalyzeMeta struct {
CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"`
Exclude bool `json:"exclude,omitempty" yaml:"exclude,omitempty"`
@@ -90,4 +98,5 @@ type Analyze struct {
StatefulsetStatus *StatefulsetStatus `json:"statefulsetStatus,omitempty" yaml:"statefulsetStatus,omitempty"`
ContainerRuntime *ContainerRuntime `json:"containerRuntime,omitempty" yaml:"containerRuntime,omitempty"`
Distribution *Distribution `json:"distribution,omitempty" yaml:"distribution,omitempty"`
TextAnalyze *TextAnalyze `json:"textAnalyze,omitempty" yaml:"textAnalyze,omitempty"`
}

View File

@@ -102,6 +102,11 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
*out = new(Distribution)
(*in).DeepCopyInto(*out)
}
if in.TextAnalyze != nil {
in, out := &in.TextAnalyze, &out.TextAnalyze
*out = new(TextAnalyze)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Analyze.
@@ -1499,3 +1504,30 @@ func (in *SupportBundleVersion) DeepCopy() *SupportBundleVersion {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TextAnalyze) DeepCopyInto(out *TextAnalyze) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(Outcome)
(*in).DeepCopyInto(*out)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TextAnalyze.
func (in *TextAnalyze) DeepCopy() *TextAnalyze {
if in == nil {
return nil
}
out := new(TextAnalyze)
in.DeepCopyInto(out)
return out
}