Skip to content

Commit 2329b3f

Browse files
committed
feat: Add server public IP attach/detach commands
1 parent 4ee0f0e commit 2329b3f

File tree

18 files changed

+883
-57
lines changed

18 files changed

+883
-57
lines changed

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,21 @@ project-tools:
1919

2020
# Lint
2121
lint-golangci-lint:
22-
@echo "Linting with golangci-lint"
22+
@echo ">> Linting with golangci-lint"
2323
@golangci-lint run ${GOLANG_CI_ARGS}
2424

2525
lint-yamllint:
26-
@echo "Linting with yamllint"
26+
@echo ">> Linting with yamllint"
2727
@yamllint -c .yamllint.yaml .
2828

2929
lint: lint-golangci-lint lint-yamllint
3030

3131
# Test
3232
test:
33-
@echo "Running tests for the CLI application"
33+
@echo ">> Running tests for the CLI application"
3434
@go test ./... -count=1
3535

3636
# Generate docs
3737
generate-docs:
38-
@echo "Generating docs..."
38+
@echo ">> Generating docs..."
3939
@go run $(SCRIPTS_BASE)/generate.go

README.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -65,28 +65,28 @@ Help is available for any command by specifying the special flag `--help` (or si
6565

6666
Below you can find a list of the STACKIT services already available in the CLI (along with their respective command names) and the ones that are currently planned to be integrated.
6767

68-
| Service | CLI Commands | Status |
69-
| ---------------------------------- |----------------------------------------------------------------| ------------------------- |
70-
| Observability | `observability` | :white_check_mark: |
71-
| Infrastructure as a Service (IaaS) | `beta network-area` <br/> `beta network` <br/> `beta volume` <br/> `beta network-interface`| :white_check_mark: (beta) |
72-
| Authorization | `project`, `organization` | :white_check_mark: |
73-
| DNS | `dns` | :white_check_mark: |
74-
| Kubernetes Engine (SKE) | `ske` | :white_check_mark: |
75-
| Load Balancer | `load-balancer` | :white_check_mark: |
76-
| LogMe | `logme` | :white_check_mark: |
77-
| MariaDB | `mariadb` | :white_check_mark: |
78-
| MongoDB Flex | `mongodbflex` | :white_check_mark: |
79-
| Object Storage | `object-storage` | :white_check_mark: |
80-
| OpenSearch | `opensearch` | :white_check_mark: |
81-
| PostgreSQL Flex | `postgresflex` | :white_check_mark: |
82-
| RabbitMQ | `rabbitmq` | :white_check_mark: |
83-
| Redis | `redis` | :white_check_mark: |
84-
| Resource Manager | `project` | :white_check_mark: |
85-
| Secrets Manager | `secrets-manager` | :white_check_mark: |
86-
| Server Backup Management | `beta server backup` | :white_check_mark: (beta) |
87-
| Server Command (Run Command) | `beta server command` | :white_check_mark: (beta) |
88-
| Service Account | `service-account` | :white_check_mark: |
89-
| SQLServer Flex | `beta sqlserverflex` | :white_check_mark: (beta) |
68+
| Service | CLI Commands | Status |
69+
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------- |
70+
| Observability | `observability` | :white_check_mark: |
71+
| Infrastructure as a Service (IaaS) | `beta network-area` <br/> `beta network` <br/> `beta volume` <br/> `beta network-interface` <br/> `beta server` | :white_check_mark: (beta) |
72+
| Authorization | `project`, `organization` | :white_check_mark: |
73+
| DNS | `dns` | :white_check_mark: |
74+
| Kubernetes Engine (SKE) | `ske` | :white_check_mark: |
75+
| Load Balancer | `load-balancer` | :white_check_mark: |
76+
| LogMe | `logme` | :white_check_mark: |
77+
| MariaDB | `mariadb` | :white_check_mark: |
78+
| MongoDB Flex | `mongodbflex` | :white_check_mark: |
79+
| Object Storage | `object-storage` | :white_check_mark: |
80+
| OpenSearch | `opensearch` | :white_check_mark: |
81+
| PostgreSQL Flex | `postgresflex` | :white_check_mark: |
82+
| RabbitMQ | `rabbitmq` | :white_check_mark: |
83+
| Redis | `redis` | :white_check_mark: |
84+
| Resource Manager | `project` | :white_check_mark: |
85+
| Secrets Manager | `secrets-manager` | :white_check_mark: |
86+
| Server Backup Management | `beta server backup` | :white_check_mark: (beta) |
87+
| Server Command (Run Command) | `beta server command` | :white_check_mark: (beta) |
88+
| Service Account | `service-account` | :white_check_mark: |
89+
| SQLServer Flex | `beta sqlserverflex` | :white_check_mark: (beta) |
9090

9191
## Authentication
9292

docs/stackit_beta_server_list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,5 @@ stackit beta server list [flags]
4646

4747
### SEE ALSO
4848

49-
* [stackit beta server](./stackit_beta_server.md) - Provides functionality for Server
49+
* [stackit beta server](./stackit_beta_server.md) - Provides functionality for servers
5050

internal/cmd/beta/network-interface/network-interface.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import (
1515
func NewCmd(p *print.Printer) *cobra.Command {
1616
cmd := &cobra.Command{
1717
Use: "network-interface",
18-
Short: "Provides functionality for Network Interface",
19-
Long: "Provides functionality for Network Interface.",
18+
Short: "Provides functionality for network interfaces",
19+
Long: "Provides functionality for network interfaces.",
2020
Args: args.NoArgs,
2121
Run: utils.CmdHelp,
2222
}

internal/cmd/beta/network/network.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import (
1616
func NewCmd(p *print.Printer) *cobra.Command {
1717
cmd := &cobra.Command{
1818
Use: "network",
19-
Short: "Provides functionality for Network",
20-
Long: "Provides functionality for Network.",
19+
Short: "Provides functionality for networks",
20+
Long: "Provides functionality for networks.",
2121
Args: args.NoArgs,
2222
Run: utils.CmdHelp,
2323
}

internal/cmd/beta/server/backup/backup.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import (
2020
func NewCmd(p *print.Printer) *cobra.Command {
2121
cmd := &cobra.Command{
2222
Use: "backup",
23-
Short: "Provides functionality for Server Backup",
24-
Long: "Provides functionality for Server Backup.",
23+
Short: "Provides functionality for server backups",
24+
Long: "Provides functionality for server backups.",
2525
Args: args.NoArgs,
2626
Run: utils.CmdHelp,
2727
}

internal/cmd/beta/server/describe/describe.go

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ func outputResult(p *print.Printer, model *inputModel, server *iaas.Server) erro
142142
return nil
143143
default:
144144
table := tables.NewTable()
145+
if model.Details {
146+
table.SetTitle("Server")
147+
}
145148
table.AddRow("ID", *server.Id)
146149
table.AddSeparator()
147150
table.AddRow("NAME", *server.Name)
@@ -184,35 +187,50 @@ func outputResult(p *print.Printer, model *inputModel, server *iaas.Server) erro
184187
table.AddSeparator()
185188
}
186189

190+
if server.ServiceAccountMails != nil && len(*server.ServiceAccountMails) > 0 {
191+
table.AddRow("SERVICE ACCOUNTS", strings.Join(*server.ServiceAccountMails, "\n"))
192+
table.AddSeparator()
193+
}
194+
187195
if server.Volumes != nil && len(*server.Volumes) > 0 {
188196
volumes := []string{}
189197
volumes = append(volumes, *server.Volumes...)
190198
table.AddRow("VOLUMES", strings.Join(volumes, "\n"))
191199
table.AddSeparator()
192200
}
193201

194-
if model.Details {
195-
if server.ServiceAccountMails != nil && len(*server.ServiceAccountMails) > 0 {
196-
emails := []string{}
197-
emails = append(emails, *server.ServiceAccountMails...)
198-
table.AddRow("SERVICE ACCOUNTS", strings.Join(emails, "\n"))
199-
table.AddSeparator()
200-
}
202+
err := table.Display(p)
203+
if err != nil {
204+
return fmt.Errorf("render table: %w", err)
205+
}
206+
207+
if !model.Details {
208+
return nil
209+
}
210+
211+
// Additional details to be displayed when --details flag is set
212+
if server.Nics != nil && len(*server.Nics) > 0 {
213+
for i, nic := range *server.Nics {
214+
nicsTable := tables.NewTable()
215+
nicsTable.SetTitle(fmt.Sprintf("Attached Network Interface #%d", i))
216+
217+
nicsTable.AddRow("ID", *nic.NicId)
218+
nicsTable.AddSeparator()
219+
nicsTable.AddRow("NETWORK ID", *nic.NetworkId)
220+
nicsTable.AddSeparator()
221+
nicsTable.AddRow("NETWORK NAME", *nic.NetworkName)
222+
nicsTable.AddSeparator()
223+
if nic.PublicIp != nil {
224+
nicsTable.AddRow("PUBLIC IP", *nic.PublicIp)
225+
}
201226

202-
if server.Nics != nil && len(*server.Nics) > 0 {
203-
nics := []string{}
204-
for _, nic := range *server.Nics {
205-
nics = append(nics, *nic.NicId)
227+
nicsTable.Display(p)
228+
if err != nil {
229+
return fmt.Errorf("render table: %w", err)
206230
}
207-
table.AddRow("NICS", strings.Join(nics, "\n"))
208-
table.AddSeparator()
209231
}
210232
}
211233

212-
err := table.Display(p)
213-
if err != nil {
214-
return fmt.Errorf("render table: %w", err)
215-
}
216234
return nil
217235
}
218236
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package attach
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/spf13/cobra"
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
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/iaas/client"
15+
iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils"
16+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
17+
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
18+
)
19+
20+
const (
21+
publicIpIdArg = "PUBLIC_IP_ID"
22+
23+
serverIdFlag = "server-id"
24+
)
25+
26+
type inputModel struct {
27+
*globalflags.GlobalFlagModel
28+
ServerId *string
29+
PublicIpId string
30+
}
31+
32+
func NewCmd(p *print.Printer) *cobra.Command {
33+
cmd := &cobra.Command{
34+
Use: "attach",
35+
Short: "Attaches a public IP to a server",
36+
Long: "Attaches a public IP to a server.",
37+
Args: args.SingleArg(publicIpIdArg, utils.ValidateUUID),
38+
Example: examples.Build(
39+
examples.NewExample(
40+
`Attach a public IP with ID "xxx" to a server with ID "yyy"`,
41+
`$ stackit beta server public-ip attach xxx --server-id yyy`,
42+
)),
43+
RunE: func(cmd *cobra.Command, args []string) error {
44+
ctx := context.Background()
45+
model, err := parseInput(p, cmd, args)
46+
if err != nil {
47+
return err
48+
}
49+
50+
// Configure API client
51+
apiClient, err := client.ConfigureClient(p)
52+
if err != nil {
53+
return err
54+
}
55+
56+
publicIpLabel, _, err := iaasUtils.GetPublicIP(ctx, apiClient, model.ProjectId, model.PublicIpId)
57+
if err != nil {
58+
p.Debug(print.ErrorLevel, "get public ip name: %v", err)
59+
}
60+
if publicIpLabel == "" {
61+
publicIpLabel = model.PublicIpId
62+
}
63+
64+
serverLabel, err := iaasUtils.GetServerName(ctx, apiClient, model.ProjectId, *model.ServerId)
65+
if err != nil {
66+
p.Debug(print.ErrorLevel, "get server name: %v", err)
67+
serverLabel = *model.ServerId
68+
}
69+
70+
if !model.AssumeYes {
71+
prompt := fmt.Sprintf("Are you sure you want to attach public IP %q to server %q?", publicIpLabel, serverLabel)
72+
err = p.PromptForConfirmation(prompt)
73+
if err != nil {
74+
return err
75+
}
76+
}
77+
78+
// Call API
79+
req := buildRequest(ctx, model, apiClient)
80+
err = req.Execute()
81+
if err != nil {
82+
return fmt.Errorf("attach server to public ip: %w", err)
83+
}
84+
85+
p.Info("Attached public IP %q to server %q\n", publicIpLabel, serverLabel)
86+
return nil
87+
},
88+
}
89+
configureFlags(cmd)
90+
return cmd
91+
}
92+
93+
func configureFlags(cmd *cobra.Command) {
94+
cmd.Flags().Var(flags.UUIDFlag(), serverIdFlag, "Server ID")
95+
96+
err := flags.MarkFlagsRequired(cmd, serverIdFlag)
97+
cobra.CheckErr(err)
98+
}
99+
100+
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
101+
volumeId := inputArgs[0]
102+
globalFlags := globalflags.Parse(p, cmd)
103+
if globalFlags.ProjectId == "" {
104+
return nil, &errors.ProjectIdError{}
105+
}
106+
107+
model := inputModel{
108+
GlobalFlagModel: globalFlags,
109+
ServerId: flags.FlagToStringPointer(p, cmd, serverIdFlag),
110+
PublicIpId: volumeId,
111+
}
112+
113+
if p.IsVerbosityDebug() {
114+
modelStr, err := print.BuildDebugStrFromInputModel(model)
115+
if err != nil {
116+
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
117+
} else {
118+
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
119+
}
120+
}
121+
122+
return &model, nil
123+
}
124+
125+
func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiAddPublicIpToServerRequest {
126+
return apiClient.AddPublicIpToServer(ctx, model.ProjectId, *model.ServerId, model.PublicIpId)
127+
}

0 commit comments

Comments
 (0)