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 create and update #8

Merged
merged 10 commits into from
Nov 21, 2023
Merged
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ dist/

# IDE
.vscode

# OS generated files
.DS_Store
12 changes: 9 additions & 3 deletions internal/cmd/ske/cluster/cluster.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package cluster

import (
"fmt"

"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/create"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/delete"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/describe"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/list"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/update"

"github.com/spf13/cobra"
)
Expand All @@ -12,11 +16,13 @@ var Cmd = &cobra.Command{
Use: "cluster",
Short: "Provides functionality for SKE cluster",
Long: "Provides functionality for SKE cluster",
Example: list.Cmd.Example,
Example: fmt.Sprintf("%s\n%s", create.Cmd.Example, list.Cmd.Example),
}

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

import (
"context"
"encoding/json"
"fmt"
"os"
"strings"

"github.com/stackitcloud/stackit-cli/internal/pkg/config"
"github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client"
skeUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/utils"
"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"
NameFlag = "name"
PayloadFlag = "payload"
)

type FlagModel struct {
ProjectId string
Name string
Payload ske.CreateOrUpdateClusterPayload
}

var Cmd = &cobra.Command{
Use: "create",
Short: "Creates an SKE cluster",
Long: "Creates an SKE cluster",
Example: `$ stackit ske cluster create --project-id xxx --payload @./payload.json`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
model, err := ParseFlags(cmd, os.ReadFile)
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\"")
}

// Check if cluster exists
exists, err := skeUtils.ClusterExists(ctx, apiClient, model.ProjectId, model.Name)
if err != nil {
return err
}
if exists {
return fmt.Errorf("cluster with name %s already exists", model.Name)
}

// Call API
req, err := BuildRequest(ctx, model, apiClient)
if err != nil {
return fmt.Errorf("build SKE cluster creation request: %w", err)
}
resp, err := req.Execute()
if err != nil {
return fmt.Errorf("create SKE cluster: %w", err)
}

// Wait for async operation
name := *resp.Name
_, err = wait.CreateOrUpdateClusterWaitHandler(ctx, apiClient, model.ProjectId, name).WaitWithContext(ctx)
if err != nil {
return fmt.Errorf("wait for SKE cluster creation: %w", err)
}

fmt.Printf("Created cluster with name %s\n", name)
return nil
},
}

func init() {
ConfigureFlags(Cmd)
}

func ConfigureFlags(cmd *cobra.Command) {
cmd.Flags().StringP(NameFlag, "n", "", "Cluster name")
cmd.Flags().String(PayloadFlag, "", `Request payload (JSON). Can be a string or a file path, if prefixed with "@". Example: @./payload.json`)

err := utils.MarkFlagsRequired(cmd, NameFlag, PayloadFlag)
cobra.CheckErr(err)
}

type FileReaderFunc func(filename string) ([]byte, error)

func ParseFlags(cmd *cobra.Command, fileReaderFunc FileReaderFunc) (*FlagModel, error) {
projectId := viper.GetString(config.ProjectIdKey)
if projectId == "" {
return nil, fmt.Errorf("project ID not set")
}

name := utils.FlagToStringValue(cmd, NameFlag)
payloadString := utils.FlagToStringValue(cmd, PayloadFlag)
payloadStringBytes := []byte(payloadString)

var payload ske.CreateOrUpdateClusterPayload
var err error
trimmedPayloadString := strings.Trim(string(payloadString), `"'`)
if strings.HasPrefix(trimmedPayloadString, "@") {
trimmedPayloadString = strings.Trim(trimmedPayloadString[1:], `"'`)
payloadStringBytes, err = fileReaderFunc(trimmedPayloadString)
if err != nil {
return nil, fmt.Errorf("read payload from file: %w", err)
}
}
err = json.Unmarshal(payloadStringBytes, &payload)
if err != nil {
return nil, fmt.Errorf("encode payload: %w", err)
}

return &FlagModel{
ProjectId: projectId,
Name: name,
Payload: payload,
}, nil
}

func BuildRequest(ctx context.Context, model *FlagModel, apiClient *ske.APIClient) (ske.ApiCreateOrUpdateClusterRequest, error) {
req := apiClient.CreateOrUpdateCluster(ctx, model.ProjectId, model.Name)

req = req.CreateOrUpdateClusterPayload(model.Payload)
return req, nil
}
Loading