Skip to content

Add sqlserverflex options command #348

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

Merged
merged 5 commits into from
May 27, 2024
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
1 change: 1 addition & 0 deletions docs/stackit_beta_sqlserverflex.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ stackit beta sqlserverflex [flags]

* [stackit beta](./stackit_beta.md) - Contains beta STACKIT CLI commands
* [stackit beta sqlserverflex instance](./stackit_beta_sqlserverflex_instance.md) - Provides functionality for SQLServer Flex instances
* [stackit beta sqlserverflex options](./stackit_beta_sqlserverflex_options.md) - Lists SQL Server Flex options

50 changes: 50 additions & 0 deletions docs/stackit_beta_sqlserverflex_options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## stackit beta sqlserverflex options

Lists SQL Server Flex options

### Synopsis

Lists SQL Server Flex options (flavors, versions and storages for a given flavor)
Pass one or more flags to filter what categories are shown.

```
stackit beta sqlserverflex options [flags]
```

### Examples

```
List SQL Server Flex flavors options
$ stackit sqlserverflex options --flavors

List SQL Server Flex available versions
$ stackit sqlserverflex options --versions

List SQL Server Flex storage options for a given flavor. The flavor ID can be retrieved by running "$ stackit sqlserverflex options --flavors"
$ stackit sqlserverflex options --storages --flavor-id <FLAVOR_ID>
```

### Options

```
--flavor-id string The flavor ID to show storages for. Only relevant when "--storages" is passed
--flavors Lists supported flavors
-h, --help Help for "stackit beta sqlserverflex options"
--storages Lists supported storages for a given flavor
--versions Lists supported versions
```

### Options inherited from parent commands

```
-y, --assume-yes If set, skips all confirmation prompts
--async If set, runs the command asynchronously
-o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"]
-p, --project-id string Project ID
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
```

### SEE ALSO

* [stackit beta sqlserverflex](./stackit_beta_sqlserverflex.md) - Provides functionality for SQLServer Flex

4 changes: 2 additions & 2 deletions internal/cmd/beta/sqlserverflex/instance/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,13 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
return &model, nil
}

