Skip to content

Commit 2cceeef

Browse files
STAC-22985: List mappings and show their status
1 parent c59cfa9 commit 2cceeef

35 files changed

+3410
-4
lines changed

cmd/otelcomponentmapping.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
"github.com/stackvista/stackstate-cli/cmd/otelcomponentmapping"
6+
"github.com/stackvista/stackstate-cli/internal/di"
7+
)
8+
9+
func OtelComponentMappingCommand(deps *di.Deps) *cobra.Command {
10+
cmd := &cobra.Command{
11+
Use: "otel-component-mapping",
12+
Short: "Manage the Otel Component Mapping",
13+
Long: "Manage the Otel Component Mapping.",
14+
}
15+
16+
cmd.AddCommand(otelcomponentmapping.OtelComponentMappingListCommand(deps))
17+
cmd.AddCommand(otelcomponentmapping.OtelComponentMappingStatusCommand(deps))
18+
19+
return cmd
20+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package otelcomponentmapping
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
"github.com/stackvista/stackstate-cli/cmd/otelmapping"
6+
"github.com/stackvista/stackstate-cli/generated/stackstate_api"
7+
"github.com/stackvista/stackstate-cli/internal/common"
8+
"github.com/stackvista/stackstate-cli/internal/di"
9+
"sort"
10+
)
11+
12+
func OtelComponentMappingListCommand(deps *di.Deps) *cobra.Command {
13+
cmd := &cobra.Command{
14+
Use: "list",
15+
Short: "Lists active Otel Component Mappings",
16+
Long: "Lists active Otel Component Mappings.",
17+
RunE: deps.CmdRunEWithApi(RunListComponentCommand),
18+
}
19+
20+
return cmd
21+
}
22+
23+
func RunListComponentCommand(cmd *cobra.Command, cli *di.Deps, api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo) common.CLIError {
24+
mappingsList, resp, err := api.OtelMappingApi.GetOtelComponentMappings(cli.Context).Execute()
25+
if err != nil {
26+
return common.NewResponseError(err, resp)
27+
}
28+
29+
sort.SliceStable(mappingsList, func(i, j int) bool {
30+
return mappingsList[i].Name < mappingsList[j].Name
31+
})
32+
33+
if cli.IsJson() {
34+
cli.Printer.PrintJson(map[string]interface{}{
35+
"otel component mappings": mappingsList,
36+
})
37+
} else {
38+
cli.Printer.Table(otelmapping.FormatOtelMappingTable(mappingsList))
39+
}
40+
41+
return nil
42+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package otelcomponentmapping
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stackvista/stackstate-cli/cmd/otelmapping"
7+
"github.com/stackvista/stackstate-cli/internal/di"
8+
"github.com/stackvista/stackstate-cli/internal/printer"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestListOtelComponentMappingsJson(t *testing.T) {
13+
cli := di.NewMockDeps(t)
14+
cmd := OtelComponentMappingListCommand(&cli.Deps)
15+
cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingsResponse.Result = otelmapping.TestAllMappingItems
16+
17+
di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "-o", "json")
18+
19+
calls := *cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingsCalls
20+
assert.Len(t, calls, 1)
21+
22+
expected := []map[string]interface{}{
23+
{
24+
"otel component mappings": otelmapping.TestAllMappingItems,
25+
},
26+
}
27+
28+
assert.Equal(t, expected, *cli.MockPrinter.PrintJsonCalls)
29+
}
30+
31+
func TestOtelComponentMappingListTable(t *testing.T) {
32+
cli := di.NewMockDeps(t)
33+
cmd := OtelComponentMappingListCommand(&cli.Deps)
34+
cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingsResponse.Result = otelmapping.TestAllMappingItems
35+
36+
di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd)
37+
38+
calls := *cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingsCalls
39+
assert.Len(t, calls, 1)
40+
41+
expectedTableCall := []printer.TableData{
42+
{
43+
Header: []string{"Name", "Identifier"},
44+
Data: [][]interface{}{
45+
{otelmapping.TestSomeOtelMappingItem.Name, "identifier"},
46+
{otelmapping.TestSomeOtelMappingItem2.Name, "identifier2"},
47+
},
48+
MissingTableDataMsg: printer.NotFoundMsg{Types: "otel mappings"},
49+
},
50+
}
51+
52+
assert.Equal(t, expectedTableCall, *cli.MockPrinter.TableCalls)
53+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package otelcomponentmapping
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
"github.com/stackvista/stackstate-cli/cmd/otelmapping"
6+
"github.com/stackvista/stackstate-cli/internal/common"
7+
"github.com/stackvista/stackstate-cli/internal/di"
8+
)
9+
10+
//if cli.IsJson() {
11+
//cli.Printer.PrintJson(
12+
13+
func OtelComponentMappingStatusCommand(deps *di.Deps) *cobra.Command {
14+
args := &otelmapping.StatusArgs{}
15+
cmd := &cobra.Command{
16+
Use: "status",
17+
Short: "Get the status of an Otel Component Mappings",
18+
Long: "Get the status of an Otel Component Mappings.",
19+
RunE: deps.CmdRunEWithApi(otelmapping.RunStatus(args, "component", otelmapping.FetchComponentStatus)),
20+
}
21+
common.AddRequiredIdentifierFlagVar(cmd, &args.Identifier, "Identifier of the Otel Component Mapping")
22+
return cmd
23+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package otelcomponentmapping
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stackvista/stackstate-cli/cmd/otelmapping"
7+
"github.com/stackvista/stackstate-cli/internal/di"
8+
"github.com/stackvista/stackstate-cli/internal/printer"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestOtelComponentMappingStatusJson(t *testing.T) {
13+
cli := di.NewMockDeps(t)
14+
cmd := OtelComponentMappingStatusCommand(&cli.Deps)
15+
cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingStatusResponse.Result = *otelmapping.TestSomeOtelMappingStatus
16+
17+
di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "--identifier", "identifier", "-o", "json")
18+
19+
calls := *cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingStatusCalls
20+
assert.Len(t, calls, 1)
21+
22+
expected := []map[string]interface{}{
23+
{
24+
"otel-component-mapping": otelmapping.TestSomeOtelMappingStatus,
25+
},
26+
}
27+
28+
assert.Equal(t, expected, *cli.MockPrinter.PrintJsonCalls)
29+
}
30+
31+
func TestOtelComponentMappingStatusTable(t *testing.T) {
32+
cli := di.NewMockDeps(t)
33+
cmd := OtelComponentMappingStatusCommand(&cli.Deps)
34+
cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingStatusResponse.Result = *otelmapping.TestSomeOtelMappingStatus
35+
36+
di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "--identifier", "identifier")
37+
38+
calls := *cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingStatusCalls
39+
assert.Len(t, calls, 1)
40+
41+
expectedTableCall := []printer.TableData{
42+
{
43+
Header: []string{"Name", "Identifier", "Components", "Relations"},
44+
Data: [][]interface{}{
45+
{otelmapping.TestSomeOtelMappingStatusItem.Name, "identifier", int64(200), int64(100)},
46+
},
47+
MissingTableDataMsg: printer.NotFoundMsg{Types: "otel mappings"},
48+
},
49+
{
50+
Header: []string{"Metric", "10s ago", "10-20s ago", "20-30s ago"},
51+
Data: [][]interface{}{
52+
{"latency seconds", float64(23.5), float64(23.5), float64(23.5)},
53+
},
54+
MissingTableDataMsg: printer.NotFoundMsg{Types: "metrics"},
55+
},
56+
{
57+
Header: []string{"Issue Id", "Level", "Message"},
58+
Data: [][]interface{}{
59+
{"-", otelmapping.TestError1.Level, otelmapping.TestError1.Message},
60+
{"-", otelmapping.TestError2.Level, otelmapping.TestError2.Message},
61+
},
62+
MissingTableDataMsg: printer.NotFoundMsg{Types: "otel component mapping errors"},
63+
},
64+
}
65+
66+
assert.Equal(t, expectedTableCall, *cli.MockPrinter.TableCalls)
67+
}

