Skip to content

Commit f7592e8

Browse files
authored
fix(server): switch to flag for service account email (#1429)
relates to STACKITCLI-346
1 parent a9f8e43 commit f7592e8

6 files changed

Lines changed: 142 additions & 20 deletions

File tree

docs/stackit_server_service-account_attach.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,22 @@ Attach a service account to a server
77
Attach a service account to a server
88

99
```
10-
stackit server service-account attach SERVICE_ACCOUNT_EMAIL [flags]
10+
stackit server service-account attach [flags]
1111
```
1212

1313
### Examples
1414

1515
```
1616
Attach a service account with mail "xxx@sa.stackit.cloud" to a server with ID "yyy"
17-
$ stackit server service-account attach xxx@sa.stackit.cloud --server-id yyy
17+
$ stackit server service-account attach --service-account-email xxx@sa.stackit.cloud --server-id yyy
1818
```
1919

2020
### Options
2121

2222
```
23-
-h, --help Help for "stackit server service-account attach"
24-
-s, --server-id string Server ID
23+
-h, --help Help for "stackit server service-account attach"
24+
-s, --server-id string Server ID
25+
-a, --service-account-email string Service Account Email
2526
```
2627

2728
### Options inherited from parent commands

docs/stackit_server_service-account_detach.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,22 @@ Detach a service account from a server
77
Detach a service account from a server
88

99
```
10-
stackit server service-account detach SERVICE_ACCOUNT_EMAIL [flags]
10+
stackit server service-account detach [flags]
1111
```
1212

1313
### Examples
1414

1515
```
1616
Detach a service account with mail "xxx@sa.stackit.cloud" from a server "yyy"
17-
$ stackit server service-account detach xxx@sa.stackit.cloud --server-id yyy
17+
$ stackit server service-account detach --service-account-email xxx@sa.stackit.cloud --server-id yyy
1818
```
1919

2020
### Options
2121

2222
```
23-
-h, --help Help for "stackit server service-account detach"
24-
-s, --server-id string Server id
23+
-h, --help Help for "stackit server service-account detach"
24+
-s, --server-id string Server id
25+
-a, --service-account-email string Service Account Email
2526
```
2627

2728
### Options inherited from parent commands

internal/cmd/server/service-account/attach/attach.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import (
2020
)
2121

2222
const (
23-
serviceAccMailArg = "SERVICE_ACCOUNT_EMAIL"
23+
serviceAccMailArg = "SERVICE_ACCOUNT_EMAIL" // Deprecated: positional argument is not used anymore, use the flag instead, will be removed after 2026-12-31
2424

2525
serverIdFlag = "server-id"
26+
27+
serviceAccFlag = "service-account-email"
2628
)
2729

2830
type inputModel struct {
@@ -33,14 +35,14 @@ type inputModel struct {
3335

3436
func NewCmd(params *types.CmdParams) *cobra.Command {
3537
cmd := &cobra.Command{
36-
Use: fmt.Sprintf("attach %s", serviceAccMailArg),
38+
Use: "attach",
3739
Short: "Attach a service account to a server",
3840
Long: "Attach a service account to a server",
39-
Args: args.SingleArg(serviceAccMailArg, nil),
41+
Args: args.SingleOptionalArg(serviceAccMailArg, nil),
4042
Example: examples.Build(
4143
examples.NewExample(
4244
`Attach a service account with mail "xxx@sa.stackit.cloud" to a server with ID "yyy"`,
43-
"$ stackit server service-account attach xxx@sa.stackit.cloud --server-id yyy",
45+
"$ stackit server service-account attach --service-account-email xxx@sa.stackit.cloud --server-id yyy",
4446
),
4547
),
4648
RunE: func(cmd *cobra.Command, args []string) error {
@@ -85,18 +87,27 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
8587

8688
func configureFlags(cmd *cobra.Command) {
8789
cmd.Flags().VarP(flags.UUIDFlag(), serverIdFlag, "s", "Server ID")
88-
90+
cmd.Flags().VarP(flags.EmailFlag(), serviceAccFlag, "a", "Service Account Email")
8991
err := flags.MarkFlagsRequired(cmd, serverIdFlag)
9092
cobra.CheckErr(err)
9193
}
9294

9395
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
94-
serviceAccMail := inputArgs[0]
9596
globalFlags := globalflags.Parse(p, cmd)
9697
if globalFlags.ProjectId == "" {
9798
return nil, &errors.ProjectIdError{}
9899
}
99100

101+
var serviceAccMail string
102+
if cmd.Flags().Changed(serviceAccFlag) {
103+
serviceAccMail = flags.FlagToStringValue(p, cmd, serviceAccFlag)
104+
} else if len(inputArgs) > 0 {
105+
serviceAccMail = inputArgs[0]
106+
p.Warn("Using a positional argument for the service account email is deprecated and will be removed after 2026-12. Please use the '--%s' flag instead.\n", serviceAccFlag)
107+
} else {
108+
return nil, fmt.Errorf(`service account must be specified by using either the --%s flag or (deprecated) as a positional argument`, serviceAccFlag)
109+
}
110+
100111
model := inputModel{
101112
GlobalFlagModel: globalFlags,
102113
ServerId: flags.FlagToStringValue(p, cmd, serverIdFlag),

internal/cmd/server/service-account/detach/detach.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import (
2020
)
2121

2222
const (
23-
serviceAccMailArg = "SERVICE_ACCOUNT_EMAIL"
23+
serviceAccMailArg = "SERVICE_ACCOUNT_EMAIL" // Deprecated: positional argument is not used anymore, use the flag instead, will be removed after 2026-12-31
2424

2525
serverIdFlag = "server-id"
26+
27+
serviceAccFlag = "service-account-email"
2628
)
2729

2830
type inputModel struct {
@@ -33,14 +35,14 @@ type inputModel struct {
3335

3436
func NewCmd(params *types.CmdParams) *cobra.Command {
3537
cmd := &cobra.Command{
36-
Use: fmt.Sprintf("detach %s", serviceAccMailArg),
38+
Use: "detach",
3739
Short: "Detach a service account from a server",
3840
Long: "Detach a service account from a server",
39-
Args: args.SingleArg(serviceAccMailArg, nil),
41+
Args: args.SingleOptionalArg(serviceAccMailArg, nil),
4042
Example: examples.Build(
4143
examples.NewExample(
4244
`Detach a service account with mail "xxx@sa.stackit.cloud" from a server "yyy"`,
43-
"$ stackit server service-account detach xxx@sa.stackit.cloud --server-id yyy",
45+
"$ stackit server service-account detach --service-account-email xxx@sa.stackit.cloud --server-id yyy",
4446
),
4547
),
4648
RunE: func(cmd *cobra.Command, args []string) error {
@@ -85,18 +87,27 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
8587

8688
func configureFlags(cmd *cobra.Command) {
8789
cmd.Flags().VarP(flags.UUIDFlag(), serverIdFlag, "s", "Server id")
88-
90+
cmd.Flags().VarP(flags.EmailFlag(), serviceAccFlag, "a", "Service Account Email")
8991
err := flags.MarkFlagsRequired(cmd, serverIdFlag)
9092
cobra.CheckErr(err)
9193
}
9294

9395
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
94-
serviceAccMail := inputArgs[0]
9596
globalFlags := globalflags.Parse(p, cmd)
9697
if globalFlags.ProjectId == "" {
9798
return nil, &errors.ProjectIdError{}
9899
}
99100

101+
var serviceAccMail string
102+
if cmd.Flags().Changed(serviceAccFlag) {
103+
serviceAccMail = flags.FlagToStringValue(p, cmd, serviceAccFlag)
104+
} else if len(inputArgs) > 0 {
105+
serviceAccMail = inputArgs[0]
106+
p.Warn("Using a positional argument for the service account email is deprecated and will be removed after 2026-12-31. Please use the '--%s' flag instead.\n", serviceAccFlag)
107+
} else {
108+
return nil, fmt.Errorf(`service account must be specified by using either the --%s flag or (deprecated) as a positional argument`, serviceAccFlag)
109+
}
110+
100111
model := inputModel{
101112
GlobalFlagModel: globalFlags,
102113
ServerId: flags.FlagToStringValue(p, cmd, serverIdFlag),

internal/pkg/flags/email.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package flags
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/spf13/pflag"
8+
)
9+
10+
type emailFlag struct {
11+
value string
12+
}
13+
14+
// Ensure the implementation satisfies the expected interface
15+
var _ pflag.Value = &emailFlag{}
16+
17+
// EmailFlag returns a flag which must be a valid Email.
18+
func EmailFlag() *emailFlag {
19+
return &emailFlag{}
20+
}
21+
22+
func (f *emailFlag) String() string {
23+
return f.value
24+
}
25+
26+
func (f *emailFlag) Set(value string) error {
27+
isEmail := value != "" && strings.Contains(value, "@")
28+
if !isEmail {
29+
return fmt.Errorf("invalid email address: %s", value)
30+
}
31+
f.value = value
32+
return nil
33+
}
34+
35+
func (f *emailFlag) Type() string {
36+
return "string"
37+
}

internal/pkg/flags/email_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package flags
2+
3+
import (
4+
"testing"
5+
6+
"github.com/spf13/cobra"
7+
)
8+
9+
func TestEmailFlag(t *testing.T) {
10+
tests := []struct {
11+
description string
12+
value string
13+
isValid bool
14+
}{
15+
{
16+
description: "valid",
17+
value: "test@test",
18+
isValid: true,
19+
},
20+
{
21+
description: "empty",
22+
value: "",
23+
isValid: false,
24+
},
25+
{
26+
description: "invalid",
27+
value: "invalid-email",
28+
isValid: false,
29+
},
30+
}
31+
32+
for _, tt := range tests {
33+
t.Run(tt.description, func(t *testing.T) {
34+
flag := EmailFlag()
35+
cmd := &cobra.Command{
36+
Use: "test",
37+
RunE: func(_ *cobra.Command, _ []string) error {
38+
return nil
39+
},
40+
}
41+
cmd.Flags().Var(flag, "test-flag", "test")
42+
43+
err := cmd.Flags().Set("test-flag", tt.value)
44+
45+
if !tt.isValid && err == nil {
46+
t.Fatalf("did not fail on invalid input")
47+
}
48+
if !tt.isValid {
49+
return
50+
}
51+
52+
if err != nil {
53+
t.Fatalf("failed on valid input: %v", err)
54+
}
55+
value := FlagToStringValue(nil, cmd, "test-flag")
56+
if value != tt.value {
57+
t.Fatalf("flag did not return set value")
58+
}
59+
})
60+
}
61+
}

0 commit comments

Comments
 (0)