Skip to content

Commit f24268a

Browse files
Add sqlserverflex options command (#348)
* Add sqlserverflex options command * Update internal/cmd/beta/sqlserverflex/options/options.go Co-authored-by: Diogo Ferrão <diogo.ferrao@freiheit.com> * Rename clients * Update client name --------- Co-authored-by: Diogo Ferrão <diogo.ferrao@freiheit.com>
1 parent 63cac36 commit f24268a

File tree

7 files changed

+654
-4
lines changed

7 files changed

+654
-4
lines changed

docs/stackit_beta_sqlserverflex.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ stackit beta sqlserverflex [flags]
3030

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
## stackit beta sqlserverflex options
2+
3+
Lists SQL Server Flex options
4+
5+
### Synopsis
6+
7+
Lists SQL Server Flex options (flavors, versions and storages for a given flavor)
8+
Pass one or more flags to filter what categories are shown.
9+
10+
```
11+
stackit beta sqlserverflex options [flags]
12+
```
13+
14+
### Examples
15+
16+
```
17+
List SQL Server Flex flavors options
18+
$ stackit sqlserverflex options --flavors
19+
20+
List SQL Server Flex available versions
21+
$ stackit sqlserverflex options --versions
22+
23+
List SQL Server Flex storage options for a given flavor. The flavor ID can be retrieved by running "$ stackit sqlserverflex options --flavors"
24+
$ stackit sqlserverflex options --storages --flavor-id <FLAVOR_ID>
25+
```
26+
27+
### Options
28+
29+
```
30+
--flavor-id string The flavor ID to show storages for. Only relevant when "--storages" is passed
31+
--flavors Lists supported flavors
32+
-h, --help Help for "stackit beta sqlserverflex options"
33+
--storages Lists supported storages for a given flavor
34+
--versions Lists supported versions
35+
```
36+
37+
### Options inherited from parent commands
38+
39+
```
40+
-y, --assume-yes If set, skips all confirmation prompts
41+
--async If set, runs the command asynchronously
42+
-o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"]
43+
-p, --project-id string Project ID
44+
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
45+
```
46+
47+
### SEE ALSO
48+
49+
* [stackit beta sqlserverflex](./stackit_beta_sqlserverflex.md) - Provides functionality for SQLServer Flex
50+

internal/cmd/beta/sqlserverflex/instance/create/create.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,13 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
195195
return &model, nil
196196
}
197197

198-
type SQLServerFlexClient interface {
198+
type sqlServerFlexClient interface {
199199
CreateInstance(ctx context.Context, projectId string) sqlserverflex.ApiCreateInstanceRequest
200200
ListFlavorsExecute(ctx context.Context, projectId string) (*sqlserverflex.ListFlavorsResponse, error)
201201
ListStoragesExecute(ctx context.Context, projectId, flavorId string) (*sqlserverflex.ListStoragesResponse, error)
202202
}
203203

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

207207
var flavorId *string

internal/cmd/beta/sqlserverflex/instance/update/update.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,14 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu
182182
return &model, nil
183183
}
184184

