Skip to content

Commit d4cf417

Browse files
author
Juan Jesús Padrón Hernández
committed
fix(sca): big refactor
1 parent 29f696e commit d4cf417

5 files changed

Lines changed: 573 additions & 93 deletions

File tree

‎internal/cmd/sca/application/create/create.go‎

Lines changed: 111 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,31 @@ import (
1515
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
1616
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
1717
"github.com/stackitcloud/stackit-cli/internal/pkg/services/sca/client"
18+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/sca/utils"
1819
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
1920
"github.com/stackitcloud/stackit-cli/internal/pkg/types"
2021
)
2122

2223
const (
2324
environmentIDFlag = "environment-id"
2425
nameFlag = "name"
25-
imageFlag = "image"
26-
publicFlag = "public"
27-
externalPortFlag = "external-port"
28-
cpuFlag = "cpu"
29-
memoryFlag = "memory"
30-
instancesFlag = "instances"
31-
minInstancesFlag = "min-instances"
32-
maxInstancesFlag = "max-instances"
33-
scaleToZeroFlag = "scale-to-zero"
34-
envVarsFlag = "environment-vars"
35-
commandsFlag = "commands"
36-
argsFlag = "args"
26+
// Container config
27+
imageFlag = "image"
28+
cpuFlag = "cpu"
29+
memoryFlag = "memory"
30+
envVarsFlag = "environment-vars"
31+
commandsFlag = "commands"
32+
argsFlag = "args"
33+
// Scaling
34+
instancesFlag = "instances"
35+
minInstancesFlag = "min-instances"
36+
maxInstancesFlag = "max-instances"
37+
scaleToZeroFlag = "scale-to-zero"
38+
rpsFlag = "http-rule-rps"
39+
concurrencyFlag = "http-rule-concurrency"
40+
// Networking
41+
publicFlag = "public"
42+
externalPortFlag = "external-port"
3743
)
3844

