Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement stackit ske cluster list #5

Merged
merged 3 commits into from
Nov 20, 2023
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
2 changes: 2 additions & 0 deletions internal/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/stackitcloud/stackit-cli/internal/cmd/config"
"github.com/stackitcloud/stackit-cli/internal/cmd/dns"
"github.com/stackitcloud/stackit-cli/internal/cmd/postgresql"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske"
configPkg "github.com/stackitcloud/stackit-cli/internal/pkg/config"
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"

Expand Down Expand Up @@ -42,6 +43,7 @@ func init() {
RootCmd.AddCommand(config.Cmd)
RootCmd.AddCommand(dns.Cmd)
RootCmd.AddCommand(postgresql.Cmd)
RootCmd.AddCommand(ske.Cmd)

configureFlags(RootCmd)
}
Expand Down
18 changes: 18 additions & 0 deletions internal/cmd/ske/cluster/cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package cluster

import (
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/list"

"github.com/spf13/cobra"
)

var Cmd = &cobra.Command{
Use: "cluster",
Short: "Provides functionality for SKE cluster",
Long: "Provides functionality for SKE cluster",
Example: list.Cmd.Example,
}

func init() {
Cmd.AddCommand(list.Cmd)
}
80 changes: 80 additions & 0 deletions internal/cmd/ske/cluster/list/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package list

import (
"context"
"fmt"

"github.com/stackitcloud/stackit-cli/internal/pkg/config"
"github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client"
"github.com/stackitcloud/stackit-cli/internal/pkg/tables"

"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
)

const (
projectIdFlag = "project-id"
)

type flagModel struct {
ProjectId string
}

var Cmd = &cobra.Command{
Use: "list",
Short: "List all SKE clusters",
Long: "List all SKE clusters",
Example: `$ stackit ske cluster list --project-id xxx`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
model, err := parseFlags()
if err != nil {
return err
}

// Configure API client
apiClient, err := client.ConfigureClient(cmd)
if err != nil {
return fmt.Errorf("authentication failed, please run \"stackit auth login\" or \"stackit auth activate-service-account\"")
}

// Call API
req := buildRequest(ctx, model, apiClient)
resp, err := req.Execute()
if err != nil {
return fmt.Errorf("get SKE clusters: %w", err)
}
clusters := *resp.Items
if len(clusters) == 0 {
fmt.Printf("No clusters found for project with ID %s\n", model.ProjectId)
return nil
}

// Show output as table
table := tables.NewTable()
table.SetHeader("NAME", "STATE")
for _, cluster := range clusters {
table.AddRow(*cluster.Name, *cluster.Status.Aggregated)
}
table.Render()

return nil
},
}

func parseFlags() (*flagModel, error) {
projectId := viper.GetString(config.ProjectIdKey)
if projectId == "" {
return nil, fmt.Errorf("project ID not set")
}

return &flagModel{
ProjectId: projectId,
}, nil
}

func buildRequest(ctx context.Context, model *flagModel, apiClient *ske.APIClient) ske.ApiGetClustersRequest {
req := apiClient.GetClusters(ctx, model.ProjectId)
return req
}
165 changes: 165 additions & 0 deletions internal/cmd/ske/cluster/list/list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package list

import (
"context"
"testing"

"github.com/stackitcloud/stackit-cli/internal/pkg/config"
"github.com/stackitcloud/stackit-cli/internal/pkg/testutils"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/uuid"
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
)

type testCtxKey struct{}

var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo")
var testClient = &ske.APIClient{}
var testProjectId = uuid.NewString()

func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
flagValues := map[string]string{
projectIdFlag: testProjectId,
}
for _, mod := range mods {
mod(flagValues)
}
return flagValues
}

func fixtureFlagModel(mods ...func(model *flagModel)) *flagModel {
model := &flagModel{
ProjectId: testProjectId,
}
for _, mod := range mods {
mod(model)
}
return model
}

func fixtureRequest(mods ...func(request *ske.ApiGetClustersRequest)) ske.ApiGetClustersRequest {
request := testClient.GetClusters(testCtx, testProjectId)
for _, mod := range mods {
mod(&request)
}
return request
}

func TestParseFlags(t *testing.T) {
tests := []struct {
description string
flagValues map[string]string
isValid bool
expectedModel *flagModel
}{
{
description: "base",
flagValues: fixtureFlagValues(),
isValid: true,
expectedModel: fixtureFlagModel(),
},
{
description: "no values",
flagValues: map[string]string{},
isValid: false,
},
{
description: "project id missing",
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
delete(flagValues, projectIdFlag)
}),
isValid: false,
},
{
description: "project id invalid 1",
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
flagValues[projectIdFlag] = ""
}),
isValid: false,
},
{
description: "project id invalid 2",
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
flagValues[projectIdFlag] = "invalid-uuid"
}),
isValid: false,
},
}

for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
cmd := &cobra.Command{}

// Flag defined in root command
err := testutils.ConfigureBindUUIDFlag(cmd, projectIdFlag, config.ProjectIdKey)
if err != nil {
t.Fatalf("configure global flag --%s: %v", projectIdFlag, err)
}

for flag, value := range tt.flagValues {
err := cmd.Flags().Set(flag, value)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
}
}

err = cmd.ValidateRequiredFlags()
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("error validating flags: %v", err)
}

model, err := parseFlags()
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("error parsing flags: %v", err)
}

if !tt.isValid {
t.Fatalf("did not fail on invalid input")
}
diff := cmp.Diff(model, tt.expectedModel)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
})
}
}

func TestBuildRequest(t *testing.T) {
tests := []struct {
description string
model *flagModel
expectedRequest ske.ApiGetClustersRequest
}{
{
description: "base",
model: fixtureFlagModel(),
expectedRequest: fixtureRequest(),
},
}

for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
request := buildRequest(testCtx, tt.model, testClient)

diff := cmp.Diff(request, tt.expectedRequest,
cmp.AllowUnexported(tt.expectedRequest),
cmpopts.EquateComparable(testCtx),
)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
})
}
}
18 changes: 18 additions & 0 deletions internal/cmd/ske/ske.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ske

import (
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster"

"github.com/spf13/cobra"
)

var Cmd = &cobra.Command{
Use: "ske",
Short: "Provides functionality for SKE",
Long: "Provides functionality for STACKIT Kubernetes engine (SKE)",
Example: cluster.Cmd.Example,
}

func init() {
Cmd.AddCommand(cluster.Cmd)
}