-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from stackitcloud/hs/ske-cluster-delete
Implement `stackit ske cluster delete`
- Loading branch information
Showing
3 changed files
with
279 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package delete | ||
|
||
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/utils" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
"github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
"github.com/stackitcloud/stackit-sdk-go/services/ske/wait" | ||
) | ||
|
||
const ( | ||
projectIdFlag = "project-id" | ||
clusterNameFlag = "name" | ||
) | ||
|
||
type flagModel struct { | ||
ProjectId string | ||
ClusterName string | ||
} | ||
|
||
var Cmd = &cobra.Command{ | ||
Use: "delete", | ||
Short: "Delete a SKE cluster", | ||
Long: "Delete a SKE cluster", | ||
Example: `$ stackit ske cluster delete --project-id xxx --name xxx`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
ctx := context.Background() | ||
model, err := parseFlags(cmd) | ||
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) | ||
_, err = req.Execute() | ||
if err != nil { | ||
return fmt.Errorf("delete SKE cluster: %w", err) | ||
} | ||
|
||
// Wait for async operation | ||
_, err = wait.DeleteClusterWaitHandler(ctx, apiClient, model.ProjectId, model.ClusterName).WaitWithContext(ctx) | ||
if err != nil { | ||
return fmt.Errorf("wait for SKE cluster deletion: %w", err) | ||
} | ||
|
||
fmt.Println("Cluster deleted") | ||
return nil | ||
}, | ||
} | ||
|
||
func init() { | ||
configureFlags(Cmd) | ||
} | ||
|
||
func configureFlags(cmd *cobra.Command) { | ||
cmd.Flags().StringP(clusterNameFlag, "n", "", "Cluster name") | ||
|
||
err := utils.MarkFlagsRequired(cmd, clusterNameFlag) | ||
cobra.CheckErr(err) | ||
} | ||
|
||
func parseFlags(cmd *cobra.Command) (*flagModel, error) { | ||
projectId := viper.GetString(config.ProjectIdKey) | ||
if projectId == "" { | ||
return nil, fmt.Errorf("project ID not set") | ||
} | ||
clusterName := utils.FlagToStringValue(cmd, clusterNameFlag) | ||
if clusterName == "" { | ||
return nil, fmt.Errorf("cluster name can't be empty") | ||
} | ||
|
||
return &flagModel{ | ||
ProjectId: projectId, | ||
ClusterName: clusterName, | ||
}, nil | ||
} | ||
|
||
func buildRequest(ctx context.Context, model *flagModel, apiClient *ske.APIClient) ske.ApiDeleteClusterRequest { | ||
req := apiClient.DeleteCluster(ctx, model.ProjectId, model.ClusterName) | ||
return req | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
package delete | ||
|
||
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, | ||
clusterNameFlag: "cluster", | ||
} | ||
for _, mod := range mods { | ||
mod(flagValues) | ||
} | ||
return flagValues | ||
} | ||
|
||
func fixtureFlagModel(mods ...func(model *flagModel)) *flagModel { | ||
model := &flagModel{ | ||
ProjectId: testProjectId, | ||
ClusterName: "cluster", | ||
} | ||
for _, mod := range mods { | ||
mod(model) | ||
} | ||
return model | ||
} | ||
|
||
func fixtureRequest(mods ...func(request *ske.ApiDeleteClusterRequest)) ske.ApiDeleteClusterRequest { | ||
request := testClient.DeleteCluster(testCtx, testProjectId, "cluster") | ||
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, | ||
}, | ||
{ | ||
description: "cluster name missing", | ||
flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
delete(flagValues, clusterNameFlag) | ||
}), | ||
isValid: false, | ||
}, | ||
{ | ||
description: "cluster name invalid", | ||
flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
flagValues[clusterNameFlag] = "" | ||
}), | ||
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) | ||
} | ||
|
||
configureFlags(cmd) | ||
|
||
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(cmd) | ||
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 | ||
isValid bool | ||
expectedRequest ske.ApiDeleteClusterRequest | ||
}{ | ||
{ | ||
description: "base", | ||
model: fixtureFlagModel(), | ||
isValid: true, | ||
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) | ||
} | ||
}) | ||
} | ||
} |