Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions cmd/ingestionapikey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cmd

import (
"github.com/spf13/cobra"
"github.com/stackvista/stackstate-cli/cmd/ingestionapikey"
"github.com/stackvista/stackstate-cli/internal/di"
)

func IngestionApiKeyCommand(deps *di.Deps) *cobra.Command {
cmd := &cobra.Command{
Use: "ingestion-api-key",
Short: "Manage Ingestion API Keys",
Long: "Manage API Keys used by ingestion pipelines, means data (spans, metrics, logs an so on) send by STS Agent, OTel and so on.",
}

cmd.AddCommand(ingestionapikey.CreateCommand(deps))
cmd.AddCommand(ingestionapikey.ListCommand(deps))
cmd.AddCommand(ingestionapikey.DeleteCommand(deps))
return cmd
}
71 changes: 71 additions & 0 deletions cmd/ingestionapikey/ingestionapikey_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package ingestionapikey

import (
"fmt"
"time"

"github.com/gookit/color"
"github.com/spf13/cobra"
"github.com/stackvista/stackstate-cli/generated/stackstate_api"
"github.com/stackvista/stackstate-cli/internal/common"
"github.com/stackvista/stackstate-cli/internal/di"
)

const (
DateFormat = "2006-01-02"
)

type CreateArgs struct {
Name string
Expiration time.Time
Description string
}

func CreateCommand(deps *di.Deps) *cobra.Command {
args := &CreateArgs{}
cmd := &cobra.Command{
Use: "create",
Short: "Create a new Ingestion Api Key",
Long: "Creates a token and then returns it in the response, the token can't be obtained any more after that so store it in the safe space.",
RunE: deps.CmdRunEWithApi(RunIngestionApiKeyGenerationCommand(args)),
}

common.AddRequiredNameFlagVar(cmd, &args.Name, "Name of the API Key")
cmd.Flags().TimeVar(&args.Expiration, "expiration", time.Time{}, []string{DateFormat}, "Expiration date of the API Key")
cmd.Flags().StringVar(&args.Description, "description", "", "Optional description of the API Key")
return cmd
}

func RunIngestionApiKeyGenerationCommand(args *CreateArgs) di.CmdWithApiFn {
return func(cmd *cobra.Command, cli *di.Deps, api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo) common.CLIError {
req := stackstate_api.GenerateIngestionApiKeyRequest{
Name: args.Name,
}

if len(args.Description) > 0 {
req.Description = &args.Description
}

if !args.Expiration.IsZero() {
m := args.Expiration.UnixMilli()
req.Expiration = &m
}

ingestionApiKeyAPI := api.IngestionApiKeyApi.GenerateIngestionApiKey(cli.Context)

serviceToken, resp, err := ingestionApiKeyAPI.GenerateIngestionApiKeyRequest(req).Execute()
if err != nil {
return common.NewResponseError(err, resp)
}

if cli.IsJson() {
cli.Printer.PrintJson(map[string]interface{}{
"ingestion-api-key": serviceToken,
})
} else {
cli.Printer.Success(fmt.Sprintf("Ingestion API Key generated: %s\n", color.White.Render(serviceToken.ApiKey)))
}

return nil
}
}
93 changes: 93 additions & 0 deletions cmd/ingestionapikey/ingestionapikey_create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package ingestionapikey

import (
"testing"

"github.com/stackvista/stackstate-cli/generated/stackstate_api"
"github.com/stackvista/stackstate-cli/internal/di"
"github.com/stretchr/testify/assert"
)

func TestIngestApiKeyGenerate(t *testing.T) {
cli := di.NewMockDeps(t)
cmd := CreateCommand(&cli.Deps)

cli.MockClient.ApiMocks.IngestionApiKeyApi.GenerateIngestionApiKeyResponse.Result = stackstate_api.GeneratedIngestionApiKeyResponse{
Name: "test-token",
ApiKey: "test-token-key",
Expiration: int64p(1590105600000),
Description: stringp("test-token-description"),
}

di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "--name", "test-token", "--description", "test-token-description", "--expiration", "2020-05-22")

checkCreateCall(t, cli.MockClient.ApiMocks.IngestionApiKeyApi.GenerateIngestionApiKeyCalls, "test-token", stringp("test-token-description"), int64p(1590105600000))
assert.Equal(t, []string{"Ingestion API Key generated: \x1b[37mtest-token-key\x1b[0m\n"}, *cli.MockPrinter.SuccessCalls)
}