type SQLServerFlexClient interface {
type sqlServerFlexClient interface {
CreateInstance(ctx context.Context, projectId string) sqlserverflex.ApiCreateInstanceRequest
ListFlavorsExecute(ctx context.Context, projectId string) (*sqlserverflex.ListFlavorsResponse, error)
ListStoragesExecute(ctx context.Context, projectId, flavorId string) (*sqlserverflex.ListStoragesResponse, error)
}

func buildRequest(ctx context.Context, model *inputModel, apiClient SQLServerFlexClient) (sqlserverflex.ApiCreateInstanceRequest, error) {
func buildRequest(ctx context.Context, model *inputModel, apiClient sqlServerFlexClient) (sqlserverflex.ApiCreateInstanceRequest, error) {
req := apiClient.CreateInstance(ctx, model.ProjectId)

var flavorId *string
Expand Down
4 changes: 2 additions & 2 deletions internal/cmd/beta/sqlserverflex/instance/update/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,14 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu
return &model, nil
}

type SQLServerFlexClient interface {
type sqlServerFlexClient interface {
PartialUpdateInstance(ctx context.Context, projectId, instanceId string) sqlserverflex.ApiPartialUpdateInstanceRequest
GetInstanceExecute(ctx context.Context, projectId, instanceId string) (*sqlserverflex.GetInstanceResponse, error)
ListFlavorsExecute(ctx context.Context, projectId string) (*sqlserverflex.ListFlavorsResponse, error)
ListStoragesExecute(ctx context.Context, projectId, flavorId string) (*sqlserverflex.ListStoragesResponse, error)
}

func buildRequest(ctx context.Context, model *inputModel, apiClient SQLServerFlexClient) (sqlserverflex.ApiPartialUpdateInstanceRequest, error) {
func buildRequest(ctx context.Context, model *inputModel, apiClient sqlServerFlexClient) (sqlserverflex.ApiPartialUpdateInstanceRequest, error) {
req := apiClient.PartialUpdateInstance(ctx, model.ProjectId, model.InstanceId)

var flavorId *string
Expand Down
273 changes: 273 additions & 0 deletions internal/cmd/beta/sqlserverflex/options/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
package options

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

"github.com/goccy/go-yaml"
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-cli/internal/pkg/services/sqlserverflex/client"
"github.com/stackitcloud/stackit-cli/internal/pkg/tables"

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

const (
flavorsFlag = "flavors"
versionsFlag = "versions"
storagesFlag = "storages"
flavorIdFlag = "flavor-id"
)

type inputModel struct {
*globalflags.GlobalFlagModel

Flavors bool
Versions bool
Storages bool
FlavorId *string
}

type options struct {
Flavors *[]sqlserverflex.InstanceFlavorEntry `json:"flavors,omitempty"`
Versions *[]string `json:"versions,omitempty"`
Storages *flavorStorages `json:"flavorStorages,omitempty"`
}

type flavorStorages struct {
FlavorId string `json:"flavorId"`
Storages *sqlserverflex.ListStoragesResponse `json:"storages"`
}

func NewCmd(p *print.Printer) *cobra.Command {
cmd := &cobra.Command{
Use: "options",
Short: "Lists SQL Server Flex options",
Long: "Lists SQL Server Flex options (flavors, versions and storages for a given flavor)\nPass one or more flags to filter what categories are shown.",
Args: args.NoArgs,
Example: examples.Build(
examples.NewExample(
`List SQL Server Flex flavors options`,
"$ stackit sqlserverflex options --flavors"),
examples.NewExample(
`List SQL Server Flex available versions`,
"$ stackit sqlserverflex options --versions"),
examples.NewExample(
`List SQL Server Flex storage options for a given flavor. The flavor ID can be retrieved by running "$ stackit sqlserverflex options --flavors"`,
"$ stackit sqlserverflex options --storages --flavor-id <FLAVOR_ID>"),
),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
model, err := parseInput(p, cmd)
if err != nil {
return err
}

// Configure API client
apiClient, err := client.ConfigureClient(p)
if err != nil {
return err
}

// Call API
err = buildAndExecuteRequest(ctx, p, model, apiClient)
if err != nil {
return fmt.Errorf("get SQL Server Flex options: %w", err)
}

return nil
},
}
configureFlags(cmd)
return cmd
}

func configureFlags(cmd *cobra.Command) {
cmd.Flags().Bool(flavorsFlag, false, "Lists supported flavors")
cmd.Flags().Bool(versionsFlag, false, "Lists supported versions")
cmd.Flags().Bool(storagesFlag, false, "Lists supported storages for a given flavor")
cmd.Flags().String(flavorIdFlag, "", `The flavor ID to show storages for. Only relevant when "--storages" is passed`)
}

func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
globalFlags := globalflags.Parse(p, cmd)
flavors := flags.FlagToBoolValue(p, cmd, flavorsFlag)
versions := flags.FlagToBoolValue(p, cmd, versionsFlag)
storages := flags.FlagToBoolValue(p, cmd, storagesFlag)
flavorId := flags.FlagToStringPointer(p, cmd, flavorIdFlag)

if !flavors && !versions && !storages {
return nil, fmt.Errorf("%s\n\n%s",
"please specify at least one category for which to list the available options.",
"Get details on the available flags by re-running your command with the --help flag.")
}

if storages && flavorId == nil {
return nil, fmt.Errorf("%s\n\n%s\n%s",
`please specify a flavor ID to show storages for by setting the flag "--flavor-id <FLAVOR_ID>".`,
"You can get the available flavor IDs by running:",
" $ stackit sqlserverflex options --flavors")
}

model := inputModel{
GlobalFlagModel: globalFlags,
Flavors: flavors,
Versions: versions,
Storages: storages,
FlavorId: flags.FlagToStringPointer(p, cmd, flavorIdFlag),
}

if p.IsVerbosityDebug() {
modelStr, err := print.BuildDebugStrFromInputModel(model)
if err != nil {
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
} else {
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
}
}

return &model, nil
}

type sqlServerFlexOptionsClient interface {
ListFlavorsExecute(ctx context.Context, projectId string) (*sqlserverflex.ListFlavorsResponse, error)
ListVersionsExecute(ctx context.Context, projectId string) (*sqlserverflex.ListVersionsResponse, error)
ListStoragesExecute(ctx context.Context, projectId, flavorId string) (*sqlserverflex.ListStoragesResponse, error)
}

func buildAndExecuteRequest(ctx context.Context, p *print.Printer, model *inputModel, apiClient sqlServerFlexOptionsClient) error {
var flavors *sqlserverflex.ListFlavorsResponse
var versions *sqlserverflex.ListVersionsResponse
var storages *sqlserverflex.ListStoragesResponse
var err error

if model.Flavors {
flavors, err = apiClient.ListFlavorsExecute(ctx, model.ProjectId)
if err != nil {
return fmt.Errorf("get SQL Server Flex flavors: %w", err)
}
}
if model.Versions {
versions, err = apiClient.ListVersionsExecute(ctx, model.ProjectId)
if err != nil {
return fmt.Errorf("get SQL Server Flex versions: %w", err)
}
}
if model.Storages {
storages, err = apiClient.ListStoragesExecute(ctx, model.ProjectId, *model.FlavorId)
if err != nil {
return fmt.Errorf("get SQL Server Flex storages: %w", err)
}
}

return outputResult(p, model, flavors, versions, storages)
}

func outputResult(p *print.Printer, model *inputModel, flavors *sqlserverflex.ListFlavorsResponse, versions *sqlserverflex.ListVersionsResponse, storages *sqlserverflex.ListStoragesResponse) error {
options := &options{}
if flavors != nil {
options.Flavors = flavors.Flavors
}
if versions != nil {
options.Versions = versions.Versions
}
if storages != nil && model.FlavorId != nil {
options.Storages = &flavorStorages{
FlavorId: *model.FlavorId,
Storages: storages,
}
}

switch model.OutputFormat {
case print.JSONOutputFormat:
details, err := json.MarshalIndent(options, "", " ")
if err != nil {
return fmt.Errorf("marshal SQL Server Flex options: %w", err)
}
p.Outputln(string(details))
return nil
case print.YAMLOutputFormat:
details, err := yaml.MarshalWithOptions(options, yaml.IndentSequence(true))
if err != nil {
return fmt.Errorf("marshal SQL Server Flex options: %w", err)
}
p.Outputln(string(details))

return nil
default:
return outputResultAsTable(p, model, options)
}
}

func outputResultAsTable(p *print.Printer, model *inputModel, options *options) error {
content := ""
if model.Flavors {
content += renderFlavors(*options.Flavors)
}
if model.Versions {
content += renderVersions(*options.Versions)
}
if model.Storages {
content += renderStorages(options.Storages.Storages)
}

err := p.PagerDisplay(content)
if err != nil {
return fmt.Errorf("display output: %w", err)
}

return nil
}

func renderFlavors(flavors []sqlserverflex.InstanceFlavorEntry) string {
if len(flavors) == 0 {
return ""
}

table := tables.NewTable()
table.SetTitle("Flavors")
table.SetHeader("ID", "CPU", "MEMORY", "DESCRIPTION", "VALID INSTANCE TYPES")
for i := range flavors {
f := flavors[i]
table.AddRow(*f.Id, *f.Cpu, *f.Memory, *f.Description, *f.Categories)
}
return table.Render()
}

func renderVersions(versions []string) string {
if len(versions) == 0 {
return ""
}

table := tables.NewTable()
table.SetTitle("Versions")
table.SetHeader("VERSION")
for i := range versions {
v := versions[i]
table.AddRow(v)
}
return table.Render()
}

func renderStorages(resp *sqlserverflex.ListStoragesResponse) string {
if resp.StorageClasses == nil || len(*resp.StorageClasses) == 0 {
return ""
}
storageClasses := *resp.StorageClasses

table := tables.NewTable()
table.SetTitle("Storages")
table.SetHeader("MINIMUM", "MAXIMUM", "STORAGE CLASS")
for i := range storageClasses {
sc := storageClasses[i]
table.AddRow(*resp.StorageRange.Min, *resp.StorageRange.Max, sc)
}
table.EnableAutoMergeOnColumns(1, 2, 3)
return table.Render()
}
Loading