diff --git a/cli/pkg/cmd/cli.go b/cli/pkg/cmd/cli.go index 0d46dec5e..83f44c347 100644 --- a/cli/pkg/cmd/cli.go +++ b/cli/pkg/cmd/cli.go @@ -81,6 +81,8 @@ func RunCLI() ReturnCode { return handleDeleteAppLock(*kpClientParams, subflags) case "delete-team-lock": return handleDeleteTeamLock(*kpClientParams, subflags) + case "delete-group-lock": + return handleDeleteGroupLock(*kpClientParams, subflags) default: log.Printf("unknown subcommand %s\n", subcommand) return ReturnCodeInvalidArguments diff --git a/cli/pkg/cmd/handlers.go b/cli/pkg/cmd/handlers.go index e2031e477..f8c571c5b 100644 --- a/cli/pkg/cmd/handlers.go +++ b/cli/pkg/cmd/handlers.go @@ -123,6 +123,16 @@ func handleCreateGroupLock(kpClientParams kuberpultClientParameters, args []stri return handleLockRequest(kpClientParams, parsedArgs) } +func handleDeleteGroupLock(kpClientParams kuberpultClientParameters, args []string) ReturnCode { + parsedArgs, err := locks.ParseArgsDeleteGroupLock(args) + + if err != nil { + log.Printf("error while parsing command line args, error: %v", err) + return ReturnCodeInvalidArguments + } + return handleLockRequest(kpClientParams, parsedArgs) +} + func handleLockRequest(kpClientParams kuberpultClientParameters, parsedArgs locks.LockParameters) ReturnCode { authParams := kutil.AuthenticationParameters{ IapToken: kpClientParams.iapToken, diff --git a/cli/pkg/locks/group_lock_parsing.go b/cli/pkg/locks/group_lock_parsing.go index 345dc266b..b310f4001 100644 --- a/cli/pkg/locks/group_lock_parsing.go +++ b/cli/pkg/locks/group_lock_parsing.go @@ -75,7 +75,7 @@ func convertToCreateGroupLockParams(cmdArgs CreateEnvGroupLockCommandLineArgumen return nil, fmt.Errorf("the provided command line arguments structure is invalid, cause: %s", msg) } - rp := EnvironmentGroupLockParameters{ + rp := CreateEnvironmentGroupLockParameters{ LockId: cmdArgs.lockId.Values[0], EnvironmentGroup: cmdArgs.environmentGroup.Values[0], UseDexAuthentication: false, //For now there is no ambiguity as to which endpoint to use @@ -99,3 +99,71 @@ func ParseArgsCreateGroupLock(args []string) (LockParameters, error) { return rp, nil } + +type DeleteEnvGroupLockCommandLineArguments struct { + environmentGroup cli_utils.RepeatedString + lockId cli_utils.RepeatedString +} + +func argsValidDeleteEnvGroupLock(cmdArgs *DeleteEnvGroupLockCommandLineArguments) (result bool, errorMessage string) { + if len(cmdArgs.lockId.Values) != 1 { + return false, "the --lockID arg must be set exactly once" + } + if len(cmdArgs.environmentGroup.Values) != 1 { + return false, "the --environment-group arg must be set exactly once" + } + + return true, "" +} + +// takes the raw command line flags and converts them to an intermediate represnetations for easy validation +func readDeleteGroupLockArgs(args []string) (*DeleteEnvGroupLockCommandLineArguments, error) { + cmdArgs := DeleteEnvGroupLockCommandLineArguments{} //exhaustruct:ignore + + fs := flag.NewFlagSet("flag set", flag.ContinueOnError) + + fs.Var(&cmdArgs.environmentGroup, "environment-group", "the environment-group of the lock you are trying to delete") + fs.Var(&cmdArgs.lockId, "lockID", "the ID of the lock you are trying to delete") + + if err := fs.Parse(args); err != nil { + return nil, fmt.Errorf("error while parsing command line arguments, error: %w", err) + } + + if len(fs.Args()) != 0 { // kuberpult-cli release does not accept any positional arguments, so this is an error + return nil, fmt.Errorf("these arguments are not recognized: \"%v\"", strings.Join(fs.Args(), " ")) + } + + if ok, msg := argsValidDeleteEnvGroupLock(&cmdArgs); !ok { + return nil, fmt.Errorf(msg) + } + + return &cmdArgs, nil +} + +// converts the intermediate representation of the command line flags into the final structure containing parameters for the create lock endpoint +func convertToDeleteGroupLockParams(cmdArgs DeleteEnvGroupLockCommandLineArguments) (LockParameters, error) { + if ok, msg := argsValidDeleteEnvGroupLock(&cmdArgs); !ok { + // this should never happen, as the validation is already performed by the readArgs function + return nil, fmt.Errorf("the provided command line arguments structure is invalid, cause: %s", msg) + } + + rp := DeleteEnvironmentGroupLockParameters{ + LockId: cmdArgs.lockId.Values[0], + EnvironmentGroup: cmdArgs.environmentGroup.Values[0], + UseDexAuthentication: false, //For now there is no ambiguity as to which endpoint to use + } + return &rp, nil +} + +func ParseArgsDeleteGroupLock(args []string) (LockParameters, error) { + cmdArgs, err := readDeleteGroupLockArgs(args) + if err != nil { + return nil, fmt.Errorf("error while reading command line arguments for deleting an environment group lock, error: %w", err) + } + rp, err := convertToDeleteGroupLockParams(*cmdArgs) + if err != nil { + return nil, fmt.Errorf("error while creating parameters for deleting an environment group lock, error: %w", err) + } + + return rp, nil +} diff --git a/cli/pkg/locks/group_lock_parsing_test.go b/cli/pkg/locks/group_lock_parsing_test.go index 6e5ce1621..949674f85 100644 --- a/cli/pkg/locks/group_lock_parsing_test.go +++ b/cli/pkg/locks/group_lock_parsing_test.go @@ -127,6 +127,86 @@ func TestReadGroupLockArgs(t *testing.T) { } } +func TestReadDeleteGroupLockArgs(t *testing.T) { + type testCase struct { + name string + args []string + expectedCmdArgs *DeleteEnvGroupLockCommandLineArguments + expectedError error + } + + tcs := []testCase{ + { + name: "some unrecognized positional arguments", + args: []string{"potato", "tomato"}, + expectedError: errMatcher{ + msg: "these arguments are not recognized: \"potato tomato\"", + }, + }, + { + name: "some flags that don't exist", + args: []string{"--environment-group", "development", "--potato", "tomato"}, + expectedError: errMatcher{ + msg: "error while parsing command line arguments, error: flag provided but not defined: -potato", + }, + }, + { + name: "nothing provided", + args: []string{}, + expectedError: errMatcher{ + msg: "the --lockID arg must be set exactly once", + }, + }, + { + name: "only --environment-group is properly provided but without --lockID", + args: []string{"--environment-group", "potato"}, + expectedError: errMatcher{ + msg: "the --lockID arg must be set exactly once", + }, + }, + { + name: "only --lockID is properly provided but without --environment-group", + args: []string{"--lockID", "potato"}, + expectedError: errMatcher{ + msg: "the --environment-group arg must be set exactly once", + }, + }, + { + name: "--environment-group and lockID", + args: []string{"--environment-group", "development", "--lockID", "my-lock"}, + expectedCmdArgs: &DeleteEnvGroupLockCommandLineArguments{ + environmentGroup: cli_utils.RepeatedString{ + Values: []string{ + "development", + }, + }, + lockId: cli_utils.RepeatedString{ + Values: []string{ + "my-lock", + }, + }, + }, + }, + } + + for _, tc := range tcs { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + cmdArgs, err := readDeleteGroupLockArgs(tc.args) + // check errors + if diff := cmp.Diff(tc.expectedError, err, cmpopts.EquateErrors()); diff != "" { + t.Fatalf("error mismatch (-want, +got):\n%s", diff) + } + + if diff := cmp.Diff(cmdArgs, tc.expectedCmdArgs, cmp.AllowUnexported(DeleteEnvGroupLockCommandLineArguments{})); diff != "" { + t.Fatalf("expected args:\n %v\n, got:\n %v, diff:\n %s\n", tc.expectedCmdArgs, cmdArgs, diff) + } + }) + } +} + func TestParseEnvGroupArgs(t *testing.T) { type testCase struct { name string @@ -139,7 +219,7 @@ func TestParseEnvGroupArgs(t *testing.T) { { name: "with environment and lockID and message", cmdArgs: []string{"--environment-group", "development", "--lockID", "my-lock", "--message", "message"}, - expectedParams: &EnvironmentGroupLockParameters{ + expectedParams: &CreateEnvironmentGroupLockParameters{ EnvironmentGroup: "development", LockId: "my-lock", Message: "message", @@ -148,7 +228,7 @@ func TestParseEnvGroupArgs(t *testing.T) { { name: "with environment and lockID and no message", cmdArgs: []string{"--environment-group", "development", "--lockID", "my-lock"}, - expectedParams: &EnvironmentGroupLockParameters{ + expectedParams: &CreateEnvironmentGroupLockParameters{ EnvironmentGroup: "development", LockId: "my-lock", Message: "", @@ -158,7 +238,7 @@ func TestParseEnvGroupArgs(t *testing.T) { { name: "with environment and lockID and multi word message message", cmdArgs: []string{"--environment-group", "development", "--lockID", "my-lock", "--message", "this is a very long message"}, - expectedParams: &EnvironmentGroupLockParameters{ + expectedParams: &CreateEnvironmentGroupLockParameters{ EnvironmentGroup: "development", LockId: "my-lock", Message: "this is a very long message", @@ -184,3 +264,41 @@ func TestParseEnvGroupArgs(t *testing.T) { }) } } + +func TestParseDeleteEnvGroupArgs(t *testing.T) { + type testCase struct { + name string + cmdArgs []string + expectedParams LockParameters + expectedError error + } + + tcs := []testCase{ + { + name: "with environment groups and lockID", + cmdArgs: []string{"--environment-group", "development", "--lockID", "my-lock"}, + expectedParams: &DeleteEnvironmentGroupLockParameters{ + EnvironmentGroup: "development", + LockId: "my-lock", + }, + }, + } + + for _, tc := range tcs { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + params, err := ParseArgsDeleteGroupLock(tc.cmdArgs) + // check errors + if diff := cmp.Diff(tc.expectedError, err, cmpopts.EquateErrors()); diff != "" { + t.Fatalf("error mismatch (-want, +got):\n%s", diff) + } + + // check result + if diff := cmp.Diff(tc.expectedParams, params); diff != "" { + t.Fatalf("expected args:\n %v\n, got:\n %v\n, diff:\n %s\n", tc.expectedParams, params, diff) + } + }) + } +} diff --git a/cli/pkg/locks/lock.go b/cli/pkg/locks/lock.go index f69e10e2c..1d0834b17 100644 --- a/cli/pkg/locks/lock.go +++ b/cli/pkg/locks/lock.go @@ -73,12 +73,17 @@ type DeleteTeamLockParameters struct { UseDexAuthentication bool } -type EnvironmentGroupLockParameters struct { +type CreateEnvironmentGroupLockParameters struct { EnvironmentGroup string LockId string Message string UseDexAuthentication bool } +type DeleteEnvironmentGroupLockParameters struct { + EnvironmentGroup string + LockId string + UseDexAuthentication bool +} type LockJsonData struct { Message string `json:"message"` @@ -111,11 +116,11 @@ func (e *CreateEnvironmentLockParameters) FillHttpInfo() (*HttpInfo, error) { d := LockJsonData{ Message: e.Message, } + var jsonData, err = json.Marshal(d) if err != nil { return nil, fmt.Errorf("Could not EnvironmentLockParameters data to json: %w\n", err) } - prefix := "environments" if e.UseDexAuthentication { prefix = "api/environments" @@ -207,13 +212,13 @@ func (e *DeleteTeamLockParameters) FillHttpInfo() (*HttpInfo, error) { }, nil } -func (e *EnvironmentGroupLockParameters) FillHttpInfo() (*HttpInfo, error) { +func (e *CreateEnvironmentGroupLockParameters) FillHttpInfo() (*HttpInfo, error) { d := LockJsonData{ Message: e.Message, } var jsonData, err = json.Marshal(d) if err != nil { - return nil, fmt.Errorf("Could not marshal EnvironmentGroupLockParameters data to json: %w\n", err) + return nil, fmt.Errorf("Could not marshal CreateEnvironmentGroupLockParameters data to json: %w\n", err) } prefix := "environment-groups" if e.UseDexAuthentication { @@ -227,6 +232,19 @@ func (e *EnvironmentGroupLockParameters) FillHttpInfo() (*HttpInfo, error) { }, nil } +func (e *DeleteEnvironmentGroupLockParameters) FillHttpInfo() (*HttpInfo, error) { + prefix := "environment-groups" + if e.UseDexAuthentication { + prefix = "api/environment-groups" + } + return &HttpInfo{ + jsonData: []byte{}, + ContentType: "application/json", + HttpMethod: http.MethodDelete, + RestPath: fmt.Sprintf("%s/%s/locks/%s", prefix, e.EnvironmentGroup, e.LockId), + }, nil +} + func createHttpRequest(url string, authParams kutil.AuthenticationParameters, requestInfo *HttpInfo) (*http.Request, error) { urlStruct, err := urllib.Parse(url) if err != nil {