cmd/otelmapping/otelmapping.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package otelmapping
2+
3+
import (
4+
"fmt"
5+
"github.com/spf13/cobra"
6+
"github.com/stackvista/stackstate-cli/generated/stackstate_api"
7+
"github.com/stackvista/stackstate-cli/internal/common"
8+
"github.com/stackvista/stackstate-cli/internal/di"
9+
"github.com/stackvista/stackstate-cli/internal/printer"
10+
"golang.org/x/text/cases"
11+
"golang.org/x/text/language"
12+
"net/http"
13+
)
14+
15+
type StatusArgs struct {
16+
Identifier string
17+
}
18+
19+
func FormatOtelMappingStatusTable(otelmappings []stackstate_api.OtelMappingStatusItem) printer.TableData {
20+
data := make([][]interface{}, len(otelmappings))
21+
22+
for i, otelmapping := range otelmappings {
23+
identifier := "-"
24+
if otelmapping.HasIdentifier() {
25+
identifier = otelmapping.GetIdentifier()
26+
}
27+
data[i] = []interface{}{
28+
otelmapping.Name,
29+
identifier,
30+
otelmapping.ComponentCount,
31+
otelmapping.RelationCount,
32+
}
33+
}
34+
return printer.TableData{
35+
Header: []string{"Name", "Identifier", "Components", "Relations"},
36+
Data: data,
37+
MissingTableDataMsg: printer.NotFoundMsg{Types: "otel mappings"},
38+
}
39+
}
40+
41+
func FormatOtelMappingTable(otelmappings []stackstate_api.OtelMappingItem) printer.TableData {
42+
data := make([][]interface{}, len(otelmappings))
43+
44+
for i, otelmapping := range otelmappings {
45+
identifier := "-"
46+
if otelmapping.HasIdentifier() {
47+
identifier = otelmapping.GetIdentifier()
48+
}
49+
data[i] = []interface{}{
50+
otelmapping.Name,
51+
identifier,
52+
}
53+
}
54+
return printer.TableData{
55+
Header: []string{"Name", "Identifier"},
56+
Data: data,
57+
MissingTableDataMsg: printer.NotFoundMsg{Types: "otel mappings"},
58+
}
59+
}
60+
61+
type StatusFetcher func(cli *di.Deps, api *stackstate_api.APIClient, identifier string) (*stackstate_api.OtelMappingStatus, *http.Response, error)
62+
63+
func FetchComponentStatus(cli *di.Deps, api *stackstate_api.APIClient, identifier string) (*stackstate_api.OtelMappingStatus, *http.Response, error) {
64+
return api.OtelMappingApi.GetOtelComponentMappingStatus(cli.Context, identifier).Execute()
65+
}
66+
67+
func FetchRelationStatus(cli *di.Deps, api *stackstate_api.APIClient, identifier string) (*stackstate_api.OtelMappingStatus, *http.Response, error) {
68+
return api.OtelMappingApi.GetOtelRelationMappingStatus(cli.Context, identifier).Execute()
69+
}
70+
71+
func RunStatus(args *StatusArgs, mappingType string, fetch StatusFetcher) di.CmdWithApiFn {
72+
return func(cmd *cobra.Command, cli *di.Deps, api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo) common.CLIError {
73+
mappingStatus, resp, err := fetch(cli, api, args.Identifier)
74+
if err != nil {
75+
return common.NewResponseError(err, resp)
76+
}
77+
78+
jsonKey := fmt.Sprintf("otel-%s-mapping", mappingType)
79+
title := cases.Title(language.English).String(mappingType)
80+
mappingTitle := fmt.Sprintf("Otel %s Mapping:", title)
81+
metricsTitle := fmt.Sprintf("Otel %s Mapping Metrics:", title)
82+
errorsTitle := fmt.Sprintf("Otel %s Mapping Errors:", title)
83+
errorsNotFoundMsg := fmt.Sprintf("otel %s mapping errors", mappingType)
84+
85+
if cli.IsJson() {
86+
cli.Printer.PrintJson(map[string]interface{}{
87+
jsonKey: mappingStatus,
88+
})
89+
} else {
90+
cli.Printer.PrintLn("\n")
91+
cli.Printer.PrintLn(mappingTitle)
92+
cli.Printer.Table(FormatOtelMappingStatusTable([]stackstate_api.OtelMappingStatusItem{
93+
mappingStatus.Item,
94+
}))
95+
96+
if mappingStatus.HasMetrics() {
97+
cli.Printer.PrintLn("\n")
98+
cli.Printer.PrintLn(metricsTitle)
99+
size := mappingStatus.Metrics.BucketSizeSeconds
100+
cli.Printer.Table(printer.TableData{
101+
Header: []string{"Metric", fmt.Sprintf("%ds ago", size), fmt.Sprintf("%d-%ds ago", size, 2*size), fmt.Sprintf("%d-%ds ago", 2*size, 3*size)}, //nolint:mnd
102+
Data: [][]interface{}{
103+
printer.MetricBucketToRow("latency seconds", mappingStatus.Metrics.LatencySeconds),
104+
},
105+
MissingTableDataMsg: printer.NotFoundMsg{Types: "metrics"},
106+
})
107+
}
108+
109+
data := make([][]interface{}, len(mappingStatus.ErrorDetails))
110+
for i, error := range mappingStatus.ErrorDetails {
111+
id := "-"
112+
if error.HasIssueId() {
113+
id = *error.IssueId
114+
}
115+
data[i] = []interface{}{id, error.Level, error.Message}
116+
}
117+
118+
cli.Printer.PrintLn("\n")
119+
cli.Printer.PrintLn(errorsTitle)
120+
cli.Printer.Table(printer.TableData{
121+
Header: []string{"Issue Id", "Level", "Message"},
122+
Data: data,
123+
MissingTableDataMsg: printer.NotFoundMsg{Types: errorsNotFoundMsg},
124+
})
125+
}
126+
127+
return nil
128+
}
129+
}

