From 02f28be0d5f9e24d5fc329ec8d4785ccfa837828 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 10:41:09 +0000 Subject: [PATCH 01/19] Add user utils --- .../pkg/services/postgresflex/utils/utils.go | 9 +++ .../services/postgresflex/utils/utils_test.go | 61 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/internal/pkg/services/postgresflex/utils/utils.go b/internal/pkg/services/postgresflex/utils/utils.go index 9b6f4aadd..fca087f21 100644 --- a/internal/pkg/services/postgresflex/utils/utils.go +++ b/internal/pkg/services/postgresflex/utils/utils.go @@ -112,6 +112,7 @@ func LoadFlavorId(cpu, ram int64, flavors *[]postgresflex.Flavor) (*string, erro type PostgresFlexClient interface { ListVersionsExecute(ctx context.Context, projectId string) (*postgresflex.ListVersionsResponse, error) GetInstanceExecute(ctx context.Context, projectId, instanceId string) (*postgresflex.InstanceResponse, error) + GetUserExecute(ctx context.Context, projectId, instanceId, userId string) (*postgresflex.GetUserResponse, error) } func GetLatestPostgreSQLVersion(ctx context.Context, apiClient PostgresFlexClient, projectId string) (string, error) { @@ -143,3 +144,11 @@ func GetInstanceName(ctx context.Context, apiClient PostgresFlexClient, projectI } return *resp.Item.Name, nil } + +func GetUserName(ctx context.Context, apiClient PostgresFlexClient, projectId, instanceId, userId string) (string, error) { + resp, err := apiClient.GetUserExecute(ctx, projectId, instanceId, userId) + if err != nil { + return "", fmt.Errorf("get PostgreSQL Flex user: %w", err) + } + return *resp.Item.Username, nil +} diff --git a/internal/pkg/services/postgresflex/utils/utils_test.go b/internal/pkg/services/postgresflex/utils/utils_test.go index 0ed72f9b1..e5a2c6455 100644 --- a/internal/pkg/services/postgresflex/utils/utils_test.go +++ b/internal/pkg/services/postgresflex/utils/utils_test.go @@ -15,10 +15,12 @@ import ( var ( testProjectId = uuid.NewString() testInstanceId = uuid.NewString() + testUserId = uuid.NewString() ) const ( testInstanceName = "instance" + testUserName = "user" ) type postgresFlexClientMocked struct { @@ -26,6 +28,8 @@ type postgresFlexClientMocked struct { listVersionsResp *postgresflex.ListVersionsResponse getInstanceFails bool getInstanceResp *postgresflex.InstanceResponse + getUserFails bool + getUserResp *postgresflex.GetUserResponse } func (m *postgresFlexClientMocked) ListVersionsExecute(_ context.Context, _ string) (*postgresflex.ListVersionsResponse, error) { @@ -42,6 +46,13 @@ func (m *postgresFlexClientMocked) GetInstanceExecute(_ context.Context, _, _ st return m.getInstanceResp, nil } +func (m *postgresFlexClientMocked) GetUserExecute(_ context.Context, _, _, _ string) (*postgresflex.GetUserResponse, error) { + if m.getUserFails { + return nil, fmt.Errorf("could not get user") + } + return m.getUserResp, nil +} + func TestValidateStorage(t *testing.T) { tests := []struct { description string @@ -457,3 +468,53 @@ func TestGetInstanceName(t *testing.T) { }) } } + +func TestGetUserName(t *testing.T) { + tests := []struct { + description string + getUserFails bool + getUserResp *postgresflex.GetUserResponse + isValid bool + expectedOutput string + }{ + { + description: "base", + getUserResp: &postgresflex.GetUserResponse{ + Item: &postgresflex.UserResponse{ + Username: utils.Ptr(testUserName), + }, + }, + isValid: true, + expectedOutput: testUserName, + }, + { + description: "get user fails", + getUserFails: true, + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + client := &postgresFlexClientMocked{ + getUserFails: tt.getUserFails, + getUserResp: tt.getUserResp, + } + + output, err := GetUserName(context.Background(), client, testProjectId, testInstanceId, testUserId) + + if tt.isValid && err != nil { + t.Errorf("failed on valid input") + } + if !tt.isValid && err == nil { + t.Errorf("did not fail on invalid input") + } + if !tt.isValid { + return + } + if output != tt.expectedOutput { + t.Errorf("expected output to be %s, got %s", tt.expectedOutput, output) + } + }) + } +} From f1d8355b95b11dc7cdc0b85de85695df51a8673e Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 10:52:04 +0000 Subject: [PATCH 02/19] Add user create --- .../cmd/postgresflex/user/create/create.go | 139 +++++++++++ .../postgresflex/user/create/create_test.go | 232 ++++++++++++++++++ internal/cmd/postgresflex/user/user.go | 25 ++ 3 files changed, 396 insertions(+) create mode 100644 internal/cmd/postgresflex/user/create/create.go create mode 100644 internal/cmd/postgresflex/user/create/create_test.go create mode 100644 internal/cmd/postgresflex/user/user.go diff --git a/internal/cmd/postgresflex/user/create/create.go b/internal/cmd/postgresflex/user/create/create.go new file mode 100644 index 000000000..5544e08a8 --- /dev/null +++ b/internal/cmd/postgresflex/user/create/create.go @@ -0,0 +1,139 @@ +package create + +import ( + "context" + "fmt" + + "stackit/internal/pkg/args" + "stackit/internal/pkg/confirm" + "stackit/internal/pkg/errors" + "stackit/internal/pkg/examples" + "stackit/internal/pkg/flags" + "stackit/internal/pkg/globalflags" + "stackit/internal/pkg/services/postgresflex/client" + postgresflexUtils "stackit/internal/pkg/services/postgresflex/utils" + + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +const ( + instanceIdFlag = "instance-id" + usernameFlag = "username" + rolesFlag = "roles" +) + +var ( + rolesDefault = []string{"read"} +) + +type inputModel struct { + *globalflags.GlobalFlagModel + + InstanceId string + Username *string + Roles *[]string +} + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create", + Short: "Create a PostgreSQL Flex user", + Long: fmt.Sprintf("%s\n%s\n%s\n%s", + "Create a PostgreSQL Flex user.", + "The password is only visible upon creation and cannot be retrieved later.", + "Alternatively, you can reset the password and access the new one by running:", + " $ stackit postgresflex user reset-password --instance-id --user-id ", + ), + Example: examples.Build( + examples.NewExample( + `Create a PostgreSQL Flex user for instance with ID "xxx" and specify the username`, + "$ stackit postgresflex user create --instance-id xxx --username johndoe --roles read"), + examples.NewExample( + `Create a PostgreSQL Flex user for instance with ID "xxx" with an automatically generated username`, + "$ stackit postgresflex user create --instance-id xxx --roles read"), + ), + Args: args.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + model, err := parseInput(cmd) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(cmd) + if err != nil { + return err + } + + instanceLabel, err := postgresflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) + if err != nil { + instanceLabel = model.InstanceId + } + + if !model.AssumeYes { + prompt := fmt.Sprintf("Are you sure you want to create a user for instance %s?", instanceLabel) + err = confirm.PromptForConfirmation(cmd, prompt) + if err != nil { + return err + } + } + + // Call API + req := buildRequest(ctx, model, apiClient) + resp, err := req.Execute() + if err != nil { + return fmt.Errorf("create PostgreSQL Flex user: %w", err) + } + user := resp.Item + + cmd.Printf("Created user for instance %s. User ID: %s\n\n", instanceLabel, *user.Id) + cmd.Printf("Username: %s\n", *user.Username) + cmd.Printf("Password: %s\n", *user.Password) + cmd.Printf("Roles: %v\n", *user.Roles) + cmd.Printf("Host: %s\n", *user.Host) + cmd.Printf("Port: %d\n", *user.Port) + cmd.Printf("URI: %s\n", *user.Uri) + + return nil + }, + } + + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + rolesOptions := []string{"read", "readWrite"} + + cmd.Flags().Var(flags.UUIDFlag(), instanceIdFlag, "ID of the instance") + cmd.Flags().String(usernameFlag, "", "Username of the user. If not specified, a random username will be assigned") + cmd.Flags().Var(flags.EnumSliceFlag(false, rolesDefault, rolesOptions...), rolesFlag, fmt.Sprintf("Roles of the user, possible values are %q", rolesOptions)) + + err := flags.MarkFlagsRequired(cmd, instanceIdFlag) + cobra.CheckErr(err) +} + +func parseInput(cmd *cobra.Command) (*inputModel, error) { + globalFlags := globalflags.Parse(cmd) + if globalFlags.ProjectId == "" { + return nil, &errors.ProjectIdError{} + } + + return &inputModel{ + GlobalFlagModel: globalFlags, + InstanceId: flags.FlagToStringValue(cmd, instanceIdFlag), + Username: flags.FlagToStringPointer(cmd, usernameFlag), + Roles: flags.FlagWithDefaultToStringSlicePointer(cmd, rolesFlag), + }, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *postgresflex.APIClient) postgresflex.ApiCreateUserRequest { + req := apiClient.CreateUser(ctx, model.ProjectId, model.InstanceId) + req = req.CreateUserPayload(postgresflex.CreateUserPayload{ + Username: model.Username, + Roles: model.Roles, + }) + return req +} diff --git a/internal/cmd/postgresflex/user/create/create_test.go b/internal/cmd/postgresflex/user/create/create_test.go new file mode 100644 index 000000000..17e793633 --- /dev/null +++ b/internal/cmd/postgresflex/user/create/create_test.go @@ -0,0 +1,232 @@ +package create + +import ( + "context" + "testing" + + "stackit/internal/pkg/globalflags" + "stackit/internal/pkg/utils" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &postgresflex.APIClient{} +var testProjectId = uuid.NewString() +var testInstanceId = uuid.NewString() + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + instanceIdFlag: testInstanceId, + usernameFlag: "johndoe", + rolesFlag: "read", + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + }, + InstanceId: testInstanceId, + Username: utils.Ptr("johndoe"), + Roles: utils.Ptr([]string{"read"}), + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *postgresflex.ApiCreateUserRequest)) postgresflex.ApiCreateUserRequest { + request := testClient.CreateUser(testCtx, testProjectId, testInstanceId) + request = request.CreateUserPayload(postgresflex.CreateUserPayload{ + Username: utils.Ptr("johndoe"), + Roles: utils.Ptr([]string{"read"}), + }) + + for _, mod := range mods { + mod(&request) + } + return request +} + +func TestParseInput(t *testing.T) { + tests := []struct { + description string + flagValues map[string]string + isValid bool + expectedModel *inputModel + }{ + + { + description: "base", + flagValues: fixtureFlagValues(), + isValid: true, + expectedModel: fixtureInputModel(), + }, + { + description: "no username specified", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, usernameFlag) + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.Username = nil + }), + }, + { + description: "no values", + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "project id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "instance id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, instanceIdFlag) + }), + isValid: false, + }, + { + description: "instance id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[instanceIdFlag] = "" + }), + isValid: false, + }, + { + description: "roles missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, rolesFlag) + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.Roles = &rolesDefault + }), + }, + { + description: "invalid role", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[rolesFlag] = "invalid-role" + }), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + cmd := &cobra.Command{} + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + configureFlags(cmd) + + 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.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(cmd) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing flags: %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) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest postgresflex.ApiCreateUserRequest + }{ + { + description: "base", + model: fixtureInputModel(), + expectedRequest: fixtureRequest(), + }, + { + description: "no username specified", + model: fixtureInputModel(func(model *inputModel) { + model.Username = nil + }), + expectedRequest: fixtureRequest().CreateUserPayload(postgresflex.CreateUserPayload{ + Roles: utils.Ptr([]string{"read"}), + }), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/postgresflex/user/user.go b/internal/cmd/postgresflex/user/user.go new file mode 100644 index 000000000..43cb1453d --- /dev/null +++ b/internal/cmd/postgresflex/user/user.go @@ -0,0 +1,25 @@ +package user + +import ( + "stackit/internal/cmd/postgresflex/user/create" + "stackit/internal/pkg/args" + "stackit/internal/pkg/utils" + + "github.com/spf13/cobra" +) + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "user", + Short: "Provides functionality for PostgreSQL Flex users", + Long: "Provides functionality for PostgreSQL Flex users", + Args: args.NoArgs, + Run: utils.CmdHelp, + } + addSubcommands(cmd) + return cmd +} + +func addSubcommands(cmd *cobra.Command) { + cmd.AddCommand(create.NewCmd()) +} From 8d76b13b92b25f0efd0e9aa55c9ee842bce7bfcb Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 11:04:38 +0000 Subject: [PATCH 03/19] Fix typo --- docs/stackit_mongodbflex_user_describe.md | 2 +- internal/cmd/mongodbflex/user/describe/describe.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/stackit_mongodbflex_user_describe.md b/docs/stackit_mongodbflex_user_describe.md index a1c488ee7..437efd93a 100644 --- a/docs/stackit_mongodbflex_user_describe.md +++ b/docs/stackit_mongodbflex_user_describe.md @@ -18,7 +18,7 @@ stackit mongodbflex user describe USER_ID [flags] Get details of a MongoDB Flex user with ID "xxx" of instance with ID "yyy" $ stackit mongodbflex user list xxx --instance-id yyy - Get details of a MongoDB Flex user with ID "xxx" of instance with ID "xxx" in table format + Get details of a MongoDB Flex user with ID "xxx" of instance with ID "yyy" in table format $ stackit mongodbflex user list xxx --instance-id yyy --output-format pretty ``` diff --git a/internal/cmd/mongodbflex/user/describe/describe.go b/internal/cmd/mongodbflex/user/describe/describe.go index e13b7c5c0..ce160d944 100644 --- a/internal/cmd/mongodbflex/user/describe/describe.go +++ b/internal/cmd/mongodbflex/user/describe/describe.go @@ -45,7 +45,7 @@ func NewCmd() *cobra.Command { `Get details of a MongoDB Flex user with ID "xxx" of instance with ID "yyy"`, "$ stackit mongodbflex user list xxx --instance-id yyy"), examples.NewExample( - `Get details of a MongoDB Flex user with ID "xxx" of instance with ID "xxx" in table format`, + `Get details of a MongoDB Flex user with ID "xxx" of instance with ID "yyy" in table format`, "$ stackit mongodbflex user list xxx --instance-id yyy --output-format pretty"), ), Args: args.SingleArg(userIdArg, utils.ValidateUUID), From 5cfbcd23539dc25458c2e8beeea8f1ca8f696d66 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 11:31:18 +0000 Subject: [PATCH 04/19] Add user describe --- .../postgresflex/user/describe/describe.go | 136 ++++++++++ .../user/describe/describe_test.go | 242 ++++++++++++++++++ internal/cmd/postgresflex/user/user.go | 2 + 3 files changed, 380 insertions(+) create mode 100644 internal/cmd/postgresflex/user/describe/describe.go create mode 100644 internal/cmd/postgresflex/user/describe/describe_test.go diff --git a/internal/cmd/postgresflex/user/describe/describe.go b/internal/cmd/postgresflex/user/describe/describe.go new file mode 100644 index 000000000..3ae7bbb50 --- /dev/null +++ b/internal/cmd/postgresflex/user/describe/describe.go @@ -0,0 +1,136 @@ +package describe + +import ( + "context" + "encoding/json" + "fmt" + + "stackit/internal/pkg/args" + "stackit/internal/pkg/errors" + "stackit/internal/pkg/examples" + "stackit/internal/pkg/flags" + "stackit/internal/pkg/globalflags" + "stackit/internal/pkg/services/postgresflex/client" + "stackit/internal/pkg/tables" + "stackit/internal/pkg/utils" + + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +const ( + userIdArg = "USER_ID" + + instanceIdFlag = "instance-id" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + + InstanceId string + UserId string +} + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("describe %s", userIdArg), + Short: "Get details of a PostgreSQL Flex user", + Long: fmt.Sprintf("%s\n%s\n%s", + "Get details of a PostgreSQL Flex user.", + `The user password is hidden inside the "host" field and replaced with asterisks, as it is only visible upon creation. You can reset it by running:`, + " $ stackit postgresflex user reset-password --instance-id ", + ), + Example: examples.Build( + examples.NewExample( + `Get details of a PostgreSQL Flex user with ID "xxx" of instance with ID "yyy"`, + "$ stackit postgresflex user list xxx --instance-id yyy"), + examples.NewExample( + `Get details of a PostgreSQL Flex user with ID "xxx" of instance with ID "yyy" in table format`, + "$ stackit postgresflex user list xxx --instance-id yyy --output-format pretty"), + ), + Args: args.SingleArg(userIdArg, utils.ValidateUUID), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + model, err := parseInput(cmd, args) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(cmd) + if err != nil { + return err + } + + // Call API + req := buildRequest(ctx, model, apiClient) + resp, err := req.Execute() + if err != nil { + return fmt.Errorf("get MongoDB Flex user: %w", err) + } + + return outputResult(cmd, model.OutputFormat, *resp.Item) + }, + } + + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + cmd.Flags().Var(flags.UUIDFlag(), instanceIdFlag, "ID of the instance") + + err := flags.MarkFlagsRequired(cmd, instanceIdFlag) + cobra.CheckErr(err) +} + +func parseInput(cmd *cobra.Command, inputArgs []string) (*inputModel, error) { + userId := inputArgs[0] + + globalFlags := globalflags.Parse(cmd) + if globalFlags.ProjectId == "" { + return nil, &errors.ProjectIdError{} + } + + return &inputModel{ + GlobalFlagModel: globalFlags, + InstanceId: flags.FlagToStringValue(cmd, instanceIdFlag), + UserId: userId, + }, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *postgresflex.APIClient) postgresflex.ApiGetUserRequest { + req := apiClient.GetUser(ctx, model.ProjectId, model.InstanceId, model.UserId) + return req +} + +func outputResult(cmd *cobra.Command, outputFormat string, user postgresflex.UserResponse) error { + switch outputFormat { + case globalflags.PrettyOutputFormat: + table := tables.NewTable() + table.AddRow("ID", *user.Id) + table.AddSeparator() + table.AddRow("USERNAME", *user.Username) + table.AddSeparator() + table.AddRow("ROLES", *user.Roles) + table.AddSeparator() + table.AddRow("HOST", *user.Host) + table.AddSeparator() + table.AddRow("PORT", *user.Port) + + err := table.Display(cmd) + if err != nil { + return fmt.Errorf("render table: %w", err) + } + + return nil + default: + details, err := json.MarshalIndent(user, "", " ") + if err != nil { + return fmt.Errorf("marshal MongoDB Flex user: %w", err) + } + cmd.Println(string(details)) + + return nil + } +} diff --git a/internal/cmd/postgresflex/user/describe/describe_test.go b/internal/cmd/postgresflex/user/describe/describe_test.go new file mode 100644 index 000000000..7e83e7dc9 --- /dev/null +++ b/internal/cmd/postgresflex/user/describe/describe_test.go @@ -0,0 +1,242 @@ +package describe + +import ( + "context" + "testing" + + "stackit/internal/pkg/globalflags" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &postgresflex.APIClient{} +var testProjectId = uuid.NewString() +var testInstanceId = uuid.NewString() +var testUserId = uuid.NewString() + +func fixtureArgValues(mods ...func(argValues []string)) []string { + argValues := []string{ + testUserId, + } + for _, mod := range mods { + mod(argValues) + } + return argValues +} + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + instanceIdFlag: testInstanceId, + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + }, + InstanceId: testInstanceId, + UserId: testUserId, + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *postgresflex.ApiGetUserRequest)) postgresflex.ApiGetUserRequest { + request := testClient.GetUser(testCtx, testProjectId, testInstanceId, testUserId) + for _, mod := range mods { + mod(&request) + } + return request +} + +func TestParseInput(t *testing.T) { + tests := []struct { + description string + argValues []string + flagValues map[string]string + isValid bool + expectedModel *inputModel + }{ + { + description: "base", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(), + isValid: true, + expectedModel: fixtureInputModel(), + }, + { + description: "no values", + argValues: fixtureArgValues(), + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "no arg values", + argValues: []string{}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "no flag values", + argValues: fixtureArgValues(), + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "project id missing", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "instance id missing", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, instanceIdFlag) + }), + isValid: false, + }, + { + description: "instance id invalid 1", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[instanceIdFlag] = "" + }), + isValid: false, + }, + { + description: "instance id invalid 2", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[instanceIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "user id invalid 1", + argValues: []string{""}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "user id invalid 2", + argValues: []string{"invalid-uuid"}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + cmd := NewCmd() + 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(cmd, tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing flags: %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) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest postgresflex.ApiGetUserRequest + }{ + { + description: "base", + model: fixtureInputModel(), + expectedRequest: fixtureRequest(), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/postgresflex/user/user.go b/internal/cmd/postgresflex/user/user.go index 43cb1453d..8afcb33d6 100644 --- a/internal/cmd/postgresflex/user/user.go +++ b/internal/cmd/postgresflex/user/user.go @@ -2,6 +2,7 @@ package user import ( "stackit/internal/cmd/postgresflex/user/create" + "stackit/internal/cmd/postgresflex/user/describe" "stackit/internal/pkg/args" "stackit/internal/pkg/utils" @@ -22,4 +23,5 @@ func NewCmd() *cobra.Command { func addSubcommands(cmd *cobra.Command) { cmd.AddCommand(create.NewCmd()) + cmd.AddCommand(describe.NewCmd()) } From bd2086f8917b958fef4bb33646680d126831fc89 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 15:09:30 +0000 Subject: [PATCH 05/19] Add user list --- internal/cmd/postgresflex/user/list/list.go | 150 +++++++++++++ .../cmd/postgresflex/user/list/list_test.go | 202 ++++++++++++++++++ internal/cmd/postgresflex/user/user.go | 2 + 3 files changed, 354 insertions(+) create mode 100644 internal/cmd/postgresflex/user/list/list.go create mode 100644 internal/cmd/postgresflex/user/list/list_test.go diff --git a/internal/cmd/postgresflex/user/list/list.go b/internal/cmd/postgresflex/user/list/list.go new file mode 100644 index 000000000..30e3fed3e --- /dev/null +++ b/internal/cmd/postgresflex/user/list/list.go @@ -0,0 +1,150 @@ +package list + +import ( + "context" + "encoding/json" + "fmt" + + "stackit/internal/pkg/args" + "stackit/internal/pkg/errors" + "stackit/internal/pkg/examples" + "stackit/internal/pkg/flags" + "stackit/internal/pkg/globalflags" + "stackit/internal/pkg/services/postgresflex/client" + postgresflexUtils "stackit/internal/pkg/services/postgresflex/utils" + "stackit/internal/pkg/tables" + + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +const ( + instanceIdFlag = "instance-id" + limitFlag = "limit" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + + InstanceId *string + Limit *int64 +} + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List all PostgreSQL Flex users of an instance", + Long: "List all PostgreSQL Flex users of an instance.", + Example: examples.Build( + examples.NewExample( + `List all PostgreSQL Flex users of instance with ID "xxx"`, + "$ stackit postgresflex user list --instance-id xxx"), + examples.NewExample( + `List all PostgreSQL Flex users of instance with ID "xxx" in JSON format`, + "$ stackit postgresflex user list --instance-id xxx --output-format json"), + examples.NewExample( + `List up to 10 PostgreSQL Flex users of instance with ID "xxx"`, + "$ stackit postgresflex user list --instance-id xxx --limit 10"), + ), + Args: args.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + model, err := parseInput(cmd) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(cmd) + if err != nil { + return err + } + + // Call API + req := buildRequest(ctx, model, apiClient) + resp, err := req.Execute() + if err != nil { + return fmt.Errorf("get PostgreSQL Flex users: %w", err) + } + if resp.Items == nil || len(*resp.Items) == 0 { + instanceLabel, err := postgresflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, *model.InstanceId) + if err != nil { + instanceLabel = *model.InstanceId + } + cmd.Printf("No users found for instance %s\n", instanceLabel) + return nil + } + users := *resp.Items + + // Truncate output + if model.Limit != nil && len(users) > int(*model.Limit) { + users = users[:*model.Limit] + } + + return outputResult(cmd, model.OutputFormat, users) + }, + } + + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + cmd.Flags().Var(flags.UUIDFlag(), instanceIdFlag, "Instance ID") + cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list") + + err := flags.MarkFlagsRequired(cmd, instanceIdFlag) + cobra.CheckErr(err) +} + +func parseInput(cmd *cobra.Command) (*inputModel, error) { + globalFlags := globalflags.Parse(cmd) + if globalFlags.ProjectId == "" { + return nil, &errors.ProjectIdError{} + } + + limit := flags.FlagToInt64Pointer(cmd, limitFlag) + if limit != nil && *limit < 1 { + return nil, &errors.FlagValidationError{ + Flag: limitFlag, + Details: "must be greater than 0", + } + } + + return &inputModel{ + GlobalFlagModel: globalFlags, + InstanceId: flags.FlagToStringPointer(cmd, instanceIdFlag), + Limit: flags.FlagToInt64Pointer(cmd, limitFlag), + }, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *postgresflex.APIClient) postgresflex.ApiListUsersRequest { + req := apiClient.ListUsers(ctx, model.ProjectId, *model.InstanceId) + return req +} + +func outputResult(cmd *cobra.Command, outputFormat string, users []postgresflex.ListUsersResponseItem) error { + switch outputFormat { + case globalflags.JSONOutputFormat: + details, err := json.MarshalIndent(users, "", " ") + if err != nil { + return fmt.Errorf("marshal PostgreSQL Flex user list: %w", err) + } + cmd.Println(string(details)) + + return nil + default: + table := tables.NewTable() + table.SetHeader("ID", "USERNAME") + for i := range users { + user := users[i] + table.AddRow(*user.Id, *user.Username) + } + err := table.Display(cmd) + if err != nil { + return fmt.Errorf("render table: %w", err) + } + + return nil + } +} diff --git a/internal/cmd/postgresflex/user/list/list_test.go b/internal/cmd/postgresflex/user/list/list_test.go new file mode 100644 index 000000000..f18bbac0d --- /dev/null +++ b/internal/cmd/postgresflex/user/list/list_test.go @@ -0,0 +1,202 @@ +package list + +import ( + "context" + "testing" + + "stackit/internal/pkg/globalflags" + "stackit/internal/pkg/utils" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &postgresflex.APIClient{} +var testProjectId = uuid.NewString() +var testInstanceId = uuid.NewString() + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + instanceIdFlag: testInstanceId, + limitFlag: "10", + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + }, + InstanceId: utils.Ptr(testInstanceId), + Limit: utils.Ptr(int64(10)), + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *postgresflex.ApiListUsersRequest)) postgresflex.ApiListUsersRequest { + request := testClient.ListUsers(testCtx, testProjectId, testInstanceId) + for _, mod := range mods { + mod(&request) + } + return request +} + +func TestParseInput(t *testing.T) { + tests := []struct { + description string + flagValues map[string]string + isValid bool + expectedModel *inputModel + }{ + { + description: "base", + flagValues: fixtureFlagValues(), + isValid: true, + expectedModel: fixtureInputModel(), + }, + { + description: "no values", + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "project id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "instance id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, instanceIdFlag) + }), + isValid: false, + }, + { + description: "instance id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[instanceIdFlag] = "" + }), + isValid: false, + }, + { + description: "limit invalid", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[limitFlag] = "invalid" + }), + isValid: false, + }, + { + description: "limit invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[limitFlag] = "0" + }), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + cmd := &cobra.Command{} + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + configureFlags(cmd) + + 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.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(cmd) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing flags: %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) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest postgresflex.ApiListUsersRequest + }{ + { + description: "base", + model: fixtureInputModel(), + expectedRequest: fixtureRequest(), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/postgresflex/user/user.go b/internal/cmd/postgresflex/user/user.go index 8afcb33d6..7e34ad913 100644 --- a/internal/cmd/postgresflex/user/user.go +++ b/internal/cmd/postgresflex/user/user.go @@ -3,6 +3,7 @@ package user import ( "stackit/internal/cmd/postgresflex/user/create" "stackit/internal/cmd/postgresflex/user/describe" + "stackit/internal/cmd/postgresflex/user/list" "stackit/internal/pkg/args" "stackit/internal/pkg/utils" @@ -23,5 +24,6 @@ func NewCmd() *cobra.Command { func addSubcommands(cmd *cobra.Command) { cmd.AddCommand(create.NewCmd()) + cmd.AddCommand(list.NewCmd()) cmd.AddCommand(describe.NewCmd()) } From 791720938c3bfc20a348c0fffca9f219b2e79c1b Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 15:18:42 +0000 Subject: [PATCH 06/19] Add user delete --- .../cmd/postgresflex/user/delete/delete.go | 119 +++++++++ .../postgresflex/user/delete/delete_test.go | 242 ++++++++++++++++++ internal/cmd/postgresflex/user/user.go | 2 + 3 files changed, 363 insertions(+) create mode 100644 internal/cmd/postgresflex/user/delete/delete.go create mode 100644 internal/cmd/postgresflex/user/delete/delete_test.go diff --git a/internal/cmd/postgresflex/user/delete/delete.go b/internal/cmd/postgresflex/user/delete/delete.go new file mode 100644 index 000000000..a07efc74a --- /dev/null +++ b/internal/cmd/postgresflex/user/delete/delete.go @@ -0,0 +1,119 @@ +package delete + +import ( + "context" + "fmt" + + "stackit/internal/pkg/args" + "stackit/internal/pkg/confirm" + "stackit/internal/pkg/errors" + "stackit/internal/pkg/examples" + "stackit/internal/pkg/flags" + "stackit/internal/pkg/globalflags" + "stackit/internal/pkg/services/postgresflex/client" + postgresflexUtils "stackit/internal/pkg/services/postgresflex/utils" + "stackit/internal/pkg/utils" + + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +const ( + userIdArg = "USER_ID" + + instanceIdFlag = "instance-id" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + + InstanceId string + UserId string +} + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("delete %s", userIdArg), + Short: "Delete a PostgreSQL Flex user", + Long: fmt.Sprintf("%s\n%s", + "Delete a PostgreSQL Flex user by ID. You can get the IDs of users for an instance by running:", + " $ stackit postgresflex user list --instance-id ", + ), + Example: examples.Build( + examples.NewExample( + `Delete a PostgreSQL Flex user with ID "xxx" for instance with ID "yyy"`, + "$ stackit postgresflex user delete xxx --instance-id yyy"), + ), + Args: args.SingleArg(userIdArg, utils.ValidateUUID), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + model, err := parseInput(cmd, args) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(cmd) + if err != nil { + return err + } + + instanceLabel, err := postgresflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) + if err != nil { + instanceLabel = model.InstanceId + } + + userLabel, err := postgresflexUtils.GetUserName(ctx, apiClient, model.ProjectId, model.InstanceId, model.UserId) + if err != nil { + userLabel = model.UserId + } + + if !model.AssumeYes { + prompt := fmt.Sprintf("Are you sure you want to delete user %s of instance %s? (This cannot be undone)", userLabel, instanceLabel) + err = confirm.PromptForConfirmation(cmd, prompt) + if err != nil { + return err + } + } + + // Call API + req := buildRequest(ctx, model, apiClient) + err = req.Execute() + if err != nil { + return fmt.Errorf("delete PostgreSQL Flex user: %w", err) + } + + cmd.Printf("Deleted user %s of instance %s\n", userLabel, instanceLabel) + return nil + }, + } + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + cmd.Flags().Var(flags.UUIDFlag(), instanceIdFlag, "Instance ID") + + err := flags.MarkFlagsRequired(cmd, instanceIdFlag) + cobra.CheckErr(err) +} + +func parseInput(cmd *cobra.Command, inputArgs []string) (*inputModel, error) { + userId := inputArgs[0] + + globalFlags := globalflags.Parse(cmd) + if globalFlags.ProjectId == "" { + return nil, &errors.ProjectIdError{} + } + + return &inputModel{ + GlobalFlagModel: globalFlags, + InstanceId: flags.FlagToStringValue(cmd, instanceIdFlag), + UserId: userId, + }, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *postgresflex.APIClient) postgresflex.ApiDeleteUserRequest { + req := apiClient.DeleteUser(ctx, model.ProjectId, model.InstanceId, model.UserId) + return req +} diff --git a/internal/cmd/postgresflex/user/delete/delete_test.go b/internal/cmd/postgresflex/user/delete/delete_test.go new file mode 100644 index 000000000..723f9021b --- /dev/null +++ b/internal/cmd/postgresflex/user/delete/delete_test.go @@ -0,0 +1,242 @@ +package delete + +import ( + "context" + "testing" + + "stackit/internal/pkg/globalflags" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &postgresflex.APIClient{} +var testProjectId = uuid.NewString() +var testInstanceId = uuid.NewString() +var testUserId = uuid.NewString() + +func fixtureArgValues(mods ...func(argValues []string)) []string { + argValues := []string{ + testUserId, + } + for _, mod := range mods { + mod(argValues) + } + return argValues +} + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + instanceIdFlag: testInstanceId, + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + }, + InstanceId: testInstanceId, + UserId: testUserId, + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *postgresflex.ApiDeleteUserRequest)) postgresflex.ApiDeleteUserRequest { + request := testClient.DeleteUser(testCtx, testProjectId, testInstanceId, testUserId) + for _, mod := range mods { + mod(&request) + } + return request +} + +func TestParseInput(t *testing.T) { + tests := []struct { + description string + argValues []string + flagValues map[string]string + isValid bool + expectedModel *inputModel + }{ + { + description: "base", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(), + isValid: true, + expectedModel: fixtureInputModel(), + }, + { + description: "no values", + argValues: fixtureArgValues(), + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "no arg values", + argValues: []string{}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "no flag values", + argValues: fixtureArgValues(), + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "project id missing", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "instance id missing", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, instanceIdFlag) + }), + isValid: false, + }, + { + description: "instance id invalid 1", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[instanceIdFlag] = "" + }), + isValid: false, + }, + { + description: "instance id invalid 2", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[instanceIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "user id invalid 1", + argValues: []string{""}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "user id invalid 2", + argValues: []string{"invalid-uuid"}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + cmd := NewCmd() + 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(cmd, tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing flags: %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) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest postgresflex.ApiDeleteUserRequest + }{ + { + description: "base", + model: fixtureInputModel(), + expectedRequest: fixtureRequest(), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/postgresflex/user/user.go b/internal/cmd/postgresflex/user/user.go index 7e34ad913..6a9843093 100644 --- a/internal/cmd/postgresflex/user/user.go +++ b/internal/cmd/postgresflex/user/user.go @@ -2,6 +2,7 @@ package user import ( "stackit/internal/cmd/postgresflex/user/create" + "stackit/internal/cmd/postgresflex/user/delete" "stackit/internal/cmd/postgresflex/user/describe" "stackit/internal/cmd/postgresflex/user/list" "stackit/internal/pkg/args" @@ -26,4 +27,5 @@ func addSubcommands(cmd *cobra.Command) { cmd.AddCommand(create.NewCmd()) cmd.AddCommand(list.NewCmd()) cmd.AddCommand(describe.NewCmd()) + cmd.AddCommand(delete.NewCmd()) } From 14743862d83d4537ac17cf40d40796f46a428c93 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 15:24:35 +0000 Subject: [PATCH 07/19] Add alias --- internal/cmd/postgresflex/postgresflex.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/cmd/postgresflex/postgresflex.go b/internal/cmd/postgresflex/postgresflex.go index c95d15d62..f6d48c1b9 100644 --- a/internal/cmd/postgresflex/postgresflex.go +++ b/internal/cmd/postgresflex/postgresflex.go @@ -10,11 +10,12 @@ import ( func NewCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "postgresflex", - Short: "Provides functionality for PostgreSQL Flex", - Long: "Provides functionality for PostgreSQL Flex.", - Args: args.NoArgs, - Run: utils.CmdHelp, + Use: "postgresflex", + Aliases: []string{"postgresqlflex"}, + Short: "Provides functionality for PostgreSQL Flex", + Long: "Provides functionality for PostgreSQL Flex.", + Args: args.NoArgs, + Run: utils.CmdHelp, } addSubcommands(cmd) return cmd From 19c156ffa81b94f72b90f71e86066fb212f8a31b Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 17:20:44 +0000 Subject: [PATCH 08/19] Add user update --- .../cmd/postgresflex/user/update/update.go | 131 +++++++++ .../postgresflex/user/update/update_test.go | 251 ++++++++++++++++++ internal/cmd/postgresflex/user/user.go | 2 + 3 files changed, 384 insertions(+) create mode 100644 internal/cmd/postgresflex/user/update/update.go create mode 100644 internal/cmd/postgresflex/user/update/update_test.go diff --git a/internal/cmd/postgresflex/user/update/update.go b/internal/cmd/postgresflex/user/update/update.go new file mode 100644 index 000000000..0a6566677 --- /dev/null +++ b/internal/cmd/postgresflex/user/update/update.go @@ -0,0 +1,131 @@ +package update + +import ( + "context" + "fmt" + + "stackit/internal/pkg/args" + "stackit/internal/pkg/confirm" + "stackit/internal/pkg/errors" + "stackit/internal/pkg/examples" + "stackit/internal/pkg/flags" + "stackit/internal/pkg/globalflags" + "stackit/internal/pkg/services/postgresflex/client" + postgresflexUtils "stackit/internal/pkg/services/postgresflex/utils" + "stackit/internal/pkg/utils" + + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +const ( + userIdArg = "USER_ID" + + instanceIdFlag = "instance-id" + rolesFlag = "roles" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + + InstanceId string + UserId string + Roles *[]string +} + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("update %s", userIdArg), + Short: "Update a PostgreSQL Flex user", + Long: "Update a PostgreSQL Flex user.", + Example: examples.Build( + examples.NewExample( + `Update the roles of a PostgreSQL Flex user with ID "xxx" of instance with ID "yyy"`, + "$ stackit postgresflex user update xxx --instance-id yyy --roles read"), + ), + Args: args.SingleArg(userIdArg, utils.ValidateUUID), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + model, err := parseInput(cmd, args) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(cmd) + if err != nil { + return err + } + + instanceLabel, err := postgresflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) + if err != nil { + instanceLabel = model.InstanceId + } + + userLabel, err := postgresflexUtils.GetUserName(ctx, apiClient, model.ProjectId, model.InstanceId, model.UserId) + if err != nil { + userLabel = model.UserId + } + + if !model.AssumeYes { + prompt := fmt.Sprintf("Are you sure you want to update user %s of instance %s?", userLabel, instanceLabel) + err = confirm.PromptForConfirmation(cmd, prompt) + if err != nil { + return err + } + } + + // Call API + req := buildRequest(ctx, model, apiClient) + err = req.Execute() + if err != nil { + return fmt.Errorf("update PostgreSQL Flex user: %w", err) + } + + cmd.Printf("Updated user %s of instance %s\n", userLabel, instanceLabel) + return nil + }, + } + + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + rolesOptions := []string{"read", "readWrite"} + + cmd.Flags().Var(flags.UUIDFlag(), instanceIdFlag, "ID of the instance") + cmd.Flags().Var(flags.EnumSliceFlag(false, nil, rolesOptions...), rolesFlag, fmt.Sprintf("Roles of the user, possible values are %q", rolesOptions)) + + err := flags.MarkFlagsRequired(cmd, instanceIdFlag) + cobra.CheckErr(err) +} + +func parseInput(cmd *cobra.Command, inputArgs []string) (*inputModel, error) { + userId := inputArgs[0] + + globalFlags := globalflags.Parse(cmd) + if globalFlags.ProjectId == "" { + return nil, &errors.ProjectIdError{} + } + + roles := flags.FlagToStringSlicePointer(cmd, rolesFlag) + if roles == nil { + return nil, &errors.EmptyUpdateError{} + } + + return &inputModel{ + GlobalFlagModel: globalFlags, + InstanceId: flags.FlagToStringValue(cmd, instanceIdFlag), + UserId: userId, + Roles: roles, + }, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *postgresflex.APIClient) postgresflex.ApiPartialUpdateUserRequest { + req := apiClient.PartialUpdateUser(ctx, model.ProjectId, model.InstanceId, model.UserId) + req = req.PartialUpdateUserPayload(postgresflex.PartialUpdateUserPayload{ + Roles: model.Roles, + }) + return req +} diff --git a/internal/cmd/postgresflex/user/update/update_test.go b/internal/cmd/postgresflex/user/update/update_test.go new file mode 100644 index 000000000..a322a1a73 --- /dev/null +++ b/internal/cmd/postgresflex/user/update/update_test.go @@ -0,0 +1,251 @@ +package update + +import ( + "context" + "testing" + + "stackit/internal/pkg/globalflags" + "stackit/internal/pkg/utils" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &postgresflex.APIClient{} +var testProjectId = uuid.NewString() +var testInstanceId = uuid.NewString() +var testUserId = uuid.NewString() + +func fixtureArgValues(mods ...func(argValues []string)) []string { + argValues := []string{ + testUserId, + } + for _, mod := range mods { + mod(argValues) + } + return argValues +} + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + instanceIdFlag: testInstanceId, + rolesFlag: "read", + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + }, + InstanceId: testInstanceId, + UserId: testUserId, + Roles: utils.Ptr([]string{"read"}), + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *postgresflex.ApiPartialUpdateUserRequest)) postgresflex.ApiPartialUpdateUserRequest { + request := testClient.PartialUpdateUser(testCtx, testProjectId, testInstanceId, testUserId) + request = request.PartialUpdateUserPayload(postgresflex.PartialUpdateUserPayload{ + Roles: utils.Ptr([]string{"read"}), + }) + for _, mod := range mods { + mod(&request) + } + return request +} + +func TestParseInput(t *testing.T) { + tests := []struct { + description string + argValues []string + flagValues map[string]string + isValid bool + expectedModel *inputModel + }{ + + { + description: "base", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(), + isValid: true, + expectedModel: fixtureInputModel(), + }, + { + description: "no values", + argValues: fixtureArgValues(), + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "no arg values", + argValues: []string{}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "project id missing", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "instance id missing", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, instanceIdFlag) + }), + isValid: false, + }, + { + description: "instance id invalid 1", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[instanceIdFlag] = "" + }), + isValid: false, + }, + { + description: "user id invalid 1", + argValues: []string{""}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "user id invalid 2", + argValues: []string{"invalid-uuid"}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "invalid role", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[rolesFlag] = "invalid-role" + }), + isValid: false, + }, + { + description: "empty update", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, rolesFlag) + }), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + cmd := NewCmd() + 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(cmd, tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing flags: %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) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest postgresflex.ApiPartialUpdateUserRequest + }{ + { + description: "base", + model: fixtureInputModel(func(model *inputModel) {}), + expectedRequest: fixtureRequest(), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/postgresflex/user/user.go b/internal/cmd/postgresflex/user/user.go index 6a9843093..c23ea5675 100644 --- a/internal/cmd/postgresflex/user/user.go +++ b/internal/cmd/postgresflex/user/user.go @@ -5,6 +5,7 @@ import ( "stackit/internal/cmd/postgresflex/user/delete" "stackit/internal/cmd/postgresflex/user/describe" "stackit/internal/cmd/postgresflex/user/list" + "stackit/internal/cmd/postgresflex/user/update" "stackit/internal/pkg/args" "stackit/internal/pkg/utils" @@ -27,5 +28,6 @@ func addSubcommands(cmd *cobra.Command) { cmd.AddCommand(create.NewCmd()) cmd.AddCommand(list.NewCmd()) cmd.AddCommand(describe.NewCmd()) + cmd.AddCommand(update.NewCmd()) cmd.AddCommand(delete.NewCmd()) } From 55ba47116473a56fbe97618ba69e09724f0d834a Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 17:23:43 +0000 Subject: [PATCH 09/19] Fix tests --- .../mongodbflex/user/update/update_test.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/internal/cmd/mongodbflex/user/update/update_test.go b/internal/cmd/mongodbflex/user/update/update_test.go index 3335520ca..53dbae081 100644 --- a/internal/cmd/mongodbflex/user/update/update_test.go +++ b/internal/cmd/mongodbflex/user/update/update_test.go @@ -62,6 +62,9 @@ func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { func fixtureRequest(mods ...func(request *mongodbflex.ApiPartialUpdateUserRequest)) mongodbflex.ApiPartialUpdateUserRequest { request := testClient.PartialUpdateUser(testCtx, testProjectId, testInstanceId, testUserId) + request = request.PartialUpdateUserPayload(mongodbflex.PartialUpdateUserPayload{ + Database: utils.Ptr("default"), + }) for _, mod := range mods { mod(&request) } @@ -249,20 +252,18 @@ func TestBuildRequest(t *testing.T) { expectedRequest mongodbflex.ApiPartialUpdateUserRequest }{ { - description: "update database only", - model: fixtureInputModel(func(model *inputModel) {}), - expectedRequest: fixtureRequest().PartialUpdateUserPayload(mongodbflex.PartialUpdateUserPayload{ - Database: utils.Ptr("default"), - }), + description: "base", + model: fixtureInputModel(), + expectedRequest: fixtureRequest(), }, { - description: "update roles only", + description: "update database only", model: fixtureInputModel(func(model *inputModel) { - model.Database = nil - model.Roles = utils.Ptr([]string{"reader"}) + model.Roles = nil + model.Database = utils.Ptr("default") }), expectedRequest: fixtureRequest().PartialUpdateUserPayload(mongodbflex.PartialUpdateUserPayload{ - Roles: &[]string{"reader"}, + Database: utils.Ptr("default"), }), }, } From 9a55b84695bf39ecacab7f22c769f870c134bcef Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 17:26:11 +0000 Subject: [PATCH 10/19] Add user reset-password --- .../user/reset-password/reset_password.go | 120 +++++++++ .../reset-password/reset_password_test.go | 242 ++++++++++++++++++ internal/cmd/postgresflex/user/user.go | 2 + 3 files changed, 364 insertions(+) create mode 100644 internal/cmd/postgresflex/user/reset-password/reset_password.go create mode 100644 internal/cmd/postgresflex/user/reset-password/reset_password_test.go diff --git a/internal/cmd/postgresflex/user/reset-password/reset_password.go b/internal/cmd/postgresflex/user/reset-password/reset_password.go new file mode 100644 index 000000000..285b93c69 --- /dev/null +++ b/internal/cmd/postgresflex/user/reset-password/reset_password.go @@ -0,0 +1,120 @@ +package resetpassword + +import ( + "context" + "fmt" + + "stackit/internal/pkg/args" + "stackit/internal/pkg/confirm" + "stackit/internal/pkg/errors" + "stackit/internal/pkg/examples" + "stackit/internal/pkg/flags" + "stackit/internal/pkg/globalflags" + "stackit/internal/pkg/services/postgresflex/client" + postgresflexUtils "stackit/internal/pkg/services/postgresflex/utils" + "stackit/internal/pkg/utils" + + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +const ( + userIdArg = "USER_ID" + + instanceIdFlag = "instance-id" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + + InstanceId string + UserId string +} + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("reset-password %s", userIdArg), + Short: "Reset the password of a PostgreSQL Flex user", + Long: "Reset the password of a PostgreSQL Flex user. The new password is returned in the response.", + Example: examples.Build( + examples.NewExample( + `Reset the password of a PostgreSQL Flex user with ID "xxx" of instance with ID "yyy"`, + "$ stackit postgresflex user reset-password xxx --instance-id yyy"), + ), + Args: args.SingleArg(userIdArg, utils.ValidateUUID), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + model, err := parseInput(cmd, args) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(cmd) + if err != nil { + return err + } + + instanceLabel, err := postgresflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) + if err != nil { + instanceLabel = model.InstanceId + } + + userLabel, err := postgresflexUtils.GetUserName(ctx, apiClient, model.ProjectId, model.InstanceId, model.UserId) + if err != nil { + userLabel = model.UserId + } + + if !model.AssumeYes { + prompt := fmt.Sprintf("Are you sure you want to reset the password of user %s of instance %s? (This cannot be undone)", userLabel, instanceLabel) + err = confirm.PromptForConfirmation(cmd, prompt) + if err != nil { + return err + } + } + + // Call API + req := buildRequest(ctx, model, apiClient) + user, err := req.Execute() + if err != nil { + return fmt.Errorf("reset PostgreSQL Flex user password: %w", err) + } + + cmd.Printf("Reset password for user %s of instance %s\n\n", userLabel, instanceLabel) + cmd.Printf("Username: %s\n", *user.Item.Username) + cmd.Printf("New password: %s\n", *user.Item.Password) + cmd.Printf("New URI: %s\n", *user.Item.Uri) + return nil + }, + } + + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + cmd.Flags().Var(flags.UUIDFlag(), instanceIdFlag, "ID of the instance") + + err := flags.MarkFlagsRequired(cmd, instanceIdFlag) + cobra.CheckErr(err) +} + +func parseInput(cmd *cobra.Command, inputArgs []string) (*inputModel, error) { + userId := inputArgs[0] + + globalFlags := globalflags.Parse(cmd) + if globalFlags.ProjectId == "" { + return nil, &errors.ProjectIdError{} + } + + return &inputModel{ + GlobalFlagModel: globalFlags, + InstanceId: flags.FlagToStringValue(cmd, instanceIdFlag), + UserId: userId, + }, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *postgresflex.APIClient) postgresflex.ApiResetUserRequest { + req := apiClient.ResetUser(ctx, model.ProjectId, model.InstanceId, model.UserId) + return req +} diff --git a/internal/cmd/postgresflex/user/reset-password/reset_password_test.go b/internal/cmd/postgresflex/user/reset-password/reset_password_test.go new file mode 100644 index 000000000..80bc0139e --- /dev/null +++ b/internal/cmd/postgresflex/user/reset-password/reset_password_test.go @@ -0,0 +1,242 @@ +package resetpassword + +import ( + "context" + "testing" + + "stackit/internal/pkg/globalflags" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &postgresflex.APIClient{} +var testProjectId = uuid.NewString() +var testInstanceId = uuid.NewString() +var testUserId = uuid.NewString() + +func fixtureArgValues(mods ...func(argValues []string)) []string { + argValues := []string{ + testUserId, + } + for _, mod := range mods { + mod(argValues) + } + return argValues +} + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + instanceIdFlag: testInstanceId, + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + }, + InstanceId: testInstanceId, + UserId: testUserId, + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *postgresflex.ApiResetUserRequest)) postgresflex.ApiResetUserRequest { + request := testClient.ResetUser(testCtx, testProjectId, testInstanceId, testUserId) + for _, mod := range mods { + mod(&request) + } + return request +} + +func TestParseInput(t *testing.T) { + tests := []struct { + description string + argValues []string + flagValues map[string]string + isValid bool + expectedModel *inputModel + }{ + { + description: "base", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(), + isValid: true, + expectedModel: fixtureInputModel(), + }, + { + description: "no values", + argValues: fixtureArgValues(), + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "no arg values", + argValues: []string{}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "no flag values", + argValues: fixtureArgValues(), + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "project id missing", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "instance id missing", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, instanceIdFlag) + }), + isValid: false, + }, + { + description: "instance id invalid 1", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[instanceIdFlag] = "" + }), + isValid: false, + }, + { + description: "instance id invalid 2", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[instanceIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "user id invalid 1", + argValues: []string{""}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "user id invalid 2", + argValues: []string{"invalid-uuid"}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + cmd := NewCmd() + 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(cmd, tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing flags: %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) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest postgresflex.ApiResetUserRequest + }{ + { + description: "base", + model: fixtureInputModel(), + expectedRequest: fixtureRequest(), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/postgresflex/user/user.go b/internal/cmd/postgresflex/user/user.go index c23ea5675..a7e5f6e03 100644 --- a/internal/cmd/postgresflex/user/user.go +++ b/internal/cmd/postgresflex/user/user.go @@ -5,6 +5,7 @@ import ( "stackit/internal/cmd/postgresflex/user/delete" "stackit/internal/cmd/postgresflex/user/describe" "stackit/internal/cmd/postgresflex/user/list" + resetpassword "stackit/internal/cmd/postgresflex/user/reset-password" "stackit/internal/cmd/postgresflex/user/update" "stackit/internal/pkg/args" "stackit/internal/pkg/utils" @@ -30,4 +31,5 @@ func addSubcommands(cmd *cobra.Command) { cmd.AddCommand(describe.NewCmd()) cmd.AddCommand(update.NewCmd()) cmd.AddCommand(delete.NewCmd()) + cmd.AddCommand(resetpassword.NewCmd()) } From f6730d8304fcaf568fc539c258ad039bceb44841 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 2 Feb 2024 18:13:19 +0000 Subject: [PATCH 11/19] Fix import paths --- .../cmd/postgresflex/user/create/create.go | 16 ++++++++-------- .../postgresflex/user/create/create_test.go | 4 ++-- .../cmd/postgresflex/user/delete/delete.go | 18 +++++++++--------- .../postgresflex/user/delete/delete_test.go | 2 +- .../cmd/postgresflex/user/describe/describe.go | 16 ++++++++-------- .../user/describe/describe_test.go | 2 +- internal/cmd/postgresflex/user/list/list.go | 16 ++++++++-------- .../cmd/postgresflex/user/list/list_test.go | 4 ++-- .../user/reset-password/reset_password.go | 18 +++++++++--------- .../user/reset-password/reset_password_test.go | 2 +- .../cmd/postgresflex/user/update/update.go | 18 +++++++++--------- .../postgresflex/user/update/update_test.go | 4 ++-- internal/cmd/postgresflex/user/user.go | 16 ++++++++-------- 13 files changed, 68 insertions(+), 68 deletions(-) diff --git a/internal/cmd/postgresflex/user/create/create.go b/internal/cmd/postgresflex/user/create/create.go index 5544e08a8..197e2b1e9 100644 --- a/internal/cmd/postgresflex/user/create/create.go +++ b/internal/cmd/postgresflex/user/create/create.go @@ -4,14 +4,14 @@ import ( "context" "fmt" - "stackit/internal/pkg/args" - "stackit/internal/pkg/confirm" - "stackit/internal/pkg/errors" - "stackit/internal/pkg/examples" - "stackit/internal/pkg/flags" - "stackit/internal/pkg/globalflags" - "stackit/internal/pkg/services/postgresflex/client" - postgresflexUtils "stackit/internal/pkg/services/postgresflex/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/confirm" + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/flags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/client" + postgresflexUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/utils" "github.com/spf13/cobra" "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" diff --git a/internal/cmd/postgresflex/user/create/create_test.go b/internal/cmd/postgresflex/user/create/create_test.go index 17e793633..c178302ad 100644 --- a/internal/cmd/postgresflex/user/create/create_test.go +++ b/internal/cmd/postgresflex/user/create/create_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "stackit/internal/pkg/globalflags" - "stackit/internal/pkg/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" diff --git a/internal/cmd/postgresflex/user/delete/delete.go b/internal/cmd/postgresflex/user/delete/delete.go index a07efc74a..8f8293580 100644 --- a/internal/cmd/postgresflex/user/delete/delete.go +++ b/internal/cmd/postgresflex/user/delete/delete.go @@ -4,15 +4,15 @@ import ( "context" "fmt" - "stackit/internal/pkg/args" - "stackit/internal/pkg/confirm" - "stackit/internal/pkg/errors" - "stackit/internal/pkg/examples" - "stackit/internal/pkg/flags" - "stackit/internal/pkg/globalflags" - "stackit/internal/pkg/services/postgresflex/client" - postgresflexUtils "stackit/internal/pkg/services/postgresflex/utils" - "stackit/internal/pkg/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/confirm" + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/flags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/client" + postgresflexUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" "github.com/spf13/cobra" "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" diff --git a/internal/cmd/postgresflex/user/delete/delete_test.go b/internal/cmd/postgresflex/user/delete/delete_test.go index 723f9021b..4fe8ecf1f 100644 --- a/internal/cmd/postgresflex/user/delete/delete_test.go +++ b/internal/cmd/postgresflex/user/delete/delete_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "stackit/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" diff --git a/internal/cmd/postgresflex/user/describe/describe.go b/internal/cmd/postgresflex/user/describe/describe.go index 3ae7bbb50..a4036284d 100644 --- a/internal/cmd/postgresflex/user/describe/describe.go +++ b/internal/cmd/postgresflex/user/describe/describe.go @@ -5,14 +5,14 @@ import ( "encoding/json" "fmt" - "stackit/internal/pkg/args" - "stackit/internal/pkg/errors" - "stackit/internal/pkg/examples" - "stackit/internal/pkg/flags" - "stackit/internal/pkg/globalflags" - "stackit/internal/pkg/services/postgresflex/client" - "stackit/internal/pkg/tables" - "stackit/internal/pkg/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/flags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/client" + "github.com/stackitcloud/stackit-cli/internal/pkg/tables" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" "github.com/spf13/cobra" "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" diff --git a/internal/cmd/postgresflex/user/describe/describe_test.go b/internal/cmd/postgresflex/user/describe/describe_test.go index 7e83e7dc9..dfa7a73c7 100644 --- a/internal/cmd/postgresflex/user/describe/describe_test.go +++ b/internal/cmd/postgresflex/user/describe/describe_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "stackit/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" diff --git a/internal/cmd/postgresflex/user/list/list.go b/internal/cmd/postgresflex/user/list/list.go index 30e3fed3e..4cb1de956 100644 --- a/internal/cmd/postgresflex/user/list/list.go +++ b/internal/cmd/postgresflex/user/list/list.go @@ -5,14 +5,14 @@ import ( "encoding/json" "fmt" - "stackit/internal/pkg/args" - "stackit/internal/pkg/errors" - "stackit/internal/pkg/examples" - "stackit/internal/pkg/flags" - "stackit/internal/pkg/globalflags" - "stackit/internal/pkg/services/postgresflex/client" - postgresflexUtils "stackit/internal/pkg/services/postgresflex/utils" - "stackit/internal/pkg/tables" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/flags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/client" + postgresflexUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/tables" "github.com/spf13/cobra" "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" diff --git a/internal/cmd/postgresflex/user/list/list_test.go b/internal/cmd/postgresflex/user/list/list_test.go index f18bbac0d..38e70b921 100644 --- a/internal/cmd/postgresflex/user/list/list_test.go +++ b/internal/cmd/postgresflex/user/list/list_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "stackit/internal/pkg/globalflags" - "stackit/internal/pkg/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" diff --git a/internal/cmd/postgresflex/user/reset-password/reset_password.go b/internal/cmd/postgresflex/user/reset-password/reset_password.go index 285b93c69..75890232e 100644 --- a/internal/cmd/postgresflex/user/reset-password/reset_password.go +++ b/internal/cmd/postgresflex/user/reset-password/reset_password.go @@ -4,15 +4,15 @@ import ( "context" "fmt" - "stackit/internal/pkg/args" - "stackit/internal/pkg/confirm" - "stackit/internal/pkg/errors" - "stackit/internal/pkg/examples" - "stackit/internal/pkg/flags" - "stackit/internal/pkg/globalflags" - "stackit/internal/pkg/services/postgresflex/client" - postgresflexUtils "stackit/internal/pkg/services/postgresflex/utils" - "stackit/internal/pkg/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/confirm" + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/flags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/client" + postgresflexUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" "github.com/spf13/cobra" "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" diff --git a/internal/cmd/postgresflex/user/reset-password/reset_password_test.go b/internal/cmd/postgresflex/user/reset-password/reset_password_test.go index 80bc0139e..88a2e75e6 100644 --- a/internal/cmd/postgresflex/user/reset-password/reset_password_test.go +++ b/internal/cmd/postgresflex/user/reset-password/reset_password_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "stackit/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" diff --git a/internal/cmd/postgresflex/user/update/update.go b/internal/cmd/postgresflex/user/update/update.go index 0a6566677..77e851cb1 100644 --- a/internal/cmd/postgresflex/user/update/update.go +++ b/internal/cmd/postgresflex/user/update/update.go @@ -4,15 +4,15 @@ import ( "context" "fmt" - "stackit/internal/pkg/args" - "stackit/internal/pkg/confirm" - "stackit/internal/pkg/errors" - "stackit/internal/pkg/examples" - "stackit/internal/pkg/flags" - "stackit/internal/pkg/globalflags" - "stackit/internal/pkg/services/postgresflex/client" - postgresflexUtils "stackit/internal/pkg/services/postgresflex/utils" - "stackit/internal/pkg/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/confirm" + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/flags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/client" + postgresflexUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" "github.com/spf13/cobra" "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" diff --git a/internal/cmd/postgresflex/user/update/update_test.go b/internal/cmd/postgresflex/user/update/update_test.go index a322a1a73..edf2931a4 100644 --- a/internal/cmd/postgresflex/user/update/update_test.go +++ b/internal/cmd/postgresflex/user/update/update_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "stackit/internal/pkg/globalflags" - "stackit/internal/pkg/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" diff --git a/internal/cmd/postgresflex/user/user.go b/internal/cmd/postgresflex/user/user.go index a7e5f6e03..7fc81ce90 100644 --- a/internal/cmd/postgresflex/user/user.go +++ b/internal/cmd/postgresflex/user/user.go @@ -1,14 +1,14 @@ package user import ( - "stackit/internal/cmd/postgresflex/user/create" - "stackit/internal/cmd/postgresflex/user/delete" - "stackit/internal/cmd/postgresflex/user/describe" - "stackit/internal/cmd/postgresflex/user/list" - resetpassword "stackit/internal/cmd/postgresflex/user/reset-password" - "stackit/internal/cmd/postgresflex/user/update" - "stackit/internal/pkg/args" - "stackit/internal/pkg/utils" + "github.com/stackitcloud/stackit-cli/internal/cmd/postgresflex/user/create" + "github.com/stackitcloud/stackit-cli/internal/cmd/postgresflex/user/delete" + "github.com/stackitcloud/stackit-cli/internal/cmd/postgresflex/user/describe" + "github.com/stackitcloud/stackit-cli/internal/cmd/postgresflex/user/list" + resetpassword "github.com/stackitcloud/stackit-cli/internal/cmd/postgresflex/user/reset-password" + "github.com/stackitcloud/stackit-cli/internal/cmd/postgresflex/user/update" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" "github.com/spf13/cobra" ) From 3b57207c22197a11882b7efcc62c46b17f648763 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Mon, 5 Feb 2024 12:29:56 +0000 Subject: [PATCH 12/19] Reword long description --- internal/cmd/postgresflex/user/delete/delete.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/cmd/postgresflex/user/delete/delete.go b/internal/cmd/postgresflex/user/delete/delete.go index 8f8293580..db5823c7d 100644 --- a/internal/cmd/postgresflex/user/delete/delete.go +++ b/internal/cmd/postgresflex/user/delete/delete.go @@ -35,8 +35,9 @@ func NewCmd() *cobra.Command { cmd := &cobra.Command{ Use: fmt.Sprintf("delete %s", userIdArg), Short: "Delete a PostgreSQL Flex user", - Long: fmt.Sprintf("%s\n%s", - "Delete a PostgreSQL Flex user by ID. You can get the IDs of users for an instance by running:", + Long: fmt.Sprintf("%s\n%s\n%s", + "Delete a PostgreSQL Flex user by ID.", + "You can get the IDs of users for an instance by running:", " $ stackit postgresflex user list --instance-id ", ), Example: examples.Build( From 60a53e016e28905d5d7c535e17dca7fc2c6aa93a Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Mon, 5 Feb 2024 12:30:31 +0000 Subject: [PATCH 13/19] Add user group --- internal/cmd/postgresflex/postgresflex.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/cmd/postgresflex/postgresflex.go b/internal/cmd/postgresflex/postgresflex.go index f6d48c1b9..24674b1e9 100644 --- a/internal/cmd/postgresflex/postgresflex.go +++ b/internal/cmd/postgresflex/postgresflex.go @@ -2,6 +2,7 @@ package postgresflex import ( "github.com/stackitcloud/stackit-cli/internal/cmd/postgresflex/instance" + "github.com/stackitcloud/stackit-cli/internal/cmd/postgresflex/user" "github.com/stackitcloud/stackit-cli/internal/pkg/args" "github.com/stackitcloud/stackit-cli/internal/pkg/utils" @@ -23,4 +24,5 @@ func NewCmd() *cobra.Command { func addSubcommands(cmd *cobra.Command) { cmd.AddCommand(instance.NewCmd()) + cmd.AddCommand(user.NewCmd()) } From 42a126ab4c4584c7f1866b87dbc69d86a9c647c1 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Mon, 5 Feb 2024 12:30:38 +0000 Subject: [PATCH 14/19] Generate docs --- docs/stackit_postgresflex.md | 1 + docs/stackit_postgresflex_user.md | 37 +++++++++++++++ docs/stackit_postgresflex_user_create.md | 47 +++++++++++++++++++ docs/stackit_postgresflex_user_delete.md | 41 ++++++++++++++++ docs/stackit_postgresflex_user_describe.md | 44 +++++++++++++++++ docs/stackit_postgresflex_user_list.md | 46 ++++++++++++++++++ ...tackit_postgresflex_user_reset-password.md | 39 +++++++++++++++ docs/stackit_postgresflex_user_update.md | 40 ++++++++++++++++ 8 files changed, 295 insertions(+) create mode 100644 docs/stackit_postgresflex_user.md create mode 100644 docs/stackit_postgresflex_user_create.md create mode 100644 docs/stackit_postgresflex_user_delete.md create mode 100644 docs/stackit_postgresflex_user_describe.md create mode 100644 docs/stackit_postgresflex_user_list.md create mode 100644 docs/stackit_postgresflex_user_reset-password.md create mode 100644 docs/stackit_postgresflex_user_update.md diff --git a/docs/stackit_postgresflex.md b/docs/stackit_postgresflex.md index fe5382c11..fa7c9e081 100644 --- a/docs/stackit_postgresflex.md +++ b/docs/stackit_postgresflex.md @@ -29,4 +29,5 @@ stackit postgresflex [flags] * [stackit](./stackit.md) - Manage STACKIT resources using the command line * [stackit postgresflex instance](./stackit_postgresflex_instance.md) - Provides functionality for PostgreSQL Flex instances +* [stackit postgresflex user](./stackit_postgresflex_user.md) - Provides functionality for PostgreSQL Flex users diff --git a/docs/stackit_postgresflex_user.md b/docs/stackit_postgresflex_user.md new file mode 100644 index 000000000..15991ab23 --- /dev/null +++ b/docs/stackit_postgresflex_user.md @@ -0,0 +1,37 @@ +## stackit postgresflex user + +Provides functionality for PostgreSQL Flex users + +### Synopsis + +Provides functionality for PostgreSQL Flex users + +``` +stackit postgresflex user [flags] +``` + +### Options + +``` + -h, --help Help for "stackit postgresflex user" +``` + +### 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"] + -p, --project-id string Project ID +``` + +### SEE ALSO + +* [stackit postgresflex](./stackit_postgresflex.md) - Provides functionality for PostgreSQL Flex +* [stackit postgresflex user create](./stackit_postgresflex_user_create.md) - Create a PostgreSQL Flex user +* [stackit postgresflex user delete](./stackit_postgresflex_user_delete.md) - Delete a PostgreSQL Flex user +* [stackit postgresflex user describe](./stackit_postgresflex_user_describe.md) - Get details of a PostgreSQL Flex user +* [stackit postgresflex user list](./stackit_postgresflex_user_list.md) - List all PostgreSQL Flex users of an instance +* [stackit postgresflex user reset-password](./stackit_postgresflex_user_reset-password.md) - Reset the password of a PostgreSQL Flex user +* [stackit postgresflex user update](./stackit_postgresflex_user_update.md) - Update a PostgreSQL Flex user + diff --git a/docs/stackit_postgresflex_user_create.md b/docs/stackit_postgresflex_user_create.md new file mode 100644 index 000000000..e6832cc83 --- /dev/null +++ b/docs/stackit_postgresflex_user_create.md @@ -0,0 +1,47 @@ +## stackit postgresflex user create + +Create a PostgreSQL Flex user + +### Synopsis + +Create a PostgreSQL Flex user. +The password is only visible upon creation and cannot be retrieved later. +Alternatively, you can reset the password and access the new one by running: + $ stackit postgresflex user reset-password --instance-id --user-id + +``` +stackit postgresflex user create [flags] +``` + +### Examples + +``` + Create a PostgreSQL Flex user for instance with ID "xxx" and specify the username + $ stackit postgresflex user create --instance-id xxx --username johndoe --roles read + + Create a PostgreSQL Flex user for instance with ID "xxx" with an automatically generated username + $ stackit postgresflex user create --instance-id xxx --roles read +``` + +### Options + +``` + -h, --help Help for "stackit postgresflex user create" + --instance-id string ID of the instance + --roles strings Roles of the user, possible values are ["read" "readWrite"] (default [read]) + --username string Username of the user. If not specified, a random username will be assigned +``` + +### 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"] + -p, --project-id string Project ID +``` + +### SEE ALSO + +* [stackit postgresflex user](./stackit_postgresflex_user.md) - Provides functionality for PostgreSQL Flex users + diff --git a/docs/stackit_postgresflex_user_delete.md b/docs/stackit_postgresflex_user_delete.md new file mode 100644 index 000000000..00c6e5596 --- /dev/null +++ b/docs/stackit_postgresflex_user_delete.md @@ -0,0 +1,41 @@ +## stackit postgresflex user delete + +Delete a PostgreSQL Flex user + +### Synopsis + +Delete a PostgreSQL Flex user by ID. +You can get the IDs of users for an instance by running: + $ stackit postgresflex user list --instance-id + +``` +stackit postgresflex user delete USER_ID [flags] +``` + +### Examples + +``` + Delete a PostgreSQL Flex user with ID "xxx" for instance with ID "yyy" + $ stackit postgresflex user delete xxx --instance-id yyy +``` + +### Options + +``` + -h, --help Help for "stackit postgresflex user delete" + --instance-id string Instance ID +``` + +### 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"] + -p, --project-id string Project ID +``` + +### SEE ALSO + +* [stackit postgresflex user](./stackit_postgresflex_user.md) - Provides functionality for PostgreSQL Flex users + diff --git a/docs/stackit_postgresflex_user_describe.md b/docs/stackit_postgresflex_user_describe.md new file mode 100644 index 000000000..e60d834fb --- /dev/null +++ b/docs/stackit_postgresflex_user_describe.md @@ -0,0 +1,44 @@ +## stackit postgresflex user describe + +Get details of a PostgreSQL Flex user + +### Synopsis + +Get details of a PostgreSQL Flex user. +The user password is hidden inside the "host" field and replaced with asterisks, as it is only visible upon creation. You can reset it by running: + $ stackit postgresflex user reset-password --instance-id + +``` +stackit postgresflex user describe USER_ID [flags] +``` + +### Examples + +``` + Get details of a PostgreSQL Flex user with ID "xxx" of instance with ID "yyy" + $ stackit postgresflex user list xxx --instance-id yyy + + Get details of a PostgreSQL Flex user with ID "xxx" of instance with ID "yyy" in table format + $ stackit postgresflex user list xxx --instance-id yyy --output-format pretty +``` + +### Options + +``` + -h, --help Help for "stackit postgresflex user describe" + --instance-id string ID of the instance +``` + +### 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"] + -p, --project-id string Project ID +``` + +### SEE ALSO + +* [stackit postgresflex user](./stackit_postgresflex_user.md) - Provides functionality for PostgreSQL Flex users + diff --git a/docs/stackit_postgresflex_user_list.md b/docs/stackit_postgresflex_user_list.md new file mode 100644 index 000000000..aee026faa --- /dev/null +++ b/docs/stackit_postgresflex_user_list.md @@ -0,0 +1,46 @@ +## stackit postgresflex user list + +List all PostgreSQL Flex users of an instance + +### Synopsis + +List all PostgreSQL Flex users of an instance. + +``` +stackit postgresflex user list [flags] +``` + +### Examples + +``` + List all PostgreSQL Flex users of instance with ID "xxx" + $ stackit postgresflex user list --instance-id xxx + + List all PostgreSQL Flex users of instance with ID "xxx" in JSON format + $ stackit postgresflex user list --instance-id xxx --output-format json + + List up to 10 PostgreSQL Flex users of instance with ID "xxx" + $ stackit postgresflex user list --instance-id xxx --limit 10 +``` + +### Options + +``` + -h, --help Help for "stackit postgresflex user list" + --instance-id string Instance ID + --limit int Maximum number of entries to 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"] + -p, --project-id string Project ID +``` + +### SEE ALSO + +* [stackit postgresflex user](./stackit_postgresflex_user.md) - Provides functionality for PostgreSQL Flex users + diff --git a/docs/stackit_postgresflex_user_reset-password.md b/docs/stackit_postgresflex_user_reset-password.md new file mode 100644 index 000000000..7d010c99a --- /dev/null +++ b/docs/stackit_postgresflex_user_reset-password.md @@ -0,0 +1,39 @@ +## stackit postgresflex user reset-password + +Reset the password of a PostgreSQL Flex user + +### Synopsis + +Reset the password of a PostgreSQL Flex user. The new password is returned in the response. + +``` +stackit postgresflex user reset-password USER_ID [flags] +``` + +### Examples + +``` + Reset the password of a PostgreSQL Flex user with ID "xxx" of instance with ID "yyy" + $ stackit postgresflex user reset-password xxx --instance-id yyy +``` + +### Options + +``` + -h, --help Help for "stackit postgresflex user reset-password" + --instance-id string ID of the instance +``` + +### 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"] + -p, --project-id string Project ID +``` + +### SEE ALSO + +* [stackit postgresflex user](./stackit_postgresflex_user.md) - Provides functionality for PostgreSQL Flex users + diff --git a/docs/stackit_postgresflex_user_update.md b/docs/stackit_postgresflex_user_update.md new file mode 100644 index 000000000..5f478ac28 --- /dev/null +++ b/docs/stackit_postgresflex_user_update.md @@ -0,0 +1,40 @@ +## stackit postgresflex user update + +Update a PostgreSQL Flex user + +### Synopsis + +Update a PostgreSQL Flex user. + +``` +stackit postgresflex user update USER_ID [flags] +``` + +### Examples + +``` + Update the roles of a PostgreSQL Flex user with ID "xxx" of instance with ID "yyy" + $ stackit postgresflex user update xxx --instance-id yyy --roles read +``` + +### Options + +``` + -h, --help Help for "stackit postgresflex user update" + --instance-id string ID of the instance + --roles strings Roles of the user, possible values are ["read" "readWrite"] (default []) +``` + +### 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"] + -p, --project-id string Project ID +``` + +### SEE ALSO + +* [stackit postgresflex user](./stackit_postgresflex_user.md) - Provides functionality for PostgreSQL Flex users + From 4e031766cc369e6cead51755eaf56ec4c51f44cf Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Tue, 6 Feb 2024 11:59:24 +0000 Subject: [PATCH 15/19] Update dependency --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c95165801..d853f67a2 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/stackitcloud/stackit-sdk-go/services/membership v0.3.4 github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v0.10.3 github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.9.2 - github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.9.0 + github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.9.1 github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.7.5 github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.3.4 github.com/stackitcloud/stackit-sdk-go/services/ske v0.9.2 diff --git a/go.sum b/go.sum index 85f4032e2..a7d6d952c 100644 --- a/go.sum +++ b/go.sum @@ -87,8 +87,8 @@ github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v0.10.3 h1:M7ALIg1tE github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v0.10.3/go.mod h1:LWfUBjGQWF3SZivQdUdAC/WxJkx8ImJKy5GFMV3tXHY= github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.9.2 h1:dwZ1NDD+AxTaZqAeR/0PY7yt32dbABhQH1Vsnt8A+hg= github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.9.2/go.mod h1:M8mjTS5yR0XXoH9EpuULme9fEkLhUz4UOT7XSHUSRQ8= -github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.9.0 h1:b4RFEDSMa+p8iUsMS/RzhkT6uLhD607lhQyQPfrO8nU= -github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.9.0/go.mod h1:bVXMSjQH1eGUF5IoPAD8BjAzhRMunDlnrwLsaCPgRQ4= +github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.9.1 h1:41M0IWsHFmt2KvoY/4vwWYNG94Tx3U3diSlYdz0thJE= +github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.9.1/go.mod h1:bVXMSjQH1eGUF5IoPAD8BjAzhRMunDlnrwLsaCPgRQ4= github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.7.5 h1:Gu0z8MpErzBHxb9xx8B/4DduxckDmBRPWNaeoVcE8cQ= github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.7.5/go.mod h1:MQ5eGWFmnDf9wUArqZ2g+nwJgMDkYDQUkoRVutaHrms= github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.3.4 h1:XNL7bk5mwCovV8a3oIIC9PlNpPTUG3XNwdRqHS5V2no= From 5960d85bc2bea241e678e2d8235e9761f3904196 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Tue, 6 Feb 2024 14:45:47 +0000 Subject: [PATCH 16/19] Reword descriptions, fix command indication --- internal/cmd/postgresflex/user/create/create.go | 6 +++--- internal/cmd/postgresflex/user/delete/delete.go | 4 ++-- internal/cmd/postgresflex/user/describe/describe.go | 6 +++--- internal/cmd/postgresflex/user/list/list.go | 4 ++-- .../cmd/postgresflex/user/reset-password/reset_password.go | 4 ++-- internal/cmd/postgresflex/user/update/update.go | 4 ++-- internal/cmd/postgresflex/user/user.go | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/internal/cmd/postgresflex/user/create/create.go b/internal/cmd/postgresflex/user/create/create.go index 197e2b1e9..f544200c1 100644 --- a/internal/cmd/postgresflex/user/create/create.go +++ b/internal/cmd/postgresflex/user/create/create.go @@ -38,12 +38,12 @@ type inputModel struct { func NewCmd() *cobra.Command { cmd := &cobra.Command{ Use: "create", - Short: "Create a PostgreSQL Flex user", + Short: "Creates a PostgreSQL Flex user", Long: fmt.Sprintf("%s\n%s\n%s\n%s", - "Create a PostgreSQL Flex user.", + "Creates a PostgreSQL Flex user.", "The password is only visible upon creation and cannot be retrieved later.", "Alternatively, you can reset the password and access the new one by running:", - " $ stackit postgresflex user reset-password --instance-id --user-id ", + " $ stackit postgresflex user reset-password USER_ID --instance-id INSTANCE_ID", ), Example: examples.Build( examples.NewExample( diff --git a/internal/cmd/postgresflex/user/delete/delete.go b/internal/cmd/postgresflex/user/delete/delete.go index db5823c7d..f79bdf5db 100644 --- a/internal/cmd/postgresflex/user/delete/delete.go +++ b/internal/cmd/postgresflex/user/delete/delete.go @@ -34,9 +34,9 @@ type inputModel struct { func NewCmd() *cobra.Command { cmd := &cobra.Command{ Use: fmt.Sprintf("delete %s", userIdArg), - Short: "Delete a PostgreSQL Flex user", + Short: "Deletes a PostgreSQL Flex user", Long: fmt.Sprintf("%s\n%s\n%s", - "Delete a PostgreSQL Flex user by ID.", + "Deletes a PostgreSQL Flex user by ID.", "You can get the IDs of users for an instance by running:", " $ stackit postgresflex user list --instance-id ", ), diff --git a/internal/cmd/postgresflex/user/describe/describe.go b/internal/cmd/postgresflex/user/describe/describe.go index a4036284d..0fbbcd01b 100644 --- a/internal/cmd/postgresflex/user/describe/describe.go +++ b/internal/cmd/postgresflex/user/describe/describe.go @@ -34,11 +34,11 @@ type inputModel struct { func NewCmd() *cobra.Command { cmd := &cobra.Command{ Use: fmt.Sprintf("describe %s", userIdArg), - Short: "Get details of a PostgreSQL Flex user", + Short: "Shows details of a PostgreSQL Flex user", Long: fmt.Sprintf("%s\n%s\n%s", - "Get details of a PostgreSQL Flex user.", + "Shows details of a PostgreSQL Flex user.", `The user password is hidden inside the "host" field and replaced with asterisks, as it is only visible upon creation. You can reset it by running:`, - " $ stackit postgresflex user reset-password --instance-id ", + " $ stackit postgresflex user reset-password USER_ID --instance-id INSTANCE_ID", ), Example: examples.Build( examples.NewExample( diff --git a/internal/cmd/postgresflex/user/list/list.go b/internal/cmd/postgresflex/user/list/list.go index 4cb1de956..e00991b48 100644 --- a/internal/cmd/postgresflex/user/list/list.go +++ b/internal/cmd/postgresflex/user/list/list.go @@ -33,8 +33,8 @@ type inputModel struct { func NewCmd() *cobra.Command { cmd := &cobra.Command{ Use: "list", - Short: "List all PostgreSQL Flex users of an instance", - Long: "List all PostgreSQL Flex users of an instance.", + Short: "Lists all PostgreSQL Flex users of an instance", + Long: "Lists all PostgreSQL Flex users of an instance.", Example: examples.Build( examples.NewExample( `List all PostgreSQL Flex users of instance with ID "xxx"`, diff --git a/internal/cmd/postgresflex/user/reset-password/reset_password.go b/internal/cmd/postgresflex/user/reset-password/reset_password.go index 75890232e..05033a9ee 100644 --- a/internal/cmd/postgresflex/user/reset-password/reset_password.go +++ b/internal/cmd/postgresflex/user/reset-password/reset_password.go @@ -34,8 +34,8 @@ type inputModel struct { func NewCmd() *cobra.Command { cmd := &cobra.Command{ Use: fmt.Sprintf("reset-password %s", userIdArg), - Short: "Reset the password of a PostgreSQL Flex user", - Long: "Reset the password of a PostgreSQL Flex user. The new password is returned in the response.", + Short: "Resets the password of a PostgreSQL Flex user", + Long: "Resets the password of a PostgreSQL Flex user. The new password is returned in the response.", Example: examples.Build( examples.NewExample( `Reset the password of a PostgreSQL Flex user with ID "xxx" of instance with ID "yyy"`, diff --git a/internal/cmd/postgresflex/user/update/update.go b/internal/cmd/postgresflex/user/update/update.go index 77e851cb1..d65f844c3 100644 --- a/internal/cmd/postgresflex/user/update/update.go +++ b/internal/cmd/postgresflex/user/update/update.go @@ -36,8 +36,8 @@ type inputModel struct { func NewCmd() *cobra.Command { cmd := &cobra.Command{ Use: fmt.Sprintf("update %s", userIdArg), - Short: "Update a PostgreSQL Flex user", - Long: "Update a PostgreSQL Flex user.", + Short: "Updates a PostgreSQL Flex user", + Long: "Updates a PostgreSQL Flex user.", Example: examples.Build( examples.NewExample( `Update the roles of a PostgreSQL Flex user with ID "xxx" of instance with ID "yyy"`, diff --git a/internal/cmd/postgresflex/user/user.go b/internal/cmd/postgresflex/user/user.go index 7fc81ce90..df90fac71 100644 --- a/internal/cmd/postgresflex/user/user.go +++ b/internal/cmd/postgresflex/user/user.go @@ -17,7 +17,7 @@ func NewCmd() *cobra.Command { cmd := &cobra.Command{ Use: "user", Short: "Provides functionality for PostgreSQL Flex users", - Long: "Provides functionality for PostgreSQL Flex users", + Long: "Provides functionality for PostgreSQL Flex users.", Args: args.NoArgs, Run: utils.CmdHelp, } From 45e460f09a9072020a0457b63b80356e559e0994 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Tue, 6 Feb 2024 14:46:34 +0000 Subject: [PATCH 17/19] Fix command description --- internal/cmd/mongodbflex/user/create/create.go | 2 +- internal/cmd/mongodbflex/user/describe/describe.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cmd/mongodbflex/user/create/create.go b/internal/cmd/mongodbflex/user/create/create.go index bdcdeb252..bdf4e591a 100644 --- a/internal/cmd/mongodbflex/user/create/create.go +++ b/internal/cmd/mongodbflex/user/create/create.go @@ -45,7 +45,7 @@ func NewCmd() *cobra.Command { "Creates a MongoDB Flex user.", "The password is only visible upon creation and cannot be retrieved later.", "Alternatively, you can reset the password and access the new one by running:", - " $ stackit mongodbflex user reset-password --instance-id --user-id ", + " $ stackit mongodbflex user reset-password USER_ID --instance-id INSTANCE_ID", ), Example: examples.Build( examples.NewExample( diff --git a/internal/cmd/mongodbflex/user/describe/describe.go b/internal/cmd/mongodbflex/user/describe/describe.go index ce160d944..cd03a0244 100644 --- a/internal/cmd/mongodbflex/user/describe/describe.go +++ b/internal/cmd/mongodbflex/user/describe/describe.go @@ -38,7 +38,7 @@ func NewCmd() *cobra.Command { Long: fmt.Sprintf("%s\n%s\n%s", "Shows details of a MongoDB Flex user.", `The user password is hidden inside the "host" field and replaced with asterisks, as it is only visible upon creation. You can reset it by running:`, - " $ stackit mongodbflex user reset-password --instance-id ", + " $ stackit mongodbflex user reset-password USER_ID --instance-id INSTANCE_ID", ), Example: examples.Build( examples.NewExample( From bbb60d3f4529be0df6c353e27ee18bcdb5d505c3 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Tue, 6 Feb 2024 14:47:17 +0000 Subject: [PATCH 18/19] Generate docs --- docs/stackit_mongodbflex_instance_create.md | 2 +- docs/stackit_mongodbflex_user_create.md | 2 +- docs/stackit_mongodbflex_user_describe.md | 2 +- docs/stackit_postgresflex_instance_create.md | 6 +++--- docs/stackit_postgresflex_instance_update.md | 2 +- docs/stackit_postgresflex_user.md | 14 +++++++------- docs/stackit_postgresflex_user_create.md | 6 +++--- docs/stackit_postgresflex_user_delete.md | 4 ++-- docs/stackit_postgresflex_user_describe.md | 6 +++--- docs/stackit_postgresflex_user_list.md | 4 ++-- docs/stackit_postgresflex_user_reset-password.md | 4 ++-- docs/stackit_postgresflex_user_update.md | 4 ++-- 12 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/stackit_mongodbflex_instance_create.md b/docs/stackit_mongodbflex_instance_create.md index effdd1011..8e9e4fd7e 100644 --- a/docs/stackit_mongodbflex_instance_create.md +++ b/docs/stackit_mongodbflex_instance_create.md @@ -35,7 +35,7 @@ stackit mongodbflex instance create [flags] --ram int Amount of RAM (in GB) --storage-class string Storage class (default "premium-perf2-mongodb") --storage-size int Storage size (in GB) (default 10) - --type string Instance type, one of ["Single" "Replica" "Sharded"] (default "Replica") + --type string Instance type, one of ["Sharded" "Single" "Replica"] (default "Replica") --version string Version (default "6.0") ``` diff --git a/docs/stackit_mongodbflex_user_create.md b/docs/stackit_mongodbflex_user_create.md index 832c4dfaa..40a6366ae 100644 --- a/docs/stackit_mongodbflex_user_create.md +++ b/docs/stackit_mongodbflex_user_create.md @@ -7,7 +7,7 @@ Creates a MongoDB Flex user Creates a MongoDB Flex user. The password is only visible upon creation and cannot be retrieved later. Alternatively, you can reset the password and access the new one by running: - $ stackit mongodbflex user reset-password --instance-id --user-id + $ stackit mongodbflex user reset-password USER_ID --instance-id INSTANCE_ID ``` stackit mongodbflex user create [flags] diff --git a/docs/stackit_mongodbflex_user_describe.md b/docs/stackit_mongodbflex_user_describe.md index 437efd93a..c155eb2a4 100644 --- a/docs/stackit_mongodbflex_user_describe.md +++ b/docs/stackit_mongodbflex_user_describe.md @@ -6,7 +6,7 @@ Shows details of a MongoDB Flex user Shows details of a MongoDB Flex user. The user password is hidden inside the "host" field and replaced with asterisks, as it is only visible upon creation. You can reset it by running: - $ stackit mongodbflex user reset-password --instance-id + $ stackit mongodbflex user reset-password USER_ID --instance-id INSTANCE_ID ``` stackit mongodbflex user describe USER_ID [flags] diff --git a/docs/stackit_postgresflex_instance_create.md b/docs/stackit_postgresflex_instance_create.md index c5f9fb0f7..5a9f24fcd 100644 --- a/docs/stackit_postgresflex_instance_create.md +++ b/docs/stackit_postgresflex_instance_create.md @@ -27,7 +27,7 @@ stackit postgresflex instance create [flags] ``` --acl strings The access control list (ACL). Must contain at least one valid subnet, for instance '0.0.0.0/0' for open access (discouraged), '1.2.3.0/24 for a public IP range of an organization, '1.2.3.4/32' for a single IP range, etc. (default []) - --backup-schedule string Backup schedule (default "0 0/6 * * *") + --backup-schedule string Backup schedule (default "0 0 * * *") --cpu int Number of CPUs --flavor-id string ID of the flavor -h, --help Help for "stackit postgresflex instance create" @@ -35,8 +35,8 @@ stackit postgresflex instance create [flags] --ram int Amount of RAM (in GB) --storage-class string Storage class (default "premium-perf2-stackit") --storage-size int Storage size (in GB) (default 10) - --type string Instance type, one of ["Single" "Replica" "Sharded"] (default "Replica") - --version string Version (default "6.0") + --type string Instance type, one of ["Single" "Replica"] (default "Replica") + --version string PostgreSQL version. Defaults to the latest version available ``` ### Options inherited from parent commands diff --git a/docs/stackit_postgresflex_instance_update.md b/docs/stackit_postgresflex_instance_update.md index 7196bd0b1..e924673e1 100644 --- a/docs/stackit_postgresflex_instance_update.md +++ b/docs/stackit_postgresflex_instance_update.md @@ -32,7 +32,7 @@ stackit postgresflex instance update INSTANCE_ID [flags] --ram int Amount of RAM (in GB) --storage-class string Storage class --storage-size int Storage size (in GB) - --type string Instance type, one of ["Single" "Replica" "Sharded"] + --type string Instance type, one of ["Replica" "Single"] --version string Version ``` diff --git a/docs/stackit_postgresflex_user.md b/docs/stackit_postgresflex_user.md index 15991ab23..e23ef514b 100644 --- a/docs/stackit_postgresflex_user.md +++ b/docs/stackit_postgresflex_user.md @@ -4,7 +4,7 @@ Provides functionality for PostgreSQL Flex users ### Synopsis -Provides functionality for PostgreSQL Flex users +Provides functionality for PostgreSQL Flex users. ``` stackit postgresflex user [flags] @@ -28,10 +28,10 @@ stackit postgresflex user [flags] ### SEE ALSO * [stackit postgresflex](./stackit_postgresflex.md) - Provides functionality for PostgreSQL Flex -* [stackit postgresflex user create](./stackit_postgresflex_user_create.md) - Create a PostgreSQL Flex user -* [stackit postgresflex user delete](./stackit_postgresflex_user_delete.md) - Delete a PostgreSQL Flex user -* [stackit postgresflex user describe](./stackit_postgresflex_user_describe.md) - Get details of a PostgreSQL Flex user -* [stackit postgresflex user list](./stackit_postgresflex_user_list.md) - List all PostgreSQL Flex users of an instance -* [stackit postgresflex user reset-password](./stackit_postgresflex_user_reset-password.md) - Reset the password of a PostgreSQL Flex user -* [stackit postgresflex user update](./stackit_postgresflex_user_update.md) - Update a PostgreSQL Flex user +* [stackit postgresflex user create](./stackit_postgresflex_user_create.md) - Creates a PostgreSQL Flex user +* [stackit postgresflex user delete](./stackit_postgresflex_user_delete.md) - Deletes a PostgreSQL Flex user +* [stackit postgresflex user describe](./stackit_postgresflex_user_describe.md) - Shows details of a PostgreSQL Flex user +* [stackit postgresflex user list](./stackit_postgresflex_user_list.md) - Lists all PostgreSQL Flex users of an instance +* [stackit postgresflex user reset-password](./stackit_postgresflex_user_reset-password.md) - Resets the password of a PostgreSQL Flex user +* [stackit postgresflex user update](./stackit_postgresflex_user_update.md) - Updates a PostgreSQL Flex user diff --git a/docs/stackit_postgresflex_user_create.md b/docs/stackit_postgresflex_user_create.md index e6832cc83..9ff69c806 100644 --- a/docs/stackit_postgresflex_user_create.md +++ b/docs/stackit_postgresflex_user_create.md @@ -1,13 +1,13 @@ ## stackit postgresflex user create -Create a PostgreSQL Flex user +Creates a PostgreSQL Flex user ### Synopsis -Create a PostgreSQL Flex user. +Creates a PostgreSQL Flex user. The password is only visible upon creation and cannot be retrieved later. Alternatively, you can reset the password and access the new one by running: - $ stackit postgresflex user reset-password --instance-id --user-id + $ stackit postgresflex user reset-password USER_ID --instance-id INSTANCE_ID ``` stackit postgresflex user create [flags] diff --git a/docs/stackit_postgresflex_user_delete.md b/docs/stackit_postgresflex_user_delete.md index 00c6e5596..d505538cb 100644 --- a/docs/stackit_postgresflex_user_delete.md +++ b/docs/stackit_postgresflex_user_delete.md @@ -1,10 +1,10 @@ ## stackit postgresflex user delete -Delete a PostgreSQL Flex user +Deletes a PostgreSQL Flex user ### Synopsis -Delete a PostgreSQL Flex user by ID. +Deletes a PostgreSQL Flex user by ID. You can get the IDs of users for an instance by running: $ stackit postgresflex user list --instance-id diff --git a/docs/stackit_postgresflex_user_describe.md b/docs/stackit_postgresflex_user_describe.md index e60d834fb..573bc9f57 100644 --- a/docs/stackit_postgresflex_user_describe.md +++ b/docs/stackit_postgresflex_user_describe.md @@ -1,12 +1,12 @@ ## stackit postgresflex user describe -Get details of a PostgreSQL Flex user +Shows details of a PostgreSQL Flex user ### Synopsis -Get details of a PostgreSQL Flex user. +Shows details of a PostgreSQL Flex user. The user password is hidden inside the "host" field and replaced with asterisks, as it is only visible upon creation. You can reset it by running: - $ stackit postgresflex user reset-password --instance-id + $ stackit postgresflex user reset-password USER_ID --instance-id INSTANCE_ID ``` stackit postgresflex user describe USER_ID [flags] diff --git a/docs/stackit_postgresflex_user_list.md b/docs/stackit_postgresflex_user_list.md index aee026faa..e1ee8a156 100644 --- a/docs/stackit_postgresflex_user_list.md +++ b/docs/stackit_postgresflex_user_list.md @@ -1,10 +1,10 @@ ## stackit postgresflex user list -List all PostgreSQL Flex users of an instance +Lists all PostgreSQL Flex users of an instance ### Synopsis -List all PostgreSQL Flex users of an instance. +Lists all PostgreSQL Flex users of an instance. ``` stackit postgresflex user list [flags] diff --git a/docs/stackit_postgresflex_user_reset-password.md b/docs/stackit_postgresflex_user_reset-password.md index 7d010c99a..58796eb49 100644 --- a/docs/stackit_postgresflex_user_reset-password.md +++ b/docs/stackit_postgresflex_user_reset-password.md @@ -1,10 +1,10 @@ ## stackit postgresflex user reset-password -Reset the password of a PostgreSQL Flex user +Resets the password of a PostgreSQL Flex user ### Synopsis -Reset the password of a PostgreSQL Flex user. The new password is returned in the response. +Resets the password of a PostgreSQL Flex user. The new password is returned in the response. ``` stackit postgresflex user reset-password USER_ID [flags] diff --git a/docs/stackit_postgresflex_user_update.md b/docs/stackit_postgresflex_user_update.md index 5f478ac28..d23f17020 100644 --- a/docs/stackit_postgresflex_user_update.md +++ b/docs/stackit_postgresflex_user_update.md @@ -1,10 +1,10 @@ ## stackit postgresflex user update -Update a PostgreSQL Flex user +Updates a PostgreSQL Flex user ### Synopsis -Update a PostgreSQL Flex user. +Updates a PostgreSQL Flex user. ``` stackit postgresflex user update USER_ID [flags] From 653ba4503e4efcd85d6e934c8d21f47c67bf2c41 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Tue, 6 Feb 2024 15:14:14 +0000 Subject: [PATCH 19/19] Fix test --- internal/cmd/mongodbflex/user/update/update_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/cmd/mongodbflex/user/update/update_test.go b/internal/cmd/mongodbflex/user/update/update_test.go index 53dbae081..90f6da2b9 100644 --- a/internal/cmd/mongodbflex/user/update/update_test.go +++ b/internal/cmd/mongodbflex/user/update/update_test.go @@ -257,13 +257,13 @@ func TestBuildRequest(t *testing.T) { expectedRequest: fixtureRequest(), }, { - description: "update database only", + description: "update roles only", model: fixtureInputModel(func(model *inputModel) { - model.Roles = nil - model.Database = utils.Ptr("default") + model.Database = nil + model.Roles = &[]string{"default"} }), expectedRequest: fixtureRequest().PartialUpdateUserPayload(mongodbflex.PartialUpdateUserPayload{ - Database: utils.Ptr("default"), + Roles: &[]string{"default"}, }), }, }