Skip to content

Commit

Permalink
feature(CLI): Analyzer Resource Integration (#2726)
Browse files Browse the repository at this point in the history
* feature(CLI): Analyzer Resource Integration

* feature(CLI): including e2e tests

* feature(CLI): including e2e tests

* feature(CLI): including e2e tests

* fixing comments

* fixing test file names
  • Loading branch information
xoscar committed Jun 15, 2023
1 parent 7f1e120 commit 707f918
Show file tree
Hide file tree
Showing 19 changed files with 517 additions and 9 deletions.
58 changes: 58 additions & 0 deletions cli/actions/analyzer.go
@@ -0,0 +1,58 @@
package actions

import (
"context"

"github.com/kubeshop/tracetest/cli/file"
"github.com/kubeshop/tracetest/cli/openapi"
"github.com/kubeshop/tracetest/cli/utils"
"github.com/kubeshop/tracetest/server/model/yaml"
)

type analyzerActions struct {
resourceArgs
}

var _ ResourceActions = &analyzerActions{}

func NewAnalyzerActions(options ...ResourceArgsOption) analyzerActions {
args := NewResourceArgs(options...)

return analyzerActions{
resourceArgs: args,
}
}

func (analyzerActions) FileType() yaml.FileType {
return yaml.FileTypeAnalyzer
}

func (analyzerActions) Name() string {
return "analyzer"
}

func (analyzer analyzerActions) GetID(file *file.File) (string, error) {
resource, err := analyzer.formatter.ToStruct(file)
if err != nil {
return "", err
}

return *resource.(openapi.LinterResource).Spec.Id, nil
}

func (analyzer analyzerActions) Apply(ctx context.Context, fileContent file.File) (result *file.File, err error) {
result, err = analyzer.resourceClient.Update(ctx, fileContent, currentConfigID)
return result, err
}

func (analyzer analyzerActions) Get(ctx context.Context, ID string) (*file.File, error) {
return analyzer.resourceClient.Get(ctx, currentConfigID)
}

func (analyzer analyzerActions) List(ctx context.Context, listArgs utils.ListArgs) (*file.File, error) {
return analyzer.resourceClient.List(ctx, listArgs)
}

func (analyzer analyzerActions) Delete(ctx context.Context, ID string) (string, error) {
return "", ErrNotSupportedResourceAction
}
8 changes: 8 additions & 0 deletions cli/cmd/config.go
Expand Up @@ -73,6 +73,14 @@ func setupCommand(options ...setupOption) func(cmd *cobra.Command, args []string
configActions := actions.NewConfigActions(configOptions...)
resourceRegistry.Register(configActions)

analyzerOptions := append(
baseOptions,
actions.WithClient(utils.GetResourceAPIClient("analyzers", cliConfig)),
actions.WithFormatter(formatters.NewAnalyzerFormatter()),
)
analyzerActions := actions.NewAnalyzerActions(analyzerOptions...)
resourceRegistry.Register(analyzerActions)

pollingOptions := append(
baseOptions,
actions.WithClient(utils.GetResourceAPIClient("pollingprofiles", cliConfig)),
Expand Down
102 changes: 102 additions & 0 deletions cli/formatters/analyzer.go
@@ -0,0 +1,102 @@
package formatters

import (
"fmt"

"github.com/alexeyco/simpletable"
"github.com/goccy/go-yaml"
"github.com/kubeshop/tracetest/cli/file"
"github.com/kubeshop/tracetest/cli/openapi"
)

type AnalyzerFormatter struct{}

var _ ResourceFormatter = AnalyzerFormatter{}

func NewAnalyzerFormatter() AnalyzerFormatter {
return AnalyzerFormatter{}
}

func (f AnalyzerFormatter) ToTable(file *file.File) (*simpletable.Header, *simpletable.Body, error) {
rawConfig, err := f.ToStruct(file)
if err != nil {
return nil, nil, err
}

linterResource := rawConfig.(openapi.LinterResource)
row, err := f.getTableRow(linterResource)
if err != nil {
return nil, nil, err
}

body := simpletable.Body{}
body.Cells = [][]*simpletable.Cell{row}

return f.getTableHeader(), &body, nil
}

func (f AnalyzerFormatter) ToListTable(file *file.File) (*simpletable.Header, *simpletable.Body, error) {
rawConfigList, err := f.ToListStruct(file)
if err != nil {
return nil, nil, err
}

body := simpletable.Body{}
for _, rawConfig := range rawConfigList {
linterResource := rawConfig.(openapi.LinterResource)
row, err := f.getTableRow(linterResource)
if err != nil {
return nil, nil, err
}

body.Cells = append(body.Cells, row)
}

return f.getTableHeader(), &body, nil
}

func (f AnalyzerFormatter) ToStruct(file *file.File) (interface{}, error) {
var linterResource openapi.LinterResource
err := yaml.Unmarshal([]byte(file.Contents()), &linterResource)
if err != nil {
return nil, err
}

return linterResource, nil
}

func (f AnalyzerFormatter) ToListStruct(file *file.File) ([]interface{}, error) {
var analyzerList openapi.LinterResourceList

err := yaml.Unmarshal([]byte(file.Contents()), &analyzerList)
if err != nil {
return nil, err
}

items := make([]interface{}, len(analyzerList.Items))
for i, item := range analyzerList.Items {
items[i] = item
}

return items, nil
}

func (f AnalyzerFormatter) getTableHeader() *simpletable.Header {
return &simpletable.Header{
Cells: []*simpletable.Cell{
{Text: "ID"},
{Text: "NAME"},
{Text: "ENABLED"},
{Text: "MINIMUM SCORE"},
},
}
}

func (f AnalyzerFormatter) getTableRow(t openapi.LinterResource) ([]*simpletable.Cell, error) {
return []*simpletable.Cell{
{Text: *t.Spec.Id},
{Text: *t.Spec.Name},
{Text: fmt.Sprintf("%t", *t.Spec.Enabled)},
{Text: fmt.Sprintf("%d", *t.Spec.MinimumScore)},
}, nil
}
2 changes: 1 addition & 1 deletion cli/parameters/resources.go
Expand Up @@ -87,7 +87,7 @@ type ResourceParams struct {
}

var _ Params = &ResourceParams{}
var ValidResources = []string{"config", "datastore", "demo", "environment", "pollingprofile", "transaction"}
var ValidResources = []string{"config", "datastore", "demo", "environment", "pollingprofile", "transaction", "analyzer"}

func (p *ResourceParams) Validate(cmd *cobra.Command, args []string) []ParamError {
errors := make([]ParamError, 0)
Expand Down
1 change: 1 addition & 0 deletions server/model/yaml/file.go
Expand Up @@ -20,6 +20,7 @@ const (
FileTypeConfig FileType = "Config"
FileTypeDemo FileType = "Demo"
FileTypePollingProfile FileType = "PollingProfile"
FileTypeAnalyzer FileType = "Analyzer"
)

type File struct {
Expand Down
46 changes: 46 additions & 0 deletions testing/cli-e2etest/testscenarios/analyzer/apply_analyzer_test.go
@@ -0,0 +1,46 @@
package analyzer

import (
"fmt"
"testing"

"github.com/kubeshop/tracetest/cli-e2etest/environment"
"github.com/kubeshop/tracetest/cli-e2etest/helpers"
"github.com/kubeshop/tracetest/cli-e2etest/testscenarios/types"
"github.com/kubeshop/tracetest/cli-e2etest/tracetestcli"
"github.com/stretchr/testify/require"
)

func TestApplyAnalyzer(t *testing.T) {
// instantiate require with testing helper
require := require.New(t)

// setup isolated e2e environment
env := environment.CreateAndStart(t)
defer env.Close(t)

cliConfig := env.GetCLIConfigPath(t)

// Given I am a Tracetest CLI user
// And I have my server recently created

// When I try to set up a new analyzer
// Then it should be applied with success
configPath := env.GetTestResourcePath(t, "new-analyzer")

result := tracetestcli.Exec(t, fmt.Sprintf("apply analyzer --file %s", configPath), tracetestcli.WithCLIConfig(cliConfig))
helpers.RequireExitCodeEqual(t, result, 0)

// When I try to get a analyzer again
// Then it should return the analyzer applied on the last step, with analytics disabled
result = tracetestcli.Exec(t, "get analyzer --id current", tracetestcli.WithCLIConfig(cliConfig))
helpers.RequireExitCodeEqual(t, result, 0)

analyzer := helpers.UnmarshalYAML[types.AnalyzerResource](t, result.StdOut)
require.Equal("Analyzer", analyzer.Type)
require.Equal("current", analyzer.Spec.Id)
require.Equal("analyzer", analyzer.Spec.Name)
require.True(analyzer.Spec.Enabled)
require.Equal(analyzer.Spec.MinimumScore, 95)
require.Len(analyzer.Spec.Plugins, 3)
}
30 changes: 30 additions & 0 deletions testing/cli-e2etest/testscenarios/analyzer/delete_analyzer_test.go
@@ -0,0 +1,30 @@
package analyzer

import (
"testing"

"github.com/kubeshop/tracetest/cli-e2etest/environment"
"github.com/kubeshop/tracetest/cli-e2etest/helpers"
"github.com/kubeshop/tracetest/cli-e2etest/tracetestcli"
"github.com/stretchr/testify/require"
)

func TestDeleteAnalyzer(t *testing.T) {
// instantiate require with testing helper
require := require.New(t)

// setup isolated e2e environment
env := environment.CreateAndStart(t)
defer env.Close(t)

cliConfig := env.GetCLIConfigPath(t)

// Given I am a Tracetest CLI user
// And I have my server recently created

// When I try to delete the analyzer
// Then it should return a error message, showing that we cannot delete a analyzer
result := tracetestcli.Exec(t, "delete analyzer --id current", tracetestcli.WithCLIConfig(cliConfig))
helpers.RequireExitCodeEqual(t, result, 1)
require.Contains(result.StdErr, "the specified resource type doesn't support the action")
}
108 changes: 108 additions & 0 deletions testing/cli-e2etest/testscenarios/analyzer/get_analyzer_test.go
@@ -0,0 +1,108 @@
package analyzer

import (
"fmt"
"strings"
"testing"

"github.com/kubeshop/tracetest/cli-e2etest/environment"
"github.com/kubeshop/tracetest/cli-e2etest/helpers"
"github.com/kubeshop/tracetest/cli-e2etest/testscenarios/types"
"github.com/kubeshop/tracetest/cli-e2etest/tracetestcli"
"github.com/stretchr/testify/require"
)

func addGetAnalyzerPreReqs(t *testing.T, env environment.Manager) {
cliConfig := env.GetCLIConfigPath(t)

// When I try to set up a analyzer
// Then it should be applied with success
configPath := env.GetTestResourcePath(t, "new-analyzer")

result := tracetestcli.Exec(t, fmt.Sprintf("apply analyzer --file %s", configPath), tracetestcli.WithCLIConfig(cliConfig))
helpers.RequireExitCodeEqual(t, result, 0)
}

func TestGetAnalyzer(t *testing.T) {
// instantiate require with testing helper
require := require.New(t)

env := environment.CreateAndStart(t)
defer env.Close(t)

cliConfig := env.GetCLIConfigPath(t)

t.Run("get with no analyzer initialized", func(t *testing.T) {
// Given I am a Tracetest CLI user
// And I have my server recently created
// And no analyzer previously registered

// When I try to get a analyzer on yaml mode
// Then it should print a YAML with the default analyzer
result := tracetestcli.Exec(t, "get analyzer --id current --output yaml", tracetestcli.WithCLIConfig(cliConfig))
require.Equal(0, result.ExitCode)

analyzer := helpers.UnmarshalYAML[types.AnalyzerResource](t, result.StdOut)
require.Equal("Analyzer", analyzer.Type)
require.Equal("current", analyzer.Spec.Id)
require.Equal("analyzer", analyzer.Spec.Name)
require.True(analyzer.Spec.Enabled)
require.Equal(analyzer.Spec.MinimumScore, 0)
require.Len(analyzer.Spec.Plugins, 3)
})

addGetAnalyzerPreReqs(t, env)

t.Run("get with YAML format", func(t *testing.T) {
// Given I am a Tracetest CLI user
// And I have my server recently created
// And I have a config already set

// When I try to get a config on yaml mode
// Then it should print a YAML
result := tracetestcli.Exec(t, "get analyzer --id current --output yaml", tracetestcli.WithCLIConfig(cliConfig))
require.Equal(0, result.ExitCode)

analyzer := helpers.UnmarshalYAML[types.AnalyzerResource](t, result.StdOut)
require.Equal("Analyzer", analyzer.Type)
require.Equal("current", analyzer.Spec.Id)
require.Equal("analyzer", analyzer.Spec.Name)
require.True(analyzer.Spec.Enabled)
require.Equal(analyzer.Spec.MinimumScore, 95)
require.Len(analyzer.Spec.Plugins, 3)
})

t.Run("get with JSON format", func(t *testing.T) {
// Given I am a Tracetest CLI user
// And I have my server recently created
// And I have a analyzer already set

// When I try to get a analyzer on json mode
// Then it should print a json
result := tracetestcli.Exec(t, "get analyzer --id current --output json", tracetestcli.WithCLIConfig(cliConfig))
helpers.RequireExitCodeEqual(t, result, 0)

analyzer := helpers.UnmarshalJSON[types.AnalyzerResource](t, result.StdOut)
require.Equal("Analyzer", analyzer.Type)
require.Equal("current", analyzer.Spec.Id)
require.Equal("analyzer", analyzer.Spec.Name)
require.True(analyzer.Spec.Enabled)
require.Equal(analyzer.Spec.MinimumScore, 95)
require.Len(analyzer.Spec.Plugins, 3)
})

t.Run("get with pretty format", func(t *testing.T) {
// Given I am a Tracetest CLI user
// And I have my server recently created
// And I have a analyzer already set

// When I try to get a analyzer on pretty mode
// Then it should print a table with 4 lines printed: header, separator, a analyzer item and empty line
result := tracetestcli.Exec(t, "get analyzer --id current --output pretty", tracetestcli.WithCLIConfig(cliConfig))
helpers.RequireExitCodeEqual(t, result, 0)
require.Contains(result.StdOut, "current")

lines := strings.Split(result.StdOut, "\n")
require.Len(lines, 4)
})
}

0 comments on commit 707f918

Please sign in to comment.