diff --git a/cmd/bucketpolicy/add/add.go b/cmd/bucketpolicy/add/add.go index 6d84d1a..5e4a655 100644 --- a/cmd/bucketpolicy/add/add.go +++ b/cmd/bucketpolicy/add/add.go @@ -7,11 +7,13 @@ import ( "os" "strings" + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" + "github.com/aws/aws-sdk-go/service/s3/s3iface" "github.com/bilalcaliskan/s3-manager/cmd/bucketpolicy/options" - "github.com/bilalcaliskan/s3-manager/cmd/bucketpolicy/utils" "github.com/bilalcaliskan/s3-manager/internal/aws" "github.com/bilalcaliskan/s3-manager/internal/prompt" + "github.com/bilalcaliskan/s3-manager/internal/utils" "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -28,29 +30,21 @@ var ( AddCmd = &cobra.Command{ Use: "add", Short: "adds a bucket policy configuration for the target bucket by specifying a valid policy file", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# add a bucket policy configuration onto target bucket s3-manager bucketpolicy add my_custom_policy.json `, RunE: func(cmd *cobra.Command, args []string) (err error) { - svc, bucketPolicyOpts, logger = utils.PrepareConstants(cmd, options.GetBucketPolicyOptions()) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + bucketPolicyOpts.RootOptions = rootOpts - if len(args) < 1 { - err = errors.New("no argument provided, you should provide bucket policy path on your filesystem") - logger.Error().Msg(err.Error()) - return err - } else if len(args) > 1 { - err = errors.New("too many argument provided, just provide bucket policy path on your filesystem") + if err := utils.CheckArgs(args, 1); err != nil { logger.Error().Msg(err.Error()) return err } - /*if err := utils.CheckArgs(cmd, args); err != nil { - logger.Error().Msg(err.Error()) - return err - }*/ - logger = logger.With().Str("policyFilePath", args[0]).Logger() logger.Info().Msg("trying to read target policy file") diff --git a/cmd/bucketpolicy/bucketpolicy.go b/cmd/bucketpolicy/bucketpolicy.go index 696842c..1a260fe 100644 --- a/cmd/bucketpolicy/bucketpolicy.go +++ b/cmd/bucketpolicy/bucketpolicy.go @@ -18,7 +18,7 @@ var ( BucketPolicyCmd = &cobra.Command{ Use: "bucketpolicy", Short: "shows/sets the bucket policy configuration of the target bucket", - SilenceUsage: true, - SilenceErrors: true, + SilenceUsage: false, + SilenceErrors: false, } ) diff --git a/cmd/bucketpolicy/remove/remove.go b/cmd/bucketpolicy/remove/remove.go index 3e1eeda..0eb6596 100644 --- a/cmd/bucketpolicy/remove/remove.go +++ b/cmd/bucketpolicy/remove/remove.go @@ -1,13 +1,17 @@ package remove import ( - "errors" "fmt" "strings" + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" + "github.com/aws/aws-sdk-go/service/s3/s3iface" - options "github.com/bilalcaliskan/s3-manager/cmd/bucketpolicy/options" - "github.com/bilalcaliskan/s3-manager/cmd/bucketpolicy/utils" + "github.com/bilalcaliskan/s3-manager/cmd/bucketpolicy/options" + "github.com/bilalcaliskan/s3-manager/internal/utils" + + "github.com/bilalcaliskan/s3-manager/internal/constants" + "github.com/bilalcaliskan/s3-manager/internal/aws" "github.com/bilalcaliskan/s3-manager/internal/prompt" "github.com/rs/zerolog" @@ -27,15 +31,17 @@ var ( RemoveCmd = &cobra.Command{ Use: "remove", Short: "removes the current bucket policy configuration of the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# remove the current bucket policy configuration onto target bucket s3-manager bucketpolicy remove `, RunE: func(cmd *cobra.Command, args []string) (err error) { - svc, bucketPolicyOpts, logger = utils.PrepareConstants(cmd, options.GetBucketPolicyOptions()) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + bucketPolicyOpts.RootOptions = rootOpts - if err := utils.CheckArgs(args); err != nil { + if err := utils.CheckArgs(args, 0); err != nil { logger.Error(). Msg(err.Error()) return err @@ -62,11 +68,11 @@ s3-manager bucketpolicy remove if !bucketPolicyOpts.AutoApprove { var res string if res, err = confirmRunner.Run(); err != nil { - return err - } + if strings.ToLower(res) == "n" { + return constants.ErrUserTerminated + } - if strings.ToLower(res) == "n" { - return errors.New("user terminated the process") + return constants.ErrInvalidInput } } diff --git a/cmd/bucketpolicy/remove/remove_test.go b/cmd/bucketpolicy/remove/remove_test.go index 2204e4f..7a4a885 100644 --- a/cmd/bucketpolicy/remove/remove_test.go +++ b/cmd/bucketpolicy/remove/remove_test.go @@ -7,6 +7,8 @@ import ( "errors" "testing" + "github.com/bilalcaliskan/s3-manager/internal/constants" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3/s3iface" @@ -145,7 +147,7 @@ func TestExecuteRemoveCmd(t *testing.T) { nil, &s3.DeleteBucketPolicyOutput{}, &promptMock{ msg: "n", - err: nil, + err: constants.ErrInjected, }, false, false, }, {"Failure caused by prompt error", []string{}, false, true, @@ -155,7 +157,7 @@ func TestExecuteRemoveCmd(t *testing.T) { nil, &s3.DeleteBucketPolicyOutput{}, &promptMock{ msg: "nasdfadf", - err: errors.New("injected error"), + err: constants.ErrInjected, }, false, false, }, } diff --git a/cmd/bucketpolicy/show/show.go b/cmd/bucketpolicy/show/show.go index 8cae1f6..2fb535b 100644 --- a/cmd/bucketpolicy/show/show.go +++ b/cmd/bucketpolicy/show/show.go @@ -3,7 +3,9 @@ package show import ( "fmt" - "github.com/bilalcaliskan/s3-manager/cmd/bucketpolicy/utils" + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" + + "github.com/bilalcaliskan/s3-manager/internal/utils" options2 "github.com/bilalcaliskan/s3-manager/cmd/bucketpolicy/options" "github.com/bilalcaliskan/s3-manager/internal/aws" @@ -24,15 +26,17 @@ var ( ShowCmd = &cobra.Command{ Use: "show", Short: "shows the bucket policy configuration of the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# show the current bucket policy configuration for target bucket s3-manager bucketpolicy show `, RunE: func(cmd *cobra.Command, args []string) (err error) { - svc, bucketPolicyOpts, logger = utils.PrepareConstants(cmd, options2.GetBucketPolicyOptions()) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + bucketPolicyOpts.RootOptions = rootOpts - if err := utils.CheckArgs(args); err != nil { + if err := utils.CheckArgs(args, 0); err != nil { logger.Error(). Msg(err.Error()) return err diff --git a/cmd/bucketpolicy/utils/utils.go b/cmd/bucketpolicy/utils/utils.go deleted file mode 100644 index bcc79d8..0000000 --- a/cmd/bucketpolicy/utils/utils.go +++ /dev/null @@ -1,32 +0,0 @@ -package utils - -import ( - "errors" - - "github.com/aws/aws-sdk-go/service/s3/s3iface" - "github.com/bilalcaliskan/s3-manager/cmd/bucketpolicy/options" - rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" - "github.com/bilalcaliskan/s3-manager/internal/logging" - "github.com/rs/zerolog" - "github.com/spf13/cobra" -) - -const errTooManyArgsProvided = "too many arguments provided" - -func CheckArgs(args []string) error { - if len(args) > 0 { - return errors.New(errTooManyArgsProvided) - } - - return nil -} - -func PrepareConstants(cmd *cobra.Command, opts *options.BucketPolicyOptions) (s3iface.S3API, *options.BucketPolicyOptions, zerolog.Logger) { - svc := cmd.Context().Value(rootopts.S3SvcKey{}).(s3iface.S3API) - rootOpts := cmd.Context().Value(rootopts.OptsKey{}).(*rootopts.RootOptions) - opts.RootOptions = rootOpts - - logger := logging.GetLogger(opts.RootOptions) - - return svc, opts, logger -} diff --git a/cmd/bucketpolicy/utils/utils_test.go b/cmd/bucketpolicy/utils/utils_test.go deleted file mode 100644 index 0ca9e31..0000000 --- a/cmd/bucketpolicy/utils/utils_test.go +++ /dev/null @@ -1,72 +0,0 @@ -//go:build unit - -package utils - -import ( - "context" - "errors" - "testing" - - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3iface" - "github.com/bilalcaliskan/s3-manager/cmd/bucketpolicy/options" - rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" - internalaws "github.com/bilalcaliskan/s3-manager/internal/aws" - "github.com/rs/zerolog" - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" -) - -func createSvc(rootOpts *rootopts.RootOptions) (*s3.S3, error) { - return internalaws.CreateAwsService(rootOpts) -} - -func TestCheckArgs(t *testing.T) { - tests := []struct { - name string - args []string - expected error - }{ - { - name: "Failure caused by too many arguments", - args: []string{"foo", "bar"}, - expected: errors.New(errTooManyArgsProvided), - }, - { - name: "Success", - args: []string{}, - expected: nil, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := CheckArgs(test.args) - assert.Equal(t, test.expected, err) - }) - } -} - -func TestPrepareConstants(t *testing.T) { - var ( - svc s3iface.S3API - bucketPolicyOpts *options.BucketPolicyOptions - logger zerolog.Logger - ) - - cmd := &cobra.Command{} - cmd.SetContext(context.Background()) - - rootOpts := rootopts.GetMockedRootOptions() - svc, err := createSvc(rootOpts) - assert.NotNil(t, svc) - assert.Nil(t, err) - - cmd.SetContext(context.WithValue(context.Background(), rootopts.OptsKey{}, rootOpts)) - cmd.SetContext(context.WithValue(cmd.Context(), rootopts.S3SvcKey{}, svc)) - - svc, bucketPolicyOpts, logger = PrepareConstants(cmd, options.GetBucketPolicyOptions()) - assert.NotNil(t, svc) - assert.NotNil(t, bucketPolicyOpts) - assert.NotNil(t, logger) -} diff --git a/cmd/clean/clean.go b/cmd/clean/clean.go index c18c646..2b8f0c4 100644 --- a/cmd/clean/clean.go +++ b/cmd/clean/clean.go @@ -1,7 +1,6 @@ package clean import ( - "errors" "fmt" "github.com/aws/aws-sdk-go/service/s3/s3iface" @@ -27,18 +26,12 @@ var ( ValidSortByOpts = []string{"size", "lastModificationDate"} cleanOpts *options.CleanOptions svc s3iface.S3API - promptRunner prompt.PromptRunner = prompt.GetPromptRunner("Delete Files? (y/N)", true, func(s string) error { - if len(s) == 1 { - return nil - } - - return errors.New("invalid input") - }) + confirmRunner prompt.PromptRunner = prompt.GetConfirmRunner() // CleanCmd represents the clean command CleanCmd = &cobra.Command{ Use: "clean", Short: "finds and clears desired files by a pre-configured rule set", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# clean the desired files on target bucket s3-manager clean --min-size-mb=1 --max-size-mb=1000 --keep-last-n-files=2 --sort-by=lastModificationDate @@ -50,6 +43,11 @@ s3-manager clean --min-size-mb=1 --max-size-mb=1000 --keep-last-n-files=2 --sort cleanOpts.RootOptions = rootOpts logger = logging.GetLogger(rootOpts) + if err := utils.CheckArgs(args, 0); err != nil { + logger.Error().Msg(err.Error()) + return err + } + if cleanOpts.MinFileSizeInMb > cleanOpts.MaxFileSizeInMb && (cleanOpts.MinFileSizeInMb != 0 && cleanOpts.MaxFileSizeInMb != 0) { err := fmt.Errorf("flag '--min-size-mb' must be equal or lower than '--max-size-mb'") logger.Error().Str("error", err.Error()).Msg("an error occured while validating flags") @@ -65,7 +63,7 @@ s3-manager clean --min-size-mb=1 --max-size-mb=1000 --keep-last-n-files=2 --sort logger.Info().Msg("trying to search files on target bucket") - if err := cleaner.StartCleaning(svc, promptRunner, cleanOpts, logger); err != nil { + if err := cleaner.StartCleaning(svc, confirmRunner, cleanOpts, logger); err != nil { logger.Error().Str("error", err.Error()).Msg("an error occurred while cleaning") return err } diff --git a/cmd/clean/options/options.go b/cmd/clean/options/options.go index a996e7a..56a30ba 100644 --- a/cmd/clean/options/options.go +++ b/cmd/clean/options/options.go @@ -14,8 +14,6 @@ type CleanOptions struct { FileExtensions string FileNamePrefix string KeepLastNFiles int - DryRun bool - AutoApprove bool SortBy string *options.RootOptions } @@ -33,9 +31,6 @@ func (opts *CleanOptions) InitFlags(cmd *cobra.Command) { "defines how many of the files to skip deletion in specified criteria, 0 means clean them all") cmd.Flags().StringVarP(&opts.SortBy, "sort-by", "", "lastModificationDate", "defines the ascending order in the specified criteria, valid options are \"lastModificationDate\" and \"size\"") - cmd.Flags().BoolVarP(&opts.AutoApprove, "auto-approve", "", false, "Skip interactive approval (default false)") - cmd.Flags().BoolVarP(&opts.DryRun, "dry-run", "", false, "specifies that if you "+ - "just want to see what to delete or completely delete them all (default false)") } // GetCleanOptions returns the pointer of CleanOptions @@ -48,8 +43,6 @@ func (opts *CleanOptions) SetZeroValues() { opts.MaxFileSizeInMb = 0 opts.FileExtensions = "" opts.KeepLastNFiles = 2 - opts.DryRun = false - opts.AutoApprove = false opts.SortBy = "lastModificationDate" opts.RootOptions.SetZeroValues() } diff --git a/cmd/root/root.go b/cmd/root/root.go index aec51fa..ebf57dd 100644 --- a/cmd/root/root.go +++ b/cmd/root/root.go @@ -46,10 +46,12 @@ var ( ver = version.Get() logger zerolog.Logger rootCmd = &cobra.Command{ - Use: "s3-manager", - Short: "configure subcommand configures the bucket level settings", - Long: ``, - Version: ver.GitVersion, + Use: "s3-manager", + Short: "configure subcommand configures the bucket level settings", + Long: ``, + Version: ver.GitVersion, + SilenceUsage: false, + SilenceErrors: false, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { // TODO: uncomment when interactivity enabled again /*if !opts.Interactive { diff --git a/cmd/search/file/file.go b/cmd/search/file/file.go index 1de6ac9..c76e7ec 100644 --- a/cmd/search/file/file.go +++ b/cmd/search/file/file.go @@ -3,7 +3,9 @@ package file import ( "fmt" - "github.com/bilalcaliskan/s3-manager/cmd/search/utils" + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" + + "github.com/bilalcaliskan/s3-manager/internal/utils" "github.com/aws/aws-sdk-go/service/s3/s3iface" "github.com/bilalcaliskan/s3-manager/cmd/search/options" @@ -14,7 +16,6 @@ import ( func init() { searchOpts = options.GetSearchOptions() - //searchOpts.InitFlags(FileCmd) } var ( @@ -24,15 +25,17 @@ var ( FileCmd = &cobra.Command{ Use: "file", Short: "searches the files which has desired file name pattern in it (supports regex)", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# search a file on target bucket by specifying regex for files s3-manager search file ".*.json" `, PreRunE: func(cmd *cobra.Command, args []string) error { - svc, searchOpts, logger = utils.PrepareConstants(cmd, options.GetSearchOptions()) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + searchOpts.RootOptions = rootOpts - if err := utils.CheckFlags(args); err != nil { + if err := utils.CheckArgs(args, 1); err != nil { logger.Error().Msg(err.Error()) return err } diff --git a/cmd/search/search.go b/cmd/search/search.go index d06c7ec..1085db6 100644 --- a/cmd/search/search.go +++ b/cmd/search/search.go @@ -17,6 +17,7 @@ var ( Use: "search", Short: "searches the files which has desired substrings in it", // we should not define PreRunE since it will override the PreRunE which is inherited from RootCmd - SilenceUsage: true, + SilenceUsage: false, + SilenceErrors: false, } ) diff --git a/cmd/search/text/text.go b/cmd/search/text/text.go index b62ee9a..a1c0e70 100644 --- a/cmd/search/text/text.go +++ b/cmd/search/text/text.go @@ -3,7 +3,9 @@ package text import ( "fmt" - "github.com/bilalcaliskan/s3-manager/cmd/search/utils" + "github.com/bilalcaliskan/s3-manager/internal/utils" + + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" "github.com/aws/aws-sdk-go/service/s3/s3iface" "github.com/bilalcaliskan/s3-manager/cmd/search/options" @@ -24,15 +26,17 @@ var ( TextCmd = &cobra.Command{ Use: "text", Short: "searches the texts in files which has desired file name pattern and string pattern in it (supports regex)", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# search a text on target bucket by specifying regex for files s3-manager search text "catch me if you can" --file-name=".*.txt" `, PreRunE: func(cmd *cobra.Command, args []string) error { - svc, searchOpts, logger = utils.PrepareConstants(cmd, options.GetSearchOptions()) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + searchOpts.RootOptions = rootOpts - if err := utils.CheckFlags(args); err != nil { + if err := utils.CheckArgs(args, 1); err != nil { logger.Error().Msg(err.Error()) return err } diff --git a/cmd/search/utils/utils.go b/cmd/search/utils/utils.go deleted file mode 100644 index 4711de7..0000000 --- a/cmd/search/utils/utils.go +++ /dev/null @@ -1,39 +0,0 @@ -package utils - -import ( - "errors" - - "github.com/aws/aws-sdk-go/service/s3/s3iface" - rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" - "github.com/bilalcaliskan/s3-manager/cmd/search/options" - "github.com/bilalcaliskan/s3-manager/internal/logging" - "github.com/rs/zerolog" - "github.com/spf13/cobra" -) - -const ( - ErrTooManyArguments = "too many arguments" - ErrNoArgument = "no argument provided" -) - -func CheckFlags(args []string) (err error) { - if len(args) == 0 { - return errors.New(ErrNoArgument) - } - - if len(args) > 1 { - return errors.New(ErrTooManyArguments) - } - - return nil -} - -func PrepareConstants(cmd *cobra.Command, searchOptions *options.SearchOptions) (s3iface.S3API, *options.SearchOptions, zerolog.Logger) { - svc := cmd.Context().Value(rootopts.S3SvcKey{}).(s3iface.S3API) - rootOpts := cmd.Context().Value(rootopts.OptsKey{}).(*rootopts.RootOptions) - searchOptions.RootOptions = rootOpts - - logger := logging.GetLogger(searchOptions.RootOptions) - - return svc, searchOptions, logger -} diff --git a/cmd/search/utils/utils_test.go b/cmd/search/utils/utils_test.go deleted file mode 100644 index 5bef94d..0000000 --- a/cmd/search/utils/utils_test.go +++ /dev/null @@ -1,82 +0,0 @@ -//go:build unit - -package utils - -import ( - "context" - "testing" - - options3 "github.com/bilalcaliskan/s3-manager/cmd/search/options" - - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3iface" - options2 "github.com/bilalcaliskan/s3-manager/cmd/root/options" - internalaws "github.com/bilalcaliskan/s3-manager/internal/aws" - "github.com/rs/zerolog" - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" -) - -func createSvc(rootOpts *options2.RootOptions) (*s3.S3, error) { - return internalaws.CreateAwsService(rootOpts) -} - -func TestCheckFlags(t *testing.T) { - cases := []struct { - caseName string - flags []string - shouldPass bool - }{ - {"Success", - []string{"file"}, - true, - }, - {"Failure caused by no arguments provided", - []string{}, - false, - }, - {"Failure caused by too many arguments provided", - []string{"foo", "bar"}, - false, - }, - } - - for _, tc := range cases { - t.Logf("starting case '%s'", tc.caseName) - - err := CheckFlags(tc.flags) - - if tc.shouldPass { - assert.Nil(t, err) - } else { - assert.NotNil(t, err) - } - - t.Logf("ending case '%s'", tc.caseName) - } -} - -func TestPrepareConstants(t *testing.T) { - var ( - svc s3iface.S3API - searchOpts *options3.SearchOptions - logger zerolog.Logger - ) - - cmd := &cobra.Command{} - cmd.SetContext(context.Background()) - - rootOpts := options2.GetMockedRootOptions() - - svc, err := createSvc(rootOpts) - assert.NotNil(t, svc) - assert.Nil(t, err) - - cmd.SetContext(context.WithValue(context.Background(), options2.OptsKey{}, rootOpts)) - cmd.SetContext(context.WithValue(cmd.Context(), options2.S3SvcKey{}, svc)) - - svc, searchOpts, logger = PrepareConstants(cmd, options3.GetSearchOptions()) - assert.NotNil(t, svc) - assert.NotNil(t, searchOpts) - assert.NotNil(t, logger) -} diff --git a/cmd/tags/add/add.go b/cmd/tags/add/add.go index 3cb574b..1b87da3 100644 --- a/cmd/tags/add/add.go +++ b/cmd/tags/add/add.go @@ -5,9 +5,10 @@ import ( "fmt" "strings" - "github.com/bilalcaliskan/s3-manager/internal/prompt" + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" + "github.com/bilalcaliskan/s3-manager/internal/utils" - "github.com/bilalcaliskan/s3-manager/cmd/tags/utils" + "github.com/bilalcaliskan/s3-manager/internal/prompt" "github.com/aws/aws-sdk-go/service/s3/s3iface" "github.com/bilalcaliskan/s3-manager/cmd/tags/options" @@ -28,15 +29,17 @@ var ( AddCmd = &cobra.Command{ Use: "add", Short: "adds the tagging configuration for the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# add comma separated tagging configuration into bucket s3-manager tags add foo1=bar1,foo2=bar2 `, PreRunE: func(cmd *cobra.Command, args []string) (err error) { - svc, tagOpts, logger = utils.PrepareConstants(cmd, options.GetTagOptions()) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + tagOpts.RootOptions = rootOpts - if err := utils.CheckArgs(cmd, args); err != nil { + if err := utils.CheckArgs(args, 1); err != nil { logger.Error().Msg(err.Error()) return err } diff --git a/cmd/tags/remove/remove.go b/cmd/tags/remove/remove.go index 9cb4f81..7cfeb41 100644 --- a/cmd/tags/remove/remove.go +++ b/cmd/tags/remove/remove.go @@ -5,9 +5,9 @@ import ( "fmt" "strings" - "github.com/bilalcaliskan/s3-manager/internal/prompt" + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" - utils2 "github.com/bilalcaliskan/s3-manager/cmd/tags/utils" + "github.com/bilalcaliskan/s3-manager/internal/prompt" "github.com/bilalcaliskan/s3-manager/internal/aws" "github.com/bilalcaliskan/s3-manager/internal/utils" @@ -32,15 +32,17 @@ var ( RemoveCmd = &cobra.Command{ Use: "remove", Short: "removes the tagging configuration for the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# remove comma separated tagging configuration from bucket s3-manager tags remove foo1=bar1,foo2=bar2 `, PreRunE: func(cmd *cobra.Command, args []string) (err error) { - svc, tagOpts, logger = utils2.PrepareConstants(cmd, options.GetTagOptions()) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + tagOpts.RootOptions = rootOpts - if err := utils2.CheckArgs(cmd, args); err != nil { + if err := utils.CheckArgs(args, 1); err != nil { logger.Error().Msg(err.Error()) return err } diff --git a/cmd/tags/show/show.go b/cmd/tags/show/show.go index bda94a2..9c2b634 100644 --- a/cmd/tags/show/show.go +++ b/cmd/tags/show/show.go @@ -1,15 +1,15 @@ package show import ( - "errors" "fmt" + "github.com/bilalcaliskan/s3-manager/internal/utils" + "github.com/bilalcaliskan/s3-manager/cmd/tags/options" "github.com/aws/aws-sdk-go/service/s3/s3iface" rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" "github.com/bilalcaliskan/s3-manager/internal/aws" - "github.com/bilalcaliskan/s3-manager/internal/logging" "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -25,22 +25,18 @@ var ( ShowCmd = &cobra.Command{ Use: "show", Short: "shows the tagging configuration for the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# show the current tagging configuration for bucket s3-manager tags show `, PreRunE: func(cmd *cobra.Command, args []string) (err error) { - rootOpts := cmd.Context().Value(rootopts.OptsKey{}).(*rootopts.RootOptions) - svc = cmd.Context().Value(rootopts.S3SvcKey{}).(s3iface.S3API) - + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) tagOpts.RootOptions = rootOpts - logger = logging.GetLogger(rootOpts) - if len(args) > 0 { - err = errors.New("too many arguments provided") - logger.Error(). - Msg(err.Error()) + if err := utils.CheckArgs(args, 0); err != nil { + logger.Error().Msg(err.Error()) return err } diff --git a/cmd/tags/tags.go b/cmd/tags/tags.go index edaba21..706db1b 100644 --- a/cmd/tags/tags.go +++ b/cmd/tags/tags.go @@ -17,8 +17,7 @@ var ( TagsCmd = &cobra.Command{ Use: "tags", Short: "shows/sets the tagging configuration of the target bucket", - SilenceUsage: true, - SilenceErrors: true, - // we should not define PreRunE since it will override the PreRunE which is inherited from RootCmd + SilenceUsage: false, + SilenceErrors: false, } ) diff --git a/cmd/tags/utils/utils.go b/cmd/tags/utils/utils.go deleted file mode 100644 index 3f8021d..0000000 --- a/cmd/tags/utils/utils.go +++ /dev/null @@ -1,38 +0,0 @@ -package utils - -import ( - "errors" - - "github.com/aws/aws-sdk-go/service/s3/s3iface" - rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" - options2 "github.com/bilalcaliskan/s3-manager/cmd/tags/options" - "github.com/bilalcaliskan/s3-manager/internal/logging" - "github.com/rs/zerolog" - "github.com/spf13/cobra" -) - -func CheckArgs(cmd *cobra.Command, args []string) error { - if cmd.Name() == "show" && len(args) == 0 { - return nil - } else if cmd.Name() == "show" && len(args) > 0 { - return errors.New("too many argument provided") - } - - if len(args) == 0 { - return errors.New("no argument provided") - } else if len(args) > 1 { - return errors.New("too many argument provided") - } - - return nil -} - -func PrepareConstants(cmd *cobra.Command, tagOpts *options2.TagOptions) (s3iface.S3API, *options2.TagOptions, zerolog.Logger) { - svc := cmd.Context().Value(rootopts.S3SvcKey{}).(s3iface.S3API) - rootOpts := cmd.Context().Value(rootopts.OptsKey{}).(*rootopts.RootOptions) - tagOpts.RootOptions = rootOpts - - logger := logging.GetLogger(tagOpts.RootOptions) - - return svc, tagOpts, logger -} diff --git a/cmd/tags/utils/utils_test.go b/cmd/tags/utils/utils_test.go deleted file mode 100644 index c5b636e..0000000 --- a/cmd/tags/utils/utils_test.go +++ /dev/null @@ -1,98 +0,0 @@ -//go:build unit - -package utils - -import ( - "context" - "errors" - "testing" - - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3iface" - options2 "github.com/bilalcaliskan/s3-manager/cmd/root/options" - "github.com/bilalcaliskan/s3-manager/cmd/tags/options" - internalaws "github.com/bilalcaliskan/s3-manager/internal/aws" - "github.com/rs/zerolog" - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" -) - -func createSvc(rootOpts *options2.RootOptions) (*s3.S3, error) { - return internalaws.CreateAwsService(rootOpts) -} - -func TestCheckArgs(t *testing.T) { - tests := []struct { - name string - cmd *cobra.Command - args []string - expected error - }{ - { - name: "Show command with no arguments", - cmd: &cobra.Command{Use: "show"}, - args: []string{}, - expected: nil, - }, - { - name: "Success", - cmd: &cobra.Command{Use: "anothercommand"}, - args: []string{"foo"}, - expected: nil, - }, - { - name: "Show command with arguments", - cmd: &cobra.Command{Use: "show"}, - args: []string{"arg1"}, - expected: errors.New("too many argument provided"), - }, - { - name: "Command with no arguments", - cmd: &cobra.Command{}, - args: []string{}, - expected: errors.New("no argument provided"), - }, - { - name: "Command with too many arguments", - cmd: &cobra.Command{}, - args: []string{"arg1", "arg2"}, - expected: errors.New("too many argument provided"), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := CheckArgs(test.cmd, test.args) - assert.Equal(t, test.expected, err) - }) - } -} - -func TestPrepareConstants(t *testing.T) { - var ( - svc s3iface.S3API - tagOpts *options.TagOptions - logger zerolog.Logger - ) - - cmd := &cobra.Command{} - cmd.SetContext(context.Background()) - - rootOpts := options2.GetRootOptions() - rootOpts.AccessKey = "thisisaccesskey" - rootOpts.SecretKey = "thisissecretkey" - rootOpts.Region = "thisisregion" - rootOpts.BucketName = "thisisbucketname" - - svc, err := createSvc(rootOpts) - assert.NotNil(t, svc) - assert.Nil(t, err) - - cmd.SetContext(context.WithValue(context.Background(), options2.OptsKey{}, rootOpts)) - cmd.SetContext(context.WithValue(cmd.Context(), options2.S3SvcKey{}, svc)) - - svc, tagOpts, logger = PrepareConstants(cmd, options.GetTagOptions()) - assert.NotNil(t, svc) - assert.NotNil(t, tagOpts) - assert.NotNil(t, logger) -} diff --git a/cmd/transferacceleration/set/disabled/disabled.go b/cmd/transferacceleration/set/disabled/disabled.go index 8734e28..c898134 100644 --- a/cmd/transferacceleration/set/disabled/disabled.go +++ b/cmd/transferacceleration/set/disabled/disabled.go @@ -2,10 +2,11 @@ package disabled import ( "github.com/aws/aws-sdk-go/service/s3/s3iface" + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" options2 "github.com/bilalcaliskan/s3-manager/cmd/transferacceleration/options" - "github.com/bilalcaliskan/s3-manager/cmd/transferacceleration/utils" "github.com/bilalcaliskan/s3-manager/internal/aws" "github.com/bilalcaliskan/s3-manager/internal/prompt" + "github.com/bilalcaliskan/s3-manager/internal/utils" "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -22,17 +23,18 @@ var ( DisabledCmd = &cobra.Command{ Use: "disabled", Short: "disables the transfer acceleration configuration for the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# set the transfer acceleration configuration for bucket as disabled s3-manager transferacceleration set disabled `, RunE: func(cmd *cobra.Command, args []string) error { - svc, transferAccelerationOpts, logger = utils.PrepareConstants(cmd, transferAccelerationOpts) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + transferAccelerationOpts.RootOptions = rootOpts - if err := utils.CheckArgs(args); err != nil { - logger.Error(). - Msg(err.Error()) + if err := utils.CheckArgs(args, 0); err != nil { + logger.Error().Msg(err.Error()) return err } diff --git a/cmd/transferacceleration/set/disabled/disabled_test.go b/cmd/transferacceleration/set/disabled/disabled_test.go index 35f339a..b17cc1d 100644 --- a/cmd/transferacceleration/set/disabled/disabled_test.go +++ b/cmd/transferacceleration/set/disabled/disabled_test.go @@ -4,9 +4,10 @@ package disabled import ( "context" - "errors" "testing" + "github.com/bilalcaliskan/s3-manager/internal/constants" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3iface" @@ -125,7 +126,7 @@ func TestExecuteDisabledCmd(t *testing.T) { }, nil, &s3.PutBucketAccelerateConfigurationOutput{}, &promptMock{ msg: "n", - err: nil, + err: constants.ErrInjected, }, false, false, }, {"Failure caused by prompt error", []string{}, false, true, nil, @@ -134,7 +135,7 @@ func TestExecuteDisabledCmd(t *testing.T) { }, nil, &s3.PutBucketAccelerateConfigurationOutput{}, &promptMock{ msg: "nasdfasf", - err: errors.New("injected error"), + err: constants.ErrInjected, }, false, false, }, } diff --git a/cmd/transferacceleration/set/enabled/enabled.go b/cmd/transferacceleration/set/enabled/enabled.go index 220ea93..9256df2 100644 --- a/cmd/transferacceleration/set/enabled/enabled.go +++ b/cmd/transferacceleration/set/enabled/enabled.go @@ -2,10 +2,11 @@ package enabled import ( "github.com/aws/aws-sdk-go/service/s3/s3iface" + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" "github.com/bilalcaliskan/s3-manager/cmd/transferacceleration/options" - "github.com/bilalcaliskan/s3-manager/cmd/transferacceleration/utils" "github.com/bilalcaliskan/s3-manager/internal/aws" "github.com/bilalcaliskan/s3-manager/internal/prompt" + "github.com/bilalcaliskan/s3-manager/internal/utils" "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -22,15 +23,17 @@ var ( EnabledCmd = &cobra.Command{ Use: "enabled", Short: "enables the transfer acceleration configuration for the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# set the transfer acceleration configuration for bucket as enabled s3-manager transferacceleration set enabled `, RunE: func(cmd *cobra.Command, args []string) error { - svc, transferAccelerationOpts, logger = utils.PrepareConstants(cmd, transferAccelerationOpts) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + transferAccelerationOpts.RootOptions = rootOpts - if err := utils.CheckArgs(args); err != nil { + if err := utils.CheckArgs(args, 0); err != nil { logger.Error(). Msg(err.Error()) return err diff --git a/cmd/transferacceleration/set/enabled/enabled_test.go b/cmd/transferacceleration/set/enabled/enabled_test.go index e0764d8..47b6b8f 100644 --- a/cmd/transferacceleration/set/enabled/enabled_test.go +++ b/cmd/transferacceleration/set/enabled/enabled_test.go @@ -4,9 +4,10 @@ package enabled import ( "context" - "errors" "testing" + "github.com/bilalcaliskan/s3-manager/internal/constants" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3iface" @@ -125,7 +126,7 @@ func TestExecuteEnabledCmd(t *testing.T) { }, nil, &s3.PutBucketAccelerateConfigurationOutput{}, &promptMock{ msg: "asdfadsf", - err: errors.New("injected error"), + err: constants.ErrInjected, }, false, false, }, {"Failure caused by user terminated the process", []string{}, false, true, nil, @@ -134,7 +135,7 @@ func TestExecuteEnabledCmd(t *testing.T) { }, nil, &s3.PutBucketAccelerateConfigurationOutput{}, &promptMock{ msg: "n", - err: nil, + err: constants.ErrInjected, }, false, false, }, } diff --git a/cmd/transferacceleration/set/set.go b/cmd/transferacceleration/set/set.go index e011296..3f5196a 100644 --- a/cmd/transferacceleration/set/set.go +++ b/cmd/transferacceleration/set/set.go @@ -15,7 +15,7 @@ var ( SetCmd = &cobra.Command{ Use: "set", Short: "sets the transfer acceleration configuration for the target bucket (enabled/disabled)", - SilenceUsage: true, - SilenceErrors: true, + SilenceUsage: false, + SilenceErrors: false, } ) diff --git a/cmd/transferacceleration/show/show.go b/cmd/transferacceleration/show/show.go index 3afac72..3a0be93 100644 --- a/cmd/transferacceleration/show/show.go +++ b/cmd/transferacceleration/show/show.go @@ -1,15 +1,15 @@ package show import ( - "errors" "fmt" + "github.com/bilalcaliskan/s3-manager/internal/utils" + "github.com/bilalcaliskan/s3-manager/internal/aws" "github.com/aws/aws-sdk-go/service/s3/s3iface" rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" "github.com/bilalcaliskan/s3-manager/cmd/transferacceleration/options" - "github.com/bilalcaliskan/s3-manager/internal/logging" "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -25,30 +25,22 @@ var ( ShowCmd = &cobra.Command{ Use: "show", Short: "shows the transfer acceleration configuration for the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# show the current transfer acceleration configuration for bucket s3-manager transferacceleration show `, RunE: func(cmd *cobra.Command, args []string) (err error) { - rootOpts := cmd.Context().Value(rootopts.OptsKey{}).(*rootopts.RootOptions) - svc = cmd.Context().Value(rootopts.S3SvcKey{}).(s3iface.S3API) - + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) transferAccelerationOpts.RootOptions = rootOpts - logger = logging.GetLogger(rootOpts) - if len(args) > 0 { - err = errors.New("too many arguments provided") + if err := utils.CheckArgs(args, 0); err != nil { logger.Error(). Msg(err.Error()) return err } - /*ta, err := aws.GetTransferAcceleration(svc, transferAccelerationOpts) - if err != nil { - return err - }*/ - res, err := aws.GetTransferAcceleration(svc, transferAccelerationOpts) if err != nil { logger.Error().Msg(err.Error()) diff --git a/cmd/transferacceleration/transferacceleration.go b/cmd/transferacceleration/transferacceleration.go index 45887b5..1573029 100644 --- a/cmd/transferacceleration/transferacceleration.go +++ b/cmd/transferacceleration/transferacceleration.go @@ -15,8 +15,7 @@ var ( TransferAccelerationCmd = &cobra.Command{ Use: "transferacceleration", Short: "shows/sets the transfer acceleration configuration of the target bucket", - SilenceUsage: true, - SilenceErrors: true, - // we should not define PreRunE since it will override the PreRunE which is inherited from RootCmd + SilenceUsage: false, + SilenceErrors: false, } ) diff --git a/cmd/transferacceleration/utils/utils.go b/cmd/transferacceleration/utils/utils.go deleted file mode 100644 index 145cc3f..0000000 --- a/cmd/transferacceleration/utils/utils.go +++ /dev/null @@ -1,35 +0,0 @@ -package utils - -import ( - "errors" - - "github.com/aws/aws-sdk-go/service/s3/s3iface" - rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" - options2 "github.com/bilalcaliskan/s3-manager/cmd/transferacceleration/options" - "github.com/bilalcaliskan/s3-manager/internal/logging" - "github.com/rs/zerolog" - "github.com/spf13/cobra" -) - -const ( - ErrTooManyArguments = "too many arguments. please provide just 'enabled' or 'disabled'" - ErrUnknownStatus = "unknown status '%s' returned from AWS SDK" -) - -func CheckArgs(args []string) error { - if len(args) != 0 { - return errors.New(ErrTooManyArguments) - } - - return nil -} - -func PrepareConstants(cmd *cobra.Command, transferAccelerationOpts *options2.TransferAccelerationOptions) (s3iface.S3API, *options2.TransferAccelerationOptions, zerolog.Logger) { - svc := cmd.Context().Value(rootopts.S3SvcKey{}).(s3iface.S3API) - rootOpts := cmd.Context().Value(rootopts.OptsKey{}).(*rootopts.RootOptions) - transferAccelerationOpts.RootOptions = rootOpts - - logger := logging.GetLogger(transferAccelerationOpts.RootOptions) - - return svc, transferAccelerationOpts, logger -} diff --git a/cmd/transferacceleration/utils/utils_test.go b/cmd/transferacceleration/utils/utils_test.go deleted file mode 100644 index d0aa1aa..0000000 --- a/cmd/transferacceleration/utils/utils_test.go +++ /dev/null @@ -1,61 +0,0 @@ -//go:build unit - -package utils - -import ( - "context" - "testing" - - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3iface" - "github.com/bilalcaliskan/s3-manager/cmd/root/options" - options3 "github.com/bilalcaliskan/s3-manager/cmd/transferacceleration/options" - internalaws "github.com/bilalcaliskan/s3-manager/internal/aws" - "github.com/rs/zerolog" - "github.com/spf13/cobra" - - "github.com/stretchr/testify/assert" -) - -func createSvc(rootOpts *options.RootOptions) (*s3.S3, error) { - return internalaws.CreateAwsService(rootOpts) -} - -func TestCheckArgsSuccess(t *testing.T) { - err := CheckArgs([]string{}) - assert.Nil(t, err) -} - -func TestCheckArgsFailure(t *testing.T) { - err := CheckArgs([]string{"foo"}) - assert.NotNil(t, err) -} - -func TestPrepareConstants(t *testing.T) { - var ( - svc s3iface.S3API - taOpts *options3.TransferAccelerationOptions - logger zerolog.Logger - ) - - cmd := &cobra.Command{} - cmd.SetContext(context.Background()) - - rootOpts := options.GetRootOptions() - rootOpts.AccessKey = "thisisaccesskey" - rootOpts.SecretKey = "thisissecretkey" - rootOpts.Region = "thisisregion" - rootOpts.BucketName = "thisisbucketname" - - svc, err := createSvc(rootOpts) - assert.NotNil(t, svc) - assert.Nil(t, err) - - cmd.SetContext(context.WithValue(context.Background(), options.OptsKey{}, rootOpts)) - cmd.SetContext(context.WithValue(cmd.Context(), options.S3SvcKey{}, svc)) - - svc, taOpts, logger = PrepareConstants(cmd, options3.GetTransferAccelerationOptions()) - assert.NotNil(t, svc) - assert.NotNil(t, taOpts) - assert.NotNil(t, logger) -} diff --git a/cmd/versioning/set/disabled/disabled.go b/cmd/versioning/set/disabled/disabled.go index 451b754..db490aa 100644 --- a/cmd/versioning/set/disabled/disabled.go +++ b/cmd/versioning/set/disabled/disabled.go @@ -2,10 +2,11 @@ package disabled import ( "github.com/aws/aws-sdk-go/service/s3/s3iface" + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" "github.com/bilalcaliskan/s3-manager/cmd/versioning/options" - "github.com/bilalcaliskan/s3-manager/cmd/versioning/set/utils" "github.com/bilalcaliskan/s3-manager/internal/aws" "github.com/bilalcaliskan/s3-manager/internal/prompt" + "github.com/bilalcaliskan/s3-manager/internal/utils" "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -22,15 +23,17 @@ var ( DisabledCmd = &cobra.Command{ Use: "disabled", Short: "disables the versioning configuration for the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# set the versioning configuration for bucket as disabled s3-manager versioning set disabled `, RunE: func(cmd *cobra.Command, args []string) (err error) { - svc, versioningOpts, logger = utils.PrepareConstants(cmd, options.GetVersioningOptions()) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + versioningOpts.RootOptions = rootOpts - if err := utils.CheckArgs(args); err != nil { + if err := utils.CheckArgs(args, 0); err != nil { logger.Error(). Msg(err.Error()) return err diff --git a/cmd/versioning/set/disabled/disabled_test.go b/cmd/versioning/set/disabled/disabled_test.go index d750b39..29675b5 100644 --- a/cmd/versioning/set/disabled/disabled_test.go +++ b/cmd/versioning/set/disabled/disabled_test.go @@ -4,9 +4,10 @@ package disabled import ( "context" - "errors" "testing" + "github.com/bilalcaliskan/s3-manager/internal/constants" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3iface" @@ -125,7 +126,7 @@ func TestExecuteDisabledCmd(t *testing.T) { }, nil, &s3.PutBucketVersioningOutput{}, &promptMock{ msg: "n", - err: nil, + err: constants.ErrInjected, }, false, false, }, {"Failure caused by prompt error", []string{}, false, true, nil, @@ -134,7 +135,7 @@ func TestExecuteDisabledCmd(t *testing.T) { }, nil, &s3.PutBucketVersioningOutput{}, &promptMock{ msg: "asdfasfd", - err: errors.New("injected error"), + err: constants.ErrInjected, }, false, false, }, } diff --git a/cmd/versioning/set/enabled/enabled.go b/cmd/versioning/set/enabled/enabled.go index 3d81f1d..b98ebe7 100644 --- a/cmd/versioning/set/enabled/enabled.go +++ b/cmd/versioning/set/enabled/enabled.go @@ -2,10 +2,11 @@ package enabled import ( "github.com/aws/aws-sdk-go/service/s3/s3iface" + rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" "github.com/bilalcaliskan/s3-manager/cmd/versioning/options" - "github.com/bilalcaliskan/s3-manager/cmd/versioning/set/utils" "github.com/bilalcaliskan/s3-manager/internal/aws" "github.com/bilalcaliskan/s3-manager/internal/prompt" + "github.com/bilalcaliskan/s3-manager/internal/utils" "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -22,15 +23,17 @@ var ( EnabledCmd = &cobra.Command{ Use: "enabled", Short: "enables the versioning configuration for the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# set the versioning configuration for bucket as enabled s3-manager versioning set enabled `, RunE: func(cmd *cobra.Command, args []string) error { - svc, versioningOpts, logger = utils.PrepareConstants(cmd, options.GetVersioningOptions()) + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) + versioningOpts.RootOptions = rootOpts - if err := utils.CheckArgs(args); err != nil { + if err := utils.CheckArgs(args, 0); err != nil { logger.Error(). Msg(err.Error()) return err diff --git a/cmd/versioning/set/enabled/enabled_test.go b/cmd/versioning/set/enabled/enabled_test.go index 8f64324..d485385 100644 --- a/cmd/versioning/set/enabled/enabled_test.go +++ b/cmd/versioning/set/enabled/enabled_test.go @@ -4,9 +4,10 @@ package enabled import ( "context" - "errors" "testing" + "github.com/bilalcaliskan/s3-manager/internal/constants" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3iface" @@ -125,7 +126,7 @@ func TestExecuteEnabledCmd(t *testing.T) { }, nil, &s3.PutBucketVersioningOutput{}, &promptMock{ msg: "asdfafj", - err: errors.New("injected error"), + err: constants.ErrInjected, }, false, false, }, {"Failure caused by user terminated the process", []string{}, false, true, nil, @@ -134,7 +135,7 @@ func TestExecuteEnabledCmd(t *testing.T) { }, nil, &s3.PutBucketVersioningOutput{}, &promptMock{ msg: "n", - err: nil, + err: constants.ErrInjected, }, false, false, }, } diff --git a/cmd/versioning/set/set.go b/cmd/versioning/set/set.go index 8908fa5..304420b 100644 --- a/cmd/versioning/set/set.go +++ b/cmd/versioning/set/set.go @@ -15,7 +15,7 @@ var ( SetCmd = &cobra.Command{ Use: "set", Short: "sets the versioning configuration for the target bucket (enabled/disabled)", - SilenceUsage: true, - SilenceErrors: true, + SilenceUsage: false, + SilenceErrors: false, } ) diff --git a/cmd/versioning/set/utils/utils.go b/cmd/versioning/set/utils/utils.go index eabb5c3..36e08b9 100644 --- a/cmd/versioning/set/utils/utils.go +++ b/cmd/versioning/set/utils/utils.go @@ -1,22 +1,14 @@ package utils import ( - "errors" "fmt" - "github.com/aws/aws-sdk-go/service/s3/s3iface" - rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" - "github.com/bilalcaliskan/s3-manager/internal/logging" - "github.com/rs/zerolog" - "github.com/spf13/cobra" - "github.com/aws/aws-sdk-go/service/s3" "github.com/bilalcaliskan/s3-manager/cmd/versioning/options" ) const ( - ErrTooManyArguments = "too many arguments. please provide just 'enabled' or 'disabled'" - ErrUnknownStatus = "unknown status '%s' returned from AWS SDK" + ErrUnknownStatus = "unknown status '%s' returned from AWS SDK" WarnDesiredState = "versioning is already at the desired state, skipping configuration" @@ -25,14 +17,6 @@ const ( InfSettingVersioning = "setting versioning as %v" ) -func CheckArgs(args []string) error { - if len(args) != 0 { - return errors.New(ErrTooManyArguments) - } - - return nil -} - func DecideActualState(versioning *s3.GetBucketVersioningOutput, opts *options.VersioningOptions) error { switch *versioning.Status { case "Enabled": @@ -42,16 +26,6 @@ func DecideActualState(versioning *s3.GetBucketVersioningOutput, opts *options.V default: return fmt.Errorf(ErrUnknownStatus, *versioning.Status) } - - return nil -} - -func PrepareConstants(cmd *cobra.Command, versioningOpts *options.VersioningOptions) (s3iface.S3API, *options.VersioningOptions, zerolog.Logger) { - svc := cmd.Context().Value(rootopts.S3SvcKey{}).(s3iface.S3API) - rootOpts := cmd.Context().Value(rootopts.OptsKey{}).(*rootopts.RootOptions) - versioningOpts.RootOptions = rootOpts - logger := logging.GetLogger(versioningOpts.RootOptions) - - return svc, versioningOpts, logger + return nil } diff --git a/cmd/versioning/set/utils/utils_test.go b/cmd/versioning/set/utils/utils_test.go index 532f135..acce957 100644 --- a/cmd/versioning/set/utils/utils_test.go +++ b/cmd/versioning/set/utils/utils_test.go @@ -3,18 +3,13 @@ package utils import ( - "context" - "errors" "fmt" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3iface" - rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" options2 "github.com/bilalcaliskan/s3-manager/cmd/versioning/options" - "github.com/rs/zerolog" - "github.com/spf13/cobra" "github.com/stretchr/testify/assert" ) @@ -23,61 +18,6 @@ type mockS3Client struct { s3iface.S3API } -func TestCheckArgs(t *testing.T) { - tests := []struct { - name string - args []string - expected error - }{ - { - name: "Failure caused by too many arguments", - args: []string{"foo", "bar"}, - expected: errors.New(ErrTooManyArguments), - }, - { - name: "Success", - args: []string{}, - expected: nil, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := CheckArgs(test.args) - assert.Equal(t, test.expected, err) - }) - } -} - -func TestPrepareConstants(t *testing.T) { - var ( - svc s3iface.S3API - versioningOpts *options2.VersioningOptions - logger zerolog.Logger - ) - - cmd := &cobra.Command{} - cmd.SetContext(context.Background()) - - rootOpts := rootopts.GetRootOptions() - rootOpts.AccessKey = "thisisaccesskey" - rootOpts.SecretKey = "thisissecretkey" - rootOpts.Region = "thisisregion" - rootOpts.BucketName = "thisisbucketname" - - mockSvc := &mockS3Client{} - svc = mockSvc - assert.NotNil(t, mockSvc) - - cmd.SetContext(context.WithValue(context.Background(), rootopts.OptsKey{}, rootOpts)) - cmd.SetContext(context.WithValue(cmd.Context(), rootopts.S3SvcKey{}, svc)) - - svc, versioningOpts, logger = PrepareConstants(cmd, options2.GetVersioningOptions()) - assert.NotNil(t, svc) - assert.NotNil(t, versioningOpts) - assert.NotNil(t, logger) -} - func TestDecideActualState(t *testing.T) { tests := []struct { name string diff --git a/cmd/versioning/show/show.go b/cmd/versioning/show/show.go index c082c54..359bb74 100644 --- a/cmd/versioning/show/show.go +++ b/cmd/versioning/show/show.go @@ -1,14 +1,14 @@ package show import ( - "errors" "fmt" + "github.com/bilalcaliskan/s3-manager/internal/utils" + "github.com/aws/aws-sdk-go/service/s3/s3iface" rootopts "github.com/bilalcaliskan/s3-manager/cmd/root/options" "github.com/bilalcaliskan/s3-manager/cmd/versioning/options" "github.com/bilalcaliskan/s3-manager/internal/aws" - "github.com/bilalcaliskan/s3-manager/internal/logging" "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -24,22 +24,18 @@ var ( ShowCmd = &cobra.Command{ Use: "show", Short: "shows the versioning configuration for the target bucket", - SilenceUsage: true, + SilenceUsage: false, SilenceErrors: true, Example: `# show the current versioning configuration for bucket s3-manager versioning show `, RunE: func(cmd *cobra.Command, args []string) (err error) { - rootOpts := cmd.Context().Value(rootopts.OptsKey{}).(*rootopts.RootOptions) - svc = cmd.Context().Value(rootopts.S3SvcKey{}).(s3iface.S3API) - + var rootOpts *rootopts.RootOptions + svc, rootOpts, logger = utils.PrepareConstants(cmd) versioningOpts.RootOptions = rootOpts - logger = logging.GetLogger(rootOpts) - if len(args) > 0 { - err = errors.New("too many arguments provided") - logger.Error(). - Msg(err.Error()) + if err := utils.CheckArgs(args, 0); err != nil { + logger.Error().Msg(err.Error()) return err } diff --git a/cmd/versioning/versioning.go b/cmd/versioning/versioning.go index fc7ad27..ed563b6 100644 --- a/cmd/versioning/versioning.go +++ b/cmd/versioning/versioning.go @@ -15,8 +15,8 @@ var ( VersioningCmd = &cobra.Command{ Use: "versioning", Short: "shows/sets the versioning configuration of the target bucket", - SilenceUsage: true, - SilenceErrors: true, + SilenceUsage: false, + SilenceErrors: false, // we should not define PreRunE since it will override the PreRunE which is inherited from RootCmd } ) diff --git a/internal/aws/aws.go b/internal/aws/aws.go index 18f04d5..cea6a16 100644 --- a/internal/aws/aws.go +++ b/internal/aws/aws.go @@ -3,11 +3,12 @@ package aws import ( "bytes" "fmt" - "log" "regexp" "strings" "sync" + "github.com/bilalcaliskan/s3-manager/internal/constants" + "github.com/bilalcaliskan/s3-manager/internal/prompt" internalutil "github.com/bilalcaliskan/s3-manager/internal/utils" @@ -35,6 +36,9 @@ import ( ) // createSession initializes session with provided credentials +// +// It returns a pointer to session.Session along with the error that encountered during +// session initialization process. func createSession(accessKey, secretKey, region string) (*session.Session, error) { return session.NewSession(&aws.Config{ Region: aws.String(region), @@ -43,12 +47,11 @@ func createSession(accessKey, secretKey, region string) (*session.Session, error } func CreateAwsService(opts *options.RootOptions) (svc *s3.S3, err error) { - var sess *session.Session - if opts.AccessKey == "" || opts.SecretKey == "" || opts.Region == "" { return svc, errors.New("missing required fields") } + var sess *session.Session sess, err = createSession(opts.AccessKey, opts.SecretKey, opts.Region) if err != nil { return svc, err @@ -117,11 +120,11 @@ func SetTransferAcceleration(svc s3iface.S3API, opts *options6.TransferAccelerat if !opts.AutoApprove { var res string if res, err = confirmRunner.Run(); err != nil { - return err - } + if strings.ToLower(res) == "n" { + return constants.ErrUserTerminated + } - if strings.ToLower(res) == "n" { - return errors.New("user terminated the process") + return constants.ErrInvalidInput } } @@ -204,31 +207,31 @@ func GetBucketVersioning(svc s3iface.S3API, opts *options.RootOptions) (res *s3. } // SetBucketVersioning sets the target bucket -func SetBucketVersioning(svc s3iface.S3API, versioningOpts *options4.VersioningOptions, confirmRunner prompt.PromptRunner, logger zerolog.Logger) error { +func SetBucketVersioning(svc s3iface.S3API, versioningOpts *options4.VersioningOptions, confirmRunner prompt.PromptRunner, logger zerolog.Logger) (err error) { if versioningOpts.DryRun { logger.Info().Msg("skipping operation since '--dry-run' flag is passed") return nil } - var err error if !versioningOpts.AutoApprove { var res string if res, err = confirmRunner.Run(); err != nil { - return err - } + if strings.ToLower(res) == "n" { + return constants.ErrUserTerminated + } - if strings.ToLower(res) == "n" { - return errors.New("user terminated the process") + return constants.ErrInvalidInput } } - versioning, err := GetBucketVersioning(svc, versioningOpts.RootOptions) + var versioning *s3.GetBucketVersioningOutput + versioning, err = GetBucketVersioning(svc, versioningOpts.RootOptions) if err != nil { logger.Error().Msg(err.Error()) return err } - if err := utils.DecideActualState(versioning, versioningOpts); err != nil { + if err = utils.DecideActualState(versioning, versioningOpts); err != nil { logger.Error().Msg(err.Error()) return err } @@ -251,16 +254,18 @@ func SetBucketVersioning(svc s3iface.S3API, versioningOpts *options4.VersioningO str = "Suspended" } - _, err = svc.PutBucketVersioning(&s3.PutBucketVersioningInput{ + if _, err = svc.PutBucketVersioning(&s3.PutBucketVersioningInput{ Bucket: aws.String(versioningOpts.BucketName), VersioningConfiguration: &s3.VersioningConfiguration{ Status: aws.String(str), }, - }) + }); err != nil { + logger.Error().Msg(err.Error()) + return err + } logger.Info().Msgf(utils.InfSuccess, versioningOpts.DesiredState) - - return err + return nil } // DeleteFiles deletes the slice of []*s3.Object objects in the target bucket @@ -270,16 +275,13 @@ func DeleteFiles(svc s3iface.S3API, bucketName string, slice []*s3.Object, dryRu Float64("size", float64(*v.Size)/1000000).Msg("will try to delete file") if !dryRun { - _, err := svc.DeleteObject(&s3.DeleteObjectInput{ + if _, err := svc.DeleteObject(&s3.DeleteObjectInput{ Bucket: aws.String(bucketName), Key: aws.String(*v.Key), - }) - - if err != nil { + }); err != nil { return err } - log.Printf("successfully deleted file %s", *v.Key) logger.Info().Str("key", *v.Key).Msg("successfully deleted file") } } @@ -307,31 +309,12 @@ func GetDesiredFiles(svc s3iface.S3API, opts *options2.SearchOptions) (matchedFi } // SearchString does the heavy lifting, communicates with the S3 and finds the files -func SearchString(svc s3iface.S3API, opts *options2.SearchOptions) ([]string, []error) { - var errs []error - var matchedFiles []string - mu := &sync.Mutex{} - - // fetch all the objects in target bucket - /*listResult, err := svc.ListObjects(&s3.ListObjectsInput{ - Bucket: aws.String(opts.BucketName), - }) - if err != nil { - errs = append(errs, err) - return matchedFiles, errs - }*/ - +// +// It returns the string array that contains keys of matched files, along with the error array +// that contains errors during search process for each individual file. +func SearchString(svc s3iface.S3API, opts *options2.SearchOptions) (matchedFiles []string, errs []error) { var wg sync.WaitGroup - - //extensions := strings.Split(opts.FileExtensions, ",") - - // separate the txt files from all of the fetched objects from bucket - /*for _, v := range listResult.Contents { - if *v.Key == opts.FileName || (strings.HasSuffix(*v.Key, opts.FileNameSuffix) || strings.HasPrefix(*v.Key, opts.FileNamePrefix)) { - logger.Debug().Str("fileName", *v.Key).Msg("found file") - resultArr = append(resultArr, v) - } - }*/ + mu := &sync.Mutex{} resultArr, err := GetDesiredFiles(svc, opts) if err != nil { @@ -379,8 +362,6 @@ func SearchString(svc s3iface.S3API, opts *options2.SearchOptions) ([]string, [] }(obj, &wg) } - // wait for all the goroutines to complete wg.Wait() - return matchedFiles, errs } diff --git a/internal/aws/aws_test.go b/internal/aws/aws_test.go index 8603fc9..f8549f4 100644 --- a/internal/aws/aws_test.go +++ b/internal/aws/aws_test.go @@ -7,6 +7,8 @@ import ( "testing" "time" + "github.com/bilalcaliskan/s3-manager/internal/constants" + "github.com/bilalcaliskan/s3-manager/internal/prompt" "github.com/pkg/errors" @@ -29,24 +31,10 @@ import ( ) var ( - injectedErr = errors.New("injected error") - defaultListObjectsErr error - defaultGetObjectErr error - defaultDeleteObjectErr error - fileNamePrefix string - /*defaultListObjectsOutput = &s3.ListObjectsOutput{ - Name: aws.String(""), - Marker: aws.String(""), - MaxKeys: aws.Int64(1000), - Prefix: aws.String(""), - IsTruncated: aws.Bool(false), - }*/ - defaultListObjectsOutput = &s3.ListObjectsOutput{} - /*defaultDeleteObjectOutput = &s3.DeleteObjectOutput{ - DeleteMarker: nil, - RequestCharged: nil, - VersionId: nil, - }*/ + defaultListObjectsErr error + defaultGetObjectErr error + defaultDeleteObjectErr error + defaultListObjectsOutput = &s3.ListObjectsOutput{} defaultDeleteObjectOutput = &s3.DeleteObjectOutput{} defaultGetBucketVersioningOutput = &s3.GetBucketVersioningOutput{ Status: aws.String("Enabled"), @@ -207,7 +195,7 @@ func TestGetAllFiles(t *testing.T) { }, }, {"Failure caused by List objects error", - injectedErr, injectedErr, + constants.ErrInjected, constants.ErrInjected, nil, }, } @@ -262,7 +250,7 @@ func TestDeleteFiles(t *testing.T) { }, }, {"Failure caused by delete object err", - injectedErr, injectedErr, false, + constants.ErrInjected, constants.ErrInjected, false, []*s3.Object{ { ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), @@ -521,7 +509,8 @@ func TestSetBucketVersioning(t *testing.T) { DesiredState: "disabled", RootOptions: rootOpts, }, - nil, injectedErr, nil, injectedErr, false, false, + nil, constants.ErrInjected, nil, + constants.ErrInjected, false, false, promptMock{ msg: "y", err: nil, @@ -536,7 +525,8 @@ func TestSetBucketVersioning(t *testing.T) { }, &s3.GetBucketVersioningOutput{ Status: aws.String("Enableddddd"), - }, nil, nil, errors.New("unknown status 'Enableddddd' returned from AWS SDK"), false, false, + }, nil, nil, errors.New("unknown status 'Enableddddd' returned from AWS SDK"), + false, false, promptMock{ msg: "y", err: nil, @@ -563,10 +553,10 @@ func TestSetBucketVersioning(t *testing.T) { }, &s3.GetBucketVersioningOutput{ Status: aws.String("Suspended"), - }, nil, nil, errors.New("user terminated the process"), false, false, + }, nil, nil, constants.ErrUserTerminated, false, false, promptMock{ msg: "n", - err: nil, + err: constants.ErrInjected, }, }, { @@ -578,10 +568,10 @@ func TestSetBucketVersioning(t *testing.T) { }, &s3.GetBucketVersioningOutput{ Status: aws.String("Suspended"), - }, nil, nil, injectedErr, false, false, + }, nil, nil, constants.ErrInvalidInput, false, false, promptMock{ - msg: "n", - err: injectedErr, + msg: "nasdf", + err: constants.ErrInjected, }, }, } @@ -623,8 +613,8 @@ func TestGetBucketVersioning(t *testing.T) { }, nil, }, { - "Failure", injectedErr, - nil, injectedErr, + "Failure", constants.ErrInjected, + nil, constants.ErrInjected, }, } @@ -670,11 +660,11 @@ func TestGetBucketTags(t *testing.T) { }, nil, }, { - "Failure", injectedErr, + "Failure", constants.ErrInjected, &options4.TagOptions{ RootOptions: rootOpts, }, - nil, injectedErr, + nil, constants.ErrInjected, }, } @@ -720,7 +710,7 @@ func TestSetBucketTags(t *testing.T) { }, nil, }, { - "Failure", injectedErr, + "Failure", constants.ErrInjected, &options4.TagOptions{ RootOptions: rootOpts, TagsToAdd: make(map[string]string), @@ -736,7 +726,7 @@ func TestSetBucketTags(t *testing.T) { Value: aws.String("bar2"), }, }, - injectedErr, + constants.ErrInjected, }, } @@ -771,11 +761,11 @@ func TestDeleteAllBucketTags(t *testing.T) { }, nil, }, { - "Failure", injectedErr, + "Failure", constants.ErrInjected, &options4.TagOptions{ RootOptions: rootOpts, }, - injectedErr, + constants.ErrInjected, }, } @@ -806,11 +796,11 @@ func TestGetBucketPolicy(t *testing.T) { }, nil, }, { - "Failure", injectedErr, + "Failure", constants.ErrInjected, &options6.BucketPolicyOptions{ RootOptions: rootOpts, }, - injectedErr, + constants.ErrInjected, }, } @@ -842,12 +832,12 @@ func TestSetBucketPolicy(t *testing.T) { }, nil, }, { - "Failure", injectedErr, + "Failure", constants.ErrInjected, &options6.BucketPolicyOptions{ RootOptions: rootOpts, BucketPolicyContent: bucketPolicyStr, }, - injectedErr, + constants.ErrInjected, }, } @@ -883,12 +873,12 @@ func TestGetBucketPolicyString(t *testing.T) { }, nil, }, { - "Failure", injectedErr, + "Failure", constants.ErrInjected, &options6.BucketPolicyOptions{ RootOptions: rootOpts, BucketPolicyContent: bucketPolicyStr, }, - nil, injectedErr, + nil, constants.ErrInjected, }, } @@ -925,11 +915,11 @@ func TestDeleteBucketPolicy(t *testing.T) { }, nil, }, { - "Failure", injectedErr, + "Failure", constants.ErrInjected, &options6.BucketPolicyOptions{ RootOptions: rootOpts, BucketPolicyContent: bucketPolicyStr, - }, injectedErr, + }, constants.ErrInjected, }, } @@ -960,10 +950,10 @@ func TestGetTransferAcceleration(t *testing.T) { }, nil, }, { - "Failure", injectedErr, + "Failure", constants.ErrInjected, &options5.TransferAccelerationOptions{ RootOptions: rootOpts, - }, injectedErr, + }, constants.ErrInjected, }, } @@ -1046,19 +1036,20 @@ func TestSetTransferAcceleration(t *testing.T) { }, }, { - "Failure caused by get transfer acceleration error", injectedErr, + "Failure caused by get transfer acceleration error", constants.ErrInjected, &options5.TransferAccelerationOptions{ RootOptions: rootOpts, DesiredState: "disabled", }, - nil, injectedErr, nil, false, false, + nil, constants.ErrInjected, nil, + false, false, promptMock{ msg: "y", err: nil, }, }, { - "Failure caused by unknown status returned by get transfer acceleration", injectedErr, + "Failure caused by unknown status returned by get transfer acceleration", constants.ErrInjected, &options5.TransferAccelerationOptions{ RootOptions: rootOpts, DesiredState: "disabled", @@ -1072,21 +1063,21 @@ func TestSetTransferAcceleration(t *testing.T) { }, }, { - "Failure caused by put transfer acceleration error", injectedErr, + "Failure caused by put transfer acceleration error", constants.ErrInjected, &options5.TransferAccelerationOptions{ RootOptions: rootOpts, DesiredState: "disabled", }, &s3.GetBucketAccelerateConfigurationOutput{ Status: aws.String("Enabled"), - }, nil, injectedErr, false, false, + }, nil, constants.ErrInjected, false, false, promptMock{ msg: "y", err: nil, }, }, { - "Failure caused by prompt error", injectedErr, + "Failure caused by prompt error", constants.ErrInvalidInput, &options5.TransferAccelerationOptions{ RootOptions: rootOpts, DesiredState: "disabled", @@ -1096,11 +1087,11 @@ func TestSetTransferAcceleration(t *testing.T) { }, nil, nil, false, false, promptMock{ msg: "dkslfa", - err: injectedErr, + err: constants.ErrInjected, }, }, { - "Failure caused by user terminated the process", errors.New("user terminated the process"), + "Failure caused by user terminated the process", constants.ErrUserTerminated, &options5.TransferAccelerationOptions{ RootOptions: rootOpts, DesiredState: "disabled", @@ -1110,7 +1101,7 @@ func TestSetTransferAcceleration(t *testing.T) { }, nil, nil, false, false, promptMock{ msg: "n", - err: nil, + err: constants.ErrInjected, }, }, } diff --git a/internal/cleaner/cleaner.go b/internal/cleaner/cleaner.go index a3147f8..f4a62bd 100644 --- a/internal/cleaner/cleaner.go +++ b/internal/cleaner/cleaner.go @@ -2,9 +2,10 @@ package cleaner import ( "bytes" - "errors" "strings" + "github.com/bilalcaliskan/s3-manager/internal/constants" + "github.com/bilalcaliskan/s3-manager/internal/prompt" "github.com/aws/aws-sdk-go/service/s3/s3iface" @@ -25,7 +26,7 @@ func StartCleaning(svc s3iface.S3API, runner prompt.PromptRunner, cleanOpts *sta sortObjects(res, cleanOpts) border := len(res) - cleanOpts.KeepLastNFiles - if border < 0 { + if border <= 0 { logger.Warn(). Int("arrayLength", len(res)). Int("keepLastNFiles", cleanOpts.KeepLastNFiles). @@ -34,10 +35,6 @@ func StartCleaning(svc s3iface.S3API, runner prompt.PromptRunner, cleanOpts *sta } targetObjects := res[:len(res)-cleanOpts.KeepLastNFiles] - if err := checkLength(targetObjects); err != nil { - logger.Warn().Msg(err.Error()) - return nil - } keys := utils.GetKeysOnly(targetObjects) var buffer bytes.Buffer @@ -56,18 +53,13 @@ func StartCleaning(svc s3iface.S3API, runner prompt.PromptRunner, cleanOpts *sta if res, err := runner.Run(); err != nil { if strings.ToLower(res) == "n" { - return errors.New("user terminated the process") + return constants.ErrUserTerminated } - return errors.New("invalid input") + return constants.ErrInvalidInput } } - /*if err := promptDeletion(cleanOpts, logger, keys); err != nil { - logger.Warn().Str("error", err.Error()).Msg("an error occurred while prompting file deletion") - return err - }*/ - if err := aws.DeleteFiles(svc, cleanOpts.RootOptions.BucketName, targetObjects, cleanOpts.DryRun, logger); err != nil { logger.Error().Str("error", err.Error()).Msg("an error occurred while deleting target files") return err diff --git a/internal/cleaner/cleaner_test.go b/internal/cleaner/cleaner_test.go index 2dbae56..71319f3 100644 --- a/internal/cleaner/cleaner_test.go +++ b/internal/cleaner/cleaner_test.go @@ -3,26 +3,28 @@ package cleaner import ( - "errors" "os" "testing" "time" - "github.com/bilalcaliskan/s3-manager/cmd/root/options" + "github.com/bilalcaliskan/s3-manager/internal/constants" + "github.com/bilalcaliskan/s3-manager/internal/prompt" + + rootoptions "github.com/bilalcaliskan/s3-manager/cmd/root/options" "github.com/aws/aws-sdk-go/service/s3/s3iface" "github.com/bilalcaliskan/s3-manager/internal/logging" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" - options2 "github.com/bilalcaliskan/s3-manager/cmd/clean/options" + "github.com/bilalcaliskan/s3-manager/cmd/clean/options" "github.com/stretchr/testify/assert" ) var ( - listObjectsErr error - getObjectsErr error - deleteObjectErr error + defaultListObjectsErr error + defaultGetObjectErr error + defaultDeleteObjectErr error defaultListObjectsOutput = &s3.ListObjectsOutput{ Name: aws.String(""), Marker: aws.String(""), @@ -58,7 +60,6 @@ var ( RequestCharged: nil, VersionId: nil, } - mockLogger = logging.GetLogger(options.GetRootOptions()) ) type promptMock struct { @@ -77,7 +78,7 @@ type mockS3Client struct { // ListObjects mocks the S3API ListObjects method func (m *mockS3Client) ListObjects(obj *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) { - return defaultListObjectsOutput, listObjectsErr + return defaultListObjectsOutput, defaultListObjectsErr } // GetObject mocks the S3API GetObject method @@ -93,482 +94,543 @@ func (m *mockS3Client) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, ContentLength: aws.Int64(1000), ContentType: aws.String("text/plain"), ETag: aws.String("d73a503d212d9279e6b2ed8ac6bb81f3"), - }, getObjectsErr + }, defaultGetObjectErr } func (m *mockS3Client) DeleteObject(input *s3.DeleteObjectInput) (*s3.DeleteObjectOutput, error) { - return defaultDeleteObjectOutput, deleteObjectErr + return defaultDeleteObjectOutput, defaultDeleteObjectErr } func TestStartCleaning(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = false - cleanOpts.AutoApprove = true - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() -} - -func TestStartCleaningNotAutoApprovedSuccess(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = false - cleanOpts.AutoApprove = false - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() -} - -func TestStartCleaningNotAutoApprovedFailure(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = false - cleanOpts.AutoApprove = false - promptMock := promptMock{ - msg: "y", - err: errors.New("dummy error"), - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.NotNil(t, err) - - cleanOpts.SetZeroValues() -} - -func TestStartCleaningNotAutoApprovedExit(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = false - cleanOpts.AutoApprove = false - promptMock := promptMock{ - msg: "n", - err: errors.New("dummy error"), - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.NotNil(t, err) - - cleanOpts.SetZeroValues() -} - -func TestStartCleaningSortBySize(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = false - cleanOpts.AutoApprove = true - cleanOpts.SortBy = "size" - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() -} - -func TestStartCleaningWrongBorder(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = true - cleanOpts.AutoApprove = true - cleanOpts.MinFileSizeInMb = 0 - cleanOpts.MaxFileSizeInMb = 0 - cleanOpts.KeepLastNFiles = 300 - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - // normally we would expect err not to be nil but we are ignoring the error in that case - assert.Nil(t, err) - - cleanOpts.SetZeroValues() -} - -func TestStartCleaningDryRunEqualMinMaxValues(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = true - cleanOpts.AutoApprove = true - cleanOpts.MinFileSizeInMb = 0 - cleanOpts.MaxFileSizeInMb = 0 - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() -} - -func TestStartCleaningDirectorySuffix(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = true - cleanOpts.AutoApprove = true - cleanOpts.MinFileSizeInMb = 0 - cleanOpts.MaxFileSizeInMb = 0 - cleanOpts.FileExtensions = "txt" - defaultListObjectsOutput = &s3.ListObjectsOutput{ - Name: aws.String(""), - Marker: aws.String(""), - MaxKeys: aws.Int64(1000), - Prefix: aws.String(""), - IsTruncated: aws.Bool(false), - Contents: []*s3.Object{ - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), - Key: aws.String("file1/"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(1000), - LastModified: aws.Time(time.Now()), + cases := []struct { + caseName string + expected error + *options.CleanOptions + promptMock prompt.PromptRunner + *s3.ListObjectsOutput + listObjectsErr error + deleteObjectErr error + dryRun bool + autoApprove bool + }{ + { + "Success while sorting by lastModificationDate", nil, + &options.CleanOptions{ + MinFileSizeInMb: 0, + MaxFileSizeInMb: 0, + FileExtensions: "", + FileNamePrefix: "", + KeepLastNFiles: 2, + SortBy: "lastModificationDate", + RootOptions: rootoptions.GetMockedRootOptions(), }, + promptMock{ + msg: "y", + err: nil, + }, + &s3.ListObjectsOutput{ + Name: aws.String(""), + Marker: aws.String(""), + MaxKeys: aws.Int64(1000), + Prefix: aws.String(""), + IsTruncated: aws.Bool(false), + Contents: []*s3.Object{ + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), + Key: aws.String("file1.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(10000000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), + Key: aws.String("file2.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(20000000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file3.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(30000000), + LastModified: aws.Time(time.Now()), + }, + }, + }, + nil, nil, false, false, }, - } - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() - setZeroValuesForVars() -} - -func TestStartCleaningSkippedExtensions(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = true - cleanOpts.AutoApprove = true - cleanOpts.MinFileSizeInMb = 0 - cleanOpts.MaxFileSizeInMb = 0 - cleanOpts.FileExtensions = "txt" - defaultListObjectsOutput = &s3.ListObjectsOutput{ - Name: aws.String(""), - Marker: aws.String(""), - MaxKeys: aws.Int64(1000), - Prefix: aws.String(""), - IsTruncated: aws.Bool(false), - Contents: []*s3.Object{ - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), - Key: aws.String("file1.csv"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(1000), - LastModified: aws.Time(time.Now()), + { + "Success while specifying minfilesizeinmb", nil, + &options.CleanOptions{ + MinFileSizeInMb: 100, + MaxFileSizeInMb: 0, + FileExtensions: "txt", + FileNamePrefix: "", + KeepLastNFiles: 2, + SortBy: "lastModificationDate", + RootOptions: rootoptions.GetMockedRootOptions(), + }, + promptMock{ + msg: "y", + err: nil, + }, + &s3.ListObjectsOutput{ + Name: aws.String(""), + Marker: aws.String(""), + MaxKeys: aws.Int64(1000), + Prefix: aws.String(""), + IsTruncated: aws.Bool(false), + Contents: []*s3.Object{ + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), + Key: aws.String("file1.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(100000000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), + Key: aws.String("file2.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(200000000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file3.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(300000000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file4.json"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(300000000), + LastModified: aws.Time(time.Now()), + }, + }, }, + nil, nil, false, false, }, - } - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() - setZeroValuesForVars() -} - -func TestStartCleaningCase1(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = true - cleanOpts.AutoApprove = true - cleanOpts.MinFileSizeInMb = 10 - cleanOpts.MaxFileSizeInMb = 20 - cleanOpts.FileExtensions = "txt" - defaultListObjectsOutput = &s3.ListObjectsOutput{ - Name: aws.String(""), - Marker: aws.String(""), - MaxKeys: aws.Int64(1000), - Prefix: aws.String(""), - IsTruncated: aws.Bool(false), - Contents: []*s3.Object{ - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), - Key: aws.String("file1.txt"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(15000000), - LastModified: aws.Time(time.Now()), + { + "Success while specifying both minfilesizeinmb and maxfilesizeinmb", nil, + &options.CleanOptions{ + MinFileSizeInMb: 100, + MaxFileSizeInMb: 500, + FileExtensions: "", + FileNamePrefix: "", + KeepLastNFiles: 2, + SortBy: "lastModificationDate", + RootOptions: rootoptions.GetMockedRootOptions(), }, + promptMock{ + msg: "y", + err: nil, + }, + &s3.ListObjectsOutput{ + Name: aws.String(""), + Marker: aws.String(""), + MaxKeys: aws.Int64(1000), + Prefix: aws.String(""), + IsTruncated: aws.Bool(false), + Contents: []*s3.Object{ + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), + Key: aws.String("file1.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(100000000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), + Key: aws.String("file2.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(200000000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file3.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(300000000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("/"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(300000000), + LastModified: aws.Time(time.Now()), + }, + }, + }, + nil, nil, false, false, }, - } - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() - setZeroValuesForVars() -} - -func TestStartCleaningCase2(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = true - cleanOpts.AutoApprove = true - cleanOpts.MinFileSizeInMb = 10 - cleanOpts.MaxFileSizeInMb = 0 - cleanOpts.FileExtensions = "txt" - defaultListObjectsOutput = &s3.ListObjectsOutput{ - Name: aws.String(""), - Marker: aws.String(""), - MaxKeys: aws.Int64(1000), - Prefix: aws.String(""), - IsTruncated: aws.Bool(false), - Contents: []*s3.Object{ - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), - Key: aws.String("file1.txt"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(15000000), - LastModified: aws.Time(time.Now()), + { + "Success while sorting by size", nil, + &options.CleanOptions{ + MinFileSizeInMb: 0, + MaxFileSizeInMb: 10000, + FileExtensions: "", + FileNamePrefix: "", + KeepLastNFiles: 2, + SortBy: "size", + RootOptions: rootoptions.GetMockedRootOptions(), + }, + promptMock{ + msg: "y", + err: nil, + }, + &s3.ListObjectsOutput{ + Name: aws.String(""), + Marker: aws.String(""), + MaxKeys: aws.Int64(1000), + Prefix: aws.String(""), + IsTruncated: aws.Bool(false), + Contents: []*s3.Object{ + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), + Key: aws.String("file1.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(1000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), + Key: aws.String("file2.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(2000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file3.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(3000), + LastModified: aws.Time(time.Now()), + }, + }, }, + nil, nil, false, false, }, - } - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() - setZeroValuesForVars() -} - -func TestStartCleaningCase3(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = true - cleanOpts.AutoApprove = true - cleanOpts.KeepLastNFiles = 500 - cleanOpts.FileExtensions = "txt" - defaultListObjectsOutput = &s3.ListObjectsOutput{ - Name: aws.String(""), - Marker: aws.String(""), - MaxKeys: aws.Int64(1000), - Prefix: aws.String(""), - IsTruncated: aws.Bool(false), - Contents: []*s3.Object{ - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), - Key: aws.String("file1.txt"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(15000000), - LastModified: aws.Time(time.Now()), + { + "Success when dry-run enabled", nil, + &options.CleanOptions{ + MinFileSizeInMb: 0, + MaxFileSizeInMb: 0, + FileExtensions: "", + FileNamePrefix: "", + KeepLastNFiles: 2, + SortBy: "lastModificationDate", + RootOptions: rootoptions.GetMockedRootOptions(), + }, + promptMock{ + msg: "y", + err: nil, }, + &s3.ListObjectsOutput{ + Name: aws.String(""), + Marker: aws.String(""), + MaxKeys: aws.Int64(1000), + Prefix: aws.String(""), + IsTruncated: aws.Bool(false), + Contents: []*s3.Object{ + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), + Key: aws.String("file1.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(1000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), + Key: aws.String("file2.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(2000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file3.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(3000), + LastModified: aws.Time(time.Now()), + }, + }, + }, + nil, nil, true, false, }, - } - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() - setZeroValuesForVars() -} - -func TestStartCleaningSpecificFileExtensions(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = true - cleanOpts.AutoApprove = true - cleanOpts.MinFileSizeInMb = 0 - cleanOpts.MaxFileSizeInMb = 0 - cleanOpts.FileExtensions = "txt,json" - defaultListObjectsOutput = &s3.ListObjectsOutput{ - Name: aws.String(""), - Marker: aws.String(""), - MaxKeys: aws.Int64(1000), - Prefix: aws.String(""), - IsTruncated: aws.Bool(false), - Contents: []*s3.Object{ - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), - Key: aws.String("file1.txt"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(1000), - LastModified: aws.Time(time.Now()), + { + "Failure caused by get all files error", constants.ErrInjected, + &options.CleanOptions{ + MinFileSizeInMb: 0, + MaxFileSizeInMb: 0, + FileExtensions: "", + FileNamePrefix: "", + KeepLastNFiles: 2, + SortBy: "lastModificationDate", + RootOptions: rootoptions.GetMockedRootOptions(), }, - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), - Key: aws.String("file2.txt"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(2000), - LastModified: aws.Time(time.Now()), + promptMock{ + msg: "y", + err: nil, }, - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), - Key: aws.String("file3.txt"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(3000), - LastModified: aws.Time(time.Now()), + nil, constants.ErrInjected, nil, false, false, + }, + { + "Warning caused by no file to remove caused by --keep-last-n-file flag 1", nil, + &options.CleanOptions{ + MinFileSizeInMb: 0, + MaxFileSizeInMb: 0, + FileExtensions: "", + FileNamePrefix: "", + KeepLastNFiles: 5, + SortBy: "lastModificationDate", + RootOptions: rootoptions.GetMockedRootOptions(), }, - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), - Key: aws.String("file5.json"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(3000), - LastModified: aws.Time(time.Now()), + promptMock{ + msg: "y", + err: nil, }, - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), - Key: aws.String("file6.json"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(3000), - LastModified: aws.Time(time.Now()), + &s3.ListObjectsOutput{ + Name: aws.String(""), + Marker: aws.String(""), + MaxKeys: aws.Int64(1000), + Prefix: aws.String(""), + IsTruncated: aws.Bool(false), + Contents: []*s3.Object{ + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), + Key: aws.String("file1.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(1000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), + Key: aws.String("file2.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(2000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file3.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(3000), + LastModified: aws.Time(time.Now()), + }, + }, }, + nil, nil, false, false, + }, + { + "Warning caused by no file to remove caused by --keep-last-n-file flag 2", nil, + &options.CleanOptions{ + MinFileSizeInMb: 0, + MaxFileSizeInMb: 0, + FileExtensions: "", + FileNamePrefix: "", + KeepLastNFiles: 3, + SortBy: "lastModificationDate", + RootOptions: rootoptions.GetMockedRootOptions(), + }, + promptMock{ + msg: "y", + err: nil, + }, + &s3.ListObjectsOutput{ + Name: aws.String(""), + Marker: aws.String(""), + MaxKeys: aws.Int64(1000), + Prefix: aws.String(""), + IsTruncated: aws.Bool(false), + Contents: []*s3.Object{ + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), + Key: aws.String("file1.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(1000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), + Key: aws.String("file2.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(2000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file3.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(3000), + LastModified: aws.Time(time.Now()), + }, + }, + }, + nil, nil, false, false, + }, + { + "Failure caused by user terminated the process", constants.ErrUserTerminated, + &options.CleanOptions{ + MinFileSizeInMb: 0, + MaxFileSizeInMb: 0, + FileExtensions: "", + FileNamePrefix: "", + KeepLastNFiles: 2, + SortBy: "lastModificationDate", + RootOptions: rootoptions.GetMockedRootOptions(), + }, + promptMock{ + msg: "n", + err: constants.ErrInjected, + }, + &s3.ListObjectsOutput{ + Name: aws.String(""), + Marker: aws.String(""), + MaxKeys: aws.Int64(1000), + Prefix: aws.String(""), + IsTruncated: aws.Bool(false), + Contents: []*s3.Object{ + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), + Key: aws.String("file1.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(1000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), + Key: aws.String("file2.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(2000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file3.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(3000), + LastModified: aws.Time(time.Now()), + }, + }, + }, + nil, nil, false, false, + }, + { + "Failure caused by prompt error", constants.ErrInvalidInput, + &options.CleanOptions{ + MinFileSizeInMb: 0, + MaxFileSizeInMb: 0, + FileExtensions: "", + FileNamePrefix: "", + KeepLastNFiles: 2, + SortBy: "lastModificationDate", + RootOptions: rootoptions.GetMockedRootOptions(), + }, + promptMock{ + msg: "asdfadsf", + err: constants.ErrInjected, + }, + &s3.ListObjectsOutput{ + Name: aws.String(""), + Marker: aws.String(""), + MaxKeys: aws.Int64(1000), + Prefix: aws.String(""), + IsTruncated: aws.Bool(false), + Contents: []*s3.Object{ + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), + Key: aws.String("file1.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(1000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), + Key: aws.String("file2.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(2000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file3.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(3000), + LastModified: aws.Time(time.Now()), + }, + }, + }, + nil, nil, false, false, + }, + { + "Failure caused by delete files error", constants.ErrInjected, + &options.CleanOptions{ + MinFileSizeInMb: 0, + MaxFileSizeInMb: 0, + FileExtensions: "", + FileNamePrefix: "", + KeepLastNFiles: 2, + SortBy: "lastModificationDate", + RootOptions: rootoptions.GetMockedRootOptions(), + }, + promptMock{ + msg: "y", + err: nil, + }, + &s3.ListObjectsOutput{ + Name: aws.String(""), + Marker: aws.String(""), + MaxKeys: aws.Int64(1000), + Prefix: aws.String(""), + IsTruncated: aws.Bool(false), + Contents: []*s3.Object{ + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), + Key: aws.String("file1.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(1000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), + Key: aws.String("file2.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(2000), + LastModified: aws.Time(time.Now()), + }, + { + ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), + Key: aws.String("file3.txt"), + StorageClass: aws.String("STANDARD"), + Size: aws.Int64(3000), + LastModified: aws.Time(time.Now()), + }, + }, + }, + nil, constants.ErrInjected, false, true, }, } - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() - setZeroValuesForVars() -} - -func TestStartCleaningDryRunNotEqualMinMaxValues(t *testing.T) { - m := &mockS3Client{} - - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = true - cleanOpts.AutoApprove = true - cleanOpts.MinFileSizeInMb = 0 - cleanOpts.MaxFileSizeInMb = 10 - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.Nil(t, err) - - cleanOpts.SetZeroValues() -} -func TestStartCleaningListError(t *testing.T) { - m := &mockS3Client{} + for _, tc := range cases { + t.Logf("starting case %s", tc.caseName) - listObjectsErr = errors.New("dummy show error") - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = false - cleanOpts.AutoApprove = true - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.NotNil(t, err) + tc.DryRun = tc.dryRun + tc.AutoApprove = tc.autoApprove - cleanOpts.SetZeroValues() - setZeroValuesForVars() -} + defaultListObjectsOutput = tc.ListObjectsOutput + defaultListObjectsErr = tc.listObjectsErr -func TestStartCleaningDeleteError(t *testing.T) { - m := &mockS3Client{} + defaultDeleteObjectErr = tc.deleteObjectErr - deleteObjectErr = errors.New("dummy delete error") - cleanOpts := options2.GetCleanOptions() - cleanOpts.RootOptions = options.GetRootOptions() - cleanOpts.DryRun = false - cleanOpts.AutoApprove = true - promptMock := promptMock{ - msg: "y", - err: nil, - } - err := StartCleaning(m, promptMock, cleanOpts, mockLogger) - assert.NotNil(t, err) + mockSvc := &mockS3Client{} + assert.NotNil(t, mockSvc) - cleanOpts.SetZeroValues() - setZeroValuesForVars() -} - -func setZeroValuesForVars() { - listObjectsErr = nil - getObjectsErr = nil - deleteObjectErr = nil - defaultListObjectsOutput = &s3.ListObjectsOutput{ - Name: aws.String(""), - Marker: aws.String(""), - MaxKeys: aws.Int64(1000), - Prefix: aws.String(""), - IsTruncated: aws.Bool(false), - Contents: []*s3.Object{ - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5449d"), - Key: aws.String("file1.txt"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(1000), - LastModified: aws.Time(time.Now()), - }, - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e54122"), - Key: aws.String("file2.txt"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(2000), - LastModified: aws.Time(time.Now()), - }, - { - ETag: aws.String("03c0fe42b7efa3470fc99037a8e5443d"), - Key: aws.String("file3.txt"), - StorageClass: aws.String("STANDARD"), - Size: aws.Int64(3000), - LastModified: aws.Time(time.Now()), - }, - }, - } - defaultDeleteObjectOutput = &s3.DeleteObjectOutput{ - DeleteMarker: nil, - RequestCharged: nil, - VersionId: nil, + err := StartCleaning(mockSvc, tc.promptMock, tc.CleanOptions, logging.GetLogger(tc.CleanOptions.RootOptions)) + assert.Equal(t, tc.expected, err) } } diff --git a/internal/cleaner/utils.go b/internal/cleaner/utils.go index 21a365c..0d4b698 100644 --- a/internal/cleaner/utils.go +++ b/internal/cleaner/utils.go @@ -1,16 +1,15 @@ package cleaner import ( - "errors" "sort" "strings" "github.com/aws/aws-sdk-go/service/s3" - start "github.com/bilalcaliskan/s3-manager/cmd/clean/options" + "github.com/bilalcaliskan/s3-manager/cmd/clean/options" "github.com/rs/zerolog" ) -func getProperObjects(cleanOpts *start.CleanOptions, allFiles *s3.ListObjectsOutput, logger zerolog.Logger) (res []*s3.Object) { +func getProperObjects(cleanOpts *options.CleanOptions, allFiles *s3.ListObjectsOutput, logger zerolog.Logger) (res []*s3.Object) { extensions := strings.Split(cleanOpts.FileExtensions, ",") for _, v := range allFiles.Contents { @@ -29,22 +28,22 @@ func getProperObjects(cleanOpts *start.CleanOptions, allFiles *s3.ListObjectsOut return res } -func makeDecisionBySize(startOpts *start.CleanOptions, res []*s3.Object, object *s3.Object) []*s3.Object { - if (startOpts.MinFileSizeInMb == 0 && startOpts.MaxFileSizeInMb != 0) && *object.Size < startOpts.MaxFileSizeInMb*1000000 { +func makeDecisionBySize(opts *options.CleanOptions, res []*s3.Object, object *s3.Object) []*s3.Object { + if (opts.MinFileSizeInMb == 0 && opts.MaxFileSizeInMb != 0) && *object.Size < opts.MaxFileSizeInMb*1000000 { res = append(res, object) - } else if (startOpts.MinFileSizeInMb != 0 && startOpts.MaxFileSizeInMb == 0) && *object.Size >= startOpts.MinFileSizeInMb*1000000 { + } else if (opts.MinFileSizeInMb != 0 && opts.MaxFileSizeInMb == 0) && *object.Size >= opts.MinFileSizeInMb*1000000 { res = append(res, object) - } else if startOpts.MinFileSizeInMb == 0 && startOpts.MaxFileSizeInMb == 0 { + } else if opts.MinFileSizeInMb == 0 && opts.MaxFileSizeInMb == 0 { res = append(res, object) - } else if startOpts.MinFileSizeInMb != 0 && startOpts.MaxFileSizeInMb != 0 && (*object.Size >= startOpts.MinFileSizeInMb*1000000 && *object.Size < startOpts.MaxFileSizeInMb*1000000) { + } else if opts.MinFileSizeInMb != 0 && opts.MaxFileSizeInMb != 0 && (*object.Size >= opts.MinFileSizeInMb*1000000 && *object.Size < opts.MaxFileSizeInMb*1000000) { res = append(res, object) } return res } -func sortObjects(slice []*s3.Object, startOpts *start.CleanOptions) { - switch startOpts.SortBy { +func sortObjects(slice []*s3.Object, opts *options.CleanOptions) { + switch opts.SortBy { case "lastModificationDate": sort.Slice(slice, func(i, j int) bool { return slice[i].LastModified.Before(*slice[j].LastModified) @@ -56,14 +55,6 @@ func sortObjects(slice []*s3.Object, startOpts *start.CleanOptions) { } } -func checkLength(targetObjects []*s3.Object) error { - if len(targetObjects) == 0 { - return errors.New("no deletable file found on the target bucket") - } - - return nil -} - func arrayContains(sl []string, name string) bool { for _, value := range sl { if strings.Contains(name, value) { diff --git a/internal/constants/constants.go b/internal/constants/constants.go new file mode 100644 index 0000000..d14e829 --- /dev/null +++ b/internal/constants/constants.go @@ -0,0 +1,9 @@ +package constants + +import "errors" + +var ( + ErrInjected = errors.New("injected error") + ErrUserTerminated = errors.New("user terminated the process") + ErrInvalidInput = errors.New("invalid input") +) diff --git a/internal/utils/utils.go b/internal/utils/utils.go index c5501a7..815fe9f 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -2,6 +2,13 @@ package utils import ( "encoding/json" + "errors" + + "github.com/aws/aws-sdk-go/service/s3/s3iface" + "github.com/bilalcaliskan/s3-manager/cmd/root/options" + "github.com/bilalcaliskan/s3-manager/internal/logging" + "github.com/rs/zerolog" + "github.com/spf13/cobra" "github.com/aws/aws-sdk-go/service/s3" ) @@ -51,3 +58,20 @@ func BeautifyJSON(jsonString string) (string, error) { return string(beautifiedBytes), nil } + +func PrepareConstants(cmd *cobra.Command) (s3iface.S3API, *options.RootOptions, zerolog.Logger) { + svc := cmd.Context().Value(options.S3SvcKey{}).(s3iface.S3API) + rootOpts := cmd.Context().Value(options.OptsKey{}).(*options.RootOptions) + + return svc, rootOpts, logging.GetLogger(rootOpts) +} + +func CheckArgs(args []string, allowed int) error { + if len(args) > allowed { + return errors.New("too many arguments provided") + } else if len(args) < allowed { + return errors.New("too few arguments provided") + } + + return nil +} diff --git a/testdata/file4.json b/testdata/file4.json new file mode 100644 index 0000000..d4adaa4 --- /dev/null +++ b/testdata/file4.json @@ -0,0 +1,19 @@ +{ + "id": 8, + "title": "Microsoft Surface Laptop 4", + "description": "Style and speed. Stand out on ...", + "price": 1499, + "discountPercentage": 10.23, + "rating": 4.43, + "stock": 68, + "brand": "Microsoft Surface", + "category": "laptops", + "thumbnail": "https://i.dummyjson.com/data/products/8/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/8/1.jpg", + "https://i.dummyjson.com/data/products/8/2.jpg", + "https://i.dummyjson.com/data/products/8/3.jpg", + "https://i.dummyjson.com/data/products/8/4.jpg", + "https://i.dummyjson.com/data/products/8/thumbnail.jpg" + ] +}