func TestIngestApiKeyGenerateOnlyRequriedFlags(t *testing.T) {
cli := di.NewMockDeps(t)
cmd := CreateCommand(&cli.Deps)

cli.MockClient.ApiMocks.IngestionApiKeyApi.GenerateIngestionApiKeyResponse.Result = stackstate_api.GeneratedIngestionApiKeyResponse{
Name: "test-token2",
ApiKey: "test-token2-key",
}

di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "--name", "test-token2")

checkCreateCall(t, cli.MockClient.ApiMocks.IngestionApiKeyApi.GenerateIngestionApiKeyCalls, "test-token2", nil, nil)
assert.Equal(t, []string{"Ingestion API Key generated: \x1b[37mtest-token2-key\x1b[0m\n"}, *cli.MockPrinter.SuccessCalls)
}

func TestIngestApiKeyGenerateJSON(t *testing.T) {
cli := di.NewMockDeps(t)
cmd := CreateCommand(&cli.Deps)

r := &stackstate_api.GeneratedIngestionApiKeyResponse{
Name: "test-token",
ApiKey: "test-token-key",
Expiration: int64p(1590105600000),
Description: stringp("test-token-description"),
}

cli.MockClient.ApiMocks.IngestionApiKeyApi.GenerateIngestionApiKeyResponse.Result = *r

di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "--name", "test-token", "--description", "test-token-description", "--expiration", "2020-05-22", "-o", "json")

checkCreateCall(t, cli.MockClient.ApiMocks.IngestionApiKeyApi.GenerateIngestionApiKeyCalls, "test-token", stringp("test-token-description"), int64p(1590105600000))
assert.Equal(t,
[]map[string]interface{}{{
"ingestion-api-key": r,
}},
*cli.MockPrinter.PrintJsonCalls,
)
assert.False(t, cli.MockPrinter.HasNonJsonCalls)
}

func int64p(i int64) *int64 {
return &i
}

func stringp(i string) *string {
return &i
}

func checkCreateCall(t *testing.T, calls *[]stackstate_api.GenerateIngestionApiKeyCall, name string, description *string, expiration *int64) {
assert.Len(t, *calls, 1)

call := (*calls)[0]
assert.Equal(t, name, call.PgenerateIngestionApiKeyRequest.Name)

if description != nil {
assert.Equal(t, *description, *call.PgenerateIngestionApiKeyRequest.Description)
} else {
assert.Nil(t, call.PgenerateIngestionApiKeyRequest.Description)
}

if expiration != nil {
assert.Equal(t, *expiration, *call.PgenerateIngestionApiKeyRequest.Expiration)
} else {
assert.Nil(t, call.PgenerateIngestionApiKeyRequest.Expiration)
}
}
47 changes: 47 additions & 0 deletions cmd/ingestionapikey/ingestionapikey_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package ingestionapikey

import (
"fmt"

"github.com/spf13/cobra"
"github.com/stackvista/stackstate-cli/generated/stackstate_api"
"github.com/stackvista/stackstate-cli/internal/common"
"github.com/stackvista/stackstate-cli/internal/di"
)

type DeleteArgs struct {
ID int64
}

func DeleteCommand(deps *di.Deps) *cobra.Command {
args := &DeleteArgs{}
cmd := &cobra.Command{
Use: "delete",
Short: "Delete an Ingestion Api Key",
Long: "Deleted key can't be used by sources, so all ingestion pipelines for that key will fail.",
RunE: deps.CmdRunEWithApi(RunIngestionApiKeyDeleteCommand(args)),
}

common.AddRequiredIDFlagVar(cmd, &args.ID, "ID of the Ingestion Api Key to delete")

return cmd
}

func RunIngestionApiKeyDeleteCommand(args *DeleteArgs) di.CmdWithApiFn {
return func(cmd *cobra.Command, cli *di.Deps, api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo) common.CLIError {
resp, err := api.IngestionApiKeyApi.DeleteIngestionApiKey(cli.Context, args.ID).Execute()
if err != nil {
return common.NewResponseError(err, resp)
}

if cli.IsJson() {
cli.Printer.PrintJson(map[string]interface{}{
"deleted-ingestion-api-key": args.ID,
})
} else {
cli.Printer.Success(fmt.Sprintf("Ingestion Api Key deleted: %d", args.ID))
}

return nil
}
}
30 changes: 30 additions & 0 deletions cmd/ingestionapikey/ingestionapikey_delete_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ingestionapikey

import (
"testing"

"github.com/stackvista/stackstate-cli/internal/di"
"github.com/stretchr/testify/assert"
)

