Skip to content

Add stackit config profile list/delete commands #359

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 20 commits into from
May 31, 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
2 changes: 2 additions & 0 deletions docs/stackit_config_profile.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ stackit config profile [flags]

* [stackit config](./stackit_config.md) - Provides functionality for CLI configuration options
* [stackit config profile create](./stackit_config_profile_create.md) - Creates a CLI configuration profile
* [stackit config profile delete](./stackit_config_profile_delete.md) - Delete a CLI configuration profile
* [stackit config profile list](./stackit_config_profile_list.md) - Lists all CLI configuration profiles
* [stackit config profile set](./stackit_config_profile_set.md) - Set a CLI configuration profile
* [stackit config profile unset](./stackit_config_profile_unset.md) - Unset the current active CLI configuration profile

40 changes: 40 additions & 0 deletions docs/stackit_config_profile_delete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## stackit config profile delete

Delete a CLI configuration profile

### Synopsis

Delete a CLI configuration profile.
If the deleted profile is the active profile, the default profile will be set to active.

```
stackit config profile delete PROFILE [flags]
```

### Examples

```
Delete the configuration profile "my-profile"
$ stackit config profile delete my-profile
```

### Options

```
-h, --help Help for "stackit config profile delete"
```

### 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 config profile](./stackit_config_profile.md) - Manage the CLI configuration profiles

42 changes: 42 additions & 0 deletions docs/stackit_config_profile_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
## stackit config profile list

Lists all CLI configuration profiles

### Synopsis

Lists all CLI configuration profiles.

```
stackit config profile list [flags]
```

### Examples

```
List the configuration profiles
$ stackit config profile list

List the configuration profiles in a json format
$ stackit config profile list --output-format json
```

### Options

```
-h, --help Help for "stackit config profile list"
```

### 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 config profile](./stackit_config_profile.md) - Manage the CLI configuration profiles

105 changes: 105 additions & 0 deletions internal/cmd/config/profile/delete/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package delete

import (
"fmt"

"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/auth"
"github.com/stackitcloud/stackit-cli/internal/pkg/config"
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"

"github.com/spf13/cobra"
)

const (
profileArg = "PROFILE"
)

type inputModel struct {
*globalflags.GlobalFlagModel
Profile string
}

func NewCmd(p *print.Printer) *cobra.Command {
cmd := &cobra.Command{
Use: fmt.Sprintf("delete %s", profileArg),
Short: "Delete a CLI configuration profile",
Long: fmt.Sprintf("%s\n%s",
"Delete a CLI configuration profile.",
"If the deleted profile is the active profile, the default profile will be set to active.",
),
Args: args.SingleArg(profileArg, nil),
Example: examples.Build(
examples.NewExample(
`Delete the configuration profile "my-profile"`,
"$ stackit config profile delete my-profile"),
),
RunE: func(cmd *cobra.Command, args []string) error {
model, err := parseInput(p, cmd, args)
if err != nil {
return err
}

profileExists, err := config.ProfileExists(model.Profile)
if err != nil {
return fmt.Errorf("check if profile exists: %w", err)
}
if !profileExists {
return &errors.DeleteInexistentProfile{Profile: model.Profile}
}

if !model.AssumeYes {
prompt := fmt.Sprintf("Are you sure you want to delete profile %q? (This cannot be undone)", model.Profile)
err = p.PromptForConfirmation(prompt)
if err != nil {
return err
}
}

err = config.DeleteProfile(p, model.Profile)
if err != nil {
return fmt.Errorf("delete profile: %w", err)
}

err = auth.DeleteProfileFromKeyring(model.Profile)
if err != nil {
return fmt.Errorf("delete profile from keyring: %w", err)
}

p.Info("Successfully deleted profile %q\n", model.Profile)

return nil
},
}
return cmd
}

func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
profile := inputArgs[0]

err := config.ValidateProfile(profile)
if err != nil {
return nil, err
}

globalFlags := globalflags.Parse(p, cmd)

model := inputModel{
GlobalFlagModel: globalFlags,
Profile: profile,
}

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
}
132 changes: 132 additions & 0 deletions internal/cmd/config/profile/delete/delete_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package delete

import (
"testing"

"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"

"github.com/google/go-cmp/cmp"
)

const testProfile = "test-profile"

func fixtureArgValues(mods ...func(argValues []string)) []string {
argValues := []string{
testProfile,
}
for _, mod := range mods {
mod(argValues)
}
return argValues
}

func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
model := &inputModel{
GlobalFlagModel: &globalflags.GlobalFlagModel{
Verbosity: globalflags.VerbosityDefault,
},
Profile: testProfile,
}
for _, mod := range mods {
mod(model)
}
return model
}

func TestParseInput(t *testing.T) {
tests := []struct {
description string
argValues []string
flagValues map[string]string
isValid bool
expectedModel *inputModel
}{
{
description: "base",
argValues: fixtureArgValues(),
isValid: true,
expectedModel: fixtureInputModel(),
},
{
description: "no values",
argValues: []string{},
flagValues: map[string]string{},
isValid: false,
},
{
description: "no arg values",
argValues: []string{},
isValid: false,
},
{
description: "some global flag",
argValues: fixtureArgValues(),
flagValues: map[string]string{
globalflags.VerbosityFlag: globalflags.DebugVerbosity,
},
isValid: true,
expectedModel: fixtureInputModel(func(model *inputModel) {
model.GlobalFlagModel.Verbosity = globalflags.DebugVerbosity
}),
},
{
description: "invalid profile",
argValues: []string{"invalid-profile-&"},
isValid: false,
},
}

for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
p := print.NewPrinter()
cmd := NewCmd(p)
err := globalflags.Configure(cmd.Flags())
if err != nil {
t.Fatalf("configure global flags: %v", err)
}

for flag, value := range tt.flagValues {
err := cmd.Flags().Set(flag, value)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
}
}

err = cmd.ValidateArgs(tt.argValues)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("error validating args: %v", err)
}

err = cmd.ValidateRequiredFlags()
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("error validating flags: %v", err)
}

model, err := parseInput(p, cmd, tt.argValues)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("error parsing input: %v", err)
}

if !tt.isValid {
t.Fatalf("did not fail on invalid input")
}
diff := cmp.Diff(model, tt.expectedModel)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
})
}
}
Loading