185-
type SQLServerFlexClient interface {
185+
type sqlServerFlexClient interface {
186186
PartialUpdateInstance(ctx context.Context, projectId, instanceId string) sqlserverflex.ApiPartialUpdateInstanceRequest
187187
GetInstanceExecute(ctx context.Context, projectId, instanceId string) (*sqlserverflex.GetInstanceResponse, error)
188188
ListFlavorsExecute(ctx context.Context, projectId string) (*sqlserverflex.ListFlavorsResponse, error)
189189
ListStoragesExecute(ctx context.Context, projectId, flavorId string) (*sqlserverflex.ListStoragesResponse, error)
190190
}
191191

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

195195
var flavorId *string
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
package options
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
8+
"github.com/goccy/go-yaml"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
10+
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
14+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/sqlserverflex/client"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
16+
17+
"github.com/spf13/cobra"
18+
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex"
19+
)
20+
21+
const (
22+
flavorsFlag = "flavors"
23+
versionsFlag = "versions"
24+
storagesFlag = "storages"
25+
flavorIdFlag = "flavor-id"
26+
)
27+
28+
type inputModel struct {
29+
*globalflags.GlobalFlagModel
30+
31+
Flavors bool
32+
Versions bool
33+
Storages bool
34+
FlavorId *string
35+
}
36+
37+
type options struct {
38+
Flavors *[]sqlserverflex.InstanceFlavorEntry `json:"flavors,omitempty"`
39+
Versions *[]string `json:"versions,omitempty"`
40+
Storages *flavorStorages `json:"flavorStorages,omitempty"`
41+
}
42+
43+
type flavorStorages struct {
44+
FlavorId string `json:"flavorId"`
45+
Storages *sqlserverflex.ListStoragesResponse `json:"storages"`
46+
}
47+
48+
func NewCmd(p *print.Printer) *cobra.Command {
49+
cmd := &cobra.Command{
50+
Use: "options",
51+
Short: "Lists SQL Server Flex options",
52+
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.",
53+
Args: args.NoArgs,
54+
Example: examples.Build(
55+
examples.NewExample(
56+
`List SQL Server Flex flavors options`,
57+
"$ stackit sqlserverflex options --flavors"),
58+
examples.NewExample(
59+
`List SQL Server Flex available versions`,
60+
"$ stackit sqlserverflex options --versions"),
61+
examples.NewExample(
62+
`List SQL Server Flex storage options for a given flavor. The flavor ID can be retrieved by running "$ stackit sqlserverflex options --flavors"`,
63+
"$ stackit sqlserverflex options --storages --flavor-id <FLAVOR_ID>"),
64+
),
65+
RunE: func(cmd *cobra.Command, args []string) error {
66+
ctx := context.Background()
67+
model, err := parseInput(p, cmd)
68+
if err != nil {
69+
return err
70+
}
71+
72+
// Configure API client
73+
apiClient, err := client.ConfigureClient(p)
74+
if err != nil {
75+
return err
76+
}
77+
78+
// Call API
79+
err = buildAndExecuteRequest(ctx, p, model, apiClient)
80+
if err != nil {
81+
return fmt.Errorf("get SQL Server Flex options: %w", err)
82+
}
83+
84+
return nil
85+
},
86+
}
87+
configureFlags(cmd)
88+
return cmd
89+
}
90+
91+
func configureFlags(cmd *cobra.Command) {
92+
cmd.Flags().Bool(flavorsFlag, false, "Lists supported flavors")
93+
cmd.Flags().Bool(versionsFlag, false, "Lists supported versions")
94+
cmd.Flags().Bool(storagesFlag, false, "Lists supported storages for a given flavor")
95+
cmd.Flags().String(flavorIdFlag, "", `The flavor ID to show storages for. Only relevant when "--storages" is passed`)
96+
}
97+
98+
func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
99+
globalFlags := globalflags.Parse(p, cmd)
100+
flavors := flags.FlagToBoolValue(p, cmd, flavorsFlag)
101+
versions := flags.FlagToBoolValue(p, cmd, versionsFlag)
102+
storages := flags.FlagToBoolValue(p, cmd, storagesFlag)
103+
flavorId := flags.FlagToStringPointer(p, cmd, flavorIdFlag)
104+
105+
if !flavors && !versions && !storages {
106+
return nil, fmt.Errorf("%s\n\n%s",
107+
"please specify at least one category for which to list the available options.",
108+
"Get details on the available flags by re-running your command with the --help flag.")
109+
}
110+
111+
if storages && flavorId == nil {
112+
return nil, fmt.Errorf("%s\n\n%s\n%s",
113+
`please specify a flavor ID to show storages for by setting the flag "--flavor-id <FLAVOR_ID>".`,
114+
"You can get the available flavor IDs by running:",
115+
" $ stackit sqlserverflex options --flavors")
116+
}
117+
118+
model := inputModel{
119+
GlobalFlagModel: globalFlags,
120+
Flavors: flavors,
121+
Versions: versions,
122+
Storages: storages,
123+
FlavorId: flags.FlagToStringPointer(p, cmd, flavorIdFlag),
124+
}
125+
126+
if p.IsVerbosityDebug() {
127+
modelStr, err := print.BuildDebugStrFromInputModel(model)
128+
if err != nil {
129+
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
130+
} else {
131+
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
132+
}
133+
}
134+
135+
return &model, nil
136+
}
137+
138+
type sqlServerFlexOptionsClient interface {
139+
ListFlavorsExecute(ctx context.Context, projectId string) (*sqlserverflex.ListFlavorsResponse, error)
140+
ListVersionsExecute(ctx context.Context, projectId string) (*sqlserverflex.ListVersionsResponse, error)
141+
ListStoragesExecute(ctx context.Context, projectId, flavorId string) (*sqlserverflex.ListStoragesResponse, error)
142+
}
143+
144+
func buildAndExecuteRequest(ctx context.Context, p *print.Printer, model *inputModel, apiClient sqlServerFlexOptionsClient) error {
145+
var flavors *sqlserverflex.ListFlavorsResponse
146+
var versions *sqlserverflex.ListVersionsResponse
147+
var storages *sqlserverflex.ListStoragesResponse
148+
var err error
149+
150+
if model.Flavors {
151+
flavors, err = apiClient.ListFlavorsExecute(ctx, model.ProjectId)
152+
if err != nil {
153+
return fmt.Errorf("get SQL Server Flex flavors: %w", err)
154+
}
155+
}
156+
if model.Versions {
157+
versions, err = apiClient.ListVersionsExecute(ctx, model.ProjectId)
158+
if err != nil {
159+
return fmt.Errorf("get SQL Server Flex versions: %w", err)
160+
}
161+
}
162+
if model.Storages {
163+
storages, err = apiClient.ListStoragesExecute(ctx, model.ProjectId, *model.FlavorId)
164+
if err != nil {
165+
return fmt.Errorf("get SQL Server Flex storages: %w", err)
166+
}
167+
}
168+
169+
return outputResult(p, model, flavors, versions, storages)
170+
}
171+
172+
func outputResult(p *print.Printer, model *inputModel, flavors *sqlserverflex.ListFlavorsResponse, versions *sqlserverflex.ListVersionsResponse, storages *sqlserverflex.ListStoragesResponse) error {
173+
options := &options{}
174+
if flavors != nil {
175+
options.Flavors = flavors.Flavors
176+
}
177+
if versions != nil {
178+
options.Versions = versions.Versions
179+
}
180+
if storages != nil && model.FlavorId != nil {
181+
options.Storages = &flavorStorages{
182+
FlavorId: *model.FlavorId,
183+
Storages: storages,
184+
}
185+
}
186+
187+
switch model.OutputFormat {
188+
case print.JSONOutputFormat:
189+
details, err := json.MarshalIndent(options, "", " ")
190+
if err != nil {
191+
return fmt.Errorf("marshal SQL Server Flex options: %w", err)
192+
}
193+
p.Outputln(string(details))
194+
return nil
195+
case print.YAMLOutputFormat:
196+
details, err := yaml.MarshalWithOptions(options, yaml.IndentSequence(true))
197+
if err != nil {
198+
return fmt.Errorf("marshal SQL Server Flex options: %w", err)
199+
}
200+
p.Outputln(string(details))
201+
202+
return nil
203+
default:
204+
return outputResultAsTable(p, model, options)
205+
}
206+
}
207+
208+
func outputResultAsTable(p *print.Printer, model *inputModel, options *options) error {
209+
content := ""
210+
if model.Flavors {
211+
content += renderFlavors(*options.Flavors)
212+
}
213+
if model.Versions {
214+
content += renderVersions(*options.Versions)
215+
}
216+
if model.Storages {
217+
content += renderStorages(options.Storages.Storages)
218+
}
219+
220+
err := p.PagerDisplay(content)
221+
if err != nil {
222+
return fmt.Errorf("display output: %w", err)
223+
}
224+
225+
return nil
226+
}
227+
228+
func renderFlavors(flavors []sqlserverflex.InstanceFlavorEntry) string {
229+
if len(flavors) == 0 {
230+
return ""
231+
}
232+
233+
table := tables.NewTable()
234+
table.SetTitle("Flavors")
235+
table.SetHeader("ID", "CPU", "MEMORY", "DESCRIPTION", "VALID INSTANCE TYPES")
236+
for i := range flavors {
237+
f := flavors[i]
238+
table.AddRow(*f.Id, *f.Cpu, *f.Memory, *f.Description, *f.Categories)
239+
}
240+
return table.Render()
241+
}
242+
243+
func renderVersions(versions []string) string {
244+
if len(versions) == 0 {
245+
return ""
246+
}
247+
248+
table := tables.NewTable()
249+
table.SetTitle("Versions")
250+
table.SetHeader("VERSION")
251+
for i := range versions {
252+
v := versions[i]
253+
table.AddRow(v)
254+
}
255+
return table.Render()
256+
}
257+
258+
func renderStorages(resp *sqlserverflex.ListStoragesResponse) string {
259+
if resp.StorageClasses == nil || len(*resp.StorageClasses) == 0 {
260+
return ""
261+
}
262+
storageClasses := *resp.StorageClasses
263+
264+
table := tables.NewTable()
265+
table.SetTitle("Storages")
266+
table.SetHeader("MINIMUM", "MAXIMUM", "STORAGE CLASS")
267+
for i := range storageClasses {
268+
sc := storageClasses[i]
269+
table.AddRow(*resp.StorageRange.Min, *resp.StorageRange.Max, sc)
270+
}
271+
table.EnableAutoMergeOnColumns(1, 2, 3)
272+
return table.Render()
273+
}

0 commit comments

Comments
 (0)