3945
const (
@@ -60,20 +66,20 @@ type inputModel struct {
6066
EnvironmentID string
6167
Name string
6268
Image string
63-
ScalingType string
64-
Public bool
65-
ContainerExternalPort int32
6669
CPU int32
6770
Memory int32
71+
EnvironmentVars map[string]string
72+
Commands []string
73+
Args []string
74+
ScalingType string
6875
Instances int32
6976
MinInstances int32
7077
MaxInstances int32
7178
ScaleToZero bool
7279
Concurrency int32
7380
RPS int32
74-
EnvironmentVars map[string]string
75-
Commands []string
76-
Args []string
81+
Public bool
82+
ContainerExternalPort int32
7783
}
7884

7985
func NewCmd(params *types.CmdParams) *cobra.Command {
@@ -171,6 +177,7 @@ func buildScalingRules(model *inputModel) []sca.ScaleRule {
171177
if model.Concurrency != 0 || model.RPS != 0 {
172178
scaleRules = append(scaleRules, sca.ScaleRule{
173179
Type: sca.RULETYPE_RULE_TYPE_HTTP,
180+
Name: "http-scaling-rule",
174181
HttpRule: &sca.HttpScaleRule{
175182
Concurrency: &model.Concurrency,
176183
Rps: &model.RPS,
@@ -187,17 +194,7 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *sca.APIClie
187194
Port: &model.ContainerExternalPort,
188195
}
189196

190-
var envVars []sca.EnvVar
191-
if model.EnvironmentVars != nil {
192-
envVars = make([]sca.EnvVar, 0, len(model.EnvironmentVars))
193-
for k, v := range model.EnvironmentVars {
194-
envVars = append(envVars, sca.EnvVar{
195-
Key: k,
196-
Value: v,
197-
Origin: sca.ENVVARTYPE_ENV_FROM_SOURCE_TYPE_MANUAL.Ptr(),
198-
})
199-
}
200-
}
197+
envVars := utils.EnvironmentVariablesFromMap(model.EnvironmentVars)
201198

202199
container := sca.Container{
203200
Name: defaultContainerName,
@@ -232,19 +229,24 @@ func outputResult(p *print.Printer, model *inputModel, application *sca.Applicat
232229
func configureFlags(cmd *cobra.Command) {
233230
cmd.Flags().Var(flags.UUIDFlag(), environmentIDFlag, "Environment ID (uses default environment if not set)")
234231
cmd.Flags().String(nameFlag, "", "Application display name")
232+
// container
235233
cmd.Flags().String(imageFlag, "", "Container image")
236-
cmd.Flags().Bool(publicFlag, defaultPublic, "Exposes your application securely to the public internet via HTTPS endpoint")
237-
cmd.Flags().Int32(externalPortFlag, defaultPort, "Container external exposed port")
238234
cmd.Flags().Int32(cpuFlag, defaultCPU, "The dedicated virtual CPU processing power allocated per container instance")
239235
cmd.Flags().Int32(memoryFlag, defaultMemory, "The total amount of memory (RAM) allocated per container instance")
240-
scalingTypeFlag.Register(cmd.Flags())
241-
cmd.Flags().Int32(instancesFlag, defaultInstances, "The number of application instances (if manually scaled)")
242-
cmd.Flags().Int32(minInstancesFlag, 0, "The minimum number of application instances (if autoscaling is enabled)")
243-
cmd.Flags().Int32(maxInstancesFlag, 0, "The maximum number of application instances (if autoscaling is enabled)")
244-
cmd.Flags().Bool(scaleToZeroFlag, false, "Enable scale to zero (if autoscaling is enabled)")
245236
cmd.Flags().StringToString(envVarsFlag, nil, "Environment variables to inject into the application")
246237
cmd.Flags().StringSlice(commandsFlag, nil, "Commands to execute in the application container")
247238
cmd.Flags().StringSlice(argsFlag, nil, "Arguments to pass to the application container command")
239+
// scaling
240+
scalingTypeFlag.Register(cmd.Flags())
241+
cmd.Flags().Int32(instancesFlag, defaultInstances, "The number of application instances (if manually scaled)")
242+
cmd.Flags().Int32(minInstancesFlag, 1, "The minimum number of application instances (if autoscaling is enabled)")
243+
cmd.Flags().Int32(maxInstancesFlag, 1, "The maximum number of application instances (if autoscaling is enabled)")
244+
cmd.Flags().Int32(rpsFlag, 0, "Target number of requests per second to trigger autoscaling (if autoscaling is enabled)")
245+
cmd.Flags().Int32(concurrencyFlag, 0, "Target number of in-flight requests to trigger autoscaling (if autoscaling is enabled)")
246+
cmd.Flags().Bool(scaleToZeroFlag, false, "Enable scale to zero (if autoscaling is enabled)")
247+
// networking
248+
cmd.Flags().Bool(publicFlag, defaultPublic, "Exposes your application securely to the public internet via HTTPS endpoint")
249+
cmd.Flags().Int32(externalPortFlag, defaultPort, "Container external exposed port")
248250

249251
cobra.CheckErr(flags.MarkFlagsRequired(cmd, nameFlag))
250252
cobra.CheckErr(flags.MarkFlagsRequired(cmd, imageFlag))
@@ -261,12 +263,8 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel,
261263
scalingType = *scalingTypeFlagValue
262264
}
263265

264-
extertalPort := flags.FlagWithDefaultToInt32Value(p, cmd, externalPortFlag)
265-
if extertalPort <= 1024 || extertalPort > 65535 {
266-
return nil, &errors.FlagValidationError{
267-
Flag: externalPortFlag,
268-
Details: "must be a valid non-privileged port (from 1025 to 65535)",
269-
}
266+
if err := validateScalingInput(cmd, scalingType); err != nil {
267+
return nil, err
270268
}
271269

272270
instances := flags.FlagWithDefaultToInt32Value(p, cmd, instancesFlag)
@@ -277,6 +275,33 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel,
277275
}
278276
}
279277

278+
minInstances := flags.FlagWithDefaultToInt32Value(p, cmd, minInstancesFlag)
279+
if minInstances < 0 || minInstances > 10 {
280+
return nil, &errors.FlagValidationError{
281+
Flag: minInstancesFlag,
282+
Details: "must be an integer between 0 and 10",
283+
}
284+
}
285+
286+
maxInstances := flags.FlagWithDefaultToInt32Value(p, cmd, maxInstancesFlag)
287+
if maxInstances == 0 {
288+
maxInstances = minInstances
289+
}
290+
if maxInstances < minInstances || minInstances > 10 {
291+
return nil, &errors.FlagValidationError{
292+
Flag: minInstancesFlag,
293+
Details: fmt.Sprintf("must be an integer between minInstances (%d) and 10", minInstances),
294+
}
295+
}
296+
297+
extertalPort := flags.FlagWithDefaultToInt32Value(p, cmd, externalPortFlag)
298+
if extertalPort <= 1024 || extertalPort > 65535 {
299+
return nil, &errors.FlagValidationError{
300+
Flag: externalPortFlag,
301+
Details: "must be a valid non-privileged port (from 1025 to 65535)",
302+
}
303+
}
304+
280305
cpu := flags.FlagWithDefaultToInt32Value(p, cmd, cpuFlag)
281306
if cpu%1000 != 0 {
282307
return nil, &errors.FlagValidationError{
@@ -300,20 +325,57 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel,
300325
EnvironmentID: environmentID,
301326
Name: flags.FlagToStringValue(p, cmd, nameFlag),
302327
Image: flags.FlagToStringValue(p, cmd, imageFlag),
303-
Public: flags.FlagToBoolValue(p, cmd, publicFlag),
304-
ContainerExternalPort: extertalPort,
305328
CPU: cpu,
306329
Memory: flags.FlagWithDefaultToInt32Value(p, cmd, memoryFlag),
307-
Instances: instances,
308-
ScalingType: scalingType,
309-
MinInstances: flags.FlagWithDefaultToInt32Value(p, cmd, minInstancesFlag),
310-
MaxInstances: flags.FlagWithDefaultToInt32Value(p, cmd, maxInstancesFlag),
311-
ScaleToZero: flags.FlagToBoolValue(p, cmd, scaleToZeroFlag),
312330
EnvironmentVars: envVars,
313331
Commands: flags.FlagToStringSliceValue(p, cmd, commandsFlag),
314332
Args: flags.FlagToStringSliceValue(p, cmd, argsFlag),
333+
ScalingType: scalingType,
334+
Instances: instances,
335+
MinInstances: minInstances,
336+
MaxInstances: maxInstances,
337+
ScaleToZero: flags.FlagToBoolValue(p, cmd, scaleToZeroFlag),
338+
RPS: flags.FlagWithDefaultToInt32Value(p, cmd, rpsFlag),
339+
Concurrency: flags.FlagWithDefaultToInt32Value(p, cmd, concurrencyFlag),
340+
Public: flags.FlagToBoolValue(p, cmd, publicFlag),
341+
ContainerExternalPort: extertalPort,
315342
}
316343

317344
p.DebugInputModel(model)
318345
return &model, nil
319346
}
347+
348+
func validateScalingInput(cmd *cobra.Command, scalingType string) error {
349+
switch scalingType {
350+
case scalingTypeManual:
351+
autoFlags := []string{minInstancesFlag, maxInstancesFlag, scaleToZeroFlag, concurrencyFlag, rpsFlag}
352+
for _, flagName := range autoFlags {
353+
if cmd.Flags().Changed(flagName) {
354+
return &errors.FlagValidationError{
355+
Flag: flagName,
356+
Details: fmt.Sprintf("is only valid when --scaling-type is %q", scalingTypeAuto),
357+
}
358+
}
359+
}
360+
case scalingTypeAuto:
361+
if cmd.Flags().Changed(instancesFlag) {
362+
return &errors.FlagValidationError{
363+
Flag: instancesFlag,
364+
Details: fmt.Sprintf("is only valid when --scaling-type is %q", scalingTypeManual),
365+
}
366+
}
367+
if !cmd.Flags().Changed(rpsFlag) && !cmd.Flags().Changed(concurrencyFlag) {
368+
return &errors.OneOfFlagsIsMissing{
369+
MissingFlags: []string{rpsFlag, concurrencyFlag},
370+
SetFlag: fmt.Sprintf("--%s=%s", scalingTypeFlag.Name(), scalingTypeAuto),
371+
}
372+
}
373+
default:
374+
return &errors.FlagValidationError{
375+
Flag: scalingType,
376+
Details: "invalid scaling type",
377+
}
378+
}
379+
380+
return nil
381+
}

‎internal/cmd/sca/application/create/create_test.go‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111

1212
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
1313
"github.com/stackitcloud/stackit-cli/internal/pkg/testutils"
14-
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
1514
)
1615

1716
const (
@@ -96,7 +95,7 @@ func fixturePayload(mods ...func(payload *sca.CreateApplicationPayload)) sca.Cre
9695
DisplayName: "test-application-name",
9796
Network: sca.Network{
9897
PublicIngress: true,
99-
Port: utils.Ptr(int32(8888)),
98+
Port: new(int32(8888)),
10099
},
101100
Scaling: sca.Scaling{
102101
Type: sca.SCALINGTYPE_SCALING_TYPE_MANUAL,
@@ -105,8 +104,8 @@ func fixturePayload(mods ...func(payload *sca.CreateApplicationPayload)) sca.Cre
105104
Containers: []sca.Container{{
106105
Name: "container-1",
107106
Image: "test-image",
108-
Cpu: utils.Ptr(int32(2000)),
109-
Memory: utils.Ptr(int32(2048)),
107+
Cpu: new(int32(2000)),
108+
Memory: new(int32(2048)),
110109
Command: []string{"/bin/sh", "-c"},
111110
Args: []string{"echo 'test'"},
112111
EnvironmentVariables: []sca.EnvVar{

0 commit comments

Comments
 (0)