├── .whitesource ├── .gitignore ├── aspera ├── version.go ├── subscriber.go ├── utils.go ├── utils_test.go └── mocks │ └── TransferService_MonitorTransfersClient.go ├── bin ├── update-translation-files ├── generate-i18n-resources ├── catch-missing-strings ├── format-translation-files └── download-aspera-mod ├── errors ├── code.go ├── endpoints_error.go ├── objectgeterrorcause_string.go ├── get_object_error.go ├── commanderrorcause_string.go ├── endpointserrorcause_string.go └── command.go ├── version └── version.go ├── config ├── app │ ├── command_template.go │ └── custom_on_usage_error.go ├── config_keys.go ├── fields │ └── fields_names.go └── flags │ └── flags_names.go ├── utils ├── terminal.go ├── cli_version_handler.go └── test_utils.go ├── i18n ├── detection │ └── detection.go └── i18n.go ├── functions ├── class.go ├── version.go ├── version_test.go ├── bucket_website_get.go ├── bucket_replication_get.go ├── bucket_versioning_get.go ├── public_access_block_get.go ├── object_lock_configuration_get.go ├── bucket_replication_delete.go ├── bucket_lifecycle_configuration_delete.go ├── bucket_website_put.go ├── object_tagging_get.go ├── bucket_lifecycle_configuration_get.go ├── object_retention_get.go ├── object_legal_hold_get.go ├── bucket_versioning_put.go ├── bucket_replication_put.go ├── object_lock_configuration_put.go ├── object_tagging_put.go ├── public_access_block_put.go ├── object_retention_put.go ├── bucket_lifecycle_configuration_put.go ├── object_legal_hold_put.go ├── bucket_cors_delete.go ├── bucket_cors_get.go ├── bucket_list.go ├── endpoints.go ├── public_access_block_delete.go ├── bucket_cors_put.go ├── object_deletes.go ├── object_tagging_delete.go ├── multipart_abort.go ├── multipart_complete.go ├── bucket_head.go ├── object_head.go ├── part_upload.go ├── wait_bucket.go ├── bucket_website_delete.go ├── bucket_delete.go ├── object_delete.go ├── multipart_create.go ├── object_put.go ├── part_upload_copy.go ├── wait_object.go ├── aspera_upload.go ├── object_copy.go ├── bucket_list_test.go ├── wait_bucket_test.go ├── upload.go ├── bucket_create.go ├── download.go ├── bucket_head_test.go ├── upload_test.go ├── bucket_list_extended.go ├── bucket_cors_get_test.go ├── wait_object_test.go ├── bucket_cors_delete_test.go └── object_list.go ├── di ├── providers │ ├── mocks │ │ ├── ListKnownRegions.go │ │ ├── Plugin.go │ │ ├── AsperaTransfer.go │ │ ├── Resolver.go │ │ ├── ReadSeekerCloser.go │ │ ├── Uploader.go │ │ ├── Downloader.go │ │ └── FileOperations.go │ └── app_context_unit.go └── injectors │ ├── wire.go │ └── wire_gen.go ├── main.go ├── render ├── render.go ├── messages.go └── helpers.go ├── .pre-commit-config.yaml ├── Makefile └── go.mod /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "settingsInheritedFrom": "whitesource-config/whitesource-config@master" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/** 2 | .DS_Store 3 | .vscode/** 4 | main 5 | **/*.DS_Store 6 | vendor/** 7 | build/** 8 | logs/** 9 | 10 | 11 | -------------------------------------------------------------------------------- /aspera/version.go: -------------------------------------------------------------------------------- 1 | package aspera 2 | 3 | const ( 4 | version = "1.1.2" 5 | commit = "753f662" 6 | prefix = "https://production-transfer-sdk.s3.us-west-2.amazonaws.com" 7 | ) 8 | -------------------------------------------------------------------------------- /bin/update-translation-files: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ROOT_DIR=$(cd $(dirname $(dirname $0)) && pwd) 6 | $ROOT_DIR/bin/format-translation-files 7 | $ROOT_DIR/bin/generate-i18n-resources -------------------------------------------------------------------------------- /bin/generate-i18n-resources: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo "Generating i18n resource file ..." 6 | $( go env GOPATH )/bin/go-bindata -pkg resources -o resources/i18n_resources.go i18n/resources 7 | echo "Done." 8 | -------------------------------------------------------------------------------- /errors/code.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | // CodeError Interface allows classify and handle errors using their code 4 | type CodeError interface { 5 | // Satisfy the generic error interface. 6 | error 7 | 8 | // Returns the Error Code 9 | Code() string 10 | } 11 | -------------------------------------------------------------------------------- /bin/catch-missing-strings: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ROOT_DIR=$(cd $(dirname $(dirname $0)) && pwd) 4 | 5 | i18n4go -c checkup -v | sed -E 's/(.+) exists in the code, but not in en_US/{"id": \1, "translation": \1},/g' 6 | # i18n4go -c checkup -v | sed -E "s/(.+) exists in the code, but not in en_US/\1/g" 7 | # cd .. 8 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 4 | 5 | // CLIName is the name of the IBM Cloud 6 | // Object Storage CLI 7 | const CLIName = "ibm-cloud-cos-cli" 8 | 9 | // CLIVersion is the version of the CLI 10 | var CLIVersion = plugin.VersionType{ 11 | Major: 1, 12 | Minor: 10, 13 | Build: 3, 14 | } 15 | -------------------------------------------------------------------------------- /errors/endpoints_error.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import "fmt" 4 | 5 | //go:generate stringer -type=EndpointsErrorCause 6 | type EndpointsErrorCause int 7 | 8 | const ( 9 | _ EndpointsErrorCause = iota 10 | EndpointsRegionInvalid 11 | EndpointsRegionEmpty 12 | EndpointsStoredRegionInvalid 13 | ) 14 | 15 | type EndpointsError struct { 16 | Region string 17 | Cause EndpointsErrorCause 18 | } 19 | 20 | func (ee *EndpointsError) Error() string { 21 | return fmt.Sprintf("Cause: %s, Region: %s", ee.Cause.String(), ee.Region) 22 | } 23 | -------------------------------------------------------------------------------- /config/app/command_template.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | // Command Help Template 4 | // Purpose: Every command has a unique usage message with flags and their descriptions 5 | // 6 | // This template maps the text for each command and outputs it in a help message 7 | // for convenience. 8 | var CommandHelpTemplate = `NAME: 9 | {{.HelpName}}{{if .Description}} - {{.Description}}{{end}} 10 | 11 | USAGE: 12 | {{.HelpName}}{{if .UsageText}} {{.UsageText}}{{end}}{{if .ArgsUsage}} {{.ArgsUsage}}{{end}}{{if .Flags}} 13 | 14 | OPTIONS: 15 | {{range .Flags}}{{.}} 16 | {{end}}{{end}} 17 | ` 18 | -------------------------------------------------------------------------------- /utils/terminal.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/terminal" 7 | ) 8 | 9 | // Locally modified terminal color 10 | // https://github.com/IBM-Cloud/ibm-cloud-cli-sdk/issues/180 11 | func EntityNameColor(in string) string { 12 | return terminal.EntityNameColor(SaneEntityForColorTerm(in)) 13 | } 14 | 15 | // Convert from %(!NOZERO) to % to remove the IBMCloud CLI bug 16 | // https://github.com/IBM-Cloud/ibm-cloud-cli-sdk/issues/180 17 | func SaneEntityForColorTerm(in string) string { 18 | return strings.Replace(in, "%", "%%", -1) 19 | } 20 | -------------------------------------------------------------------------------- /errors/objectgeterrorcause_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=ObjectGetErrorCause"; DO NOT EDIT. 2 | 3 | package errors 4 | 5 | import "strconv" 6 | 7 | const _ObjectGetErrorCause_name = "IsDirOpening" 8 | 9 | var _ObjectGetErrorCause_index = [...]uint8{0, 5, 12} 10 | 11 | func (i ObjectGetErrorCause) String() string { 12 | i -= 1 13 | if i < 0 || i >= ObjectGetErrorCause(len(_ObjectGetErrorCause_index)-1) { 14 | return "ObjectGetErrorCause(" + strconv.FormatInt(int64(i+1), 10) + ")" 15 | } 16 | return _ObjectGetErrorCause_name[_ObjectGetErrorCause_index[i]:_ObjectGetErrorCause_index[i+1]] 17 | } 18 | -------------------------------------------------------------------------------- /i18n/detection/detection.go: -------------------------------------------------------------------------------- 1 | package detection 2 | 3 | import "github.com/cloudfoundry/jibber_jabber" 4 | 5 | type Detector interface { 6 | DetectLocale() string 7 | DetectLanguage() string 8 | } 9 | 10 | type JibberJabberDetector struct{} 11 | 12 | func (d *JibberJabberDetector) DetectLocale() string { 13 | userLocale, err := jibber_jabber.DetectIETF() 14 | if err != nil { 15 | userLocale = "" 16 | } 17 | return userLocale 18 | } 19 | 20 | func (d *JibberJabberDetector) DetectLanguage() string { 21 | lang, err := jibber_jabber.DetectLanguage() 22 | if err != nil { 23 | lang = "" 24 | } 25 | return lang 26 | } 27 | -------------------------------------------------------------------------------- /errors/get_object_error.go: -------------------------------------------------------------------------------- 1 | //go:generate stringer -type=ObjectGetErrorCause 2 | 3 | package errors 4 | 5 | import "fmt" 6 | 7 | type ObjectGetErrorCause int 8 | 9 | const ( 10 | _ ObjectGetErrorCause = iota 11 | IsDir 12 | Opening 13 | ) 14 | 15 | type ObjectGetError struct { 16 | _ struct{} 17 | ParentError error 18 | Location string 19 | UsingDefaultRule bool 20 | Cause ObjectGetErrorCause 21 | } 22 | 23 | func (oge *ObjectGetError) Error() string { 24 | return fmt.Sprintf("Cause: %s, Location %s, DefaultRule: %t, Parent: %s", 25 | oge.Cause.String(), oge.Location, oge.UsingDefaultRule, oge.ParentError) 26 | } 27 | -------------------------------------------------------------------------------- /functions/class.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import "strings" 4 | 5 | // DefaultClass variable = Standard 6 | var DefaultClass = Class("standard") 7 | 8 | // Class type as string 9 | type Class string 10 | 11 | // Helper function for class of a bucket 12 | func (c Class) String() string { 13 | if c == "" { 14 | return strings.Title(string(DefaultClass)) 15 | } 16 | 17 | // Check if the class is cold, append "Cold Vault" 18 | if c == "cold" { 19 | return "Cold Vault" 20 | } 21 | 22 | // Check if the class is onerate_active, return "One-rate Active" 23 | if c == "onerate_active" { 24 | return "One-rate Active" 25 | } 26 | 27 | // Return 28 | return strings.Title(string(c)) 29 | } 30 | -------------------------------------------------------------------------------- /errors/commanderrorcause_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=CommandErrorCause"; DO NOT EDIT. 2 | 3 | package errors 4 | 5 | import "strconv" 6 | 7 | const _CommandErrorCause_name = "BadFlagSyntaxNotDefinedFlagInvalidBooleanValueInvalidBooleanFlagMissingValueInvalidValueMissingRequiredFlag" 8 | 9 | var _CommandErrorCause_index = [...]uint8{0, 13, 27, 46, 64, 76, 88, 107} 10 | 11 | func (i CommandErrorCause) String() string { 12 | i -= 1 13 | if i < 0 || i >= CommandErrorCause(len(_CommandErrorCause_index)-1) { 14 | return "CommandErrorCause(" + strconv.FormatInt(int64(i+1), 10) + ")" 15 | } 16 | return _CommandErrorCause_name[_CommandErrorCause_index[i]:_CommandErrorCause_index[i+1]] 17 | } 18 | -------------------------------------------------------------------------------- /di/providers/mocks/ListKnownRegions.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.0.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // GetAllRegions is an autogenerated mock type for the GetAllRegions type 8 | type ListKnownRegions struct { 9 | mock.Mock 10 | } 11 | 12 | // GetAllRegions provides a mock function with given fields: 13 | func (_m *ListKnownRegions) GetAllRegions() ([]string, error) { 14 | ret := _m.Called() 15 | 16 | var r0 []string 17 | if rf, ok := ret.Get(0).(func() []string); ok { 18 | r0 = rf() 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).([]string) 22 | } 23 | } 24 | 25 | var r1 error 26 | if rf, ok := ret.Get(1).(func() error); ok { 27 | r1 = rf() 28 | } else { 29 | r1 = ret.Error(1) 30 | } 31 | 32 | return r0, r1 33 | } 34 | -------------------------------------------------------------------------------- /di/providers/mocks/Plugin.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.0.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | import plugin "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 7 | 8 | // Plugin is an autogenerated mock type for the Plugin type 9 | type Plugin struct { 10 | mock.Mock 11 | } 12 | 13 | // GetMetadata provides a mock function with given fields: 14 | func (_m *Plugin) GetMetadata() plugin.PluginMetadata { 15 | ret := _m.Called() 16 | 17 | var r0 plugin.PluginMetadata 18 | if rf, ok := ret.Get(0).(func() plugin.PluginMetadata); ok { 19 | r0 = rf() 20 | } else { 21 | r0 = ret.Get(0).(plugin.PluginMetadata) 22 | } 23 | 24 | return r0 25 | } 26 | 27 | // Run provides a mock function with given fields: c, args 28 | func (_m *Plugin) Run(c plugin.PluginContext, args []string) { 29 | _m.Called(c, args) 30 | } 31 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 7 | "github.com/IBM/ibmcloud-cos-cli/cos" 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | // Start of the COS CLI Plugin Main program 12 | func main() { 13 | // var to hold exit code 14 | exitCode := 0 15 | 16 | // urfave-cli process some exit codes before bubble them up, 17 | // this wrapper will help the exit error bubble up until the error render 18 | cli.OsExiter = wrapExiter(&exitCode) 19 | // Start starts the plugin - 20 | // Calling to the IBM Cloud CLI SDK 21 | plugin.Start(new(cos.Plugin)) 22 | 23 | // Exit with an exit code 24 | os.Exit(exitCode) 25 | } 26 | 27 | func wrapExiter(exitCodeHolder *int) func(int) { 28 | return func(exitCode int) { 29 | if exitCodeHolder != nil && *exitCodeHolder == 0 { 30 | *exitCodeHolder = exitCode 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /utils/cli_version_handler.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/aws/request" 5 | "github.com/IBM/ibmcloud-cos-cli/version" 6 | ) 7 | 8 | // User Agent Handler 9 | var CLIVersionUserAgentHandler = request.NamedHandler{ 10 | Name: "plugin.CliVersionUserAgentHandler", 11 | Fn: AddCLIVersionToUserAgent, 12 | } 13 | 14 | // AddCLIVersionToUserAgent - Build a request to include 15 | // CLI in the header 16 | func AddCLIVersionToUserAgent(r *request.Request) { 17 | // Concatenate both CLI name and version 18 | cliUAgent := version.CLIName + "/" + version.CLIVersion.String() 19 | // Grab the HTTP request header's User-Agent 20 | uAgent := r.HTTPRequest.Header.Get("User-Agent") 21 | if len(uAgent) > 0 { 22 | uAgent = cliUAgent + ", " + uAgent 23 | } 24 | // Set the CLI into User Agent header 25 | r.HTTPRequest.Header.Set("User-Agent", uAgent) 26 | } 27 | -------------------------------------------------------------------------------- /functions/version.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | "github.com/IBM/ibmcloud-cos-cli/utils" 6 | "github.com/urfave/cli" 7 | ) 8 | 9 | // Version prints out the version of cos plugin 10 | // Parameter: 11 | // 12 | // CLI Context Application 13 | // 14 | // Returns: 15 | // 16 | // Error = zero or non-zero 17 | func Version(c *cli.Context) (err error) { 18 | if c.NArg() > 0 { 19 | err = &errors.CommandError{ 20 | CLIContext: c, 21 | Cause: errors.InvalidNArg, 22 | } 23 | return 24 | } 25 | 26 | var cosContext *utils.CosContext 27 | if cosContext, err = GetCosContext(c); err != nil { 28 | return 29 | } 30 | 31 | pluginName := "ibmcloud cos cli plugin" 32 | version := c.App.Version 33 | 34 | // Print version here using terminal.UI 35 | // If we use render.Display it will print OK before printing version 36 | cosContext.UI.Print("%s %s", pluginName, version) 37 | 38 | return 39 | } 40 | -------------------------------------------------------------------------------- /errors/endpointserrorcause_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=EndpointsErrorCause"; DO NOT EDIT. 2 | 3 | package errors 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[EndpointsRegionInvalid-1] 12 | _ = x[EndpointsRegionEmpty-2] 13 | _ = x[EndpointsStoredRegionInvalid-3] 14 | } 15 | 16 | const _EndpointsErrorCause_name = "EndpointsRegionInvalidEndpointsRegionEmptyEndpointsStoredRegionInvalid" 17 | 18 | var _EndpointsErrorCause_index = [...]uint8{0, 22, 42, 70} 19 | 20 | func (i EndpointsErrorCause) String() string { 21 | i -= 1 22 | if i < 0 || i >= EndpointsErrorCause(len(_EndpointsErrorCause_index)-1) { 23 | return "EndpointsErrorCause(" + strconv.FormatInt(int64(i+1), 10) + ")" 24 | } 25 | return _EndpointsErrorCause_name[_EndpointsErrorCause_index[i]:_EndpointsErrorCause_index[i+1]] 26 | } 27 | -------------------------------------------------------------------------------- /di/providers/mocks/AsperaTransfer.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.9.4. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | context "context" 7 | 8 | aspera "github.com/IBM/ibmcloud-cos-cli/aspera" 9 | 10 | mock "github.com/stretchr/testify/mock" 11 | ) 12 | 13 | // AsperaTransfer is an autogenerated mock type for the AsperaTransfer type 14 | type AsperaTransfer struct { 15 | mock.Mock 16 | } 17 | 18 | // Download provides a mock function with given fields: _a0, _a1 19 | func (_m *AsperaTransfer) Download(_a0 context.Context, _a1 *aspera.COSInput) error { 20 | ret := _m.Called(_a0, _a1) 21 | 22 | var r0 error 23 | if rf, ok := ret.Get(0).(func(context.Context, *aspera.COSInput) error); ok { 24 | r0 = rf(_a0, _a1) 25 | } else { 26 | r0 = ret.Error(0) 27 | } 28 | 29 | return r0 30 | } 31 | 32 | // Upload provides a mock function with given fields: _a0, _a1 33 | func (_m *AsperaTransfer) Upload(_a0 context.Context, _a1 *aspera.COSInput) error { 34 | ret := _m.Called(_a0, _a1) 35 | 36 | var r0 error 37 | if rf, ok := ret.Get(0).(func(context.Context, *aspera.COSInput) error); ok { 38 | r0 = rf(_a0, _a1) 39 | } else { 40 | r0 = ret.Error(0) 41 | } 42 | 43 | return r0 44 | } 45 | -------------------------------------------------------------------------------- /errors/command.go: -------------------------------------------------------------------------------- 1 | //go:generate stringer -type=CommandErrorCause 2 | 3 | package errors 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | type CommandErrorCause int 12 | 13 | const CommandErrorCode = "CommandError" 14 | 15 | // Enum Failing flag parse 16 | const ( 17 | _ CommandErrorCause = iota 18 | BadFlagSyntax 19 | NotDefinedFlag 20 | InvalidBooleanValue 21 | InvalidBooleanFlag 22 | MissingValue 23 | InvalidValue 24 | MissingRequiredFlag 25 | InvalidNArg 26 | InvalidDisplayValue 27 | ) 28 | 29 | type CommandError struct { 30 | IError error 31 | CLIContext *cli.Context 32 | Flag string 33 | Cause CommandErrorCause 34 | } 35 | 36 | func (_ *CommandError) Code() string { 37 | return CommandErrorCode 38 | } 39 | 40 | func (ce *CommandError) Error() string { 41 | if ce.IError != nil { 42 | return ce.IError.Error() 43 | } 44 | return fmt.Sprintf("%s: flag='%s' cause=%s", ce.Code(), ce.Flag, ce.Cause) 45 | } 46 | 47 | func CreateCommandError(c *cli.Context, cause CommandErrorCause, flag string, err error) error { 48 | return &CommandError{ 49 | CLIContext: c, 50 | Cause: cause, 51 | Flag: flag, 52 | IError: err, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /di/injectors/wire.go: -------------------------------------------------------------------------------- 1 | //+build wireinject 2 | 3 | package injectors 4 | 5 | import ( 6 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 7 | "github.com/IBM/ibm-cos-sdk-go/aws/endpoints" 8 | "github.com/IBM/ibmcloud-cos-cli/render" 9 | "github.com/google/wire" 10 | 11 | "github.com/IBM/ibmcloud-cos-cli/di/providers" 12 | 13 | "github.com/IBM/ibmcloud-cos-cli/utils" 14 | ) 15 | 16 | func InitializeCosContext(_ plugin.PluginContext) (*utils.CosContext, error) { 17 | wire.Build( 18 | utils.CosContext{}, 19 | providers.NewUI, 20 | render.NewTextRender, 21 | render.NewJSONRender, 22 | render.NewErrorRender, 23 | providers.GetS3APIFn, 24 | providers.GetDownloaderAPIFn, 25 | providers.GetUploaderAPIFn, 26 | providers.GetAsperaTransferFn, 27 | providers.GetPluginConfig, 28 | providers.NewSession, 29 | providers.NewConfig, 30 | providers.NewCOSEndPointsWSClient, 31 | wire.Bind(new(utils.ListKnownRegions), new(utils.COSEndPointsWSClient)), 32 | wire.Bind(new(endpoints.Resolver), new(utils.COSEndPointsWSClient)), 33 | providers.GetFileOperations, 34 | wire.Bind(new(utils.FileOperations), new(providers.FileOperationsImpl)), 35 | providers.GetBaseConfig, 36 | ) 37 | 38 | return nil, nil 39 | } 40 | -------------------------------------------------------------------------------- /di/providers/mocks/Resolver.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.0.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import endpoints "github.com/IBM/ibm-cos-sdk-go/aws/endpoints" 6 | import mock "github.com/stretchr/testify/mock" 7 | 8 | // Resolver is an autogenerated mock type for the Resolver type 9 | type Resolver struct { 10 | mock.Mock 11 | } 12 | 13 | // EndpointFor provides a mock function with given fields: service, region, opts 14 | func (_m *Resolver) EndpointFor(service string, region string, opts ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) { 15 | _va := make([]interface{}, len(opts)) 16 | for _i := range opts { 17 | _va[_i] = opts[_i] 18 | } 19 | var _ca []interface{} 20 | _ca = append(_ca, service, region) 21 | _ca = append(_ca, _va...) 22 | ret := _m.Called(_ca...) 23 | 24 | var r0 endpoints.ResolvedEndpoint 25 | if rf, ok := ret.Get(0).(func(string, string, ...func(*endpoints.Options)) endpoints.ResolvedEndpoint); ok { 26 | r0 = rf(service, region, opts...) 27 | } else { 28 | r0 = ret.Get(0).(endpoints.ResolvedEndpoint) 29 | } 30 | 31 | var r1 error 32 | if rf, ok := ret.Get(1).(func(string, string, ...func(*endpoints.Options)) error); ok { 33 | r1 = rf(service, region, opts...) 34 | } else { 35 | r1 = ret.Error(1) 36 | } 37 | 38 | return r0, r1 39 | } 40 | -------------------------------------------------------------------------------- /render/render.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | ) 6 | 7 | // GetBucketClassOutput type alias to separate get location from get class 8 | type GetBucketClassOutput s3.GetBucketLocationOutput 9 | 10 | // DownloadOutput type to wrap s3 download result 11 | type DownloadOutput struct { 12 | _ struct{} 13 | TotalBytes int64 14 | } 15 | 16 | // AsperaUpload for displaying different results for json and text 17 | type AsperaUploadOutput struct { 18 | TotalBytes int64 19 | } 20 | 21 | // Display type - JSON or Text 22 | type Display interface { 23 | Display(interface{}, interface{}, map[string]interface{}) error 24 | } 25 | 26 | type Regions struct { 27 | ServiceType string `json:",omitempty"` 28 | Region []string `json:",omitempty"` 29 | } 30 | 31 | type RegionEndpointsOutput struct { 32 | ServiceType *string `json:",omitempty"` 33 | Region *string `json:",omitempty"` 34 | Endpoints *PublicPrivateDirectEPOutput `json:",omitempty"` 35 | Regions []*Regions `json:",omitempty"` 36 | } 37 | 38 | type PublicPrivateDirectEPOutput struct { 39 | Public map[string]*string `json:",omitempty"` 40 | Private map[string]*string `json:",omitempty"` 41 | Direct map[string]*string `json:",omitempty"` 42 | } 43 | -------------------------------------------------------------------------------- /functions/version_test.go: -------------------------------------------------------------------------------- 1 | //go:build unit 2 | // +build unit 3 | 4 | package functions_test 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | 10 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 11 | "github.com/IBM/ibmcloud-cos-cli/config/commands" 12 | "github.com/IBM/ibmcloud-cos-cli/cos" 13 | "github.com/IBM/ibmcloud-cos-cli/di/providers" 14 | "github.com/stretchr/testify/assert" 15 | "github.com/urfave/cli" 16 | ) 17 | 18 | func TestVersionSunnyPath(t *testing.T) { 19 | // Reset mocks after the test 20 | defer providers.MocksRESET() 21 | 22 | // --- Arrange --- 23 | // Disable and capture OS EXIT 24 | var exitCode *int 25 | cli.OsExiter = func(ec int) { 26 | // Capture the exit code 27 | exitCode = &ec 28 | } 29 | 30 | // --- Act ---- 31 | // Set OS args for the test 32 | os.Args = []string{"-", commands.Version} 33 | 34 | // Call the plugin 35 | plugin.Start(new(cos.Plugin)) 36 | 37 | // --- Assert ---- 38 | // Capture all output and errors 39 | output := providers.FakeUI.Outputs() 40 | errors := providers.FakeUI.Errors() 41 | 42 | // Assert that the exit code is zero 43 | assert.Equal(t, (*int)(nil), exitCode) 44 | // Assert that the output contains the expected string 45 | assert.Contains(t, output, "ibmcloud cos cli plugin") 46 | // Assert that there are no errors 47 | assert.NotContains(t, errors, "FAIL") 48 | } 49 | -------------------------------------------------------------------------------- /aspera/subscriber.go: -------------------------------------------------------------------------------- 1 | package aspera 2 | 3 | import ( 4 | "io" 5 | 6 | sdk "github.com/IBM/ibmcloud-cos-cli/aspera/transfersdk" 7 | "gopkg.in/cheggaaa/pb.v1" 8 | ) 9 | 10 | type Subscriber interface { 11 | Queued(resp *sdk.TransferResponse) 12 | Running(resp *sdk.TransferResponse) 13 | Done(resp *sdk.TransferResponse) 14 | } 15 | 16 | type DefaultSubscriber struct{} 17 | 18 | // Do Nothing by default 19 | func (b *DefaultSubscriber) Queued(resp *sdk.TransferResponse) {} 20 | func (b *DefaultSubscriber) Running(resp *sdk.TransferResponse) {} 21 | func (b *DefaultSubscriber) Done(resp *sdk.TransferResponse) {} 22 | 23 | // display progress bar for file transfer 24 | type ProgressBarSubscriber struct { 25 | bar *pb.ProgressBar 26 | } 27 | 28 | func NewProgressBarSubscriber(total int64, out io.Writer) *ProgressBarSubscriber { 29 | bar := pb.New64(total).SetUnits(pb.U_BYTES) 30 | bar.Output = out 31 | return &ProgressBarSubscriber{bar: bar} 32 | } 33 | 34 | func (p *ProgressBarSubscriber) Queued(resp *sdk.TransferResponse) { 35 | p.bar.Prefix("Queued") 36 | p.bar.Start() 37 | } 38 | 39 | func (p *ProgressBarSubscriber) Running(resp *sdk.TransferResponse) { 40 | p.bar.Prefix("Running") 41 | p.bar.Set(int(resp.TransferInfo.BytesTransferred)) 42 | } 43 | 44 | func (p *ProgressBarSubscriber) Done(resp *sdk.TransferResponse) { 45 | p.bar.Finish() 46 | } 47 | -------------------------------------------------------------------------------- /utils/test_utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/aws" 7 | ) 8 | 9 | type stringWrap struct { 10 | *bytes.Reader 11 | closeStatus *bool 12 | } 13 | 14 | func (sw *stringWrap) Close() error { 15 | if sw.closeStatus != nil { 16 | *(sw.closeStatus) = true 17 | } 18 | return nil 19 | } 20 | 21 | func WrapString(input string, closeStatus *bool) ReadSeekerCloser { 22 | return &stringWrap{ 23 | Reader: bytes.NewReader([]byte(input)), 24 | closeStatus: closeStatus, 25 | } 26 | } 27 | 28 | type writeToString struct { 29 | aws.WriteAtBuffer 30 | output *string 31 | closeStatus *bool 32 | } 33 | 34 | func (sw *writeToString) WriteAt(p []byte, off int64) (n int, err error) { 35 | n, err = sw.WriteAtBuffer.WriteAt(p, off) 36 | if sw.output != nil { 37 | *sw.output = string(sw.WriteAtBuffer.Bytes()) 38 | } 39 | return 40 | } 41 | 42 | func (sw *writeToString) Write(p []byte) (n int, err error) { 43 | return sw.WriteAt(p, int64(len(sw.WriteAtBuffer.Bytes()))) 44 | } 45 | 46 | func (sw *writeToString) Close() error { 47 | if sw.closeStatus != nil { 48 | *(sw.closeStatus) = true 49 | } 50 | return nil 51 | } 52 | 53 | func WriteToString(output *string, closeStatus *bool) WriteCloser { 54 | return &writeToString{ 55 | output: output, 56 | closeStatus: closeStatus, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # This is an example configuration to enable detect-secrets in the pre-commit hook. 2 | # Add this file to the root folder of your repository. 3 | # 4 | # Read pre-commit hook framework https://pre-commit.com/ for more details about the structure of config yaml file and how git pre-commit would invoke each hook. 5 | # 6 | # This line indicates we will use the hook from ibm/detect-secrets to run scan during committing phase. 7 | repos: 8 | - repo: https://github.com/ibm/detect-secrets 9 | # If you desire to use a specific version of detect-secrets, you can replace `master` with other git revisions such as branch, tag or commit sha. 10 | # You are encouraged to use static refs such as tags, instead of branch name 11 | # 12 | # Running "pre-commit autoupdate" automatically updates rev to latest tag 13 | rev: 0.13.1+ibm.61.dss 14 | hooks: 15 | - id: detect-secrets # pragma: whitelist secret 16 | # Add options for detect-secrets-hook binary. You can run `detect-secrets-hook --help` to list out all possible options. 17 | # You may also run `pre-commit run detect-secrets` to preview the scan result. 18 | # when "--baseline" without "--use-all-plugins", pre-commit scan with just plugins in baseline file 19 | # when "--baseline" with "--use-all-plugins", pre-commit scan with all available plugins 20 | # add "--fail-on-unaudited" to fail pre-commit for unaudited potential secrets 21 | args: [--baseline, .secrets.baseline, --use-all-plugins] 22 | -------------------------------------------------------------------------------- /render/messages.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | . "github.com/IBM/ibmcloud-cos-cli/i18n" 6 | ) 7 | 8 | // 9 | // WarningDeleteBucket - bucket delete message 10 | func WarningDeleteBucket(input *s3.DeleteBucketInput) string { 11 | return T("WARNING: This will permanently delete the bucket '{{.Bucket}}' from your account.", input) 12 | } 13 | 14 | // WarningDeleteBucketWebsite - bucket website delete message 15 | func WarningDeleteBucketWebsite(input *s3.DeleteBucketWebsiteInput) string { 16 | return T("WARNING: This will permanently delete any bucket website configuration from the bucket '{{.Bucket}}'.", input) 17 | } 18 | 19 | // WarningDeleteObject - object delete message 20 | func WarningDeleteObject(input *s3.DeleteObjectInput) string { 21 | return T("WARNING: This will permanently delete the object '{{.Key}}' from the bucket '{{.Bucket}}'.", input) 22 | } 23 | 24 | // WarningGetObject - object get message 25 | func WarningGetObject(file, location string) string { 26 | return T("WARNING: An object with the name '{{.file}}' already exists at '{{.dl}}'.", 27 | map[string]interface{}{"file": file, "dl": location}) 28 | } 29 | 30 | // MessageConfirmationContinue Confirmation message 31 | func MessageConfirmationContinue() string { return T("Are you sure you would like to continue?") } 32 | 33 | // MessageOperationCanceled - Operation cancellation message 34 | func MessageOperationCanceled() string { return T("Operation canceled.") } 35 | 36 | func MessageAsperaBinaryNotFound() string { 37 | return T("Aspera Transferd binary not found. Installing now...") 38 | } 39 | -------------------------------------------------------------------------------- /di/providers/mocks/ReadSeekerCloser.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.0.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // ReadSeekerCloser is an autogenerated mock type for the ReadSeekerCloser type 8 | type ReadSeekerCloser struct { 9 | mock.Mock 10 | } 11 | 12 | // Close provides a mock function with given fields: 13 | func (_m *ReadSeekerCloser) Close() error { 14 | ret := _m.Called() 15 | 16 | var r0 error 17 | if rf, ok := ret.Get(0).(func() error); ok { 18 | r0 = rf() 19 | } else { 20 | r0 = ret.Error(0) 21 | } 22 | 23 | return r0 24 | } 25 | 26 | // Read provides a mock function with given fields: p 27 | func (_m *ReadSeekerCloser) Read(p []byte) (int, error) { 28 | ret := _m.Called(p) 29 | 30 | var r0 int 31 | if rf, ok := ret.Get(0).(func([]byte) int); ok { 32 | r0 = rf(p) 33 | } else { 34 | r0 = ret.Get(0).(int) 35 | } 36 | 37 | var r1 error 38 | if rf, ok := ret.Get(1).(func([]byte) error); ok { 39 | r1 = rf(p) 40 | } else { 41 | r1 = ret.Error(1) 42 | } 43 | 44 | return r0, r1 45 | } 46 | 47 | // Seek provides a mock function with given fields: offset, whence 48 | func (_m *ReadSeekerCloser) Seek(offset int64, whence int) (int64, error) { 49 | ret := _m.Called(offset, whence) 50 | 51 | var r0 int64 52 | if rf, ok := ret.Get(0).(func(int64, int) int64); ok { 53 | r0 = rf(offset, whence) 54 | } else { 55 | r0 = ret.Get(0).(int64) 56 | } 57 | 58 | var r1 error 59 | if rf, ok := ret.Get(1).(func(int64, int) error); ok { 60 | r1 = rf(offset, whence) 61 | } else { 62 | r1 = ret.Error(1) 63 | } 64 | 65 | return r0, r1 66 | } 67 | -------------------------------------------------------------------------------- /config/config_keys.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/configuration/config_helpers" 7 | ) 8 | 9 | // Configuration persistence keys 10 | const ( 11 | // CRN constant 12 | CRN = "CRN" 13 | 14 | // Last time the config is updated 15 | LastUpdated = "Last Updated" 16 | 17 | // Download Location constant 18 | DownloadLocation = "Download Location" 19 | 20 | // Region constant 21 | DefaultRegion = "Default Region" 22 | 23 | // HMAC authentication constants 24 | HMACProvided = "HMACProvided" 25 | LabelAuthenticationMethod = "Authentication Method" 26 | AccessKeyID = "AccessKeyID" 27 | SecretAccessKey = "SecretAccessKey" 28 | 29 | // Regions Endpoint URL constant 30 | RegionsEndpointURL = "RegionsEndpointURL" 31 | 32 | // ForcePathStyle 33 | ForcePathStyle = "ForcePathStyle" 34 | LabelURLStyle = "URL Style" 35 | 36 | ServiceEndpointURL = "ServiceEndpointURL" 37 | ) 38 | 39 | // CLI App Context Metadata Keys 40 | const ( 41 | CosContextKey = "CosContext" 42 | ) 43 | 44 | var ( 45 | // Variables to use across the package 46 | FallbackRegion = "us-geo" 47 | FallbackDownloadLocation = filepath.Join(config_helpers.UserHomeDir(), "Downloads") 48 | 49 | // Standard time format 50 | StandardTimeFormat = "Monday, January 02 2006 at 15:04:05" 51 | ) 52 | 53 | const ( 54 | HMACProvidedDefault = false 55 | // Current Authentication Methods the CLI supports 56 | IAM = "IAM" 57 | HMAC = "HMAC" 58 | ) 59 | 60 | const ( 61 | ForcePathStyleDefault = false 62 | // Current Bucket URL Styles that the CLI supports 63 | VHost = "VHost" 64 | Path = "Path" 65 | ) 66 | -------------------------------------------------------------------------------- /functions/bucket_website_get.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 8 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 9 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 10 | "github.com/IBM/ibmcloud-cos-cli/utils" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | func BucketWebsiteGet(c *cli.Context) (err error) { 15 | // check the number of arguments 16 | if c.NArg() > 0 { 17 | err = &errors.CommandError{ 18 | CLIContext: c, 19 | Cause: errors.InvalidNArg, 20 | } 21 | return 22 | } 23 | 24 | // Load COS Context 25 | var cosContext *utils.CosContext 26 | if cosContext, err = GetCosContext(c); err != nil { 27 | return 28 | } 29 | 30 | // Initialize input structure 31 | input := new(s3.GetBucketWebsiteInput) 32 | 33 | // Required parameters 34 | mandatory := map[string]string{ 35 | fields.Bucket: flags.Bucket, 36 | } 37 | 38 | // Optional parameters 39 | options := map[string]string{} 40 | 41 | // Validate User Inputs 42 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 43 | return 44 | } 45 | 46 | // Setting client to do the call 47 | var client s3iface.S3API 48 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 49 | return 50 | } 51 | 52 | // GetBucketWebsite Op 53 | var output *s3.GetBucketWebsiteOutput 54 | if output, err = client.GetBucketWebsite(input); err != nil { 55 | return 56 | } 57 | 58 | // Display either in JSON or text 59 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 60 | 61 | // Return 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /functions/bucket_replication_get.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func BucketReplicationGet(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.GetBucketReplicationInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | } 36 | 37 | // Optional parameters 38 | options := map[string]string{} 39 | 40 | // Validate User Inputs 41 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 42 | return 43 | } 44 | 45 | // Setting client to do the call 46 | var client s3iface.S3API 47 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 48 | return 49 | } 50 | 51 | // GetBucketReplication Op 52 | var output *s3.GetBucketReplicationOutput 53 | if output, err = client.GetBucketReplication(input); err != nil { 54 | return 55 | } 56 | 57 | // Display either in JSON or text 58 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 59 | 60 | // Return 61 | return 62 | } 63 | -------------------------------------------------------------------------------- /functions/bucket_versioning_get.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 8 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 9 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 10 | "github.com/IBM/ibmcloud-cos-cli/utils" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | func BucketVersioningGet(c *cli.Context) (err error) { 15 | // check the number of arguments 16 | if c.NArg() > 0 { 17 | err = &errors.CommandError{ 18 | CLIContext: c, 19 | Cause: errors.InvalidNArg, 20 | } 21 | return 22 | } 23 | 24 | // Load COS Context 25 | var cosContext *utils.CosContext 26 | if cosContext, err = GetCosContext(c); err != nil { 27 | return 28 | } 29 | 30 | // Initialize input structure 31 | input := new(s3.GetBucketVersioningInput) 32 | 33 | // Required parameters 34 | mandatory := map[string]string{ 35 | fields.Bucket: flags.Bucket, 36 | } 37 | 38 | // Optional parameters 39 | options := map[string]string{} 40 | 41 | // Validate User Inputs 42 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 43 | return 44 | } 45 | 46 | // Setting client to do the call 47 | var client s3iface.S3API 48 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 49 | return 50 | } 51 | 52 | // GetBucketVersioning Op 53 | var output *s3.GetBucketVersioningOutput 54 | if output, err = client.GetBucketVersioning(input); err != nil { 55 | return 56 | } 57 | 58 | // Display either in JSON or text 59 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 60 | 61 | // Return 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /functions/public_access_block_get.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 8 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 9 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 10 | "github.com/IBM/ibmcloud-cos-cli/utils" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | func PublicAccessBlockGet(c *cli.Context) (err error) { 15 | // check the number of arguments 16 | if c.NArg() > 0 { 17 | err = &errors.CommandError{ 18 | CLIContext: c, 19 | Cause: errors.InvalidNArg, 20 | } 21 | return 22 | } 23 | 24 | // Load COS Context 25 | var cosContext *utils.CosContext 26 | if cosContext, err = GetCosContext(c); err != nil { 27 | return 28 | } 29 | 30 | // Initialize input structure 31 | input := new(s3.GetPublicAccessBlockInput) 32 | 33 | // Required parameters 34 | mandatory := map[string]string{ 35 | fields.Bucket: flags.Bucket, 36 | } 37 | 38 | // Optional parameters 39 | options := map[string]string{} 40 | 41 | // Validate User Inputs 42 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 43 | return 44 | } 45 | 46 | // Setting client to do the call 47 | var client s3iface.S3API 48 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 49 | return 50 | } 51 | 52 | // GetPublicAccessBlock Op 53 | var output *s3.GetPublicAccessBlockOutput 54 | if output, err = client.GetPublicAccessBlock(input); err != nil { 55 | return 56 | } 57 | 58 | // Display either in JSON or text 59 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 60 | 61 | // Return 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /functions/object_lock_configuration_get.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func ObjectLockGet(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.GetObjectLockConfigurationInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | } 36 | 37 | // Optional parameters 38 | options := map[string]string{} 39 | 40 | // Validate User Inputs 41 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 42 | return 43 | } 44 | 45 | // Setting client to do the call 46 | var client s3iface.S3API 47 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 48 | return 49 | } 50 | 51 | // GetObjectLock Op 52 | var output *s3.GetObjectLockConfigurationOutput 53 | if output, err = client.GetObjectLockConfiguration(input); err != nil { 54 | return 55 | } 56 | 57 | // Display either in JSON or text 58 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 59 | 60 | // Return 61 | return 62 | } 63 | -------------------------------------------------------------------------------- /functions/bucket_replication_delete.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func BucketReplicationDelete(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.DeleteBucketReplicationInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | } 36 | 37 | // Optional parameters 38 | options := map[string]string{} 39 | 40 | // Validate User Inputs 41 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 42 | return 43 | } 44 | 45 | // Setting client to do the call 46 | var client s3iface.S3API 47 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 48 | return 49 | } 50 | 51 | // DeleteBucketReplication Op 52 | var output *s3.DeleteBucketReplicationOutput 53 | if output, err = client.DeleteBucketReplication(input); err != nil { 54 | return 55 | } 56 | 57 | // Display either in JSON or text 58 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 59 | 60 | // Return 61 | return 62 | } 63 | -------------------------------------------------------------------------------- /bin/format-translation-files: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'json' 4 | 5 | ROOT = File.expand_path(File.join(File.dirname(__FILE__), "..")) 6 | RESOURCES_DIR = File.join(ROOT, "i18n", "resources") 7 | 8 | SRC_LANG = "en_US" 9 | 10 | def run 11 | srcLangFile = sourceLangFile() 12 | srcTranslations = loadFromFile(srcLangFile) 13 | 14 | Dir.glob(File.join(RESOURCES_DIR, "*.all.json")) do |file| 15 | puts "*** Process #{File.basename(file)}" 16 | if file == srcLangFile 17 | translations = srcTranslations 18 | else 19 | translations = getTranslations(srcTranslations, file) 20 | end 21 | normalized = normalize(translations) 22 | saveToFile(normalized, file) 23 | end 24 | end 25 | 26 | def sourceLangFile 27 | return File.join(RESOURCES_DIR, SRC_LANG + ".all.json") 28 | end 29 | 30 | def loadFromFile(file) 31 | array = JSON.parse(File.read(file)) 32 | translations = {} 33 | array.each{ |t| translations[t["id"]] = t["translation"] unless t["id"].to_s.empty? } 34 | return translations 35 | end 36 | 37 | def getTranslations(srcTranslations, file) 38 | translations = loadFromFile(file) 39 | result = {} 40 | srcTranslations.each do |k,v| 41 | if translations.include? k 42 | result[k] = translations[k] 43 | else 44 | result[k] = "" 45 | end 46 | end 47 | return result 48 | end 49 | 50 | def normalize(translations) 51 | result = {} 52 | sorted = Hash[translations.sort] 53 | sorted.each {|k,v| result[k] = v.to_s.empty?? k : v} 54 | return result 55 | end 56 | 57 | def saveToFile(translations, path) 58 | json = [] 59 | translations.each { |k,v| json << {"id" => k, "translation" => v} } 60 | File.open(path,"w") do |f| 61 | f.write(JSON.pretty_generate(json)) 62 | end 63 | end 64 | 65 | run -------------------------------------------------------------------------------- /functions/bucket_lifecycle_configuration_delete.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func BucketLifecycleConfigurationDelete(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.DeleteBucketLifecycleInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | } 36 | 37 | // Optional parameters 38 | options := map[string]string{} 39 | 40 | // Validate User Inputs 41 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 42 | return 43 | } 44 | 45 | // Setting client to do the call 46 | var client s3iface.S3API 47 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 48 | return 49 | } 50 | 51 | // DeleteBucketReplication Op 52 | var output *s3.DeleteBucketLifecycleOutput 53 | if output, err = client.DeleteBucketLifecycle(input); err != nil { 54 | return 55 | } 56 | 57 | // Display either in JSON or text 58 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 59 | 60 | // Return 61 | return 62 | } 63 | -------------------------------------------------------------------------------- /functions/bucket_website_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 8 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 9 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 10 | "github.com/IBM/ibmcloud-cos-cli/utils" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | func BucketWebsitePut(c *cli.Context) (err error) { 15 | // check the number of arguments 16 | if c.NArg() > 0 { 17 | err = &errors.CommandError{ 18 | CLIContext: c, 19 | Cause: errors.InvalidNArg, 20 | } 21 | return 22 | } 23 | 24 | // Load COS Context 25 | var cosContext *utils.CosContext 26 | if cosContext, err = GetCosContext(c); err != nil { 27 | return 28 | } 29 | 30 | // Initialize input structure 31 | input := new(s3.PutBucketWebsiteInput) 32 | 33 | // Required parameters 34 | mandatory := map[string]string{ 35 | fields.Bucket: flags.Bucket, 36 | fields.WebsiteConfiguration: flags.WebsiteConfiguration, 37 | } 38 | 39 | // Optional parameters 40 | options := map[string]string{} 41 | 42 | // Validate User Inputs 43 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 44 | return 45 | } 46 | 47 | // Setting client to do the call 48 | var client s3iface.S3API 49 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 50 | return 51 | } 52 | 53 | // PutBucketWebsite Op 54 | var output *s3.PutBucketWebsiteOutput 55 | if output, err = client.PutBucketWebsite(input); err != nil { 56 | return 57 | } 58 | 59 | // Display either in JSON or text 60 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 61 | 62 | // Return 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /functions/object_tagging_get.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 8 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 9 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 10 | "github.com/IBM/ibmcloud-cos-cli/utils" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | func ObjectTaggingGet(c *cli.Context) (err error) { 15 | // check the number of arguments 16 | if c.NArg() > 0 { 17 | err = &errors.CommandError{ 18 | CLIContext: c, 19 | Cause: errors.InvalidNArg, 20 | } 21 | return 22 | } 23 | 24 | // Load COS Context 25 | var cosContext *utils.CosContext 26 | if cosContext, err = GetCosContext(c); err != nil { 27 | return 28 | } 29 | 30 | // Initialize input structure 31 | input := new(s3.GetObjectTaggingInput) 32 | 33 | // Required parameters 34 | mandatory := map[string]string{ 35 | fields.Bucket: flags.Bucket, 36 | fields.Key: flags.Key, 37 | } 38 | 39 | // Optional parameters 40 | options := map[string]string{ 41 | fields.VersionId: flags.VersionId, 42 | } 43 | 44 | // Validate User Inputs 45 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 46 | return 47 | } 48 | 49 | // Setting client to do the call 50 | var client s3iface.S3API 51 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 52 | return 53 | } 54 | 55 | // GetObjectTagging Op 56 | var output *s3.GetObjectTaggingOutput 57 | if output, err = client.GetObjectTagging(input); err != nil { 58 | return 59 | } 60 | 61 | // Display either in JSON or text 62 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 63 | 64 | // Return 65 | return 66 | } 67 | -------------------------------------------------------------------------------- /functions/bucket_lifecycle_configuration_get.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func BucketLifecycleConfigurationGet(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.GetBucketLifecycleConfigurationInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | } 36 | 37 | // Optional parameters 38 | options := map[string]string{} 39 | 40 | // Validate User Inputs 41 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 42 | return 43 | } 44 | 45 | // Setting client to do the call 46 | var client s3iface.S3API 47 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 48 | return 49 | } 50 | 51 | // GetBucketLifecycleConfiguration Op 52 | var output *s3.GetBucketLifecycleConfigurationOutput 53 | if output, err = client.GetBucketLifecycleConfiguration(input); err != nil { 54 | return 55 | } 56 | 57 | // Display either in JSON or text 58 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 59 | 60 | // Return 61 | return 62 | } 63 | -------------------------------------------------------------------------------- /functions/object_retention_get.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func ObjectRetentionGet(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.GetObjectRetentionInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | fields.Key: flags.Key, 36 | } 37 | 38 | // Optional parameters 39 | options := map[string]string{ 40 | fields.VersionId: flags.VersionId, 41 | } 42 | 43 | // Validate User Inputs 44 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 45 | return 46 | } 47 | 48 | // Setting client to do the call 49 | var client s3iface.S3API 50 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 51 | return 52 | } 53 | 54 | // GetObjectRetention Op 55 | var output *s3.GetObjectRetentionOutput 56 | if output, err = client.GetObjectRetention(input); err != nil { 57 | return 58 | } 59 | 60 | // Display either in JSON or text 61 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 62 | 63 | // Return 64 | return 65 | } 66 | -------------------------------------------------------------------------------- /functions/object_legal_hold_get.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func ObjectLegalHoldGet(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.GetObjectLegalHoldInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | fields.Key: flags.Key, 36 | } 37 | 38 | // Optional parameters 39 | options := map[string]string{ 40 | fields.VersionId: flags.VersionId, 41 | } 42 | 43 | // Validate User Inputs 44 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 45 | return 46 | } 47 | 48 | // Setting client to do the call 49 | var client s3iface.S3API 50 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 51 | return 52 | } 53 | 54 | // GetObjectLegalHold Op 55 | var output *s3.GetObjectLegalHoldOutput 56 | if output, err = client.GetObjectLegalHold(input); err != nil { 57 | return 58 | } 59 | 60 | // Display either in JSON or text 61 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 62 | 63 | // Return 64 | return 65 | } 66 | -------------------------------------------------------------------------------- /functions/bucket_versioning_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 8 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 9 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 10 | "github.com/IBM/ibmcloud-cos-cli/utils" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | func BucketVersioningPut(c *cli.Context) (err error) { 15 | // check the number of arguments 16 | if c.NArg() > 0 { 17 | err = &errors.CommandError{ 18 | CLIContext: c, 19 | Cause: errors.InvalidNArg, 20 | } 21 | return 22 | } 23 | 24 | // Load COS Context 25 | var cosContext *utils.CosContext 26 | if cosContext, err = GetCosContext(c); err != nil { 27 | return 28 | } 29 | 30 | // Initialize input structure 31 | input := new(s3.PutBucketVersioningInput) 32 | 33 | // Required parameters 34 | mandatory := map[string]string{ 35 | fields.Bucket: flags.Bucket, 36 | fields.VersioningConfiguration: flags.VersioningConfiguration, 37 | } 38 | 39 | // Optional parameters 40 | options := map[string]string{} 41 | 42 | // Validate User Inputs 43 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 44 | return 45 | } 46 | 47 | // Setting client to do the call 48 | var client s3iface.S3API 49 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 50 | return 51 | } 52 | 53 | // PutBucketVersioning Op 54 | var output *s3.PutBucketVersioningOutput 55 | if output, err = client.PutBucketVersioning(input); err != nil { 56 | return 57 | } 58 | 59 | // Display either in JSON or text 60 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 61 | 62 | // Return 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /functions/bucket_replication_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func BucketReplicationPut(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.PutBucketReplicationInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | fields.ReplicationConfiguration: flags.ReplicationConfiguration, 36 | } 37 | 38 | // Optional parameters 39 | options := map[string]string{} 40 | 41 | // Validate User Inputs 42 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 43 | return 44 | } 45 | 46 | // Setting client to do the call 47 | var client s3iface.S3API 48 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 49 | return 50 | } 51 | 52 | // PutPutBucketReplication Op 53 | var output *s3.PutBucketReplicationOutput 54 | if output, err = client.PutBucketReplication(input); err != nil { 55 | return 56 | } 57 | 58 | // Display either in JSON or text 59 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 60 | 61 | // Return 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /functions/object_lock_configuration_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func ObjectLockPut(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.PutObjectLockConfigurationInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | fields.ObjectLockConfiguration: flags.ObjectLockConfiguration, 36 | } 37 | 38 | // Optional parameters 39 | options := map[string]string{} 40 | 41 | // Validate User Inputs 42 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 43 | return 44 | } 45 | 46 | // Setting client to do the call 47 | var client s3iface.S3API 48 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 49 | return 50 | } 51 | 52 | // PutObjectLockxx Op 53 | var output *s3.PutObjectLockConfigurationOutput 54 | if output, err = client.PutObjectLockConfiguration(input); err != nil { 55 | return 56 | } 57 | 58 | // Display either in JSON or text 59 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 60 | 61 | // Return 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /functions/object_tagging_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 8 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 9 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 10 | "github.com/IBM/ibmcloud-cos-cli/utils" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | func ObjectTaggingPut(c *cli.Context) (err error) { 15 | // check the number of arguments 16 | if c.NArg() > 0 { 17 | err = &errors.CommandError{ 18 | CLIContext: c, 19 | Cause: errors.InvalidNArg, 20 | } 21 | return 22 | } 23 | 24 | // Load COS Context 25 | var cosContext *utils.CosContext 26 | if cosContext, err = GetCosContext(c); err != nil { 27 | return 28 | } 29 | 30 | // Initialize input structure 31 | input := new(s3.PutObjectTaggingInput) 32 | 33 | // Required parameters 34 | mandatory := map[string]string{ 35 | fields.Bucket: flags.Bucket, 36 | fields.Key: flags.Key, 37 | fields.Tagging: flags.Tagging, 38 | } 39 | 40 | // Optional parameters 41 | options := map[string]string{ 42 | fields.VersionId: flags.VersionId, 43 | } 44 | 45 | // Validate User Inputs 46 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 47 | return 48 | } 49 | 50 | // Setting client to do the call 51 | var client s3iface.S3API 52 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 53 | return 54 | } 55 | 56 | // PutObjectTagging Op 57 | var output *s3.PutObjectTaggingOutput 58 | if output, err = client.PutObjectTagging(input); err != nil { 59 | return 60 | } 61 | 62 | // Display either in JSON or text 63 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 64 | 65 | // Return 66 | return 67 | } 68 | -------------------------------------------------------------------------------- /functions/public_access_block_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 8 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 9 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 10 | "github.com/IBM/ibmcloud-cos-cli/utils" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | func PublicAccessBlockPut(c *cli.Context) (err error) { 15 | // check the number of arguments 16 | if c.NArg() > 0 { 17 | err = &errors.CommandError{ 18 | CLIContext: c, 19 | Cause: errors.InvalidNArg, 20 | } 21 | return 22 | } 23 | 24 | // Load COS Context 25 | var cosContext *utils.CosContext 26 | if cosContext, err = GetCosContext(c); err != nil { 27 | return 28 | } 29 | 30 | // Initialize input structure 31 | input := new(s3.PutPublicAccessBlockInput) 32 | 33 | // Required parameters 34 | mandatory := map[string]string{ 35 | fields.Bucket: flags.Bucket, 36 | fields.PublicAccessBlockConfiguration: flags.PublicAccessBlockConfiguration, 37 | } 38 | 39 | // Optional parameters 40 | options := map[string]string{} 41 | 42 | // Validate User Inputs 43 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 44 | return 45 | } 46 | 47 | // Setting client to do the call 48 | var client s3iface.S3API 49 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 50 | return 51 | } 52 | 53 | // PutPublicAccessBlock Op 54 | var output *s3.PutPublicAccessBlockOutput 55 | if output, err = client.PutPublicAccessBlock(input); err != nil { 56 | return 57 | } 58 | 59 | // Display either in JSON or text 60 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 61 | 62 | // Return 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /functions/object_retention_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func ObjectRetentionPut(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.PutObjectRetentionInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | fields.Key: flags.Key, 36 | fields.Retention: flags.Retention, 37 | } 38 | 39 | // Optional parameters 40 | options := map[string]string{ 41 | fields.VersionId: flags.VersionId, 42 | } 43 | 44 | // Validate User Inputs 45 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 46 | return 47 | } 48 | 49 | // Setting client to do the call 50 | var client s3iface.S3API 51 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 52 | return 53 | } 54 | 55 | // PutObjectRetention Op 56 | var output *s3.PutObjectRetentionOutput 57 | if output, err = client.PutObjectRetention(input); err != nil { 58 | return 59 | } 60 | 61 | // Display either in JSON or text 62 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 63 | 64 | // Return 65 | return 66 | } 67 | -------------------------------------------------------------------------------- /functions/bucket_lifecycle_configuration_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func BucketLifecycleConfigurationPut(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.PutBucketLifecycleConfigurationInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | fields.LifecycleConfiguration: flags.LifecycleConfiguration, 36 | } 37 | 38 | // Optional parameters 39 | options := map[string]string{} 40 | 41 | // Validate User Inputs 42 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 43 | return 44 | } 45 | 46 | // Setting client to do the call 47 | var client s3iface.S3API 48 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 49 | return 50 | } 51 | 52 | // PutPutBucketReplication Op 53 | var output *s3.PutBucketLifecycleConfigurationOutput 54 | if output, err = client.PutBucketLifecycleConfiguration(input); err != nil { 55 | return 56 | } 57 | 58 | // Display either in JSON or text 59 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 60 | 61 | // Return 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /functions/object_legal_hold_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func ObjectLegalHoldPut(c *cli.Context) (err error) { 14 | // check the number of arguments 15 | if c.NArg() > 0 { 16 | err = &errors.CommandError{ 17 | CLIContext: c, 18 | Cause: errors.InvalidNArg, 19 | } 20 | return 21 | } 22 | 23 | // Load COS Context 24 | var cosContext *utils.CosContext 25 | if cosContext, err = GetCosContext(c); err != nil { 26 | return 27 | } 28 | 29 | // Initialize input structure 30 | input := new(s3.PutObjectLegalHoldInput) 31 | 32 | // Required parameters 33 | mandatory := map[string]string{ 34 | fields.Bucket: flags.Bucket, 35 | fields.Key: flags.Key, 36 | fields.LegalHold: flags.LegalHold, 37 | } 38 | 39 | // Optional parameters 40 | options := map[string]string{ 41 | fields.VersionId: flags.VersionId, 42 | } 43 | 44 | // Validate User Inputs 45 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 46 | return 47 | } 48 | 49 | // Setting client to do the call 50 | var client s3iface.S3API 51 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 52 | return 53 | } 54 | 55 | // PutObjectLegalHold Op 56 | var output *s3.PutObjectLegalHoldOutput 57 | if output, err = client.PutObjectLegalHold(input); err != nil { 58 | return 59 | } 60 | 61 | // Display either in JSON or text 62 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 63 | 64 | // Return 65 | return 66 | } 67 | -------------------------------------------------------------------------------- /di/injectors/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate wire 4 | //+build !wireinject 5 | 6 | package injectors 7 | 8 | import ( 9 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 10 | "github.com/IBM/ibmcloud-cos-cli/di/providers" 11 | "github.com/IBM/ibmcloud-cos-cli/render" 12 | "github.com/IBM/ibmcloud-cos-cli/utils" 13 | ) 14 | 15 | // Injectors from wire.go: 16 | 17 | func InitializeCosContext(pluginContext plugin.PluginContext) (*utils.CosContext, error) { 18 | ui := providers.NewUI() 19 | pluginConfig := providers.GetPluginConfig(pluginContext) 20 | baseConfig := providers.GetBaseConfig(pluginContext) 21 | cosEndPointsWSClient, err := providers.NewCOSEndPointsWSClient(pluginContext, baseConfig) 22 | if err != nil { 23 | return nil, err 24 | } 25 | config, err := providers.NewConfig(pluginContext, cosEndPointsWSClient, baseConfig) 26 | if err != nil { 27 | return nil, err 28 | } 29 | session, err := providers.NewSession(config) 30 | if err != nil { 31 | return nil, err 32 | } 33 | jsonRender := render.NewJSONRender(ui) 34 | textRender := render.NewTextRender(ui) 35 | errorRender := render.NewErrorRender(ui) 36 | v := providers.GetS3APIFn() 37 | v2 := providers.GetDownloaderAPIFn() 38 | v3 := providers.GetUploaderAPIFn() 39 | v4 := providers.GetAsperaTransferFn() 40 | if err != nil { 41 | return nil, err 42 | } 43 | fileOperationsImpl := providers.GetFileOperations() 44 | cosContext := &utils.CosContext{ 45 | UI: ui, 46 | Config: pluginConfig, 47 | Session: session, 48 | ListKnownRegions: cosEndPointsWSClient, 49 | JSONRender: jsonRender, 50 | TextRender: textRender, 51 | ErrorRender: errorRender, 52 | ClientGen: v, 53 | DownloaderGen: v2, 54 | UploaderGen: v3, 55 | AsperaTransferGen: v4, 56 | FileOperations: fileOperationsImpl, 57 | } 58 | return cosContext, nil 59 | } 60 | -------------------------------------------------------------------------------- /functions/bucket_cors_delete.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // BucketCorsDelete - Deletes CORS configuration from a bucket 14 | // Parameter: 15 | // CLI Context Application 16 | // Returns: 17 | // Error if triggered 18 | func BucketCorsDelete(c *cli.Context) (err error) { 19 | // check the number of arguments 20 | if c.NArg() > 0 { 21 | err = &errors.CommandError{ 22 | CLIContext: c, 23 | Cause: errors.InvalidNArg, 24 | } 25 | return 26 | } 27 | 28 | // Load COS Context 29 | var cosContext *utils.CosContext 30 | if cosContext, err = GetCosContext(c); err != nil { 31 | return 32 | } 33 | 34 | // Set DeleteBucketCorsInput 35 | input := new(s3.DeleteBucketCorsInput) 36 | 37 | // Required parameter and no optional parameter for DeleteBucketCors 38 | mandatory := map[string]string{ 39 | fields.Bucket: flags.Bucket, 40 | } 41 | 42 | // Optional parameters 43 | options := map[string]string{} 44 | 45 | // Check through user inputs for validation 46 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 47 | return 48 | } 49 | 50 | // Setting client to do the call 51 | var client s3iface.S3API 52 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 53 | return 54 | } 55 | // DELETE CORS 56 | var output *s3.DeleteBucketCorsOutput 57 | if output, err = client.DeleteBucketCors(input); err != nil { 58 | return 59 | } 60 | 61 | // Display either in JSON or text 62 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 63 | 64 | // Return 65 | return 66 | } 67 | -------------------------------------------------------------------------------- /functions/bucket_cors_get.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // BucketCorsGet receives CORS configuration from an existing bucket. 14 | // Parameter: 15 | // CLI Context Application 16 | // Returns: 17 | // Error = zero or non-zero 18 | func BucketCorsGet(c *cli.Context) (err error) { 19 | 20 | // check the number of arguments 21 | if c.NArg() > 0 { 22 | err = &errors.CommandError{ 23 | CLIContext: c, 24 | Cause: errors.InvalidNArg, 25 | } 26 | return 27 | } 28 | 29 | // Load COS Context 30 | var cosContext *utils.CosContext 31 | if cosContext, err = GetCosContext(c); err != nil { 32 | return 33 | } 34 | 35 | // Build GetBucketCorsInput 36 | input := new(s3.GetBucketCorsInput) 37 | 38 | // Required parameter and no optional parameter for GetBucketCors 39 | mandatory := map[string]string{ 40 | fields.Bucket: flags.Bucket, 41 | } 42 | 43 | // Optional parameters 44 | options := map[string]string{} 45 | 46 | // Check through user inputs for validation 47 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 48 | return 49 | } 50 | 51 | // Setting client to do the call 52 | var client s3iface.S3API 53 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 54 | return 55 | } 56 | 57 | // GET CORS 58 | var output *s3.GetBucketCorsOutput 59 | if output, err = client.GetBucketCors(input); err != nil { 60 | return 61 | } 62 | 63 | // Display either in JSON or text 64 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 65 | 66 | // Return 67 | return 68 | } 69 | -------------------------------------------------------------------------------- /functions/bucket_list.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // BucketsList prints out a list of all the buckets in an IBM Cloud account. 14 | // Parameter: 15 | // CLI Context Application 16 | // Returns: 17 | // Error = zero or non-zero 18 | func BucketsList(c *cli.Context) (err error) { 19 | // check the number of arguments 20 | if c.NArg() > 0 { 21 | err = &errors.CommandError{ 22 | CLIContext: c, 23 | Cause: errors.InvalidNArg, 24 | } 25 | return 26 | } 27 | 28 | // Load COS Context 29 | var cosContext *utils.CosContext 30 | if cosContext, err = GetCosContext(c); err != nil { 31 | return 32 | } 33 | 34 | // Set ListBucketsInput 35 | input := new(s3.ListBucketsInput) 36 | 37 | // Required parameter for ListBuckets 38 | mandatory := map[string]string{} 39 | 40 | // Optional parameters for ListBuckets 41 | options := map[string]string{ 42 | fields.IBMServiceInstanceID: flags.IbmServiceInstanceID, 43 | } 44 | 45 | // Check through user inputs for validation 46 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 47 | return 48 | } 49 | 50 | // Setting client to do the call 51 | var client s3iface.S3API 52 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 53 | return 54 | } 55 | 56 | // The ListBuckets API 57 | var output *s3.ListBucketsOutput 58 | if output, err = client.ListBuckets(input); err != nil { 59 | return 60 | } 61 | 62 | // Display either in JSON or text 63 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 64 | 65 | // Return 66 | return 67 | } 68 | -------------------------------------------------------------------------------- /functions/endpoints.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 6 | "github.com/IBM/ibmcloud-cos-cli/errors" 7 | "github.com/IBM/ibmcloud-cos-cli/render" 8 | "github.com/IBM/ibmcloud-cos-cli/utils" 9 | "github.com/urfave/cli" 10 | ) 11 | 12 | // Endpoints - show regions 13 | // Parameter: 14 | // 15 | // CLI Context Application 16 | // 17 | // Returns: 18 | // 19 | // Error = zero or non-zero 20 | func Endpoints(c *cli.Context) (err error) { 21 | if c.NArg() > 0 { 22 | err = &errors.CommandError{ 23 | CLIContext: c, 24 | Cause: errors.InvalidNArg, 25 | } 26 | return 27 | } 28 | 29 | cosContext, err := GetCosContext(c) 30 | if err != nil { 31 | return 32 | } 33 | 34 | flagRegion := c.String(flags.Region) 35 | 36 | region, err := cosContext.GetCurrentRegion(flagRegion) 37 | if err != nil || region == "" { 38 | return &errors.EndpointsError{ 39 | Cause: errors.EndpointsRegionEmpty, 40 | } 41 | } 42 | 43 | ibmEndpoints, err := utils.NewIBMEndPoints(cosContext.Session.Config, "") 44 | if err != nil { 45 | return err 46 | } 47 | 48 | var output render.RegionEndpointsOutput 49 | if c.IsSet(flags.ListRegions) { 50 | regions, err := ibmEndpoints.NewGetAllRegions() 51 | if err != nil { 52 | return err 53 | } 54 | 55 | output.Regions = regions 56 | return cosContext.GetDisplay(c.String(flags.Output), false).Display(nil, &output, nil) 57 | } 58 | 59 | output, err = ibmEndpoints.GetAllEndpointsFor(s3.ServiceName, region) 60 | if err != nil { 61 | var cause errors.EndpointsErrorCause 62 | if flagRegion == "" { 63 | cause = errors.EndpointsStoredRegionInvalid 64 | } else { 65 | cause = errors.EndpointsRegionInvalid 66 | } 67 | return &errors.EndpointsError{ 68 | Region: region, 69 | Cause: cause, 70 | } 71 | } 72 | 73 | return cosContext.GetDisplay(c.String(flags.Output), false).Display(nil, &output, nil) 74 | } 75 | -------------------------------------------------------------------------------- /functions/public_access_block_delete.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 8 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 9 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 10 | "github.com/IBM/ibmcloud-cos-cli/utils" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | // PublicAccessBlockDelete removes website configuration from a bucket. 15 | // Parameter: 16 | // CLI Context Application 17 | // Returns: 18 | // Error = zero or non-zero 19 | func PublicAccessBlockDelete(c *cli.Context) (err error) { 20 | // check the number of arguments 21 | if c.NArg() > 0 { 22 | // Build Command Error struct 23 | err = &errors.CommandError{ 24 | CLIContext: c, 25 | Cause: errors.InvalidNArg, 26 | } 27 | // Return error 28 | return 29 | } 30 | 31 | // Load COS Context 32 | var cosContext *utils.CosContext 33 | if cosContext, err = GetCosContext(c); err != nil { 34 | return 35 | } 36 | 37 | // Initialize input structure 38 | input := new(s3.DeletePublicAccessBlockInput) 39 | 40 | // Required parameters 41 | mandatory := map[string]string{ 42 | fields.Bucket: flags.Bucket, 43 | } 44 | 45 | // Optional parameters 46 | options := map[string]string{} 47 | 48 | // Validate User Inputs 49 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 50 | return 51 | } 52 | 53 | // Setting client to do the call 54 | var client s3iface.S3API 55 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 56 | return 57 | } 58 | 59 | // DeletePublicAccessBlock Op 60 | var output *s3.DeletePublicAccessBlockOutput 61 | if output, err = client.DeletePublicAccessBlock(input); err != nil { 62 | return 63 | } 64 | 65 | // Display either in JSON or text 66 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 67 | 68 | // Return 69 | return 70 | } 71 | -------------------------------------------------------------------------------- /functions/bucket_cors_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // BucketCorsPut creates a CORS configuration for the bucket. 14 | // Parameter: 15 | // CLI Context Application 16 | // Returns: 17 | // Error = zero or non-zero 18 | func BucketCorsPut(c *cli.Context) (err error) { 19 | // check the number of arguments 20 | if c.NArg() > 0 { 21 | err = &errors.CommandError{ 22 | CLIContext: c, 23 | Cause: errors.InvalidNArg, 24 | } 25 | return 26 | } 27 | 28 | // Load COS Context 29 | var cosContext *utils.CosContext 30 | if cosContext, err = GetCosContext(c); err != nil { 31 | return 32 | } 33 | 34 | // Set PutBucketCorsInput 35 | input := new(s3.PutBucketCorsInput) 36 | 37 | // Required parameter for PutBucketCors 38 | mandatory := map[string]string{ 39 | fields.Bucket: flags.Bucket, 40 | fields.CORSConfiguration: flags.CorsConfiguration, 41 | } 42 | 43 | // No optional parameter for PutBucketCors 44 | options := map[string]string{} 45 | 46 | // Check through user inputs for validation 47 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 48 | return 49 | } 50 | 51 | // Setting client to do the call 52 | var client s3iface.S3API 53 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 54 | return 55 | } 56 | 57 | // Put the bucket CORS by calling PutBucketCors 58 | var output *s3.PutBucketCorsOutput 59 | if output, err = client.PutBucketCors(input); err != nil { 60 | return 61 | } 62 | 63 | // Display either in JSON or text 64 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 65 | 66 | // Return 67 | return 68 | } 69 | -------------------------------------------------------------------------------- /config/app/custom_on_usage_error.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/IBM/ibmcloud-cos-cli/errors" 7 | "github.com/urfave/cli" 8 | ) 9 | 10 | var ( 11 | BadFlagSyntaxRegex = regexp.MustCompile(`bad flag syntax: ([\w\-]+)$`) 12 | NotDefinedFlagRegex = regexp.MustCompile(`flag provided but not defined: -([\w\-]+)$`) 13 | InvalidBooleanValueRegex = regexp.MustCompile(`invalid boolean value .+? for -([\w\-]+): .+$`) 14 | InvalidBooleanFlagRegex = regexp.MustCompile(`invalid boolean flag ([\w\-]+): .+$`) 15 | MissingValueRegex = regexp.MustCompile(`flag needs an argument: -([\w\-]+)$`) 16 | InvalidValueRegex = regexp.MustCompile(`invalid value ".+?" for flag -([\w\-]+): .+$`) 17 | ) 18 | 19 | func OnUsageError(context *cli.Context, err error, _ bool) error { 20 | result := new(errors.CommandError) 21 | result.CLIContext = context 22 | result.IError = err 23 | switch errorStr := err.Error(); { 24 | case BadFlagSyntaxRegex.MatchString(errorStr): 25 | result.Cause = errors.BadFlagSyntax 26 | result.Flag = BadFlagSyntaxRegex.FindStringSubmatch(errorStr)[1] 27 | case NotDefinedFlagRegex.MatchString(errorStr): 28 | result.Cause = errors.NotDefinedFlag 29 | result.Flag = NotDefinedFlagRegex.FindStringSubmatch(errorStr)[1] 30 | case InvalidBooleanValueRegex.MatchString(errorStr): 31 | result.Cause = errors.InvalidBooleanValue 32 | result.Flag = InvalidBooleanValueRegex.FindStringSubmatch(errorStr)[1] 33 | case InvalidBooleanFlagRegex.MatchString(errorStr): 34 | result.Cause = errors.InvalidBooleanFlag 35 | result.Flag = InvalidBooleanFlagRegex.FindStringSubmatch(errorStr)[1] 36 | case MissingValueRegex.MatchString(errorStr): 37 | result.Cause = errors.MissingValue 38 | result.Flag = MissingValueRegex.FindStringSubmatch(errorStr)[1] 39 | case InvalidValueRegex.MatchString(errorStr): 40 | result.Cause = errors.InvalidValue 41 | result.Flag = InvalidValueRegex.FindStringSubmatch(errorStr)[1] 42 | default: 43 | return err 44 | } 45 | return result 46 | } 47 | -------------------------------------------------------------------------------- /functions/object_deletes.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | const ( 14 | errorParsingDelete = "Error parsing parameter '--delete'" 15 | ) 16 | 17 | // ObjectDeletes deletes multiple objects from a bucket. 18 | // Parameter: 19 | // CLI Context Application 20 | // Returns: 21 | // Error = zero or non-zero 22 | func ObjectDeletes(c *cli.Context) (err error) { 23 | // check the number of arguments 24 | if c.NArg() > 0 { 25 | err = &errors.CommandError{ 26 | CLIContext: c, 27 | Cause: errors.InvalidNArg, 28 | } 29 | return 30 | } 31 | 32 | // Load COS Context 33 | var cosContext *utils.CosContext 34 | if cosContext, err = GetCosContext(c); err != nil { 35 | return 36 | } 37 | 38 | // Set DeleteObjectInput 39 | input := new(s3.DeleteObjectsInput) 40 | 41 | // Required parameters for DeleteObjectsInput 42 | mandatory := map[string]string{ 43 | fields.Bucket: flags.Bucket, 44 | fields.Delete: flags.Delete, 45 | } 46 | 47 | // No optional parameters for DeleteObjectsInput 48 | options := map[string]string{} 49 | 50 | // Validate User Inputs 51 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 52 | return 53 | } 54 | 55 | // Setting client to do the call 56 | var client s3iface.S3API 57 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 58 | return 59 | } 60 | 61 | // DeleteObjects Op 62 | var output *s3.DeleteObjectsOutput 63 | if output, err = client.DeleteObjects(input); err != nil { 64 | return 65 | } 66 | 67 | // Display either in JSON or text 68 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 69 | 70 | // Return 71 | return 72 | } 73 | -------------------------------------------------------------------------------- /aspera/utils.go: -------------------------------------------------------------------------------- 1 | package aspera 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | "runtime" 8 | 9 | "google.golang.org/grpc" 10 | "google.golang.org/grpc/credentials/insecure" 11 | ) 12 | 13 | const ( 14 | defaultAddress = "127.0.0.1" 15 | defaultPort = "55002" 16 | ) 17 | 18 | func SDKDir() string { 19 | if sdk_path, ok := os.LookupEnv("ASPERA_SDK_PATH"); ok { 20 | return sdk_path 21 | } 22 | home, err := os.UserHomeDir() 23 | if err != nil { 24 | return "" 25 | } 26 | return path.Join(home, ".aspera_sdk") 27 | } 28 | 29 | func TransferdBinPath() string { 30 | daemonName := "asperatransferd" 31 | if runtime.GOOS == "windows" { 32 | daemonName = "asperatransferd.exe" 33 | } 34 | return path.Join(SDKDir(), "bin", daemonName) 35 | } 36 | 37 | func GetSDKDownloadURL() (url string, platform string, err error) { 38 | platform, ext, err := getSDKAttributes(runtime.GOOS, runtime.GOARCH) 39 | if err != nil { 40 | return 41 | } 42 | url = fmt.Sprintf("%s/%s/%s-%s-%s.%s", prefix, version, platform, version, commit, ext) 43 | return 44 | } 45 | 46 | func getSDKAttributes(os, arch string) (platform string, ext string, err error) { 47 | platforms := map[string][]string{ 48 | "darwin": {"amd64", "arm64"}, 49 | "linux": {"amd64", "ppc64le", "s390x"}, 50 | "windows": {"amd64"}, 51 | "aix": {"ppc64"}, 52 | } 53 | 54 | ext = "tar.gz" 55 | 56 | if archs, ok := platforms[os]; ok { 57 | for _, a := range archs { 58 | if a == arch { 59 | if os == "darwin" { 60 | os = "macos" 61 | ext = "zip" 62 | } 63 | if os == "windows" { 64 | ext = "zip" 65 | } 66 | return fmt.Sprintf("%s-%s", os, arch), ext, nil 67 | } 68 | } 69 | } 70 | return "", "", fmt.Errorf("unsupported platform: %s-%s", os, arch) 71 | 72 | } 73 | 74 | func defaultConnection() (cc *grpc.ClientConn, err error) { 75 | optInsecure := grpc.WithTransportCredentials(insecure.NewCredentials()) 76 | target := fmt.Sprintf("%s:%s", defaultAddress, defaultPort) 77 | cc, err = grpc.Dial(target, optInsecure) 78 | return 79 | } 80 | -------------------------------------------------------------------------------- /functions/object_tagging_delete.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibmcloud-cos-cli/errors" 5 | 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 8 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 9 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 10 | "github.com/IBM/ibmcloud-cos-cli/utils" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | // ObjectTaggingDelete removes website configuration from a bucket. 15 | // Parameter: 16 | // CLI Context Application 17 | // Returns: 18 | // Error = zero or non-zero 19 | func ObjectTaggingDelete(c *cli.Context) (err error) { 20 | // check the number of arguments 21 | if c.NArg() > 0 { 22 | // Build Command Error struct 23 | err = &errors.CommandError{ 24 | CLIContext: c, 25 | Cause: errors.InvalidNArg, 26 | } 27 | // Return error 28 | return 29 | } 30 | 31 | // Load COS Context 32 | var cosContext *utils.CosContext 33 | if cosContext, err = GetCosContext(c); err != nil { 34 | return 35 | } 36 | 37 | // Initialize input structure 38 | input := new(s3.DeleteObjectTaggingInput) 39 | 40 | // Required parameters 41 | mandatory := map[string]string{ 42 | fields.Bucket: flags.Bucket, 43 | fields.Key: flags.Key, 44 | } 45 | 46 | // Optional parameters 47 | options := map[string]string{ 48 | fields.VersionId: flags.VersionId, 49 | } 50 | 51 | // Validate User Inputs 52 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 53 | return 54 | } 55 | 56 | // Setting client to do the call 57 | var client s3iface.S3API 58 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 59 | return 60 | } 61 | 62 | // DeleteObjectTagging Op 63 | var output *s3.DeleteObjectTaggingOutput 64 | if output, err = client.DeleteObjectTagging(input); err != nil { 65 | return 66 | } 67 | 68 | // Display either in JSON or text 69 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 70 | 71 | // Return 72 | return 73 | } 74 | -------------------------------------------------------------------------------- /functions/multipart_abort.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // MultipartAbort will end a current active multipart upload instance 14 | // Parameter: 15 | // CLI Context Application 16 | // Returns: 17 | // Error = zero or non-zero 18 | func MultipartAbort(c *cli.Context) (err error) { 19 | // check the number of arguments 20 | if c.NArg() > 0 { 21 | err = &errors.CommandError{ 22 | CLIContext: c, 23 | Cause: errors.InvalidNArg, 24 | } 25 | return 26 | } 27 | 28 | // Load COS Context 29 | var cosContext *utils.CosContext 30 | if cosContext, err = GetCosContext(c); err != nil { 31 | return 32 | } 33 | 34 | // Initialize AbortMultipartUploadInput 35 | input := new(s3.AbortMultipartUploadInput) 36 | 37 | // Required parameters and no optional parameter for AbortMultipartUpload 38 | mandatory := map[string]string{ 39 | fields.Bucket: flags.Bucket, 40 | fields.Key: flags.Key, 41 | fields.UploadID: flags.UploadID, 42 | } 43 | 44 | // No optional parameters for AbortMultipartUpload 45 | options := map[string]string{} 46 | 47 | // Check through user inputs for validation 48 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 49 | return 50 | } 51 | 52 | // Setting client to do the call 53 | var client s3iface.S3API 54 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 55 | return 56 | } 57 | 58 | // AbortMultipartUpload Op 59 | var output *s3.AbortMultipartUploadOutput 60 | if output, err = client.AbortMultipartUpload(input); err != nil { 61 | return 62 | } 63 | 64 | // Display either in JSON or text 65 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 66 | 67 | // Return 68 | return 69 | } 70 | -------------------------------------------------------------------------------- /functions/multipart_complete.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // MultipartComplete completes a multipart upload instance. 14 | // Parameter: 15 | // CLI Context Application 16 | // Returns: 17 | // Error = zero or non-zero 18 | func MultipartComplete(c *cli.Context) (err error) { 19 | // check the number of arguments 20 | if c.NArg() > 0 { 21 | err = &errors.CommandError{ 22 | CLIContext: c, 23 | Cause: errors.InvalidNArg, 24 | } 25 | return 26 | } 27 | 28 | // Load COS Context 29 | var cosContext *utils.CosContext 30 | if cosContext, err = GetCosContext(c); err != nil { 31 | return 32 | } 33 | 34 | // Initialize CompleteMultipartUploadInput 35 | input := new(s3.CompleteMultipartUploadInput) 36 | 37 | // Required parameters for CompleteMultipartUpload 38 | mandatory := map[string]string{ 39 | fields.Bucket: flags.Bucket, 40 | fields.Key: flags.Key, 41 | fields.UploadID: flags.UploadID, 42 | fields.MultipartUpload: flags.MultipartUpload, 43 | } 44 | 45 | // Optional parameter for CompleteMultipartUpload 46 | options := map[string]string{} 47 | 48 | // Check through user inputs for validation 49 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 50 | return 51 | } 52 | 53 | // Setting client to do the call 54 | var client s3iface.S3API 55 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 56 | return 57 | } 58 | 59 | // CompleteMultipartUpload API 60 | var output *s3.CompleteMultipartUploadOutput 61 | if output, err = client.CompleteMultipartUpload(input); err != nil { 62 | return 63 | } 64 | 65 | // Display either in JSON or text 66 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 67 | 68 | // Return 69 | return 70 | } 71 | -------------------------------------------------------------------------------- /functions/bucket_head.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // BucketHead determines if a bucket exists in a user's account. 14 | // Parameter: 15 | // CLI Context Application 16 | // Returns: 17 | // Error = zero or non-zero 18 | func BucketHead(c *cli.Context) (err error) { 19 | // check the number of arguments 20 | if c.NArg() > 0 { 21 | err = &errors.CommandError{ 22 | CLIContext: c, 23 | Cause: errors.InvalidNArg, 24 | } 25 | return 26 | } 27 | 28 | // Load COS Context 29 | var cosContext *utils.CosContext 30 | if cosContext, err = GetCosContext(c); err != nil { 31 | return 32 | } 33 | 34 | // Builds HeadBucketInput 35 | input := new(s3.HeadBucketInput) 36 | 37 | // Required parameter and no optional parameter for HeadBucket 38 | mandatory := map[string]string{ 39 | fields.Bucket: flags.Bucket, 40 | } 41 | 42 | // Optional parameters 43 | options := map[string]string{} 44 | 45 | // Check through user inputs for validation 46 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 47 | return 48 | } 49 | 50 | // Setting client to do the call 51 | var client s3iface.S3API 52 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 53 | return 54 | } 55 | 56 | // HeadBucket Op 57 | var output *s3.HeadBucketOutput 58 | if output, err = client.HeadBucket(input); err != nil { 59 | return 60 | } 61 | 62 | // Build region for additional parameters to display 63 | var region string 64 | if region, err = cosContext.GetCurrentRegion(c.String(flags.Region)); err != nil { 65 | return 66 | } 67 | additionalParameters := map[string]interface{}{"region": region} 68 | 69 | // Display either in JSON or text 70 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, additionalParameters) 71 | 72 | // Return 73 | return 74 | } 75 | -------------------------------------------------------------------------------- /render/helpers.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | // Possible bucket class names 11 | Classes = []string{"standard", "vault", "cold", "flex", "smart", "onerate_active"} 12 | // Class tokenizer 13 | classTokenizer = strings.Join(Classes, `|`) 14 | /// Region specifications 15 | regionSpec = fmt.Sprintf(`(?i)^(\w+(?:-\w+)??)(-geo)?(?:-(%s|\*))?$`, classTokenizer) 16 | // RegionDecoderRegex regular expression to break down regions and storage class 17 | RegionDecoderRegex = regexp.MustCompile(regionSpec) 18 | ) 19 | 20 | // Retrieve class from the location constraint 21 | func getClassFromLocationConstraint(location string) string { 22 | // Break down regions and storage class 23 | regionDetails := RegionDecoderRegex.FindStringSubmatch(location) 24 | if regionDetails != nil { 25 | return regionDetails[3] 26 | } 27 | // Return no class 28 | return "" 29 | } 30 | 31 | // Retrieve region from the location constraint 32 | func getRegionFromLocationConstraint(location string) string { 33 | // Break down regions and storage class 34 | regionDetails := RegionDecoderRegex.FindStringSubmatch(location) 35 | if regionDetails != nil { 36 | return regionDetails[1] 37 | } 38 | // Return no region 39 | return "" 40 | } 41 | 42 | // Render class helper 43 | func renderClass(class string) string { 44 | switch class { 45 | case "": 46 | return "Standard" 47 | case "cold": 48 | return "Cold Vault" 49 | case "onerate_active": 50 | return "One-rate Active" 51 | default: 52 | return strings.Title(class) 53 | } 54 | } 55 | 56 | // 57 | // FormatFileSize function is excerpted from from 58 | // the following link: 59 | // https://programming.guide/go/formatting-byte-size-to-human-readable-format.html 60 | // Description: it outputs a human readable representation of the value using 61 | // multiples of 1024 62 | func FormatFileSize(b int64) string { 63 | const unit = 1024 64 | if b < unit { 65 | return fmt.Sprintf("%d B", b) 66 | } 67 | div, exp := int64(unit), 0 68 | for n := b / unit; n >= unit; n /= unit { 69 | div *= unit 70 | exp++ 71 | } 72 | return fmt.Sprintf("%.2f %ciB", float64(b)/float64(div), "KMGTPE"[exp]) 73 | } 74 | -------------------------------------------------------------------------------- /functions/object_head.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // ObjectHead is used to get the head of an object, i.e., determine if it exists in a bucket. 14 | // It also gets the file size and the last time it was modified. 15 | // Parameter: 16 | // CLI Context Application 17 | // Returns: 18 | // Error = zero or non-zero 19 | func ObjectHead(c *cli.Context) (err error) { 20 | // check the number of arguments 21 | if c.NArg() > 0 { 22 | err = &errors.CommandError{ 23 | CLIContext: c, 24 | Cause: errors.InvalidNArg, 25 | } 26 | return 27 | } 28 | 29 | // Load COS Context 30 | var cosContext *utils.CosContext 31 | if cosContext, err = GetCosContext(c); err != nil { 32 | return 33 | } 34 | 35 | // Initialize HeadObjectInput 36 | input := new(s3.HeadObjectInput) 37 | 38 | // Required parameters for HeadObject 39 | mandatory := map[string]string{ 40 | fields.Bucket: flags.Bucket, 41 | fields.Key: flags.Key, 42 | } 43 | 44 | // Optional parameters for HeadObject 45 | options := map[string]string{ 46 | fields.IfMatch: flags.IfMatch, 47 | fields.IfModifiedSince: flags.IfModifiedSince, 48 | fields.IfNoneMatch: flags.IfNoneMatch, 49 | fields.IfUnmodifiedSince: flags.IfUnmodifiedSince, 50 | fields.Range: flags.Range, 51 | fields.VersionId: flags.VersionId, 52 | } 53 | 54 | // Validate User Inputs 55 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 56 | return 57 | } 58 | 59 | // Setting client to do the call 60 | var client s3iface.S3API 61 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 62 | return 63 | } 64 | 65 | // HeadObject API 66 | var output *s3.HeadObjectOutput 67 | if output, err = client.HeadObject(input); err != nil { 68 | return 69 | } 70 | 71 | // Display either in JSON or text 72 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 73 | 74 | // Return 75 | return 76 | } 77 | -------------------------------------------------------------------------------- /aspera/utils_test.go: -------------------------------------------------------------------------------- 1 | package aspera 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "runtime" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestSDKDir(t *testing.T) { 14 | os.Setenv("ASPERA_SDK_PATH", "/path/to/sdk") 15 | assert.Equal(t, "/path/to/sdk", SDKDir()) 16 | os.Unsetenv("ASPERA_SDK_PATH") 17 | 18 | if home, err := os.UserHomeDir(); err == nil { 19 | assert.Equal(t, filepath.Join(home, ".aspera_sdk"), SDKDir()) 20 | } else { 21 | assert.Equal(t, ".aspera_sdk", SDKDir()) 22 | } 23 | } 24 | 25 | func TestTransferdBinPath(t *testing.T) { 26 | os.Setenv("ASPERA_SDK_PATH", "/path/to/sdk") 27 | if runtime.GOOS == "windows" { 28 | assert.Equal(t, "/path/to/sdk/bin/asperatransferd.exe", TransferdBinPath()) 29 | } else { 30 | assert.Equal(t, "/path/to/sdk/bin/asperatransferd", TransferdBinPath()) 31 | } 32 | } 33 | func TestGetSDKAttributes(t *testing.T) { 34 | supportedPlatforms := []struct { 35 | os string 36 | arch string 37 | platform string 38 | ext string 39 | }{ 40 | {"darwin", "amd64", "osx-amd64", "tar.gz"}, //special os 41 | {"windows", "amd64", "windows-amd64", "zip"}, //special ext 42 | {"linux", "amd64", "linux-amd64", "tar.gz"}, //common case 43 | } 44 | 45 | for _, pair := range supportedPlatforms { 46 | platform, ext, _ := getSDKAttributes(pair.os, pair.arch) 47 | assert.Equal(t, platform, pair.platform) 48 | assert.Equal(t, ext, pair.ext) 49 | } 50 | } 51 | 52 | func TestGetSDKAttributesError(t *testing.T) { 53 | notSupportedPlatforms := []struct { 54 | os string 55 | arch string 56 | }{ 57 | {"darwin", "arm64"}, // not supported os 58 | {"freebsd", "amd64"}, // not supported arch 59 | {"orbis", "amd64"}, // either is not supported 60 | } 61 | 62 | for _, p := range notSupportedPlatforms { 63 | _, _, err := getSDKAttributes(p.os, p.arch) 64 | if err.Error() != fmt.Sprintf("unsupported platform: %s-%s", p.os, p.arch) { 65 | t.Errorf("unexpected error: %s", err) 66 | } 67 | } 68 | } 69 | 70 | func TestGetSDKDownloadURL(t *testing.T) { 71 | if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" { 72 | url, platform, _ := GetSDKDownloadURL() 73 | assert.Equal(t, url, "https://download.asperasoft.com/download/sw/sdk/transfer/1.1.1/osx-amd64-52a85ef.tar.gz") 74 | assert.Equal(t, platform, "osx-amd64") 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /functions/part_upload.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/IBM/ibmcloud-cos-cli/errors" 7 | 8 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 9 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 10 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 11 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 12 | "github.com/IBM/ibmcloud-cos-cli/utils" 13 | "github.com/urfave/cli" 14 | ) 15 | 16 | // PartUpload - uploads an individual part of a multiple upload (file). 17 | // Parameter: 18 | // CLI Context Application 19 | // Returns: 20 | // Error = zero or non-zero 21 | func PartUpload(c *cli.Context) (err error) { 22 | // check the number of arguments 23 | if c.NArg() > 0 { 24 | // Build Command Error struct 25 | err = &errors.CommandError{ 26 | CLIContext: c, 27 | Cause: errors.InvalidNArg, 28 | } 29 | // Return error 30 | return 31 | } 32 | 33 | // Load COS Context 34 | var cosContext *utils.CosContext 35 | if cosContext, err = GetCosContext(c); err != nil { 36 | return 37 | } 38 | 39 | // Initialize UploadPartInput 40 | input := new(s3.UploadPartInput) 41 | 42 | // Required parameters for UploadPart 43 | mandatory := map[string]string{ 44 | fields.Bucket: flags.Bucket, 45 | fields.Key: flags.Key, 46 | fields.UploadID: flags.UploadID, 47 | fields.PartNumber: flags.PartNumber, 48 | } 49 | 50 | // Optional parameters for UploadPart 51 | options := map[string]string{ 52 | fields.ContentLength: flags.ContentLength, 53 | fields.ContentMD5: flags.ContentMD5, 54 | fields.Body: flags.Body, 55 | } 56 | 57 | // Check through user inputs for validation 58 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 59 | return 60 | } 61 | 62 | // Defer closing body 63 | if closeAble, ok := input.Body.(io.Closer); ok { 64 | defer closeAble.Close() 65 | } 66 | 67 | // Setting client to do the call 68 | var client s3iface.S3API 69 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 70 | return 71 | } 72 | 73 | // UploadPart Op 74 | var output *s3.UploadPartOutput 75 | if output, err = client.UploadPart(input); err != nil { 76 | return 77 | } 78 | 79 | // Display either in JSON or text 80 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 81 | 82 | // Return 83 | return 84 | } 85 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | APP:=$(notdir $(CURDIR)) 2 | GOPATH:=$(shell go env GOPATH) 3 | GOHOSTOS:=$(shell go env GOHOSTOS) 4 | GOHOSTARCH:=$(shell go env GOHOSTARCH) 5 | 6 | BUILD_DATE:="$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")" 7 | GIT_COMMIT_SHA:="$(shell git rev-parse --abbrev-ref HEAD -->/dev/null)" 8 | GIT_REMOTE_URL:="$(shell git config --get remote.origin.url 2>/dev/null)" 9 | BUILD_DATE:="$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")" 10 | 11 | # Dependencies package 12 | PACKAGES:=vendor 13 | 14 | # Jenkins vars. Set to `unknown` if the variable is not yet defined 15 | BUILD_ID?=unknown 16 | BUILD_NUMBER?=unknown 17 | SHELL:=/bin/bash 18 | 19 | SupportedOperatingSystems = darwin linux windows ppc64le s390x 20 | 21 | .PHONY: all test clean cleanAll aspera build install buildSupported $(SupportedOperatingSystems) set_env_vars buildWithMod vendorWithMod 22 | 23 | .DEFAULT_GOAL := build 24 | 25 | aspera: 26 | bin/download-aspera-mod 27 | 28 | build: aspera 29 | CGO_ENABLED=0 go build -o build/${APP}-${GOHOSTOS}-${GOHOSTARCH} -ldflags "-s -w" -a -installsuffix cgo . 30 | 31 | clean: 32 | -go clean 33 | -go clean -testcache 34 | -rm -rf build 35 | -rm -rf $(APP)-* 36 | -rm -rf logs 37 | 38 | cleanAll : clean 39 | -rm -rf vendor 40 | 41 | install: 42 | ibmcloud plugin install build/${APP}-${GOHOSTOS}-${GOHOSTARCH} 43 | 44 | osx : darwin 45 | windows : EXT := .exe 46 | 47 | buildSupported: $(SupportedOperatingSystems) 48 | 49 | ${SupportedOperatingSystems}: aspera 50 | if [ "$@" != "ppc64le" ] && [ "$@" != "s390x" ]; then\ 51 | CGO_ENABLED=0 GOOS=$@ GOARCH=amd64 go build -o build/${APP}-$@-amd64${EXT} -ldflags "-s -w" -a -installsuffix cgo . ;\ 52 | if [ "$@" == "darwin" ] || [ "$@" == "linux" ]; then\ 53 | CGO_ENABLED=0 GOOS=$@ GOARCH=arm64 go build -o build/${APP}-$@-arm64${EXT} -ldflags "-s -w" -a -installsuffix cgo . ;\ 54 | fi \ 55 | else\ 56 | CGO_ENABLED=0 GOOS=linux GOARCH=$@ go build -o build/${APP}-linux-$@ -ldflags "-s -w" -a -installsuffix cgo . ;\ 57 | fi 58 | 59 | all: $(SupportedOperatingSystems) 60 | 61 | test : 62 | go test -v -tags unit ./functions 63 | 64 | #### experimental cli go mod support ### 65 | ## populate vendor folder 66 | vendorWithMod : 67 | go mod vendor 68 | 69 | ## Build using go mod 70 | buildWithMod: vendorWithMod 71 | GOFLAGS="-mod=vendor $$GOFLAGS" \ 72 | CGO_ENABLED=0 \ 73 | go build -o build/${APP}-${GOHOSTOS}-${GOHOSTARCH} -ldflags "-s -w" -a -installsuffix cgo . 74 | -------------------------------------------------------------------------------- /functions/wait_bucket.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // bucketWaiter is a function based off of HeadBucketInput to 14 | // monitor whether the object exists or not 15 | type bucketWaiter func(*s3.HeadBucketInput) error 16 | 17 | // bucketWaiterBuild type 18 | type bucketWaiterBuild func(s3iface.S3API) bucketWaiter 19 | 20 | // WaitBucketExists function 21 | func WaitBucketExists(c *cli.Context) error { 22 | bwb := func(client s3iface.S3API) bucketWaiter { return client.WaitUntilBucketExists } 23 | return doBucketWait(c, bwb) 24 | } 25 | 26 | // WaitBucketNotExists function 27 | func WaitBucketNotExists(c *cli.Context) error { 28 | bwb := func(client s3iface.S3API) bucketWaiter { return client.WaitUntilBucketNotExists } 29 | return doBucketWait(c, bwb) 30 | } 31 | 32 | // doBucketWait function waits for when object exists or not 33 | // Parameter: 34 | // CLI Context Application 35 | // Returns: 36 | // Error = zero or non-zero 37 | func doBucketWait(c *cli.Context, bwb bucketWaiterBuild) (err error) { 38 | 39 | // check the number of arguments 40 | if c.NArg() > 0 { 41 | err = &errors.CommandError{ 42 | CLIContext: c, 43 | Cause: errors.InvalidNArg, 44 | } 45 | return 46 | } 47 | 48 | // Load COS Context 49 | var cosContext *utils.CosContext 50 | if cosContext, err = GetCosContext(c); err != nil { 51 | return 52 | } 53 | 54 | // Build PutObjectInput 55 | input := new(s3.HeadBucketInput) 56 | 57 | // Required parameters and no optional parameter for Head bucket 58 | mandatory := map[string]string{ 59 | fields.Bucket: flags.Bucket, 60 | } 61 | 62 | options := map[string]string{} 63 | 64 | // Check through user inputs for validation 65 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 66 | return 67 | } 68 | 69 | // Setting client to do the call 70 | var client s3iface.S3API 71 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 72 | return 73 | } 74 | 75 | // Wait until bucket condition 76 | if err = bwb(client)(input); err != nil { 77 | cosContext.UI.Failed(err.Error()) 78 | return cli.NewExitError(err, 255) 79 | } 80 | 81 | // Return 82 | return 83 | } 84 | -------------------------------------------------------------------------------- /functions/bucket_website_delete.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/terminal" 5 | "github.com/IBM/ibmcloud-cos-cli/errors" 6 | 7 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 8 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 9 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 10 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 11 | "github.com/IBM/ibmcloud-cos-cli/render" 12 | "github.com/IBM/ibmcloud-cos-cli/utils" 13 | "github.com/urfave/cli" 14 | ) 15 | 16 | // BucketWebsiteDelete removes website configuration from a bucket. 17 | // Parameter: 18 | // CLI Context Application 19 | // Returns: 20 | // Error = zero or non-zero 21 | func BucketWebsiteDelete(c *cli.Context) (err error) { 22 | // check the number of arguments 23 | if c.NArg() > 0 { 24 | // Build Command Error struct 25 | err = &errors.CommandError{ 26 | CLIContext: c, 27 | Cause: errors.InvalidNArg, 28 | } 29 | // Return error 30 | return 31 | } 32 | 33 | // Load COS Context 34 | var cosContext *utils.CosContext 35 | if cosContext, err = GetCosContext(c); err != nil { 36 | return 37 | } 38 | 39 | // Initialize input structure 40 | input := new(s3.DeleteBucketWebsiteInput) 41 | 42 | // Required parameters 43 | mandatory := map[string]string{ 44 | fields.Bucket: flags.Bucket, 45 | } 46 | 47 | // Optional parameters 48 | options := map[string]string{} 49 | 50 | // Validate User Inputs 51 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 52 | return 53 | } 54 | 55 | // No force on deleting bucket website, alert users 56 | if !c.Bool(flags.Force) { 57 | confirmed := false 58 | // Warn the user that they're about to delete a bucket website. 59 | cosContext.UI.Warn(render.WarningDeleteBucketWebsite(input)) 60 | cosContext.UI.Prompt(render.MessageConfirmationContinue(), &terminal.PromptOptions{}).Resolve(&confirmed) 61 | if !confirmed { 62 | cosContext.UI.Say(render.MessageOperationCanceled()) 63 | return 64 | } 65 | } 66 | 67 | // Setting client to do the call 68 | var client s3iface.S3API 69 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 70 | return 71 | } 72 | 73 | // DeleteBucketWebsite Op 74 | var output *s3.DeleteBucketWebsiteOutput 75 | if output, err = client.DeleteBucketWebsite(input); err != nil { 76 | return 77 | } 78 | 79 | // Display either in JSON or text 80 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 81 | 82 | // Return 83 | return 84 | } 85 | -------------------------------------------------------------------------------- /functions/bucket_delete.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/terminal" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 7 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 8 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 9 | "github.com/IBM/ibmcloud-cos-cli/errors" 10 | "github.com/IBM/ibmcloud-cos-cli/render" 11 | "github.com/IBM/ibmcloud-cos-cli/utils" 12 | "github.com/urfave/cli" 13 | ) 14 | 15 | // BucketDelete deletes a bucket from a user's account. Requires the user to provide the region of the bucket, if it's 16 | // not provided then get DefaultRegion from credentials.json 17 | // Parameter: 18 | // CLI Context Application 19 | // Returns: 20 | // Error = zero or non-zero 21 | func BucketDelete(c *cli.Context) (err error) { 22 | // check the number of arguments 23 | if c.NArg() > 0 { 24 | err = &errors.CommandError{ 25 | CLIContext: c, 26 | Cause: errors.InvalidNArg, 27 | } 28 | return 29 | } 30 | 31 | // Load COS Context 32 | var cosContext *utils.CosContext 33 | if cosContext, err = GetCosContext(c); err != nil { 34 | return 35 | } 36 | 37 | // Set DeleteBucketInput 38 | input := new(s3.DeleteBucketInput) 39 | 40 | // Required parameter and no optional parameter for DeleteBucket 41 | mandatory := map[string]string{ 42 | fields.Bucket: flags.Bucket, 43 | } 44 | 45 | // Optional parameters 46 | options := map[string]string{} 47 | 48 | // Check through user inputs for validation 49 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 50 | return 51 | } 52 | 53 | // No force on deleting object, alert users 54 | if !c.Bool(flags.Force) { 55 | confirmed := false 56 | // Warn the user that they're about to delete a bucket. 57 | cosContext.UI.Warn(render.WarningDeleteBucket(input)) 58 | cosContext.UI.Prompt(render.MessageConfirmationContinue(), &terminal.PromptOptions{}).Resolve(&confirmed) 59 | if !confirmed { 60 | cosContext.UI.Say(render.MessageOperationCanceled()) 61 | return 62 | } 63 | } 64 | 65 | // Setting client to do the call 66 | var client s3iface.S3API 67 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 68 | return 69 | } 70 | 71 | // DeleteBucket Op 72 | var output *s3.DeleteBucketOutput 73 | if output, err = client.DeleteBucket(input); err != nil { 74 | return 75 | } 76 | 77 | // Display either in JSON or text 78 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 79 | 80 | // Return 81 | return 82 | } 83 | -------------------------------------------------------------------------------- /functions/object_delete.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/terminal" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 7 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 8 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 9 | "github.com/IBM/ibmcloud-cos-cli/errors" 10 | "github.com/IBM/ibmcloud-cos-cli/render" 11 | "github.com/IBM/ibmcloud-cos-cli/utils" 12 | "github.com/urfave/cli" 13 | ) 14 | 15 | // ObjectDelete deletes an object from a bucket. 16 | // Parameter: 17 | // CLI Context Application 18 | // Returns: 19 | // Error = zero or non-zero 20 | func ObjectDelete(c *cli.Context) (err error) { 21 | // check the number of arguments 22 | if c.NArg() > 0 { 23 | err = &errors.CommandError{ 24 | CLIContext: c, 25 | Cause: errors.InvalidNArg, 26 | } 27 | // Return error 28 | return 29 | } 30 | 31 | // Load COS Context 32 | var cosContext *utils.CosContext 33 | if cosContext, err = GetCosContext(c); err != nil { 34 | return 35 | } 36 | 37 | // Set DeleteObjectInput 38 | input := new(s3.DeleteObjectInput) 39 | 40 | // Required parameters and no optional parameter for DeleteObject 41 | mandatory := map[string]string{ 42 | fields.Bucket: flags.Bucket, 43 | fields.Key: flags.Key, 44 | } 45 | 46 | options := map[string]string{ 47 | fields.VersionId: flags.VersionId, 48 | } 49 | 50 | // Validate User Inputs 51 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 52 | return 53 | } 54 | 55 | // No force on deleting object, alert users 56 | if !c.Bool(flags.Force) { 57 | confirmed := false 58 | 59 | // Warn the user that they're about to delete the file (prevent accidental deletions) 60 | cosContext.UI.Warn(render.WarningDeleteObject(input)) 61 | cosContext.UI.Prompt(render.MessageConfirmationContinue(), &terminal.PromptOptions{}).Resolve(&confirmed) 62 | 63 | // If users cancel prior, cancel operation 64 | if !confirmed { 65 | cosContext.UI.Say(render.MessageOperationCanceled()) 66 | return 67 | } 68 | } 69 | 70 | // Setting client to do the call 71 | var client s3iface.S3API 72 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 73 | return 74 | } 75 | 76 | // DeleteObject Op 77 | var output *s3.DeleteObjectOutput 78 | if output, err = client.DeleteObject(input); err != nil { 79 | return 80 | } 81 | 82 | // Display either in JSON or text 83 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 84 | 85 | // Return 86 | return 87 | } 88 | -------------------------------------------------------------------------------- /functions/multipart_create.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // Local Constants 14 | const ( 15 | incorrectUsage = "Incorrect Usage." 16 | noRegion = "Unable to get region." 17 | ) 18 | 19 | // MultipartCreate creates a new multipart upload instance, according to the Amazon AWS multipart specification 20 | // Parameter: 21 | // CLI Context Application 22 | // Returns: 23 | // Error = zero or non-zero 24 | func MultipartCreate(c *cli.Context) (err error) { 25 | // check the number of arguments 26 | if c.NArg() > 0 { 27 | err = &errors.CommandError{ 28 | CLIContext: c, 29 | Cause: errors.InvalidNArg, 30 | } 31 | // Return with error 32 | return 33 | } 34 | 35 | // Load COS Context 36 | var cosContext *utils.CosContext 37 | if cosContext, err = GetCosContext(c); err != nil { 38 | return 39 | } 40 | 41 | // Initialize CreateMultipartUploadInput 42 | input := new(s3.CreateMultipartUploadInput) 43 | 44 | // Required parameters for CreateMultipartUpload 45 | mandatory := map[string]string{ 46 | fields.Bucket: flags.Bucket, 47 | fields.Key: flags.Key, 48 | } 49 | 50 | // 51 | // Optional parameters for CreateMultipartUpload 52 | options := map[string]string{ 53 | fields.CacheControl: flags.CacheControl, 54 | fields.ContentDisposition: flags.ContentDisposition, 55 | fields.ContentEncoding: flags.ContentEncoding, 56 | fields.ContentLanguage: flags.ContentLanguage, 57 | fields.ContentType: flags.ContentType, 58 | fields.Metadata: flags.Metadata, 59 | fields.Tagging: flags.Tagging, 60 | fields.WebsiteRedirectLocation: flags.WebsiteRedirectLocation, 61 | } 62 | 63 | // Check through user inputs for validation 64 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 65 | return 66 | } 67 | 68 | // Setting client to do the call 69 | var client s3iface.S3API 70 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 71 | return 72 | } 73 | 74 | // CreateMultipartUpload Op 75 | var output *s3.CreateMultipartUploadOutput 76 | if output, err = client.CreateMultipartUpload(input); err != nil { 77 | return 78 | } 79 | 80 | // Display either in JSON or text 81 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 82 | 83 | // Return 84 | return 85 | } 86 | -------------------------------------------------------------------------------- /i18n/i18n.go: -------------------------------------------------------------------------------- 1 | package i18n 2 | 3 | import ( 4 | "path/filepath" 5 | "strings" 6 | 7 | "github.com/nicksnyder/go-i18n/i18n" 8 | 9 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/configuration/core_config" 10 | "github.com/IBM/ibmcloud-cos-cli/i18n/detection" 11 | "github.com/IBM/ibmcloud-cos-cli/resources" 12 | ) 13 | 14 | const ( 15 | DEFAULT_LOCALE = "en_US" 16 | ) 17 | 18 | var SUPPORTED_LOCALES = []string{ 19 | "de_DE", 20 | "en_US", 21 | "es_ES", 22 | "fr_FR", 23 | "it_IT", 24 | "ja_JP", 25 | "ko_KR", 26 | "pt_BR", 27 | "zh_Hans", 28 | "zh_Hant", 29 | } 30 | 31 | var resourcePath = filepath.Join("i18n", "resources") 32 | 33 | func GetResourcePath() string { 34 | return resourcePath 35 | } 36 | 37 | func SetResourcePath(path string) { 38 | resourcePath = path 39 | } 40 | 41 | var T i18n.TranslateFunc = Init(core_config.NewCoreConfig(func(error) {}), new(detection.JibberJabberDetector)) 42 | 43 | func Init(coreConfig core_config.Repository, detector detection.Detector) i18n.TranslateFunc { 44 | userLocale := coreConfig.Locale() 45 | if userLocale != "" { 46 | return initWithLocale(userLocale) 47 | } 48 | locale := supportedLocale(detector.DetectLocale()) 49 | if locale == "" { 50 | locale = defaultLocaleForLang(detector.DetectLanguage()) 51 | } 52 | if locale == "" { 53 | locale = DEFAULT_LOCALE 54 | } 55 | return initWithLocale(locale) 56 | } 57 | 58 | func initWithLocale(locale string) i18n.TranslateFunc { 59 | err := loadFromAsset(locale) 60 | if err != nil { 61 | panic(err) 62 | } 63 | return i18n.MustTfunc(locale) 64 | } 65 | 66 | func loadFromAsset(locale string) (err error) { 67 | assetName := locale + ".all.json" 68 | assetKey := filepath.Join(resourcePath, assetName) 69 | bytes, err := resources.Asset(assetKey) 70 | if err != nil { 71 | return 72 | } 73 | err = i18n.ParseTranslationFileBytes(assetName, bytes) 74 | return 75 | } 76 | 77 | func supportedLocale(locale string) string { 78 | locale = normailizeLocale(locale) 79 | for _, l := range SUPPORTED_LOCALES { 80 | if strings.EqualFold(locale, l) { 81 | return l 82 | } 83 | } 84 | switch locale { 85 | case "zh_cn", "zh_sg": 86 | return "zh_Hans" 87 | case "zh_hk", "zh_tw": 88 | return "zh_Hant" 89 | } 90 | return "" 91 | } 92 | 93 | func normailizeLocale(locale string) string { 94 | return strings.ToLower(strings.Replace(locale, "-", "_", 1)) 95 | } 96 | 97 | func defaultLocaleForLang(lang string) string { 98 | if lang != "" { 99 | lang = strings.ToLower(lang) 100 | for _, l := range SUPPORTED_LOCALES { 101 | if lang == l[0:2] { 102 | return l 103 | } 104 | } 105 | } 106 | return "" 107 | } 108 | -------------------------------------------------------------------------------- /functions/object_put.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/IBM/ibmcloud-cos-cli/errors" 7 | 8 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 9 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 10 | 11 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 12 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 13 | "github.com/IBM/ibmcloud-cos-cli/utils" 14 | "github.com/urfave/cli" 15 | ) 16 | 17 | // ObjectPut - puts an object to a bucket without using multipart upload 18 | // Parameter: 19 | // CLI Context Application 20 | // Returns: 21 | // Error = zero or non-zero 22 | func ObjectPut(c *cli.Context) (err error) { 23 | // check the number of arguments 24 | if c.NArg() > 0 { 25 | // Build Command Error struct 26 | err = &errors.CommandError{ 27 | CLIContext: c, 28 | Cause: errors.InvalidNArg, 29 | } 30 | // Return error 31 | return 32 | } 33 | 34 | // Load COS Context 35 | var cosContext *utils.CosContext 36 | if cosContext, err = GetCosContext(c); err != nil { 37 | return 38 | } 39 | 40 | // Build PutObjectInput 41 | input := new(s3.PutObjectInput) 42 | 43 | // Required parameters for PutObject 44 | mandatory := map[string]string{ 45 | fields.Bucket: flags.Bucket, 46 | fields.Key: flags.Key, 47 | } 48 | 49 | // Optional parameters for PutObject 50 | options := map[string]string{ 51 | fields.CacheControl: flags.CacheControl, 52 | fields.ContentDisposition: flags.ContentDisposition, 53 | fields.ContentEncoding: flags.ContentEncoding, 54 | fields.ContentLanguage: flags.ContentLanguage, 55 | fields.ContentLength: flags.ContentLength, 56 | fields.ContentMD5: flags.ContentMD5, 57 | fields.ContentType: flags.ContentType, 58 | fields.Metadata: flags.Metadata, 59 | fields.Body: flags.Body, 60 | fields.Tagging: flags.Tagging, 61 | fields.WebsiteRedirectLocation: flags.WebsiteRedirectLocation, 62 | } 63 | 64 | // Validate User Inputs 65 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 66 | return 67 | } 68 | 69 | // Deferring close of Body 70 | if closeAble, ok := input.Body.(io.Closer); ok { 71 | defer closeAble.Close() 72 | } 73 | 74 | // Setting client to do the call 75 | var client s3iface.S3API 76 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 77 | return 78 | } 79 | 80 | // PutObject Op 81 | var output *s3.PutObjectOutput 82 | if output, err = client.PutObject(input); err != nil { 83 | return 84 | } 85 | 86 | // Display either in JSON or text 87 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 88 | 89 | // Return 90 | return 91 | } 92 | -------------------------------------------------------------------------------- /functions/part_upload_copy.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/IBM/ibmcloud-cos-cli/errors" 7 | 8 | "github.com/IBM/ibm-cos-sdk-go/aws/awserr" 9 | 10 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 11 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 12 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 13 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 14 | "github.com/IBM/ibmcloud-cos-cli/utils" 15 | "github.com/urfave/cli" 16 | ) 17 | 18 | // PartUploadCopy - uploads an individual part of a multiple upload (file). 19 | // Parameter: 20 | // CLI Context Application 21 | // Returns: 22 | // Error = zero or non-zero 23 | func PartUploadCopy(c *cli.Context) (err error) { 24 | // check the number of arguments 25 | if c.NArg() > 0 { 26 | // Build Command Error struct 27 | err = &errors.CommandError{ 28 | CLIContext: c, 29 | Cause: errors.InvalidNArg, 30 | } 31 | // Return error 32 | return 33 | } 34 | 35 | // Load COS Context 36 | var cosContext *utils.CosContext 37 | if cosContext, err = GetCosContext(c); err != nil { 38 | return 39 | } 40 | 41 | // Initialize UploadPartCopyInput 42 | input := new(s3.UploadPartCopyInput) 43 | 44 | // Required parameters for UploadPartCopy 45 | mandatory := map[string]string{ 46 | fields.Bucket: flags.Bucket, 47 | fields.Key: flags.Key, 48 | fields.UploadID: flags.UploadID, 49 | fields.PartNumber: flags.PartNumber, 50 | fields.CopySource: flags.CopySource, 51 | } 52 | 53 | // 54 | // Optional parameter for UploadPartCopy 55 | options := map[string]string{ 56 | fields.CopySourceIfMatch: flags.CopySourceIfMatch, 57 | fields.CopySourceIfModifiedSince: flags.CopySourceIfModifiedSince, 58 | fields.CopySourceIfNoneMatch: flags.CopySourceIfNoneMatch, 59 | fields.CopySourceIfUnmodifiedSince: flags.CopySourceIfUnmodifiedSince, 60 | fields.CopySourceRange: flags.CopySourceRange, 61 | } 62 | 63 | // Check through user inputs for validation 64 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 65 | return 66 | } 67 | 68 | // validate source 69 | if path := strings.Split(*input.CopySource, "/"); len(path[0]) == 0 { 70 | return awserr.New("copy.source.bucket.missing", "no source bucket", nil) 71 | } 72 | 73 | // Setting client to do the call 74 | var client s3iface.S3API 75 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 76 | return 77 | } 78 | 79 | // UploadPartCopy Op 80 | var output *s3.UploadPartCopyOutput 81 | if output, err = client.UploadPartCopy(input); err != nil { 82 | return 83 | } 84 | 85 | // Display either in JSON or text 86 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 87 | 88 | // Return 89 | return 90 | } 91 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/IBM/ibmcloud-cos-cli 2 | 3 | go 1.23.10 4 | 5 | require ( 6 | github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.12.0 7 | github.com/IBM/ibm-cos-sdk-go v1.12.3 8 | github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 9 | github.com/google/wire v0.6.0 10 | github.com/nicksnyder/go-i18n v1.10.3 11 | github.com/prataprc/goparsec v0.0.0-20211219142520-daac0e635e7e 12 | github.com/stretchr/testify v1.10.0 13 | github.com/urfave/cli v1.22.17 14 | google.golang.org/grpc v1.73.1 15 | google.golang.org/protobuf v1.36.10 16 | gopkg.in/cheggaaa/pb.v1 v1.0.28 17 | ) 18 | 19 | require ( 20 | github.com/IBM/go-sdk-core/v5 v5.21.0 // indirect 21 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 22 | github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect 23 | github.com/davecgh/go-spew v1.1.1 // indirect 24 | github.com/fatih/color v1.18.0 // indirect 25 | github.com/fatih/structs v1.1.0 // indirect 26 | github.com/gabriel-vasile/mimetype v1.4.9 // indirect 27 | github.com/go-openapi/errors v0.22.1 // indirect 28 | github.com/go-openapi/strfmt v0.23.0 // indirect 29 | github.com/go-playground/locales v0.14.1 // indirect 30 | github.com/go-playground/universal-translator v0.18.1 // indirect 31 | github.com/go-playground/validator/v10 v10.27.0 // indirect 32 | github.com/google/uuid v1.6.0 // indirect 33 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 34 | github.com/hashicorp/go-retryablehttp v0.7.8 // indirect 35 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 36 | github.com/jmespath/go-jmespath v0.4.0 // indirect 37 | github.com/leodido/go-urn v1.4.0 // indirect 38 | github.com/mattn/go-colorable v0.1.14 // indirect 39 | github.com/mattn/go-isatty v0.0.20 // indirect 40 | github.com/mattn/go-runewidth v0.0.16 // indirect 41 | github.com/mitchellh/mapstructure v1.5.0 // indirect 42 | github.com/oklog/ulid v1.3.1 // indirect 43 | github.com/pelletier/go-toml v1.9.5 // indirect 44 | github.com/pmezard/go-difflib v1.0.0 // indirect 45 | github.com/rivo/uniseg v0.4.7 // indirect 46 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 47 | github.com/spf13/cobra v1.9.1 // indirect 48 | github.com/spf13/pflag v1.0.7 // indirect 49 | github.com/stretchr/objx v0.5.2 // indirect 50 | go.mongodb.org/mongo-driver v1.17.4 // indirect 51 | go.yaml.in/yaml/v2 v2.4.2 // indirect 52 | golang.org/x/crypto v0.41.0 // indirect 53 | golang.org/x/net v0.43.0 // indirect 54 | golang.org/x/sys v0.35.0 // indirect 55 | golang.org/x/term v0.34.0 // indirect 56 | golang.org/x/text v0.28.0 // indirect 57 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect 58 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 59 | gopkg.in/yaml.v2 v2.4.0 // indirect 60 | gopkg.in/yaml.v3 v3.0.1 // indirect 61 | sigs.k8s.io/yaml v1.5.0 // indirect 62 | ) 63 | -------------------------------------------------------------------------------- /functions/wait_object.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // objectWaiter is a function based off of HeadObjectInput to 14 | // monitor whether the object exists or not 15 | type objectWaiter func(input *s3.HeadObjectInput) error 16 | 17 | // objectWaiterBuild is a function based off of s3 interface API 18 | // for the objectWaiter type. 19 | type objectWaiterBuild func(api s3iface.S3API) objectWaiter 20 | 21 | // WaitObjectExists function 22 | func WaitObjectExists(c *cli.Context) error { 23 | owb := func(client s3iface.S3API) objectWaiter { return client.WaitUntilObjectExists } 24 | return doObjectWait(c, owb) 25 | } 26 | 27 | // WaitObjectNotExists function 28 | func WaitObjectNotExists(c *cli.Context) error { 29 | owb := func(client s3iface.S3API) objectWaiter { return client.WaitUntilObjectNotExists } 30 | return doObjectWait(c, owb) 31 | } 32 | 33 | // doObjectWait function waits for when object exists or not 34 | // Parameter: 35 | // CLI Context Application 36 | // Returns: 37 | // Error = zero or non-zero 38 | func doObjectWait(c *cli.Context, owb objectWaiterBuild) (err error) { 39 | 40 | // check the number of arguments 41 | if c.NArg() > 0 { 42 | err = &errors.CommandError{ 43 | CLIContext: c, 44 | Cause: errors.InvalidNArg, 45 | } 46 | return 47 | } 48 | 49 | // Load COS Context 50 | var cosContext *utils.CosContext 51 | if cosContext, err = GetCosContext(c); err != nil { 52 | return 53 | } 54 | 55 | // Initialize HeadObjectInput 56 | input := new(s3.HeadObjectInput) 57 | 58 | // Required parameters for HeadObject 59 | mandatory := map[string]string{ 60 | fields.Bucket: flags.Bucket, 61 | fields.Key: flags.Key, 62 | } 63 | 64 | // Optional parameters for HeadObject 65 | options := map[string]string{ 66 | fields.IfMatch: flags.IfMatch, 67 | fields.IfModifiedSince: flags.IfModifiedSince, 68 | fields.IfNoneMatch: flags.IfNoneMatch, 69 | fields.IfUnmodifiedSince: flags.IfUnmodifiedSince, 70 | fields.Range: flags.Range, 71 | fields.PartNumber: flags.PartNumber, 72 | } 73 | 74 | // Validate User Inputs and Retrieve Region 75 | // Check through user inputs for validation 76 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 77 | return 78 | } 79 | 80 | // Setting client to do the call 81 | var client s3iface.S3API 82 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 83 | return 84 | } 85 | 86 | // Wait until object condition 87 | if err = owb(client)(input); err != nil { 88 | cosContext.UI.Failed(err.Error()) 89 | return cli.NewExitError(err, 255) 90 | } 91 | 92 | return 93 | 94 | } 95 | -------------------------------------------------------------------------------- /di/providers/mocks/Uploader.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.0.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import context "context" 6 | import mock "github.com/stretchr/testify/mock" 7 | import s3manager "github.com/IBM/ibm-cos-sdk-go/service/s3/s3manager" 8 | 9 | // Uploader is an autogenerated mock type for the Uploader type 10 | type Uploader struct { 11 | mock.Mock 12 | } 13 | 14 | // Upload provides a mock function with given fields: input, options 15 | func (_m *Uploader) Upload(input *s3manager.UploadInput, options ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error) { 16 | _va := make([]interface{}, len(options)) 17 | for _i := range options { 18 | _va[_i] = options[_i] 19 | } 20 | var _ca []interface{} 21 | _ca = append(_ca, input) 22 | _ca = append(_ca, _va...) 23 | ret := _m.Called(_ca...) 24 | 25 | var r0 *s3manager.UploadOutput 26 | if rf, ok := ret.Get(0).(func(*s3manager.UploadInput, ...func(*s3manager.Uploader)) *s3manager.UploadOutput); ok { 27 | r0 = rf(input, options...) 28 | } else { 29 | if ret.Get(0) != nil { 30 | r0 = ret.Get(0).(*s3manager.UploadOutput) 31 | } 32 | } 33 | 34 | var r1 error 35 | if rf, ok := ret.Get(1).(func(*s3manager.UploadInput, ...func(*s3manager.Uploader)) error); ok { 36 | r1 = rf(input, options...) 37 | } else { 38 | r1 = ret.Error(1) 39 | } 40 | 41 | return r0, r1 42 | } 43 | 44 | // UploadWithContext provides a mock function with given fields: ctx, input, opts 45 | func (_m *Uploader) UploadWithContext(ctx context.Context, input *s3manager.UploadInput, opts ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error) { 46 | _va := make([]interface{}, len(opts)) 47 | for _i := range opts { 48 | _va[_i] = opts[_i] 49 | } 50 | var _ca []interface{} 51 | _ca = append(_ca, ctx, input) 52 | _ca = append(_ca, _va...) 53 | ret := _m.Called(_ca...) 54 | 55 | var r0 *s3manager.UploadOutput 56 | if rf, ok := ret.Get(0).(func(context.Context, *s3manager.UploadInput, ...func(*s3manager.Uploader)) *s3manager.UploadOutput); ok { 57 | r0 = rf(ctx, input, opts...) 58 | } else { 59 | if ret.Get(0) != nil { 60 | r0 = ret.Get(0).(*s3manager.UploadOutput) 61 | } 62 | } 63 | 64 | var r1 error 65 | if rf, ok := ret.Get(1).(func(context.Context, *s3manager.UploadInput, ...func(*s3manager.Uploader)) error); ok { 66 | r1 = rf(ctx, input, opts...) 67 | } else { 68 | r1 = ret.Error(1) 69 | } 70 | 71 | return r0, r1 72 | } 73 | 74 | // UploadWithIterator provides a mock function with given fields: ctx, iter, opts 75 | func (_m *Uploader) UploadWithIterator(ctx context.Context, iter s3manager.BatchUploadIterator, opts ...func(*s3manager.Uploader)) error { 76 | _va := make([]interface{}, len(opts)) 77 | for _i := range opts { 78 | _va[_i] = opts[_i] 79 | } 80 | var _ca []interface{} 81 | _ca = append(_ca, ctx, iter) 82 | _ca = append(_ca, _va...) 83 | ret := _m.Called(_ca...) 84 | 85 | var r0 error 86 | if rf, ok := ret.Get(0).(func(context.Context, s3manager.BatchUploadIterator, ...func(*s3manager.Uploader)) error); ok { 87 | r0 = rf(ctx, iter, opts...) 88 | } else { 89 | r0 = ret.Error(0) 90 | } 91 | 92 | return r0 93 | } 94 | -------------------------------------------------------------------------------- /di/providers/mocks/Downloader.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.0.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import context "context" 6 | import io "io" 7 | import mock "github.com/stretchr/testify/mock" 8 | import s3 "github.com/IBM/ibm-cos-sdk-go/service/s3" 9 | import s3manager "github.com/IBM/ibm-cos-sdk-go/service/s3/s3manager" 10 | 11 | // Downloader is an autogenerated mock type for the Downloader type 12 | type Downloader struct { 13 | mock.Mock 14 | } 15 | 16 | // Download provides a mock function with given fields: w, input, options 17 | func (_m *Downloader) Download(w io.WriterAt, input *s3.GetObjectInput, options ...func(*s3manager.Downloader)) (int64, error) { 18 | _va := make([]interface{}, len(options)) 19 | for _i := range options { 20 | _va[_i] = options[_i] 21 | } 22 | var _ca []interface{} 23 | _ca = append(_ca, w, input) 24 | _ca = append(_ca, _va...) 25 | ret := _m.Called(_ca...) 26 | 27 | var r0 int64 28 | if rf, ok := ret.Get(0).(func(io.WriterAt, *s3.GetObjectInput, ...func(*s3manager.Downloader)) int64); ok { 29 | r0 = rf(w, input, options...) 30 | } else { 31 | r0 = ret.Get(0).(int64) 32 | } 33 | 34 | var r1 error 35 | if rf, ok := ret.Get(1).(func(io.WriterAt, *s3.GetObjectInput, ...func(*s3manager.Downloader)) error); ok { 36 | r1 = rf(w, input, options...) 37 | } else { 38 | r1 = ret.Error(1) 39 | } 40 | 41 | return r0, r1 42 | } 43 | 44 | // DownloadWithContext provides a mock function with given fields: ctx, w, input, options 45 | func (_m *Downloader) DownloadWithContext(ctx context.Context, w io.WriterAt, input *s3.GetObjectInput, options ...func(*s3manager.Downloader)) (int64, error) { 46 | _va := make([]interface{}, len(options)) 47 | for _i := range options { 48 | _va[_i] = options[_i] 49 | } 50 | var _ca []interface{} 51 | _ca = append(_ca, ctx, w, input) 52 | _ca = append(_ca, _va...) 53 | ret := _m.Called(_ca...) 54 | 55 | var r0 int64 56 | if rf, ok := ret.Get(0).(func(context.Context, io.WriterAt, *s3.GetObjectInput, ...func(*s3manager.Downloader)) int64); ok { 57 | r0 = rf(ctx, w, input, options...) 58 | } else { 59 | r0 = ret.Get(0).(int64) 60 | } 61 | 62 | var r1 error 63 | if rf, ok := ret.Get(1).(func(context.Context, io.WriterAt, *s3.GetObjectInput, ...func(*s3manager.Downloader)) error); ok { 64 | r1 = rf(ctx, w, input, options...) 65 | } else { 66 | r1 = ret.Error(1) 67 | } 68 | 69 | return r0, r1 70 | } 71 | 72 | // DownloadWithIterator provides a mock function with given fields: ctx, iter, opts 73 | func (_m *Downloader) DownloadWithIterator(ctx context.Context, iter s3manager.BatchDownloadIterator, opts ...func(*s3manager.Downloader)) error { 74 | _va := make([]interface{}, len(opts)) 75 | for _i := range opts { 76 | _va[_i] = opts[_i] 77 | } 78 | var _ca []interface{} 79 | _ca = append(_ca, ctx, iter) 80 | _ca = append(_ca, _va...) 81 | ret := _m.Called(_ca...) 82 | 83 | var r0 error 84 | if rf, ok := ret.Get(0).(func(context.Context, s3manager.BatchDownloadIterator, ...func(*s3manager.Downloader)) error); ok { 85 | r0 = rf(ctx, iter, opts...) 86 | } else { 87 | r0 = ret.Error(0) 88 | } 89 | 90 | return r0 91 | } 92 | -------------------------------------------------------------------------------- /functions/aspera_upload.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | 9 | sdk "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix" 10 | "github.com/IBM/ibm-cos-sdk-go/aws" 11 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3manager" 12 | "github.com/IBM/ibmcloud-cos-cli/aspera" 13 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 14 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 15 | "github.com/IBM/ibmcloud-cos-cli/errors" 16 | . "github.com/IBM/ibmcloud-cos-cli/i18n" 17 | "github.com/IBM/ibmcloud-cos-cli/render" 18 | "github.com/IBM/ibmcloud-cos-cli/utils" 19 | "github.com/urfave/cli" 20 | ) 21 | 22 | func AsperaUpload(c *cli.Context) (err error) { 23 | 24 | // check the number of arguments 25 | if c.NArg() > 1 { 26 | err = &errors.CommandError{ 27 | CLIContext: c, 28 | Cause: errors.InvalidNArg, 29 | } 30 | return 31 | } 32 | 33 | // Check required Environment Var 34 | APIKeyEnv := sdk.EnvAPIKey.Get() 35 | if APIKeyEnv == "" { 36 | err = fmt.Errorf(T("missing Environment Variable: %s"), "IBMCLOUD_API_KEY") 37 | return 38 | } 39 | 40 | // Load COS Context 41 | var cosContext *utils.CosContext 42 | if cosContext, err = GetCosContext(c); err != nil { 43 | return 44 | } 45 | 46 | // Download SDK if not present 47 | if err = DownloadSDK(cosContext); err != nil { 48 | return fmt.Errorf(T("unable to install Aspera SDK: %s"), err) 49 | } 50 | 51 | // Build UploadInput 52 | input := new(s3manager.UploadInput) 53 | 54 | // Required parameters for UploadInput 55 | mandatory := map[string]string{ 56 | fields.Bucket: flags.Bucket, 57 | fields.Key: flags.Key, 58 | } 59 | 60 | // 61 | // Optional parameters for UploadInput 62 | options := map[string]string{} 63 | 64 | // 65 | // Check through user inputs for validation 66 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 67 | return 68 | } 69 | 70 | srcPath := c.Args().First() 71 | 72 | // Check if source path actually exists 73 | if _, err = cosContext.GetFileInfo(srcPath); err != nil { 74 | err = fmt.Errorf("%s: %s", err, srcPath) 75 | return 76 | } 77 | 78 | asp, err := cosContext.GetAsperaTransfer(APIKeyEnv, c.String(flags.Region)) 79 | if err != nil { 80 | return 81 | } 82 | 83 | // Sync stop signal like CTRL+c 84 | ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill) 85 | defer stop() 86 | 87 | var bytes int64 88 | bytes, err = cosContext.GetTotalBytes(srcPath) 89 | transferInput := &aspera.COSInput{ 90 | Bucket: aws.StringValue(input.Bucket), 91 | Key: aws.StringValue(input.Key), 92 | Path: srcPath, 93 | Sub: aspera.NewProgressBarSubscriber(bytes, cosContext.UI.Writer()), 94 | } 95 | 96 | if err = asp.Upload(ctx, transferInput); err != nil { 97 | return 98 | } 99 | 100 | // displaying total uploaded bytes for json output 101 | output := &render.AsperaUploadOutput{TotalBytes: bytes} 102 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 103 | 104 | return 105 | } 106 | -------------------------------------------------------------------------------- /di/providers/mocks/FileOperations.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.9.4. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | fs "io/fs" 7 | 8 | utils "github.com/IBM/ibmcloud-cos-cli/utils" 9 | mock "github.com/stretchr/testify/mock" 10 | ) 11 | 12 | // FileOperations is an autogenerated mock type for the FileOperations type 13 | type FileOperations struct { 14 | mock.Mock 15 | } 16 | 17 | // GetFileInfo provides a mock function with given fields: location 18 | func (_m *FileOperations) GetFileInfo(location string) (fs.FileInfo, error) { 19 | ret := _m.Called(location) 20 | 21 | var r0 fs.FileInfo 22 | if rf, ok := ret.Get(0).(func(string) fs.FileInfo); ok { 23 | r0 = rf(location) 24 | } else { 25 | if ret.Get(0) != nil { 26 | r0 = ret.Get(0).(fs.FileInfo) 27 | } 28 | } 29 | 30 | var r1 error 31 | if rf, ok := ret.Get(1).(func(string) error); ok { 32 | r1 = rf(location) 33 | } else { 34 | r1 = ret.Error(1) 35 | } 36 | 37 | return r0, r1 38 | } 39 | 40 | // GetTotalBytes provides a mock function with given fields: location 41 | func (_m *FileOperations) GetTotalBytes(location string) (int64, error) { 42 | ret := _m.Called(location) 43 | 44 | var r0 int64 45 | if rf, ok := ret.Get(0).(func(string) int64); ok { 46 | r0 = rf(location) 47 | } else { 48 | r0 = ret.Get(0).(int64) 49 | } 50 | 51 | var r1 error 52 | if rf, ok := ret.Get(1).(func(string) error); ok { 53 | r1 = rf(location) 54 | } else { 55 | r1 = ret.Error(1) 56 | } 57 | 58 | return r0, r1 59 | } 60 | 61 | // ReadSeekerCloserOpen provides a mock function with given fields: location 62 | func (_m *FileOperations) ReadSeekerCloserOpen(location string) (utils.ReadSeekerCloser, error) { 63 | ret := _m.Called(location) 64 | 65 | var r0 utils.ReadSeekerCloser 66 | if rf, ok := ret.Get(0).(func(string) utils.ReadSeekerCloser); ok { 67 | r0 = rf(location) 68 | } else { 69 | if ret.Get(0) != nil { 70 | r0 = ret.Get(0).(utils.ReadSeekerCloser) 71 | } 72 | } 73 | 74 | var r1 error 75 | if rf, ok := ret.Get(1).(func(string) error); ok { 76 | r1 = rf(location) 77 | } else { 78 | r1 = ret.Error(1) 79 | } 80 | 81 | return r0, r1 82 | } 83 | 84 | // Remove provides a mock function with given fields: location 85 | func (_m *FileOperations) Remove(location string) error { 86 | ret := _m.Called(location) 87 | 88 | var r0 error 89 | if rf, ok := ret.Get(0).(func(string) error); ok { 90 | r0 = rf(location) 91 | } else { 92 | r0 = ret.Error(0) 93 | } 94 | 95 | return r0 96 | } 97 | 98 | // WriteCloserOpen provides a mock function with given fields: location 99 | func (_m *FileOperations) WriteCloserOpen(location string) (utils.WriteCloser, error) { 100 | ret := _m.Called(location) 101 | 102 | var r0 utils.WriteCloser 103 | if rf, ok := ret.Get(0).(func(string) utils.WriteCloser); ok { 104 | r0 = rf(location) 105 | } else { 106 | if ret.Get(0) != nil { 107 | r0 = ret.Get(0).(utils.WriteCloser) 108 | } 109 | } 110 | 111 | var r1 error 112 | if rf, ok := ret.Get(1).(func(string) error); ok { 113 | r1 = rf(location) 114 | } else { 115 | r1 = ret.Error(1) 116 | } 117 | 118 | return r0, r1 119 | } 120 | -------------------------------------------------------------------------------- /functions/object_copy.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/IBM/ibmcloud-cos-cli/errors" 7 | 8 | "github.com/IBM/ibm-cos-sdk-go/aws/awserr" 9 | 10 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 11 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 12 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 13 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 14 | "github.com/IBM/ibmcloud-cos-cli/utils" 15 | "github.com/urfave/cli" 16 | ) 17 | 18 | // ObjectCopy copies a file from one bucket to another. The source and destination 19 | // buckets must be in the same region. 20 | // Parameter: 21 | // CLI Context Application 22 | // Returns: 23 | // Error = zero or non-zero 24 | func ObjectCopy(c *cli.Context) (err error) { 25 | // check the number of arguments 26 | if c.NArg() > 0 { 27 | // Build Command Error struct 28 | err = &errors.CommandError{ 29 | CLIContext: c, 30 | Cause: errors.InvalidNArg, 31 | } 32 | // Return error 33 | return 34 | } 35 | 36 | // Load COS Context 37 | var cosContext *utils.CosContext 38 | if cosContext, err = GetCosContext(c); err != nil { 39 | return 40 | } 41 | 42 | // Initialize Copy Object Input 43 | input := new(s3.CopyObjectInput) 44 | 45 | // Required parameters for CopyObject 46 | mandatory := map[string]string{ 47 | fields.Bucket: flags.Bucket, 48 | fields.Key: flags.Key, 49 | fields.CopySource: flags.CopySource, 50 | } 51 | 52 | // 53 | // Optional parameters for CopyObject 54 | options := map[string]string{ 55 | fields.CacheControl: flags.CacheControl, 56 | fields.ContentDisposition: flags.ContentDisposition, 57 | fields.ContentEncoding: flags.ContentEncoding, 58 | fields.ContentLanguage: flags.ContentLanguage, 59 | fields.ContentType: flags.ContentType, 60 | fields.CopySourceIfMatch: flags.CopySourceIfMatch, 61 | fields.CopySourceIfModifiedSince: flags.CopySourceIfModifiedSince, 62 | fields.CopySourceIfNoneMatch: flags.CopySourceIfNoneMatch, 63 | fields.CopySourceIfUnmodifiedSince: flags.CopySourceIfUnmodifiedSince, 64 | fields.Metadata: flags.Metadata, 65 | fields.MetadataDirective: flags.MetadataDirective, 66 | fields.Tagging: flags.Tagging, 67 | fields.TaggingDirective: flags.TaggingDirective, 68 | fields.WebsiteRedirectLocation: flags.WebsiteRedirectLocation, 69 | } 70 | 71 | // Validate User Inputs 72 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 73 | return 74 | } 75 | 76 | // validate source 77 | if path := strings.Split(*input.CopySource, "/"); len(path[0]) == 0 { 78 | return awserr.New("copy.source.bucket.missing", "no source bucket", nil) 79 | } 80 | 81 | // Setting client to do the call 82 | var client s3iface.S3API 83 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 84 | return 85 | } 86 | 87 | // CopyObject Op 88 | var output *s3.CopyObjectOutput 89 | if output, err = client.CopyObject(input); err != nil { 90 | return 91 | } 92 | 93 | // Display either in JSON or text 94 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 95 | 96 | // Return 97 | return 98 | } 99 | -------------------------------------------------------------------------------- /functions/bucket_list_test.go: -------------------------------------------------------------------------------- 1 | //+build unit 2 | 3 | package functions_test 4 | 5 | import ( 6 | "errors" 7 | "os" 8 | "testing" 9 | 10 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 11 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 12 | "github.com/IBM/ibmcloud-cos-cli/config" 13 | "github.com/IBM/ibmcloud-cos-cli/config/commands" 14 | "github.com/IBM/ibmcloud-cos-cli/cos" 15 | "github.com/IBM/ibmcloud-cos-cli/di/providers" 16 | "github.com/stretchr/testify/assert" 17 | "github.com/stretchr/testify/mock" 18 | "github.com/urfave/cli" 19 | ) 20 | 21 | func TestBucketListSunnyPath(t *testing.T) { 22 | defer providers.MocksRESET() 23 | 24 | // --- Arrange --- 25 | // disable and capture OS EXIT 26 | var exitCode *int 27 | cli.OsExiter = func(ec int) { 28 | exitCode = &ec 29 | } 30 | 31 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 32 | 33 | providers.MockPluginConfig. 34 | On("GetStringWithDefault", "Default Region", mock.AnythingOfType("string")). 35 | Return("us", nil) 36 | 37 | providers.MockS3API. 38 | On("ListBuckets", mock.MatchedBy( 39 | func(input *s3.ListBucketsInput) bool { return true })). 40 | Return(new(s3.ListBucketsOutput), nil). 41 | Once() 42 | 43 | // --- Act ---- 44 | // set os args 45 | os.Args = []string{"-", commands.Buckets} 46 | //call plugin 47 | plugin.Start(new(cos.Plugin)) 48 | 49 | // --- Assert ---- 50 | // assert s3 api called once per region (since success is last) 51 | providers.MockS3API.AssertNumberOfCalls(t, "ListBuckets", 1) 52 | // assert exit code is zero 53 | assert.Equal(t, (*int)(nil), exitCode) // no exit trigger in the cli 54 | // capture all output // 55 | output := providers.FakeUI.Outputs() 56 | errors := providers.FakeUI.Errors() 57 | // assert OK 58 | assert.Contains(t, output, "OK") 59 | // assert Not Fail 60 | assert.NotContains(t, errors, "FAIL") 61 | 62 | } 63 | 64 | func TestBucketListRainyPath(t *testing.T) { 65 | defer providers.MocksRESET() 66 | 67 | // --- Arrange --- 68 | // disable and capture OS EXIT 69 | var exitCode *int 70 | cli.OsExiter = func(ec int) { 71 | exitCode = &ec 72 | } 73 | 74 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 75 | 76 | providers.MockPluginConfig. 77 | On("GetStringWithDefault", "Default Region", mock.AnythingOfType("string")). 78 | Return("us", nil) 79 | providers.MockS3API. 80 | On("ListBuckets", mock.MatchedBy( 81 | func(input *s3.ListBucketsInput) bool { return true })). 82 | Return(nil, errors.New("Internal Server Error")). 83 | Once() 84 | 85 | // --- Act ---- 86 | // set os args 87 | os.Args = []string{"-", commands.Buckets} 88 | //call plugin 89 | plugin.Start(new(cos.Plugin)) 90 | 91 | // --- Assert ---- 92 | // assert s3 api called once per region (since success is last) 93 | providers.MockS3API.AssertNumberOfCalls(t, "ListBuckets", 1) 94 | // assert exit code is zero 95 | assert.Equal(t, 1, *exitCode) // no exit trigger in the cli 96 | // capture all output // 97 | output := providers.FakeUI.Outputs() 98 | errors := providers.FakeUI.Errors() 99 | // assert Not OK 100 | assert.NotContains(t, output, "OK") 101 | // assert Fail 102 | assert.Contains(t, errors, "FAIL") 103 | 104 | } 105 | -------------------------------------------------------------------------------- /functions/wait_bucket_test.go: -------------------------------------------------------------------------------- 1 | //+build unit 2 | 3 | package functions_test 4 | 5 | import ( 6 | "os" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/mock" 11 | "github.com/urfave/cli" 12 | 13 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 14 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 15 | "github.com/IBM/ibmcloud-cos-cli/config" 16 | "github.com/IBM/ibmcloud-cos-cli/config/commands" 17 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 18 | "github.com/IBM/ibmcloud-cos-cli/cos" 19 | "github.com/IBM/ibmcloud-cos-cli/di/providers" 20 | ) 21 | 22 | func TestWaitBucketNotExistsHappy(t *testing.T) { 23 | 24 | defer providers.MocksRESET() 25 | 26 | // --- Arrange --- 27 | // disable and capture OS EXIT 28 | var exitCode *int 29 | cli.OsExiter = func(ec int) { 30 | exitCode = &ec 31 | } 32 | 33 | targetBucket := "TargetWaitBucket" 34 | 35 | var captureInput *s3.HeadBucketInput 36 | 37 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 38 | 39 | providers.MockS3API. 40 | On( 41 | "WaitUntilBucketNotExists", 42 | mock.MatchedBy(func(input *s3.HeadBucketInput) bool { captureInput = input; return true })). 43 | Return(nil) 44 | 45 | // --- Act ---- 46 | // set os args 47 | os.Args = []string{"-", 48 | commands.Wait, 49 | commands.BucketNotExists, 50 | "--" + flags.Bucket, targetBucket, 51 | "--region", "REG"} 52 | //call plugin 53 | plugin.Start(new(cos.Plugin)) 54 | 55 | // --- Assert ---- 56 | // assert s3 api called once per region (since success is last) 57 | providers.MockS3API.AssertNumberOfCalls(t, "WaitUntilBucketNotExists", 1) 58 | // assert exit code is zero 59 | assert.Equal(t, (*int)(nil), exitCode) // no exit trigger in the cli 60 | 61 | assert.Equal(t, targetBucket, *captureInput.Bucket) 62 | 63 | // capture all output // 64 | errors := providers.FakeUI.Errors() 65 | // assert Not Fail 66 | assert.NotContains(t, errors, "FAIL") 67 | } 68 | 69 | func TestWaitBucketExistsHappy(t *testing.T) { 70 | 71 | defer providers.MocksRESET() 72 | 73 | // --- Arrange --- 74 | // disable and capture OS EXIT 75 | var exitCode *int 76 | cli.OsExiter = func(ec int) { 77 | exitCode = &ec 78 | } 79 | 80 | targetBucket := "TargetWaitBucket" 81 | 82 | var captureInput *s3.HeadBucketInput 83 | 84 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 85 | 86 | providers.MockS3API. 87 | On( 88 | "WaitUntilBucketExists", 89 | mock.MatchedBy(func(input *s3.HeadBucketInput) bool { captureInput = input; return true })). 90 | Return(nil) 91 | 92 | // --- Act ---- 93 | // set os args 94 | os.Args = []string{"-", 95 | commands.Wait, 96 | commands.BucketExists, 97 | "--" + flags.Bucket, targetBucket, 98 | "--region", "REG"} 99 | //call plugin 100 | plugin.Start(new(cos.Plugin)) 101 | 102 | // --- Assert ---- 103 | // assert s3 api called once per region (since success is last) 104 | providers.MockS3API.AssertNumberOfCalls(t, "WaitUntilBucketExists", 1) 105 | // assert exit code is zero 106 | assert.Equal(t, (*int)(nil), exitCode) // no exit trigger in the cli 107 | 108 | assert.Equal(t, targetBucket, *captureInput.Bucket) 109 | 110 | // capture all output // 111 | errors := providers.FakeUI.Errors() 112 | // assert Not Fail 113 | assert.NotContains(t, errors, "FAIL") 114 | } 115 | -------------------------------------------------------------------------------- /functions/upload.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/IBM/ibmcloud-cos-cli/errors" 7 | 8 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3manager" 9 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 10 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 11 | "github.com/IBM/ibmcloud-cos-cli/utils" 12 | "github.com/urfave/cli" 13 | ) 14 | 15 | // 16 | // Upload - utilizes S3 Manager Uploader API to upload objects from S3 concurrently. 17 | // Parameter: 18 | // CLI Context Application 19 | // Returns: 20 | // Error = zero or non-zero 21 | func Upload(c *cli.Context) (err error) { 22 | 23 | // Check the number of arguments 24 | // and error out if so 25 | if c.NArg() > 0 { 26 | err = &errors.CommandError{ 27 | CLIContext: c, 28 | Cause: errors.InvalidNArg, 29 | } 30 | return 31 | } 32 | 33 | // Build S3 Manager UploadInput 34 | input := new(s3manager.UploadInput) 35 | 36 | // Required parameters for S3 Manager UploadInput 37 | mandatory := map[string]string{ 38 | fields.Bucket: flags.Bucket, 39 | fields.Key: flags.Key, 40 | fields.Body: flags.File, 41 | } 42 | 43 | // 44 | // Optional parameters for S3 Manager UploadInput 45 | options := map[string]string{ 46 | fields.CacheControl: flags.CacheControl, 47 | fields.ContentDisposition: flags.ContentDisposition, 48 | fields.ContentEncoding: flags.ContentEncoding, 49 | fields.ContentLanguage: flags.ContentLanguage, 50 | fields.ContentMD5: flags.ContentMD5, 51 | fields.ContentType: flags.ContentType, 52 | fields.Metadata: flags.Metadata, 53 | } 54 | 55 | // Check through user inputs for validation 56 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 57 | return 58 | } 59 | 60 | // Defer closing on body of the file to be uploaded 61 | if closeAble, ok := input.Body.(io.Closer); ok { 62 | defer closeAble.Close() 63 | } 64 | 65 | // 66 | // S3 Manager Uploader Options 67 | uploadOptions := map[string]string{ 68 | fields.PartSize: flags.PartSize, 69 | fields.Concurrency: flags.Concurrency, 70 | fields.LeavePartsOnErrors: flags.LeavePartsOnErrors, 71 | fields.MaxUploadParts: flags.MaxUploadParts, 72 | } 73 | 74 | // Load COS Context 75 | var cosContext *utils.CosContext 76 | if cosContext, err = GetCosContext(c); err != nil { 77 | return 78 | } 79 | 80 | // Error holding 81 | errorHolder := new(error) 82 | 83 | // Build a s3manager upload 84 | var uploader utils.Uploader 85 | if uploader, err = cosContext.GetUploader(c.String(flags.Region), 86 | applyConfigUploader(c, uploadOptions, errorHolder)); err != nil { 87 | return 88 | } 89 | 90 | // Uploader Error Checking 91 | if *errorHolder != nil { 92 | return *errorHolder 93 | } 94 | 95 | // Upload Op 96 | var output *s3manager.UploadOutput 97 | if output, err = uploader.Upload(input); err != nil { 98 | return 99 | } 100 | // Success 101 | // Output the successful message 102 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 103 | 104 | // Return 105 | return 106 | } 107 | 108 | // Build Uploader from the Config 109 | func applyConfigUploader(c *cli.Context, options map[string]string, err *error) func(u *s3manager.Uploader) { 110 | return func(u *s3manager.Uploader) { 111 | mandatory := map[string]string{} 112 | *err = MapToSDKInput(c, u, mandatory, options) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /aspera/mocks/TransferService_MonitorTransfersClient.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.9.4. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | context "context" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | metadata "google.golang.org/grpc/metadata" 10 | 11 | transfersdk "github.com/IBM/ibmcloud-cos-cli/aspera/transfersdk" 12 | ) 13 | 14 | // TransferService_MonitorTransfersClient is an autogenerated mock type for the TransferService_MonitorTransfersClient type 15 | type TransferService_MonitorTransfersClient struct { 16 | mock.Mock 17 | } 18 | 19 | // CloseSend provides a mock function with given fields: 20 | func (_m *TransferService_MonitorTransfersClient) CloseSend() error { 21 | ret := _m.Called() 22 | 23 | var r0 error 24 | if rf, ok := ret.Get(0).(func() error); ok { 25 | r0 = rf() 26 | } else { 27 | r0 = ret.Error(0) 28 | } 29 | 30 | return r0 31 | } 32 | 33 | // Context provides a mock function with given fields: 34 | func (_m *TransferService_MonitorTransfersClient) Context() context.Context { 35 | ret := _m.Called() 36 | 37 | var r0 context.Context 38 | if rf, ok := ret.Get(0).(func() context.Context); ok { 39 | r0 = rf() 40 | } else { 41 | if ret.Get(0) != nil { 42 | r0 = ret.Get(0).(context.Context) 43 | } 44 | } 45 | 46 | return r0 47 | } 48 | 49 | // Header provides a mock function with given fields: 50 | func (_m *TransferService_MonitorTransfersClient) Header() (metadata.MD, error) { 51 | ret := _m.Called() 52 | 53 | var r0 metadata.MD 54 | if rf, ok := ret.Get(0).(func() metadata.MD); ok { 55 | r0 = rf() 56 | } else { 57 | if ret.Get(0) != nil { 58 | r0 = ret.Get(0).(metadata.MD) 59 | } 60 | } 61 | 62 | var r1 error 63 | if rf, ok := ret.Get(1).(func() error); ok { 64 | r1 = rf() 65 | } else { 66 | r1 = ret.Error(1) 67 | } 68 | 69 | return r0, r1 70 | } 71 | 72 | // Recv provides a mock function with given fields: 73 | func (_m *TransferService_MonitorTransfersClient) Recv() (*transfersdk.TransferResponse, error) { 74 | ret := _m.Called() 75 | 76 | var r0 *transfersdk.TransferResponse 77 | if rf, ok := ret.Get(0).(func() *transfersdk.TransferResponse); ok { 78 | r0 = rf() 79 | } else { 80 | if ret.Get(0) != nil { 81 | r0 = ret.Get(0).(*transfersdk.TransferResponse) 82 | } 83 | } 84 | 85 | var r1 error 86 | if rf, ok := ret.Get(1).(func() error); ok { 87 | r1 = rf() 88 | } else { 89 | r1 = ret.Error(1) 90 | } 91 | 92 | return r0, r1 93 | } 94 | 95 | // RecvMsg provides a mock function with given fields: m 96 | func (_m *TransferService_MonitorTransfersClient) RecvMsg(m interface{}) error { 97 | ret := _m.Called(m) 98 | 99 | var r0 error 100 | if rf, ok := ret.Get(0).(func(interface{}) error); ok { 101 | r0 = rf(m) 102 | } else { 103 | r0 = ret.Error(0) 104 | } 105 | 106 | return r0 107 | } 108 | 109 | // SendMsg provides a mock function with given fields: m 110 | func (_m *TransferService_MonitorTransfersClient) SendMsg(m interface{}) error { 111 | ret := _m.Called(m) 112 | 113 | var r0 error 114 | if rf, ok := ret.Get(0).(func(interface{}) error); ok { 115 | r0 = rf(m) 116 | } else { 117 | r0 = ret.Error(0) 118 | } 119 | 120 | return r0 121 | } 122 | 123 | // Trailer provides a mock function with given fields: 124 | func (_m *TransferService_MonitorTransfersClient) Trailer() metadata.MD { 125 | ret := _m.Called() 126 | 127 | var r0 metadata.MD 128 | if rf, ok := ret.Get(0).(func() metadata.MD); ok { 129 | r0 = rf() 130 | } else { 131 | if ret.Get(0) != nil { 132 | r0 = ret.Get(0).(metadata.MD) 133 | } 134 | } 135 | 136 | return r0 137 | } 138 | -------------------------------------------------------------------------------- /bin/download-aspera-mod: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ROOT_DIR=$(cd $(dirname $(dirname $0)) && pwd) 6 | ASPERA_VERSION_FILE=${ROOT_DIR}/aspera/version.go 7 | SDK_PATH="${ROOT_DIR}/aspera/transfersdk" 8 | PROTOC_VERSION="28.1" 9 | GOPATH="$(go env GOPATH)" 10 | export PATH=$PATH:${GOPATH}/bin 11 | 12 | # use [[:space:]] here instead of \s for compatibility with BSD sed on macOS 13 | version=$(sed -En 's/.*version[[:space:]]+=[[:space:]]+"([^"]+)"/\1/p' "${ASPERA_VERSION_FILE}") 14 | commit=$(sed -En 's/.*commit[[:space:]]+=[[:space:]]+"([^"]+)"/\1/p' "${ASPERA_VERSION_FILE}") 15 | prefix=$(sed -En 's/.*prefix[[:space:]]+=[[:space:]]+"([^"]+)"/\1/p' "${ASPERA_VERSION_FILE}") 16 | 17 | trap 'rm -rf "$tmpdir"' EXIT 18 | tmpdir=$(mktemp -d)|| exit 1 19 | 20 | fix_bug(){ 21 | local proto_file=$1 22 | # repeated Path is supposed to be an array 23 | sed -E -i.bak 's/repeated Path path /repeated Path paths /; s/ibm.com\/(aspera\/transfersdk)/\1/;' "${proto_file}" 24 | } 25 | 26 | sdk_exists() { 27 | [ -f "${SDK_PATH}/transfer.pb.go" ] && [ -f "${SDK_PATH}/.version" ] \ 28 | && [ "$(cat ${SDK_PATH}/.version)" == "${version}" ] 29 | } 30 | 31 | command_exists() { 32 | command -v "$1" >/dev/null 2>&1 33 | } 34 | 35 | install_protoc() { 36 | local version=$1 37 | 38 | case $(uname -s) in 39 | Linux) 40 | os="linux" 41 | ;; 42 | Darwin) 43 | os="osx" 44 | ;; 45 | *) 46 | echo "unsupported os" 47 | exit 1 48 | esac 49 | 50 | case $(uname -m) in 51 | arm64|aarch64) 52 | arch="aarch_64" 53 | ;; 54 | x86_64|amd64) 55 | arch="x86_64" 56 | ;; 57 | i386|i686) 58 | arch="x86_32" 59 | ;; 60 | ppc64le) 61 | arch="ppcle_64" 62 | ;; 63 | s390x) 64 | arch="s390_64" 65 | ;; 66 | *) 67 | echo "unsupported CPU arch" 68 | exit 1 69 | ;; 70 | esac 71 | 72 | package="protoc-${version}-${os}-${arch}.zip" 73 | download_url="https://github.com/protocolbuffers/protobuf/releases/download/v${version}/${package}" 74 | echo "downloading protoc..." 75 | 76 | curl -L --fail "${download_url}" -o "${tmpdir}/${package}" 77 | # copy the protoc binary to $GOPATH/bin 78 | unzip -o "${tmpdir}/${package}" -d "${GOPATH}" bin/protoc 79 | } 80 | 81 | sdk_exists && echo "TransferSDK exists and is up-to-date" && exit 0 82 | 83 | if ! (command_exists protoc);then 84 | install_protoc "${PROTOC_VERSION}" 85 | fi 86 | 87 | if ! (command_exists protoc-gen-go && command_exists protoc-gen-go-grpc);then 88 | # go get does not install binary since 1.16 89 | go get google.golang.org/protobuf/cmd/protoc-gen-go 90 | go install google.golang.org/protobuf/cmd/protoc-gen-go 91 | 92 | go get google.golang.org/grpc/cmd/protoc-gen-go-grpc 93 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc 94 | fi 95 | 96 | # .proto file is platform independent, 97 | # so it's ok to use the osx version for any platform. 98 | 99 | if [ "$(uname)" == "Linux" ];then 100 | sdk_name="linux-amd64" 101 | sdk_pkg="${sdk_name}-${version}-${commit}.tar.gz" 102 | else 103 | sdk_name="macos-arm64" 104 | sdk_pkg="${sdk_name}-${version}-${commit}.zip" 105 | fi 106 | 107 | 108 | echo "downloading SDK..." 109 | curl --fail "${prefix}/${version}/${sdk_pkg}" -o "${tmpdir}/${sdk_pkg}" 110 | tar -xf "${tmpdir}/${sdk_pkg}" -C "${tmpdir}" 111 | 112 | fix_bug "${tmpdir}/${sdk_name}/connectors/transfer.proto" 113 | protoc --go_out="${ROOT_DIR}" --go-grpc_out="${ROOT_DIR}" \ 114 | --proto_path="${tmpdir}/${sdk_name}/connectors" \ 115 | "${tmpdir}/${sdk_name}/connectors/transfer.proto" 116 | echo "$version" > "${SDK_PATH}/.version" 117 | -------------------------------------------------------------------------------- /functions/bucket_create.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/IBM/ibmcloud-cos-cli/errors" 7 | 8 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 9 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 10 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 11 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 12 | "github.com/IBM/ibmcloud-cos-cli/utils" 13 | "github.com/urfave/cli" 14 | ) 15 | 16 | // BucketCreate creates a new bucket, allowing the user to set the name, the class, and the region. 17 | // Parameter: 18 | // CLI Context Application 19 | // Returns: 20 | // Error = zero or non-zero 21 | func BucketCreate(c *cli.Context) (err error) { 22 | // check the number of arguments 23 | if c.NArg() > 0 { 24 | err = &errors.CommandError{ 25 | CLIContext: c, 26 | Cause: errors.InvalidNArg, 27 | } 28 | return 29 | } 30 | 31 | // Load COS Context 32 | var cosContext *utils.CosContext 33 | if cosContext, err = GetCosContext(c); err != nil { 34 | return 35 | } 36 | // Set CreateBucketInput 37 | input := new(s3.CreateBucketInput) 38 | 39 | // Required parameter for CreateBucket 40 | mandatory := map[string]string{ 41 | fields.Bucket: flags.Bucket, 42 | } 43 | 44 | // Optional parameters for CreateBucket 45 | options := map[string]string{ 46 | fields.IBMServiceInstanceID: flags.IbmServiceInstanceID, 47 | fields.IBMSSEKPCustomerRootKeyCrn: flags.KmsRootKeyCrn, 48 | fields.IBMSSEKPEncryptionAlgorithm: flags.KmsEncryptionAlgorithm, 49 | } 50 | 51 | // Check through user inputs for validation 52 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 53 | return 54 | } 55 | 56 | var region string 57 | if region, err = cosContext.GetCurrentRegion(c.String(flags.Region)); err != nil { 58 | return 59 | } 60 | setLocationConstraint(input, region, c.String(flags.Class)) 61 | 62 | // Setting client to do the call 63 | var client s3iface.S3API 64 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 65 | return 66 | } 67 | 68 | // Put the bucket CORS by calling PutBucketCors 69 | var output *s3.CreateBucketOutput 70 | if output, err = client.CreateBucket(input); err != nil { 71 | return 72 | } 73 | 74 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 75 | 76 | return 77 | } 78 | 79 | // To create a bucket, AWS accepts a LocationConstraint, which holds the region and the bucket class within a 80 | // single string, such as "us-south-flex" for a Flex bucket in region us-south. We cannot pass, for example, 81 | // "us-south" into the Region, because then AWS will not know what bucket type to be created. So, we need to 82 | // generate a LocationConstraint on the fly, which is generally just the location, a hyphen, and the class. 83 | // However, if the user creates a us-geo (or eu-geo/ap-geo) bucket, we have to remove "geo" from the string 84 | // because of AWS conventions. For example, AWS doesn't have "us-geo" region - it's just "us" for a cross 85 | // region United States bucket. So instead of a "us-geo-standard" LocationConstraint, we would have 86 | // "us-standard". This code takes care of these issues. 87 | // BuildLocationConstraint builds location constraint based on region and class 88 | // More details: 89 | // https://console.bluemix.net/docs/services/cloud-object-storage/basics/classes.html#use-storage-classes 90 | func setLocationConstraint(input *s3.CreateBucketInput, region, class string) { 91 | var locationConstraint string 92 | if strings.HasSuffix(strings.ToLower(region), "-geo") { 93 | locationConstraint = region[:len(region)-3] 94 | } else { 95 | locationConstraint = region + "-" 96 | } 97 | if "" == class { 98 | class = "standard" 99 | } 100 | locationConstraint += class 101 | config := new(s3.CreateBucketConfiguration).SetLocationConstraint(strings.ToLower(locationConstraint)) 102 | input.CreateBucketConfiguration = config 103 | } 104 | -------------------------------------------------------------------------------- /config/fields/fields_names.go: -------------------------------------------------------------------------------- 1 | package fields 2 | 3 | // Definition of constants for fields the COS CLI uses to match 4 | // fields with the flags the end users specify for the commands 5 | const ( 6 | // Those are the fields we pass to the S3 Client of the IBM COS SDK 7 | // for Go. For more information on each field, check the following 8 | // library, github.com/IBM/ibm-cos-sdk-go, in the s3 service folder 9 | // where its api.go details the APIs, their structs and parameters 10 | // they use prior to making requests to the server side. 11 | Body = "Body" 12 | Bucket = "Bucket" 13 | CacheControl = "CacheControl" 14 | Concurrency = "Concurrency" 15 | ContentDisposition = "ContentDisposition" 16 | ContentEncoding = "ContentEncoding" 17 | ContentLanguage = "ContentLanguage" 18 | ContentLength = "ContentLength" 19 | ContentMD5 = "ContentMD5" 20 | ContentType = "ContentType" 21 | CopySource = "CopySource" 22 | CopySourceIfMatch = "CopySourceIfMatch" 23 | CopySourceIfModifiedSince = "CopySourceIfModifiedSince" 24 | CopySourceIfNoneMatch = "CopySourceIfNoneMatch" 25 | CopySourceIfUnmodifiedSince = "CopySourceIfUnmodifiedSince" 26 | CopySourceRange = "CopySourceRange" 27 | CORSConfiguration = "CORSConfiguration" 28 | Delete = "Delete" 29 | Delimiter = "Delimiter" 30 | EncodingType = "EncodingType" 31 | IBMServiceInstanceID = "IBMServiceInstanceId" 32 | IfMatch = "IfMatch" 33 | IfModifiedSince = "IfModifiedSince" 34 | IfNoneMatch = "IfNoneMatch" 35 | IfUnmodifiedSince = "IfUnmodifiedSince" 36 | Key = "Key" 37 | KeyMarker = "KeyMarker" 38 | IBMSSEKPCustomerRootKeyCrn = "IBMSSEKPCustomerRootKeyCrn" 39 | IBMSSEKPEncryptionAlgorithm = "IBMSSEKPEncryptionAlgorithm" 40 | LeavePartsOnErrors = "LeavePartsOnError" 41 | Marker = "Marker" 42 | MaxKeys = "MaxKeys" 43 | MaxUploadParts = "MaxUploadParts" 44 | Metadata = "Metadata" 45 | MetadataDirective = "MetadataDirective" 46 | MultipartUpload = "MultipartUpload" 47 | Output = "Output" 48 | PartNumber = "PartNumber" 49 | PartNumberMarker = "PartNumberMarker" 50 | PartSize = "PartSize" 51 | Prefix = "Prefix" 52 | PublicAccessBlockConfiguration = "PublicAccessBlockConfiguration" 53 | Range = "Range" 54 | ReplicationConfiguration = "ReplicationConfiguration" 55 | ObjectLockConfiguration = "ObjectLockConfiguration" 56 | LegalHold = "LegalHold" 57 | Retention = "Retention" 58 | ResponseCacheControl = "ResponseCacheControl" 59 | ResponseContentDisposition = "ResponseContentDisposition" 60 | ResponseContentEncoding = "ResponseContentEncoding" 61 | ResponseContentLanguage = "ResponseContentLanguage" 62 | ResponseContentType = "ResponseContentType" 63 | ResponseExpires = "ResponseExpires" 64 | Tagging = "Tagging" 65 | TaggingDirective = "TaggingDirective" 66 | VersionId = "VersionId" 67 | VersionIdMarker = "VersionIdMarker" 68 | UploadID = "UploadId" 69 | UploadIDMarker = "UploadIdMarker" 70 | VersioningConfiguration = "VersioningConfiguration" 71 | WebsiteConfiguration = "WebsiteConfiguration" 72 | WebsiteRedirectLocation = "WebsiteRedirectLocation" 73 | FetchOwner = "FetchOwner" 74 | StartAfter = "StartAfter" 75 | ContinuationToken = "ContinuationToken" 76 | LifecycleConfiguration = "LifecycleConfiguration" 77 | ) 78 | -------------------------------------------------------------------------------- /di/providers/app_context_unit.go: -------------------------------------------------------------------------------- 1 | //+build unit 2 | 3 | package providers 4 | 5 | import ( 6 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/terminal" 7 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 8 | terminalHelpers "github.com/IBM-Cloud/ibm-cloud-cli-sdk/testhelpers/terminal" 9 | "github.com/IBM/ibm-cos-sdk-go/aws" 10 | "github.com/IBM/ibm-cos-sdk-go/aws/endpoints" 11 | "github.com/IBM/ibm-cos-sdk-go/aws/session" 12 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 13 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3manager" 14 | "github.com/IBM/ibmcloud-cos-cli/di/providers/mocks" 15 | "github.com/IBM/ibmcloud-cos-cli/utils" 16 | ) 17 | 18 | // Test Environment Shared Mocks 19 | var ( 20 | FakeUI = new(terminalHelpers.FakeUI) 21 | MockS3API = new(mocks.S3API) 22 | MockUploaderAPI = new(mocks.Uploader) 23 | MockDownloaderAPI = new(mocks.Downloader) 24 | MockAsperaTransfer = new(mocks.AsperaTransfer) 25 | MockPluginConfig = new(mocks.PluginConfig) 26 | 27 | MockRegionResolver = &RegionResolverMock{ 28 | ListKnownRegions: mocks.ListKnownRegions{}, 29 | Resolver: mocks.Resolver{}, 30 | } 31 | 32 | MockFileOperations = new(mocks.FileOperations) 33 | 34 | ReferenceUploader = new(s3manager.Uploader) 35 | ReferenceDownloader = new(s3manager.Downloader) 36 | ) 37 | 38 | func MocksRESET() { 39 | FakeUI = new(terminalHelpers.FakeUI) 40 | MockS3API = new(mocks.S3API) 41 | MockUploaderAPI = new(mocks.Uploader) 42 | MockDownloaderAPI = new(mocks.Downloader) 43 | MockAsperaTransfer = new(mocks.AsperaTransfer) 44 | MockPluginConfig = new(mocks.PluginConfig) 45 | 46 | MockRegionResolver = &RegionResolverMock{ 47 | ListKnownRegions: mocks.ListKnownRegions{}, 48 | Resolver: mocks.Resolver{}, 49 | } 50 | 51 | MockFileOperations = new(mocks.FileOperations) 52 | 53 | ReferenceUploader = new(s3manager.Uploader) 54 | ReferenceDownloader = new(s3manager.Downloader) 55 | } 56 | 57 | type RegionResolverMock struct { 58 | mocks.ListKnownRegions 59 | mocks.Resolver 60 | } 61 | 62 | func NewUI() terminal.UI { 63 | return FakeUI 64 | } 65 | 66 | func GetS3APIFn() func(*session.Session) s3iface.S3API { 67 | return func(*session.Session) s3iface.S3API { 68 | return MockS3API 69 | } 70 | } 71 | 72 | func GetAsperaTransferFn() func(sess *session.Session, apikey string) (utils.AsperaTransfer, error) { 73 | return func(sess *session.Session, apikey string) (utils.AsperaTransfer, error) { 74 | return MockAsperaTransfer, nil 75 | } 76 | } 77 | 78 | func GetDownloaderAPIFn() func(svc s3iface.S3API, options ...func(*s3manager.Downloader)) utils.Downloader { 79 | return func(svc s3iface.S3API, options ...func(*s3manager.Downloader)) utils.Downloader { 80 | for _, fun := range options { 81 | fun(ReferenceDownloader) 82 | } 83 | return MockDownloaderAPI 84 | } 85 | } 86 | 87 | func GetUploaderAPIFn() func(svc s3iface.S3API, options ...func(output *s3manager.Uploader)) utils.Uploader { 88 | return func(svc s3iface.S3API, options ...func(output *s3manager.Uploader)) utils.Uploader { 89 | for _, fun := range options { 90 | fun(ReferenceUploader) 91 | } 92 | return MockUploaderAPI 93 | } 94 | } 95 | 96 | // maybe mock the provider to assert calling parameters 97 | func GetPluginConfig(_ plugin.PluginContext) plugin.PluginConfig { 98 | return MockPluginConfig 99 | } 100 | 101 | func NewSession(_ *aws.Config) (*session.Session, error) { 102 | return new(session.Session), nil 103 | } 104 | 105 | // mocks should intersect before it is effective needed 106 | func NewConfig(_ plugin.PluginContext, _ endpoints.Resolver, _ *BaseConfig) (*aws.Config, error) { 107 | return nil, nil 108 | } 109 | 110 | func NewCOSEndPointsWSClient(_ plugin.PluginContext, _ *BaseConfig) (*RegionResolverMock, error) { 111 | return MockRegionResolver, nil 112 | } 113 | 114 | func GetFileOperations() utils.FileOperations { 115 | return MockFileOperations 116 | } 117 | 118 | type BaseConfig aws.Config 119 | 120 | // mocks should intersect before it is effective needed 121 | func GetBaseConfig(_ plugin.PluginContext) *BaseConfig { 122 | return nil 123 | } 124 | -------------------------------------------------------------------------------- /config/flags/flags_names.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | const ( 4 | Body = "body" 5 | Bucket = "bucket" 6 | CacheControl = "cache-control" 7 | Class = "class" 8 | Concurrency = "concurrency" 9 | ContentDisposition = "content-disposition" 10 | ContentEncoding = "content-encoding" 11 | ContentLanguage = "content-language" 12 | ContentLength = "content-length" 13 | ContentMD5 = "content-md5" 14 | ContentType = "content-type" 15 | CopySource = "copy-source" 16 | CopySourceIfMatch = "copy-source-if-match" 17 | CopySourceIfModifiedSince = "copy-source-if-modified-since" 18 | CopySourceIfNoneMatch = "copy-source-if-none-match" 19 | CopySourceIfUnmodifiedSince = "copy-source-if-unmodified-since" 20 | CopySourceRange = "copy-source-range" 21 | CorsConfiguration = "cors-configuration" 22 | CRN = "crn" 23 | Delete = "delete" 24 | Delimiter = "delimiter" 25 | DDL = "ddl" 26 | EncodingType = "encoding-type" 27 | File = "file" 28 | Force = "force" 29 | IbmServiceInstanceID = "ibm-service-instance-id" 30 | IfMatch = "if-match" 31 | IfModifiedSince = "if-modified-since" 32 | IfNoneMatch = "if-none-match" 33 | IfUnmodifiedSince = "if-unmodified-since" 34 | Key = "key" 35 | KmsEncryptionAlgorithm = "kms-encryption-algorithm" 36 | KmsRootKeyCrn = "kms-root-key-crn" 37 | KeyMarker = "key-marker" 38 | LeavePartsOnErrors = "leave-parts-on-errors" 39 | ListRegions = "list-regions" 40 | Marker = "marker" 41 | MaxItems = "max-items" 42 | MaxUploadParts = "max-upload-parts" 43 | Metadata = "metadata" 44 | MetadataDirective = "metadata-directive" 45 | MultipartUpload = "multipart-upload" 46 | Output = "output" 47 | PageSize = "page-size" 48 | PartNumber = "part-number" 49 | PartNumberMarker = "part-number-marker" 50 | PartSize = "part-size" 51 | Prefix = "prefix" 52 | PublicAccessBlockConfiguration = "public-access-block-configuration" 53 | Range = "range" 54 | Region = "region" 55 | ReplicationConfiguration = "replication-configuration" 56 | ObjectLockConfiguration = "object-lock-configuration" 57 | LegalHold = "legal-hold" 58 | Retention = "retention" 59 | ResponseCacheControl = "response-cache-control" 60 | ResponseContentDisposition = "response-content-disposition" 61 | ResponseContentEncoding = "response-content-encoding" 62 | ResponseContentLanguage = "response-content-language" 63 | ResponseContentType = "response-content-type" 64 | ResponseExpires = "response-expires" 65 | Tagging = "tagging" 66 | TaggingDirective = "tagging-directive" 67 | VersioningConfiguration = "versioning-configuration" 68 | VersionId = "version-id" 69 | VersionIdMarker = "version-id-marker" 70 | WebsiteConfiguration = "website-configuration" 71 | WebsiteRedirectLocation = "website-redirect-location" 72 | UploadID = "upload-id" 73 | UploadIDMarker = "upload-id-marker" 74 | List = "list" 75 | Method = "method" 76 | URL = "url" 77 | Clear = "clear" 78 | Style = "style" 79 | JSON = "json" 80 | FetchOwner = "fetch-owner" 81 | StartAfter = "start-after" 82 | ContinuationToken = "starting-token" 83 | LifecycleConfiguration = "lifecycle-configuration" 84 | ) 85 | -------------------------------------------------------------------------------- /functions/download.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/aws" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 6 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3manager" 7 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 8 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 9 | "github.com/IBM/ibmcloud-cos-cli/errors" 10 | "github.com/IBM/ibmcloud-cos-cli/render" 11 | "github.com/IBM/ibmcloud-cos-cli/utils" 12 | "github.com/urfave/cli" 13 | ) 14 | 15 | // Download - utilizes S3 Manager Downloader API to download objects from S3 concurrently. 16 | // Parameter: 17 | // 18 | // CLI Context Application 19 | // 20 | // Returns: 21 | // 22 | // Error = zero or non-zero 23 | func Download(c *cli.Context) (err error) { 24 | 25 | // check the number of arguments 26 | if c.NArg() > 1 { 27 | err = &errors.CommandError{ 28 | CLIContext: c, 29 | Cause: errors.InvalidNArg, 30 | } 31 | return 32 | } 33 | 34 | // Load COS Context 35 | var cosContext *utils.CosContext 36 | if cosContext, err = GetCosContext(c); err != nil { 37 | return 38 | } 39 | 40 | // Monitor the file 41 | keepFile := false 42 | 43 | // Download location 44 | var dstPath string 45 | 46 | // In case of error removes incomplete downloads 47 | defer func() { 48 | if !keepFile && dstPath != "" { 49 | cosContext.Remove(dstPath) 50 | } 51 | }() 52 | 53 | // Build GetObjectInput 54 | input := new(s3.GetObjectInput) 55 | 56 | // Required parameters for GetObjectInput 57 | mandatory := map[string]string{ 58 | fields.Bucket: flags.Bucket, 59 | fields.Key: flags.Key, 60 | } 61 | 62 | // 63 | // Optional parameters for GetObjectInput 64 | options := map[string]string{ 65 | fields.IfMatch: flags.IfMatch, 66 | fields.IfModifiedSince: flags.IfModifiedSince, 67 | fields.IfNoneMatch: flags.IfNoneMatch, 68 | fields.IfUnmodifiedSince: flags.IfUnmodifiedSince, 69 | fields.Range: flags.Range, 70 | fields.ResponseCacheControl: flags.ResponseCacheControl, 71 | fields.ResponseContentDisposition: flags.ResponseContentDisposition, 72 | fields.ResponseContentEncoding: flags.ResponseContentEncoding, 73 | fields.ResponseContentLanguage: flags.ResponseContentLanguage, 74 | fields.ResponseContentType: flags.ResponseContentType, 75 | fields.ResponseExpires: flags.ResponseExpires, 76 | } 77 | 78 | // 79 | // Check through user inputs for validation 80 | if err = MapToSDKInput(c, input, mandatory, options); err != nil { 81 | return 82 | } 83 | 84 | // 85 | // Validate Download Location 86 | var file utils.WriteCloser 87 | if dstPath, file, err = getAndValidateDownloadPath(cosContext, c.Args().First(), 88 | aws.StringValue(input.Key), c.IsSet(flags.Force)); err != nil || file == nil { 89 | return 90 | } 91 | 92 | // Defer closing file 93 | defer file.Close() 94 | 95 | // Options for Downloader 96 | downloadOptions := map[string]string{ 97 | fields.PartSize: flags.PartSize, 98 | fields.Concurrency: flags.Concurrency, 99 | } 100 | // Error holder 101 | errorHolder := new(error) 102 | 103 | // Build a s3manager download 104 | var downloader utils.Downloader 105 | if downloader, err = cosContext.GetDownloader(c.String(flags.Region), 106 | applyConfigDownloader(c, downloadOptions, errorHolder)); err != nil { 107 | return 108 | } 109 | // Downloader Error Checking 110 | if *errorHolder != nil { 111 | return *errorHolder 112 | } 113 | 114 | // Download Op 115 | var totalBytes int64 116 | if totalBytes, err = downloader.Download(file, input); err != nil { 117 | return 118 | } 119 | 120 | // Maintain to keep file 121 | keepFile = true 122 | 123 | // Render DownloadOutput 124 | output := &render.DownloadOutput{ 125 | TotalBytes: totalBytes, 126 | } 127 | // Output the successful message 128 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 129 | 130 | // Return 131 | return 132 | } 133 | 134 | // Build Download from the Config 135 | func applyConfigDownloader(c *cli.Context, options map[string]string, err *error) func(u *s3manager.Downloader) { 136 | return func(u *s3manager.Downloader) { 137 | mandatory := map[string]string{} 138 | *err = MapToSDKInput(c, u, mandatory, options) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /functions/bucket_head_test.go: -------------------------------------------------------------------------------- 1 | //+build unit 2 | 3 | package functions_test 4 | 5 | import ( 6 | "errors" 7 | "os" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/mock" 12 | "github.com/urfave/cli" 13 | 14 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 15 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 16 | "github.com/IBM/ibmcloud-cos-cli/config" 17 | "github.com/IBM/ibmcloud-cos-cli/config/commands" 18 | "github.com/IBM/ibmcloud-cos-cli/cos" 19 | "github.com/IBM/ibmcloud-cos-cli/di/providers" 20 | ) 21 | 22 | func TestBucketHeadSunnyPath(t *testing.T) { 23 | defer providers.MocksRESET() 24 | 25 | // --- Arrange --- 26 | // disable and capture OS EXIT 27 | var exitCode *int 28 | cli.OsExiter = func(ec int) { 29 | exitCode = &ec 30 | } 31 | 32 | targetBucket := "TargetBucket" 33 | 34 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 35 | 36 | providers.MockS3API. 37 | On("HeadBucket", mock.MatchedBy( 38 | func(input *s3.HeadBucketInput) bool { 39 | return *input.Bucket == targetBucket 40 | })). 41 | Return(new(s3.HeadBucketOutput), nil). 42 | Once() 43 | 44 | // --- Act ---- 45 | // set os args 46 | os.Args = []string{"-", commands.BucketHead, "--bucket", targetBucket, "--region", "REG"} 47 | //call plugin 48 | plugin.Start(new(cos.Plugin)) 49 | 50 | // --- Assert ---- 51 | // assert s3 api called once per region (since success is last) 52 | providers.MockS3API.AssertNumberOfCalls(t, "HeadBucket", 1) 53 | // assert exit code is zero 54 | assert.Equal(t, (*int)(nil), exitCode) // no exit trigger in the cli 55 | // capture all output // 56 | output := providers.FakeUI.Outputs() 57 | errors := providers.FakeUI.Errors() 58 | // assert OK 59 | assert.Contains(t, output, "OK") 60 | // assert Not Fail 61 | assert.NotContains(t, errors, "FAIL") 62 | 63 | } 64 | 65 | func TestBucketHeadRainyPath(t *testing.T) { 66 | defer providers.MocksRESET() 67 | 68 | // --- Arrange --- 69 | // disable and capture OS EXIT 70 | var exitCode *int 71 | cli.OsExiter = func(ec int) { 72 | exitCode = &ec 73 | } 74 | 75 | targetBucket := "TargetBucket" 76 | 77 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 78 | 79 | providers.MockS3API. 80 | On("HeadBucket", mock.MatchedBy( 81 | func(input *s3.HeadBucketInput) bool { 82 | return *input.Bucket == targetBucket 83 | })). 84 | Return(nil, errors.New("NoSuchBucket")). 85 | Once() 86 | 87 | // --- Act ---- 88 | // set os args 89 | os.Args = []string{"-", commands.BucketHead, "--bucket", targetBucket, 90 | "--region", "REG"} 91 | //call plugin 92 | plugin.Start(new(cos.Plugin)) 93 | 94 | // --- Assert ---- 95 | // assert s3 api called once per region (since success is last) 96 | providers.MockS3API.AssertNumberOfCalls(t, "HeadBucket", 1) 97 | // assert exit code is zero 98 | assert.Equal(t, 1, *exitCode) // no exit trigger in the cli 99 | // capture all output // 100 | output := providers.FakeUI.Outputs() 101 | errors := providers.FakeUI.Errors() 102 | // assert Not OK 103 | assert.NotContains(t, output, "OK") 104 | // assert Fail 105 | assert.Contains(t, errors, "FAIL") 106 | 107 | } 108 | 109 | func TestBucketHeadWithoutBucket(t *testing.T) { 110 | defer providers.MocksRESET() 111 | 112 | // --- Arrange --- 113 | // disable and capture OS EXIT 114 | var exitCode *int 115 | cli.OsExiter = func(ec int) { 116 | exitCode = &ec 117 | } 118 | 119 | targetBucket := "TargetBucket" 120 | 121 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 122 | 123 | providers.MockS3API. 124 | On("HeadBucket", mock.MatchedBy( 125 | func(input *s3.HeadBucketInput) bool { 126 | return *input.Bucket == targetBucket 127 | })). 128 | Return(nil, errors.New("NoSuchBucket")). 129 | Once() 130 | 131 | // --- Act ---- 132 | // set os args 133 | os.Args = []string{"-", commands.BucketHead, 134 | "--region", "REG"} 135 | //call plugin 136 | plugin.Start(new(cos.Plugin)) 137 | 138 | // --- Assert ---- 139 | // assert s3 api called once per region (since success is last) 140 | providers.MockS3API.AssertNumberOfCalls(t, "HeadBucket", 0) 141 | // assert exit code is zero 142 | assert.Equal(t, 1, *exitCode) // no exit trigger in the cli 143 | // capture all output // 144 | output := providers.FakeUI.Outputs() 145 | errors := providers.FakeUI.Errors() 146 | // assert Not OK 147 | assert.NotContains(t, output, "OK") 148 | // assert Fail 149 | assert.Contains(t, errors, "FAIL") 150 | 151 | } 152 | -------------------------------------------------------------------------------- /functions/upload_test.go: -------------------------------------------------------------------------------- 1 | //+build unit 2 | 3 | package functions_test 4 | 5 | import ( 6 | "os" 7 | "strconv" 8 | "testing" 9 | 10 | "github.com/IBM/ibm-cos-sdk-go/aws" 11 | 12 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3manager" 13 | 14 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 15 | "github.com/IBM/ibmcloud-cos-cli/config" 16 | "github.com/IBM/ibmcloud-cos-cli/config/commands" 17 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 18 | "github.com/IBM/ibmcloud-cos-cli/cos" 19 | "github.com/IBM/ibmcloud-cos-cli/di/providers" 20 | "github.com/IBM/ibmcloud-cos-cli/utils" 21 | "github.com/stretchr/testify/assert" 22 | "github.com/stretchr/testify/mock" 23 | "github.com/urfave/cli" 24 | ) 25 | 26 | func TestUploadSunnyPath(t *testing.T) { 27 | defer providers.MocksRESET() 28 | 29 | // --- Arrange --- 30 | // disable and capture OS EXIT 31 | var exitCode *int 32 | cli.OsExiter = func(ec int) { 33 | exitCode = &ec 34 | } 35 | 36 | targetBucket := "TargetBucket" 37 | targetKey := "TargetKey" 38 | targetFile := "TargetFile" 39 | 40 | targetCacheControl := "TargetCacheControl" 41 | targetContentDisposition := "TargetContentDisposition" 42 | targetContentEncoding := "TargetContentEncoding" 43 | targetContentLanguage := "TargetContentLanguage" 44 | //targetContentLength := "TargetContentLength" 45 | targetContentMD5 := "TargetContentMD5" 46 | targetContentType := "TargetContentType" 47 | 48 | targetPartSize := int64(9999) 49 | targetConcurrency := 99 50 | targetLeavePartsOnErrors := true 51 | targetMaxUploadParts := 999 52 | 53 | isClosed := false 54 | 55 | referenceUploader := new(s3manager.Uploader) 56 | 57 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 58 | 59 | providers.MockFileOperations. 60 | On("ReadSeekerCloserOpen", mock.MatchedBy(func(fileName string) bool { 61 | return fileName == targetFile 62 | })). 63 | Return(utils.WrapString("", &isClosed), nil). 64 | Once() 65 | 66 | var inputCapture *s3manager.UploadInput 67 | providers.MockUploaderAPI. 68 | On("Upload", mock.MatchedBy(func(input *s3manager.UploadInput) bool { 69 | inputCapture = input 70 | return true 71 | })). 72 | Return(nil, nil). 73 | Once() 74 | 75 | // --- Act ---- 76 | // set os args 77 | os.Args = []string{"-", commands.Upload, "--bucket", targetBucket, 78 | "--" + flags.Key, targetKey, 79 | "--" + flags.File, targetFile, 80 | "--" + flags.Region, "REG", 81 | "--" + flags.CacheControl, targetCacheControl, 82 | "--" + flags.ContentDisposition, targetContentDisposition, 83 | "--" + flags.ContentEncoding, targetContentEncoding, 84 | "--" + flags.ContentLanguage, targetContentLanguage, 85 | "--" + flags.ContentMD5, targetContentMD5, 86 | "--" + flags.ContentType, targetContentType, 87 | 88 | "--" + flags.PartSize, strconv.FormatInt(targetPartSize, 10), 89 | "--" + flags.Concurrency, strconv.Itoa(targetConcurrency), 90 | "--" + flags.LeavePartsOnErrors, 91 | "--" + flags.MaxUploadParts, strconv.Itoa(targetMaxUploadParts), 92 | } 93 | 94 | // call plugin 95 | providers.ReferenceUploader = referenceUploader 96 | plugin.Start(new(cos.Plugin)) 97 | 98 | // --- Assert ---- 99 | output := providers.FakeUI.Outputs() 100 | 101 | // assert all command line options where set 102 | assert.NotNil(t, inputCapture) // assert file name matched 103 | assert.Equal(t, targetBucket, aws.StringValue(inputCapture.Bucket)) 104 | assert.Equal(t, targetKey, aws.StringValue(inputCapture.Key)) 105 | 106 | assert.Equal(t, targetCacheControl, aws.StringValue(inputCapture.CacheControl)) 107 | assert.Equal(t, targetContentDisposition, aws.StringValue(inputCapture.ContentDisposition)) 108 | assert.Equal(t, targetContentEncoding, aws.StringValue(inputCapture.ContentEncoding)) 109 | assert.Equal(t, targetContentLanguage, aws.StringValue(inputCapture.ContentLanguage)) 110 | assert.Equal(t, targetContentMD5, aws.StringValue(inputCapture.ContentMD5)) 111 | assert.Equal(t, targetContentType, aws.StringValue(inputCapture.ContentType)) 112 | 113 | assert.Equal(t, targetPartSize, referenceUploader.PartSize) 114 | assert.Equal(t, targetConcurrency, referenceUploader.Concurrency) 115 | assert.Equal(t, targetLeavePartsOnErrors, referenceUploader.LeavePartsOnError) 116 | assert.Equal(t, targetMaxUploadParts, referenceUploader.MaxUploadParts) 117 | 118 | // assert exit code is zero 119 | assert.Equal(t, (*int)(nil), exitCode) // no exit trigger in the cli 120 | // capture all output // 121 | //output := providers.FakeUI.Outputs() 122 | errors := providers.FakeUI.Errors() 123 | // assert OK 124 | assert.Contains(t, output, "OK") 125 | // assert Not Fail 126 | assert.NotContains(t, errors, "FAIL") 127 | } 128 | -------------------------------------------------------------------------------- /functions/bucket_list_extended.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 5 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 6 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 7 | "github.com/IBM/ibmcloud-cos-cli/errors" 8 | "github.com/IBM/ibmcloud-cos-cli/utils" 9 | 10 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 11 | 12 | "github.com/urfave/cli" 13 | ) 14 | 15 | // BucketsListExtended prints out an extensive list of all the buckets with their respective 16 | // location constraints in an IBM Cloud account. This also supports pagination. 17 | // Parameter: 18 | // CLI Context Application 19 | // Returns: 20 | // Error = zero or non-zero 21 | func BucketsListExtended(c *cli.Context) (err error) { 22 | // check the number of arguments 23 | if c.NArg() > 0 { 24 | err = &errors.CommandError{ 25 | CLIContext: c, 26 | Cause: errors.InvalidNArg, 27 | } 28 | return 29 | } 30 | 31 | // Load COS Context 32 | var cosContext *utils.CosContext 33 | if cosContext, err = GetCosContext(c); err != nil { 34 | return 35 | } 36 | 37 | // Set ListBucketsExtendedInput 38 | pageIterInput := new(s3.ListBucketsExtendedInput) 39 | 40 | // Required parameter for ListBucketsExtended 41 | mandatory := map[string]string{} 42 | 43 | // 44 | // Optional parameters for ListBucketsExtended 45 | options := map[string]string{ 46 | fields.IBMServiceInstanceID: flags.IbmServiceInstanceID, 47 | fields.Marker: flags.Marker, 48 | fields.Prefix: flags.Prefix, 49 | } 50 | 51 | // Check through user inputs for validation 52 | if err = MapToSDKInput(c, pageIterInput, mandatory, options); err != nil { 53 | return 54 | } 55 | 56 | // Initialize List Buckets Extended 57 | input := new(s3.ListBucketsExtendedInput) 58 | if err = DeepCopyIntoUsingJSON(input, pageIterInput); err != nil { 59 | return 60 | } 61 | 62 | // Build Pagination Helper 63 | var paginationHelper *PaginationHelper 64 | var nextPageSize *int64 65 | // retrieves a PaginationHelper and a pointer to the next page size 66 | if paginationHelper, nextPageSize, err = NewPaginationHelper(c, flags.MaxItems, flags.PageSize); err != nil { 67 | return 68 | } 69 | // set next page size as MaxUploads 70 | pageIterInput.MaxKeys = nextPageSize 71 | 72 | // Check if Max Items is set 73 | if c.IsSet(flags.MaxItems) { 74 | // Parse if the integer is correctly set 75 | if value, errInner := parseInt64(c.String(flags.MaxItems)); errInner != nil { 76 | commandError := new(errors.CommandError) 77 | commandError.CLIContext = c 78 | commandError.Cause = errors.InvalidValue 79 | commandError.Flag = flags.MaxItems 80 | commandError.IError = errInner 81 | err = commandError 82 | return 83 | } else { 84 | input.MaxKeys = &value 85 | } 86 | } 87 | 88 | // Setting client to do the call 89 | var client s3iface.S3API 90 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 91 | return 92 | } 93 | 94 | // ListBucketExtends Op 95 | output := new(s3.ListBucketsExtendedOutput) 96 | if err = client.ListBucketsExtendedPages(pageIterInput, 97 | ListBucketsExtendedItx(paginationHelper, output)); err != nil { 98 | return 99 | } 100 | 101 | // Display either in JSON or text 102 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 103 | 104 | // Return 105 | return 106 | } 107 | 108 | // ListBucketsExtendedItx - iterate through each page of List Buckets Extended 109 | func ListBucketsExtendedItx(paginationHelper *PaginationHelper, output *s3.ListBucketsExtendedOutput) func( 110 | *s3.ListBucketsExtendedOutput, bool) bool { 111 | // Set first page to true 112 | firstPage := true 113 | return func(page *s3.ListBucketsExtendedOutput, _ bool) bool { 114 | // Check if first page, initialize the output 115 | if firstPage { 116 | initListBucketsExtendedOutputFromPage(output, page) 117 | firstPage = false 118 | } 119 | // Merge subsequent pages into output 120 | mergeListBucketsExtendedOutputPage(output, page) 121 | 122 | // Return 123 | return paginationHelper.UpdateTotal(len(page.Buckets)) 124 | } 125 | } 126 | 127 | // Initialize List Bucket Extended Output from the first page 128 | func initListBucketsExtendedOutputFromPage(output, page *s3.ListBucketsExtendedOutput) { 129 | output.Marker = page.Marker 130 | output.Owner = page.Owner 131 | output.Prefix = page.Prefix 132 | } 133 | 134 | // Merge List Bucket Extended Output into previous page print-outs 135 | func mergeListBucketsExtendedOutputPage(output, page *s3.ListBucketsExtendedOutput) { 136 | output.Buckets = append(output.Buckets, page.Buckets...) 137 | output.IsTruncated = page.IsTruncated 138 | } 139 | -------------------------------------------------------------------------------- /functions/bucket_cors_get_test.go: -------------------------------------------------------------------------------- 1 | //+build unit 2 | 3 | package functions_test 4 | 5 | import ( 6 | "errors" 7 | "os" 8 | "testing" 9 | 10 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 11 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 12 | "github.com/IBM/ibmcloud-cos-cli/config" 13 | "github.com/IBM/ibmcloud-cos-cli/config/commands" 14 | "github.com/IBM/ibmcloud-cos-cli/cos" 15 | "github.com/IBM/ibmcloud-cos-cli/di/providers" 16 | "github.com/stretchr/testify/assert" 17 | "github.com/stretchr/testify/mock" 18 | "github.com/urfave/cli" 19 | ) 20 | 21 | func TestBucketCorsGetSunnyPath(t *testing.T) { 22 | defer providers.MocksRESET() 23 | 24 | // --- Arrange --- 25 | // disable and capture OS EXIT 26 | var exitCode *int 27 | cli.OsExiter = func(ec int) { 28 | exitCode = &ec 29 | } 30 | 31 | targetBucket := "TARGETBUCKET" 32 | 33 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 34 | 35 | providers.MockS3API. 36 | On("GetBucketCors", mock.MatchedBy( 37 | func(input *s3.GetBucketCorsInput) bool { 38 | return *input.Bucket == targetBucket 39 | })). 40 | Return(new(s3.GetBucketCorsOutput). 41 | SetCORSRules([]*s3.CORSRule{}), nil). 42 | Once() 43 | 44 | // --- Act ---- 45 | // set os args 46 | os.Args = []string{"-", commands.BucketCorsGet, "--bucket", targetBucket, "--region", "REG"} 47 | //call plugin 48 | plugin.Start(new(cos.Plugin)) 49 | 50 | // --- Assert ---- 51 | // assert s3 api called once per region (since success is last) 52 | providers.MockS3API.AssertNumberOfCalls(t, "GetBucketCors", 1) 53 | // assert exit code is zero 54 | assert.Equal(t, (*int)(nil), exitCode) // no exit trigger in the cli 55 | // capture all output // 56 | output := providers.FakeUI.Outputs() 57 | errors := providers.FakeUI.Errors() 58 | // assert OK 59 | assert.Contains(t, output, "OK") 60 | // assert CORSRules present 61 | assert.Contains(t, output, "CORSRules") 62 | // assert Not Fail 63 | assert.NotContains(t, errors, "FAIL") 64 | 65 | } 66 | func TestBucketCorsGetEmptyStaticCreds(t *testing.T) { 67 | defer providers.MocksRESET() 68 | 69 | // --- Arrange --- 70 | // disable and capture OS EXIT 71 | var exitCode *int 72 | cli.OsExiter = func(ec int) { 73 | exitCode = &ec 74 | } 75 | 76 | targetBucket := "TARGETBUCKET" 77 | 78 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 79 | 80 | providers.MockS3API. 81 | On("GetBucketCors", mock.MatchedBy( 82 | func(input *s3.GetBucketCorsInput) bool { 83 | return *input.Bucket == targetBucket 84 | })). 85 | Return(nil, errors.New("EmptyStaticCreds")). 86 | Once() 87 | 88 | // --- Act ---- 89 | // set os args 90 | os.Args = []string{"-", commands.BucketCorsGet, "--bucket", targetBucket, "--region", "REG"} 91 | //call plugin 92 | plugin.Start(new(cos.Plugin)) 93 | 94 | // --- Assert ---- 95 | // assert s3 api called once per region (since success is last) 96 | providers.MockS3API.AssertNumberOfCalls(t, "GetBucketCors", 1) 97 | // assert exit code is zero 98 | assert.Equal(t, 1, *exitCode) // no exit trigger in the cli 99 | // capture all output // 100 | output := providers.FakeUI.Outputs() 101 | errors := providers.FakeUI.Errors() 102 | // assert Not OK 103 | assert.NotContains(t, output, "OK") 104 | // assert Fail 105 | assert.Contains(t, errors, "FAIL") 106 | 107 | } 108 | 109 | func TestBucketCorsGetWithoutBucket(t *testing.T) { 110 | defer providers.MocksRESET() 111 | 112 | // --- Arrange --- 113 | // disable and capture OS EXIT 114 | var exitCode *int 115 | cli.OsExiter = func(ec int) { 116 | exitCode = &ec 117 | } 118 | 119 | targetBucket := "TARGETBUCKET" 120 | 121 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 122 | 123 | providers.MockS3API. 124 | On("GetBucketCors", mock.MatchedBy( 125 | func(input *s3.GetBucketCorsInput) bool { 126 | return *input.Bucket == targetBucket 127 | })). 128 | Return(nil, errors.New("EmptyStaticCreds")). 129 | Once() 130 | 131 | // --- Act ---- 132 | // set os args 133 | os.Args = []string{"-", commands.BucketCorsGet, 134 | "--region", "REG"} 135 | //call plugin 136 | plugin.Start(new(cos.Plugin)) 137 | 138 | // --- Assert ---- 139 | // assert s3 api called once per region (since success is last) 140 | providers.MockS3API.AssertNumberOfCalls(t, "GetBucketCors", 0) 141 | // assert exit code is zero 142 | assert.Equal(t, 1, *exitCode) // no exit trigger in the cli 143 | // capture all output // 144 | output := providers.FakeUI.Outputs() 145 | errors := providers.FakeUI.Errors() 146 | // assert Not OK 147 | assert.NotContains(t, output, "OK") 148 | // assert Fail 149 | assert.Contains(t, errors, "FAIL") 150 | 151 | } 152 | -------------------------------------------------------------------------------- /functions/wait_object_test.go: -------------------------------------------------------------------------------- 1 | //+build unit 2 | 3 | package functions_test 4 | 5 | import ( 6 | "os" 7 | "strconv" 8 | "testing" 9 | "time" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/mock" 13 | "github.com/urfave/cli" 14 | 15 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 16 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 17 | "github.com/IBM/ibmcloud-cos-cli/config" 18 | "github.com/IBM/ibmcloud-cos-cli/config/commands" 19 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 20 | "github.com/IBM/ibmcloud-cos-cli/cos" 21 | "github.com/IBM/ibmcloud-cos-cli/di/providers" 22 | ) 23 | 24 | func TestWaitObjectNotExistsHappy(t *testing.T) { 25 | 26 | defer providers.MocksRESET() 27 | 28 | // --- Arrange --- 29 | // disable and capture OS EXIT 30 | var exitCode *int 31 | cli.OsExiter = func(ec int) { 32 | exitCode = &ec 33 | } 34 | 35 | // flags 36 | targetBucket := "WaitObjBucket" 37 | targetKey := "WaitObjKey" 38 | targetIfMatch := "WaitObjIfMatch" 39 | targetIfModifiedSince, _ := time.Parse(time.RFC3339, "2001-01-01") 40 | targetIfNoneMatch := "WaitObjIfNoneMatch" 41 | targetIfUnmodifiedSince, _ := time.Parse(time.RFC3339, "2002-02-02") 42 | targetRange := "WaitObjRange" 43 | targetPartNumber := int64(42) 44 | 45 | var inputCapture *s3.HeadObjectInput 46 | 47 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 48 | 49 | providers.MockS3API. 50 | On( 51 | "WaitUntilObjectNotExists", 52 | mock.MatchedBy(func(input *s3.HeadObjectInput) bool { inputCapture = input; return true })). 53 | Return(nil) 54 | 55 | // --- Act ---- 56 | // set os args 57 | os.Args = []string{"-", 58 | commands.Wait, 59 | commands.ObjectNotExists, 60 | "--" + flags.Bucket, targetBucket, 61 | "--" + flags.Key, targetKey, 62 | "--" + flags.IfMatch, targetIfMatch, 63 | "--" + flags.IfModifiedSince, targetIfModifiedSince.Format(time.RFC3339), 64 | "--" + flags.IfNoneMatch, targetIfNoneMatch, 65 | "--" + flags.IfUnmodifiedSince, targetIfUnmodifiedSince.Format(time.RFC3339), 66 | "--" + flags.Range, targetRange, 67 | "--" + flags.PartNumber, strconv.FormatInt(targetPartNumber, 10), 68 | "--region", "REG"} 69 | //call plugin 70 | plugin.Start(new(cos.Plugin)) 71 | 72 | // --- Assert ---- 73 | // assert s3 api called once per region (since success is last) 74 | providers.MockS3API.AssertNumberOfCalls(t, "WaitUntilObjectNotExists", 1) 75 | // assert exit code is zero 76 | assert.Equal(t, (*int)(nil), exitCode) // no exit trigger in the cli 77 | 78 | assert.Equal(t, targetBucket, *inputCapture.Bucket) 79 | assert.Equal(t, targetKey, *inputCapture.Key) 80 | assert.Equal(t, targetIfMatch, *inputCapture.IfMatch) 81 | assert.Equal(t, targetIfModifiedSince, *inputCapture.IfModifiedSince) 82 | assert.Equal(t, targetIfNoneMatch, *inputCapture.IfNoneMatch) 83 | assert.Equal(t, targetIfUnmodifiedSince, *inputCapture.IfUnmodifiedSince) 84 | assert.Equal(t, targetRange, *inputCapture.Range) 85 | assert.Equal(t, targetPartNumber, *inputCapture.PartNumber) 86 | 87 | // capture all output // 88 | errors := providers.FakeUI.Errors() 89 | // assert Not Fail 90 | assert.NotContains(t, errors, "FAIL") 91 | } 92 | 93 | func TestWaitObjectExistsHappy(t *testing.T) { 94 | 95 | defer providers.MocksRESET() 96 | 97 | // --- Arrange --- 98 | // disable and capture OS EXIT 99 | var exitCode *int 100 | cli.OsExiter = func(ec int) { 101 | exitCode = &ec 102 | } 103 | 104 | // flags 105 | targetBucket := "WaitObjBucket" 106 | targetKey := "WaitObjKey" 107 | 108 | var inputCapture *s3.HeadObjectInput 109 | 110 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 111 | 112 | providers.MockS3API. 113 | On( 114 | "WaitUntilObjectExists", 115 | mock.MatchedBy(func(input *s3.HeadObjectInput) bool { inputCapture = input; return true })). 116 | Return(nil) 117 | 118 | // --- Act ---- 119 | // set os args 120 | os.Args = []string{"-", 121 | commands.Wait, 122 | commands.ObjectExists, 123 | "--" + flags.Bucket, targetBucket, 124 | "--" + flags.Key, targetKey, 125 | "--region", "REG"} 126 | //call plugin 127 | plugin.Start(new(cos.Plugin)) 128 | 129 | // --- Assert ---- 130 | // assert s3 api called once per region (since success is last) 131 | providers.MockS3API.AssertNumberOfCalls(t, "WaitUntilObjectExists", 1) 132 | // assert exit code is zero 133 | assert.Equal(t, (*int)(nil), exitCode) // no exit trigger in the cli 134 | 135 | assert.Equal(t, targetBucket, *inputCapture.Bucket) 136 | assert.Equal(t, targetKey, *inputCapture.Key) 137 | 138 | // capture all output // 139 | errors := providers.FakeUI.Errors() 140 | // assert Not Fail 141 | assert.NotContains(t, errors, "FAIL") 142 | } 143 | -------------------------------------------------------------------------------- /functions/bucket_cors_delete_test.go: -------------------------------------------------------------------------------- 1 | //+build unit 2 | 3 | package functions_test 4 | 5 | import ( 6 | "errors" 7 | "os" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/mock" 12 | "github.com/urfave/cli" 13 | 14 | "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin" 15 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 16 | "github.com/IBM/ibmcloud-cos-cli/config" 17 | "github.com/IBM/ibmcloud-cos-cli/config/commands" 18 | "github.com/IBM/ibmcloud-cos-cli/cos" 19 | "github.com/IBM/ibmcloud-cos-cli/di/providers" 20 | ) 21 | 22 | func TestBucketCorsDeleteSunnyPath(t *testing.T) { 23 | defer providers.MocksRESET() 24 | 25 | // --- Arrange --- 26 | // disable and capture OS EXIT 27 | var exitCode *int 28 | cli.OsExiter = func(ec int) { 29 | exitCode = &ec 30 | } 31 | 32 | targetBucket := "TARGETBUCKET" 33 | 34 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 35 | 36 | providers.MockS3API. 37 | On("DeleteBucketCors", mock.MatchedBy( 38 | func(input *s3.DeleteBucketCorsInput) bool { 39 | return *input.Bucket == targetBucket 40 | })). 41 | Return(new(s3.DeleteBucketCorsOutput), nil). 42 | Once() 43 | 44 | // --- Act ---- 45 | // set os args 46 | os.Args = []string{"-", commands.BucketCorsDelete, "--bucket", targetBucket, "--region", "REG"} 47 | //call plugin 48 | plugin.Start(new(cos.Plugin)) 49 | 50 | // --- Assert ---- 51 | // assert s3 api called once per region (since success is last) 52 | providers.MockS3API.AssertNumberOfCalls(t, "DeleteBucketCors", 1) 53 | // assert exit code is zero 54 | assert.Equal(t, (*int)(nil), exitCode) // no exit trigger in the cli 55 | // capture all output // 56 | output := providers.FakeUI.Outputs() 57 | errors := providers.FakeUI.Errors() 58 | // assert OK 59 | assert.Contains(t, output, "OK") 60 | // assert Not Fail 61 | assert.NotContains(t, errors, "FAIL") 62 | 63 | } 64 | 65 | // need to reach over 65 % ... meaning error handling is more than 35% ... 66 | func TestBucketCorsDeleteEmptyStaticCreds(t *testing.T) { 67 | defer providers.MocksRESET() 68 | 69 | // --- Arrange --- 70 | // disable and capture OS EXIT 71 | var exitCode *int 72 | cli.OsExiter = func(ec int) { 73 | exitCode = &ec 74 | } 75 | 76 | targetBucket := "TARGETBUCKET" 77 | 78 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 79 | 80 | providers.MockS3API. 81 | On("DeleteBucketCors", mock.MatchedBy( 82 | func(input *s3.DeleteBucketCorsInput) bool { 83 | return *input.Bucket == targetBucket 84 | })). 85 | Return(nil, errors.New("EmptyStaticCreds")). 86 | Once() 87 | 88 | // --- Act ---- 89 | // set os args 90 | os.Args = []string{"-", commands.BucketCorsDelete, "--bucket", targetBucket, "--region", "REG"} 91 | //call plugin 92 | plugin.Start(new(cos.Plugin)) 93 | 94 | // --- Assert ---- 95 | // assert s3 api called once per region (since success is last) 96 | providers.MockS3API.AssertNumberOfCalls(t, "DeleteBucketCors", 1) 97 | // assert exit code is zero 98 | assert.Equal(t, 1, *exitCode) // no exit trigger in the cli 99 | // capture all output // 100 | output := providers.FakeUI.Outputs() 101 | errors := providers.FakeUI.Errors() 102 | // assert not OK 103 | assert.NotContains(t, output, "OK") 104 | // assert Fail 105 | assert.Contains(t, errors, "FAIL") 106 | 107 | } 108 | 109 | func TestBucketCorsWithoutBucket(t *testing.T) { 110 | defer providers.MocksRESET() 111 | 112 | // --- Arrange --- 113 | // disable and capture OS EXIT 114 | var exitCode *int 115 | cli.OsExiter = func(ec int) { 116 | exitCode = &ec 117 | } 118 | 119 | targetBucket := "TARGETBUCKET" 120 | 121 | providers.MockPluginConfig.On("GetString", config.ServiceEndpointURL).Return("", nil) 122 | 123 | providers.MockS3API. 124 | On("DeleteBucketCors", mock.MatchedBy( 125 | func(input *s3.DeleteBucketCorsInput) bool { 126 | return *input.Bucket == targetBucket 127 | })). 128 | Return(nil, errors.New("EmptyStaticCreds")). 129 | Once() 130 | 131 | // --- Act ---- 132 | // set os args 133 | os.Args = []string{"-", commands.BucketCorsDelete, 134 | "--region", "REG"} 135 | //call plugin 136 | plugin.Start(new(cos.Plugin)) 137 | 138 | // --- Assert ---- 139 | // assert s3 api called once per region (since success is last) 140 | providers.MockS3API.AssertNumberOfCalls(t, "DeleteBucketCors", 0) 141 | // assert exit code is zero 142 | assert.Equal(t, 1, *exitCode) // no exit trigger in the cli 143 | // capture all output // 144 | output := providers.FakeUI.Outputs() 145 | errors := providers.FakeUI.Errors() 146 | // assert Not OK 147 | assert.NotContains(t, output, "OK") 148 | // assert Fail 149 | assert.Contains(t, errors, "FAIL") 150 | 151 | } 152 | -------------------------------------------------------------------------------- /functions/object_list.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "github.com/IBM/ibm-cos-sdk-go/service/s3" 5 | "github.com/IBM/ibm-cos-sdk-go/service/s3/s3iface" 6 | "github.com/IBM/ibmcloud-cos-cli/config/fields" 7 | "github.com/IBM/ibmcloud-cos-cli/config/flags" 8 | "github.com/IBM/ibmcloud-cos-cli/errors" 9 | "github.com/IBM/ibmcloud-cos-cli/utils" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // ObjectsList lists all the objects in a certain bucket. 14 | // Parameter: 15 | // CLI Context Application 16 | // Returns: 17 | // Error = zero or non-zero 18 | func ObjectsList(c *cli.Context) (err error) { 19 | // check the number of arguments 20 | if c.NArg() > 0 { 21 | // Build Command Error struct 22 | err = &errors.CommandError{ 23 | CLIContext: c, 24 | Cause: errors.InvalidNArg, 25 | } 26 | // Return error 27 | return 28 | } 29 | 30 | // Load COS Context 31 | var cosContext *utils.CosContext 32 | if cosContext, err = GetCosContext(c); err != nil { 33 | return 34 | } 35 | 36 | // Initialize ListObjectsInput 37 | pageIterInput := new(s3.ListObjectsInput) 38 | 39 | // Required parameter for ListObjects 40 | mandatory := map[string]string{ 41 | fields.Bucket: flags.Bucket, 42 | } 43 | 44 | // 45 | // Optional parameters for ListObjects 46 | options := map[string]string{ 47 | fields.Delimiter: flags.Delimiter, 48 | fields.EncodingType: flags.EncodingType, 49 | fields.Prefix: flags.Prefix, 50 | fields.Marker: flags.Marker, 51 | } 52 | 53 | // Check through user inputs for validation 54 | if err = MapToSDKInput(c, pageIterInput, mandatory, options); err != nil { 55 | return 56 | } 57 | 58 | // Initialize ListObjects Input 59 | input := new(s3.ListObjectsInput) 60 | if err = DeepCopyIntoUsingJSON(input, pageIterInput); err != nil { 61 | return 62 | } 63 | 64 | // Pagination Helper 65 | var paginationHelper *PaginationHelper 66 | var nextPageSize *int64 67 | // retrieves a PaginationHelper and a pointer to the next page size 68 | if paginationHelper, nextPageSize, err = NewPaginationHelper(c, flags.MaxItems, flags.PageSize); err != nil { 69 | return 70 | } 71 | // set next page size as MaxUploads 72 | pageIterInput.MaxKeys = nextPageSize 73 | 74 | // Check if Max Items is set 75 | if c.IsSet(flags.MaxItems) { 76 | // Parse if the integer is correctly set 77 | if value, errInner := parseInt64(c.String(flags.MaxItems)); errInner != nil { 78 | commandError := new(errors.CommandError) 79 | commandError.CLIContext = c 80 | commandError.Cause = errors.InvalidValue 81 | commandError.Flag = flags.MaxItems 82 | commandError.IError = errInner 83 | err = commandError 84 | return 85 | } else { 86 | input.MaxKeys = &value 87 | } 88 | } 89 | 90 | // Setting client to do the call 91 | var client s3iface.S3API 92 | if client, err = cosContext.GetClient(c.String(flags.Region)); err != nil { 93 | return 94 | } 95 | 96 | // List Objects Op 97 | output := new(s3.ListObjectsOutput) 98 | if err = client.ListObjectsPages(pageIterInput, ListObjectsItx(paginationHelper, output)); err != nil { 99 | return 100 | } 101 | 102 | // Display either in JSON or text 103 | err = cosContext.GetDisplay(c.String(flags.Output), c.Bool(flags.JSON)).Display(input, output, nil) 104 | 105 | // Return 106 | return 107 | } 108 | 109 | // ListObjectsItx - Initialize List Objects Output from the first page 110 | func ListObjectsItx(paginationHelper *PaginationHelper, output *s3.ListObjectsOutput) func( 111 | *s3.ListObjectsOutput, bool) bool { 112 | // Set first page to true 113 | firstPage := true 114 | return func(page *s3.ListObjectsOutput, _ bool) bool { 115 | if firstPage { 116 | // Check if first page, initialize the output 117 | initListObjectsOutputFromPage(output, page) 118 | firstPage = false 119 | } 120 | // Merge subsequent pages into output 121 | mergeListObjectsOutputPage(output, page) 122 | // Return 123 | return paginationHelper.UpdateTotal(len(page.Contents)) 124 | } 125 | } 126 | 127 | // 128 | // initListObjectsOutputFromPage - Initialize List Objects Output from the first page 129 | func initListObjectsOutputFromPage(output, page *s3.ListObjectsOutput) { 130 | output.Delimiter = page.Delimiter 131 | output.EncodingType = page.EncodingType 132 | output.Marker = page.Marker 133 | output.Name = page.Name 134 | output.Prefix = page.Prefix 135 | } 136 | 137 | // mergeListObjectsOutputPage - Merge List Objects Output into previous page print-outs 138 | func mergeListObjectsOutputPage(output, page *s3.ListObjectsOutput) { 139 | output.CommonPrefixes = append(output.CommonPrefixes, page.CommonPrefixes...) 140 | output.Contents = append(output.Contents, page.Contents...) 141 | output.IsTruncated = page.IsTruncated 142 | output.NextMarker = page.NextMarker 143 | } 144 | --------------------------------------------------------------------------------