cmd/otelmapping/test_data.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package otelmapping
2+
3+
import (
4+
"github.com/stackvista/stackstate-cli/generated/stackstate_api"
5+
)
6+
7+
var (
8+
TestError1 = stackstate_api.NewOtelMappingError(stackstate_api.MESSAGELEVEL_WARN, "Oops")
9+
TestError2 = stackstate_api.NewOtelMappingError(stackstate_api.MESSAGELEVEL_ERROR, "Oops2")
10+
TestSomeErrors = []stackstate_api.OtelMappingError{
11+
*TestError1,
12+
*TestError2,
13+
}
14+
TestMetricValue = (func() *stackstate_api.MetricBucketValue {
15+
v := stackstate_api.NewMetricBucketValue()
16+
v.SetValue(float64(23.5))
17+
return v
18+
})()
19+
TestSomeMetrics = stackstate_api.OtelMappingMetrics{
20+
BucketSizeSeconds: 10,
21+
LatencySeconds: []stackstate_api.MetricBucketValue{*TestMetricValue, *TestMetricValue, *TestMetricValue},
22+
}
23+
TestSomeOtelMappingStatusItem = (func() *stackstate_api.OtelMappingStatusItem {
24+
v := stackstate_api.NewOtelMappingStatusItem("name", 100, 200)
25+
v.SetIdentifier("identifier")
26+
return v
27+
})()
28+
TestSomeOtelMappingStatus = (func() *stackstate_api.OtelMappingStatus {
29+
v := stackstate_api.NewOtelMappingStatus(*TestSomeOtelMappingStatusItem, TestSomeErrors)
30+
v.SetMetrics(TestSomeMetrics)
31+
return v
32+
})()
33+
)
34+
35+
var (
36+
TestSomeOtelMappingItem = (func() *stackstate_api.OtelMappingItem {
37+
v := stackstate_api.NewOtelMappingItem("name")
38+
v.SetIdentifier("identifier")
39+
return v
40+
})()
41+
TestSomeOtelMappingItem2 = (func() *stackstate_api.OtelMappingItem {
42+
v := stackstate_api.NewOtelMappingItem("name2")
43+
v.SetIdentifier("identifier2")
44+
return v
45+
})()
46+
TestAllMappingItems = []stackstate_api.OtelMappingItem{*TestSomeOtelMappingItem, *TestSomeOtelMappingItem2}
47+
)

0 commit comments

Comments
 (0)