func TestDeleteShouldFailOnNonIntID(t *testing.T) {
cli := di.NewMockDeps(t)
cmd := DeleteCommand(&cli.Deps)

_, err := di.ExecuteCommandWithContext(&cli.Deps, cmd, "--id", "foo")

assert.NotNil(t, err)
assert.Contains(t, err.Error(), "invalid argument \"foo\" for \"-i, --id\"")
}

func TestDelete(t *testing.T) {
cli := di.NewMockDeps(t)
cmd := DeleteCommand(&cli.Deps)

di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "--id", "1")

assert.Len(t, *cli.MockClient.ApiMocks.IngestionApiKeyApi.DeleteIngestionApiKeyCalls, 1)
assert.Equal(t, int64(1), (*cli.MockClient.ApiMocks.IngestionApiKeyApi.DeleteIngestionApiKeyCalls)[0].PingestionApiKeyId)

assert.Equal(t, []string{"Ingestion Api Key deleted: 1"}, *cli.MockPrinter.SuccessCalls)
}
58 changes: 58 additions & 0 deletions cmd/ingestionapikey/ingestionapikey_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package ingestionapikey

import (
"sort"
"time"

"github.com/spf13/cobra"
"github.com/stackvista/stackstate-cli/generated/stackstate_api"
"github.com/stackvista/stackstate-cli/internal/common"
"github.com/stackvista/stackstate-cli/internal/di"
"github.com/stackvista/stackstate-cli/internal/printer"
)

func ListCommand(deps *di.Deps) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List Ingestion Api Keys",
Long: "Returns only metadata without a key itself.",
RunE: deps.CmdRunEWithApi(RunIngestionApiKeyListCommand),
}

return cmd
}

func RunIngestionApiKeyListCommand(cmd *cobra.Command, cli *di.Deps, api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo) common.CLIError {
ingestionApiKeys, resp, err := api.IngestionApiKeyApi.GetIngestionApiKeys(cli.Context).Execute()
if err != nil {
return common.NewResponseError(err, resp)
}

sort.SliceStable(ingestionApiKeys, func(i, j int) bool {
return ingestionApiKeys[i].Name < ingestionApiKeys[j].Name
})

if cli.IsJson() {
cli.Printer.PrintJson(map[string]interface{}{
"ingestion-api-keys": ingestionApiKeys,
})
} else {
data := make([][]interface{}, 0)
for _, ingestionApiKey := range ingestionApiKeys {
sid := ingestionApiKey.Id
exp := ""
if ingestionApiKey.Expiration != nil {
exp = time.UnixMilli(*ingestionApiKey.Expiration).Format(DateFormat)
}
data = append(data, []interface{}{sid, ingestionApiKey.Name, exp, ingestionApiKey.Description})
}

cli.Printer.Table(printer.TableData{
Header: []string{"id", "name", "expiration", "description"},
Data: data,
MissingTableDataMsg: printer.NotFoundMsg{Types: "ingestion api keys"},
})
}

return nil
}
46 changes: 46 additions & 0 deletions cmd/ingestionapikey/ingestionapikey_list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ingestionapikey

import (
"testing"

"github.com/stackvista/stackstate-cli/generated/stackstate_api"
"github.com/stackvista/stackstate-cli/internal/di"
"github.com/stackvista/stackstate-cli/internal/printer"
"github.com/stretchr/testify/assert"
)

func TestIngestionApiKeyList(t *testing.T) {
cli := di.NewMockDeps(t)
cmd := ListCommand(&cli.Deps)
key1desc := "main key"

cli.MockClient.ApiMocks.IngestionApiKeyApi.GetIngestionApiKeysResponse.Result = []stackstate_api.IngestionApiKey{
{
Id: 1,
Name: "key1",
Description: &key1desc,
Expiration: int64p(1590105600000),
},
{
Id: 2,
Name: "key2",
Description: nil,
Expiration: nil,
},
}

di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd)

tableData := []printer.TableData{
{
Header: []string{"id", "name", "expiration", "description"},
Data: [][]interface{}{
{int64(1), "key1", "2020-05-22", &key1desc},
{int64(2), "key2", "", (*string)(nil)},
},
MissingTableDataMsg: printer.NotFoundMsg{Types: "ingestion api keys"},
},
}

assert.Equal(t, tableData, *cli.MockPrinter.TableCalls)
}
1 change: 1 addition & 0 deletions cmd/sts.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func STSCommand(cli *di.Deps) *cobra.Command {
cmd.AddCommand(RbacCommand(cli))
cmd.AddCommand(TopicCommand(cli))
cmd.AddCommand(TopologySyncCommand(cli))
cmd.AddCommand(IngestionApiKeyCommand(cli))

return cmd
}
Loading