Skip to content

Commit

Permalink
feat(cli): cli create app locks (#1886)
Browse files Browse the repository at this point in the history
App lock creation on CLI.

Ref: SRX-QMIIYB

---------

Co-authored-by: Sven Urbanski <[email protected]>
  • Loading branch information
1 parent 0b0c8e3 commit 77403eb
Show file tree
Hide file tree
Showing 5 changed files with 344 additions and 4 deletions.
2 changes: 2 additions & 0 deletions cli/pkg/cmd/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ func RunCLI() ReturnCode {
return handleRelease(*kpClientParams, subflags)
case "create-env-lock":
return handleCreateEnvLock(*kpClientParams, subflags)
case "create-app-lock":
return handleCreateAppLock(*kpClientParams, subflags)
default:
log.Printf("unknown subcommand %s\n", subcommand)
return ReturnCodeInvalidArguments
Expand Down
10 changes: 10 additions & 0 deletions cli/pkg/cmd/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ func handleCreateEnvLock(kpClientParams kuberpultClientParameters, args []string
return handleCreateLock(kpClientParams, parsedArgs)
}

func handleCreateAppLock(kpClientParams kuberpultClientParameters, args []string) ReturnCode {
parsedArgs, err := locks.ParseArgsCreateAppLock(args)

if err != nil {
log.Printf("error while parsing command line args, error: %v", err)
return ReturnCodeInvalidArguments
}
return handleCreateLock(kpClientParams, parsedArgs)
}

func handleCreateLock(kpClientParams kuberpultClientParameters, parsedArgs locks.LockParameters) ReturnCode {
authParams := kutil.AuthenticationParameters{
IapToken: kpClientParams.iapToken,
Expand Down
106 changes: 106 additions & 0 deletions cli/pkg/locks/app_lock_parsing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*This file is part of kuberpult.
Kuberpult is free software: you can redistribute it and/or modify
it under the terms of the Expat(MIT) License as published by
the Free Software Foundation.
Kuberpult is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
MIT License for more details.
You should have received a copy of the MIT License
along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
Copyright freiheit.com*/

package locks

import (
"flag"
"fmt"
"strings"

"github.com/freiheit-com/kuberpult/cli/pkg/cli_utils"
)

type CreateAppLockCommandLineArguments struct {
environment cli_utils.RepeatedString
lockId cli_utils.RepeatedString
message cli_utils.RepeatedString
application cli_utils.RepeatedString
useDexAuthentication bool
}

func argsValidCreateAppLock(cmdArgs *CreateAppLockCommandLineArguments) (result bool, errorMessage string) {
if len(cmdArgs.lockId.Values) != 1 {
return false, "the --lockID arg must be set exactly once"
}
if len(cmdArgs.environment.Values) != 1 {
return false, "the --environment arg must be set exactly once"
}
if len(cmdArgs.application.Values) != 1 {
return false, "the --application arg must be set exactly once"
}
if len(cmdArgs.message.Values) > 1 {
return false, "the --message arg must be set at most once"
}

return true, ""
}

func readCreateAppLockArgs(args []string) (*CreateAppLockCommandLineArguments, error) {
cmdArgs := CreateAppLockCommandLineArguments{} //exhaustruct:ignore

fs := flag.NewFlagSet("flag set", flag.ContinueOnError)

fs.Var(&cmdArgs.lockId, "lockID", "the ID of the lock you are trying to create")
fs.Var(&cmdArgs.environment, "environment", "the environment to lock")
fs.Var(&cmdArgs.message, "message", "lock message")
fs.Var(&cmdArgs.application, "application", "application to lock")
fs.BoolVar(&cmdArgs.useDexAuthentication, "use_dex_auth", false, "if set to true, the /api/* endpoint will be used. Dex must be enabled on the server side and a dex token must be provided, otherwise the request will be denied")

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 := argsValidCreateAppLock(&cmdArgs); !ok {
return nil, fmt.Errorf(msg)
}

return &cmdArgs, nil
}

func convertToCreateAppLockParams(cmdArgs CreateAppLockCommandLineArguments) (LockParameters, error) {
if ok, msg := argsValidCreateAppLock(&cmdArgs); !ok {
return nil, fmt.Errorf("the provided command line arguments structure is invalid, cause: %s", msg)
}

rp := AppLockParameters{
LockId: cmdArgs.lockId.Values[0],
Environment: cmdArgs.environment.Values[0],
Application: cmdArgs.application.Values[0],
Message: "",
UseDexAuthentication: cmdArgs.useDexAuthentication,
}
if len(cmdArgs.message.Values) != 0 {
rp.Message = cmdArgs.message.Values[0]
}
return &rp, nil
}

func ParseArgsCreateAppLock(args []string) (LockParameters, error) {
cmdArgs, err := readCreateAppLockArgs(args)
if err != nil {
return nil, fmt.Errorf("error while reading command line arguments for creating an app lock, error: %w", err)
}
rp, err := convertToCreateAppLockParams(*cmdArgs)
if err != nil {
return nil, fmt.Errorf("error while creating parameters for creating an application lock, error: %w", err)
}
return rp, nil
}
191 changes: 191 additions & 0 deletions cli/pkg/locks/app_lock_parsing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*This file is part of kuberpult.
Kuberpult is free software: you can redistribute it and/or modify
it under the terms of the Expat(MIT) License as published by
the Free Software Foundation.
Kuberpult is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
MIT License for more details.
You should have received a copy of the MIT License
along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
Copyright freiheit.com*/

package locks

import (
"testing"

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

"github.com/freiheit-com/kuberpult/cli/pkg/cli_utils"
)

func TestReadArgsAppLock(t *testing.T) {
type testCase struct {
name string
args []string
expectedCmdArgs *CreateAppLockCommandLineArguments
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", "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: "lockID is not provided",
args: []string{"--environment", "development", "--application", "my-app", "--message", "\"my message\""},
expectedError: errMatcher{
msg: "the --lockID arg must be set exactly once",
},
},
{
name: "environment is not provided",
args: []string{"--application", "my-app", "--lockID", "my-lock", "--message", "\"my message\""},
expectedError: errMatcher{
msg: "the --environment arg must be set exactly once",
},
},
{
name: "application is not provided",
args: []string{"--environment", "development", "--lockID", "my-lock", "--message", "\"my message\""},
expectedError: errMatcher{
msg: "the --application arg must be set exactly once",
},
},
{
name: "only --lockID is properly provided but without --environment",
args: []string{"--lockID", "potato"},
expectedError: errMatcher{
msg: "the --environment arg must be set exactly once",
},
},
{
name: "environment, lockID, application and message are are specified",
args: []string{"--environment", "development", "--application", "my-app", "--lockID", "my-lock", "--message", "\"my message\""},
expectedCmdArgs: &CreateAppLockCommandLineArguments{
environment: cli_utils.RepeatedString{
Values: []string{
"development",
},
},
lockId: cli_utils.RepeatedString{
Values: []string{
"my-lock",
},
},
message: cli_utils.RepeatedString{
Values: []string{
"\"my message\"",
},
},
application: cli_utils.RepeatedString{
Values: []string{
"my-app",
},
},
},
},
}

for _, tc := range tcs {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

cmdArgs, err := readCreateAppLockArgs(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(CreateAppLockCommandLineArguments{})); diff != "" {
t.Fatalf("expected args:\n %v\n, got:\n %v, diff:\n %s\n", tc.expectedCmdArgs, cmdArgs, diff)
}
})
}
}

func TestParseArgsCreateAppLock(t *testing.T) {
type testCase struct {
name string
cmdArgs []string
expectedParams LockParameters
expectedError error
}

tcs := []testCase{
{
name: "with environment and lockID and message",
cmdArgs: []string{"--environment", "development", "--application", "my-app", "--lockID", "my-lock", "--message", "message"},
expectedParams: &AppLockParameters{
Environment: "development",
LockId: "my-lock",
Message: "message",
Application: "my-app",
},
},
{
name: "with environment, app and lockID and no message",
cmdArgs: []string{"--environment", "development", "--application", "my-app", "--lockID", "my-lock"},
expectedParams: &AppLockParameters{
Environment: "development",
LockId: "my-lock",
Message: "",
Application: "my-app",
},
},

{
name: "with environment and lockID and multi word message message",
cmdArgs: []string{"--environment", "development", "--application", "my-app", "--lockID", "my-lock", "--message", "this is a very long message"},
expectedParams: &AppLockParameters{
Environment: "development",
LockId: "my-lock",
Application: "my-app",
Message: "this is a very long message",
},
},
}

for _, tc := range tcs {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

params, err := ParseArgsCreateAppLock(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)
}
})
}
}
Loading

0 comments on commit 77403eb

Please sign in to comment.