From 1e123ec7ea44a2ab5e2e0d55542241ba74ea8f46 Mon Sep 17 00:00:00 2001 From: Kevin Pankonen Date: Sat, 21 Mar 2026 18:44:59 -0700 Subject: [PATCH] upgrade to v2 of aws sdk --- Makefile | 38 ++---- README.md | 23 ++-- aws/aws.go | 25 ++-- commands/commands.go | 54 ++++++--- commands/key.go | 11 +- commands/policy.go | 2 +- commands/put.go | 52 ++++---- go.mod | 36 ++++-- go.sum | 100 ++++++++------- parameterstore/parameterstore.go | 102 ++++++++-------- parameterstore/parameterstore_test.go | 167 ++++++++++++-------------- ssmsh.go | 13 +- 12 files changed, 315 insertions(+), 308 deletions(-) diff --git a/Makefile b/Makefile index 88877170..33c79a58 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,15 @@ SHELL := /bin/bash PROJECT := github.com/bwhaley/ssmsh -PKGS := $(shell go list ./... | grep -v /vendor) EXECUTABLE := ssmsh -PKG := ssmsh GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null) -.PHONY: build test golint docs $(PROJECT) $(PKGS) vendor +.PHONY: build test VERSION := $(shell echo ${SSMSH_VERSION}) ifeq "$(VERSION)" "" VERSION="auto-build" endif -GOVERSION := $(shell go version | grep 1.17) -ifeq "$(GOVERSION)" "" - $(error must be running Go version 1.17.x) -endif - ifndef $(GOPATH) GOPATH=$(shell go env GOPATH) export GOPATH @@ -24,34 +17,17 @@ endif all: test build -FGT := $(GOPATH)/bin/fgt -$(FGT): - go get github.com/GeertJohan/fgt - -GOLINT := $(GOPATH)/bin/golint -$(GOLINT): - go get golang.org/x/lint - -DEP := $(GOPATH)/bin/dep -$(DEP): - go get -u github.com/golang/dep - GO_LDFLAGS := -X $(shell go list ./$(PACKAGE)).GitCommit=$(GIT_COMMIT) -X main.Version=${VERSION} -test: $(PKGS) - -$(PKGS): $(GOLINT) +test: @echo "FORMATTING" - go fmt $@ + go fmt ./... + @echo "VETTING" + go vet -v ./... @echo "LINTING" - golint $@ - @echo "Vetting" - go vet -v $@ + golangci-lint run ./... @echo "TESTING" - go test -v $@ - -vendor: $(DEP) - $(DEP) ensure + go test -v ./... build: go build -ldflags "$(GO_LDFLAGS)" -o $(GOPATH)/bin/$(EXECUTABLE) $(PROJECT) diff --git a/README.md b/README.md index 7cab6c3b..04414bb7 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ nix-env -i ssmsh ## Configuration -Set up [AWS credentials](http://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials). +Set up [AWS credentials](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#specifying-credentials). You can set up a `.ssmshrc` to configure `ssmsh`. By default, `ssmsh` will load `~/.ssmshrc` if it exists. Use the `-config` argument to set a different path. @@ -55,7 +55,7 @@ A few notes on configuration: * When setting the region, the `AWS_REGION` env var takes top priority, followed by the setting in `.ssmshrc`, followed by the value set in the AWS profile (if configured) * When setting the profile, the `AWS_PROFILE` env var takes top priority, followed by the setting in `.ssmshrc` * If you set a KMS key, it will only work in the region where that key is located. You can use the `key` command while in the shell to change the key. -* If the configuration file has `output=json`, the results of the `get` and `history` commands will be printed in JSON. The fields of the JSON results will be the same as in the respective Go structs. See the [`Parameter`](https://docs.aws.amazon.com/sdk-for-go/api/service/ssm/#Parameter) and [`ParameterHistory`](https://docs.aws.amazon.com/sdk-for-go/api/service/ssm/#ParameterHistory) docs. +* If the configuration file has `output=json`, the results of the `get` and `history` commands will be printed in JSON. The fields of the JSON results will be the same as in the respective Go structs. See the [`Parameter`](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ssm/types#Parameter) and [`ParameterHistory`](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ssm/types#ParameterHistory) docs. ## Usage ### Help @@ -279,18 +279,21 @@ $ ssmsh put name=/dev/app/domain value="www.example.com" type=String description MIT ## Contributing/compiling -1. Ensure you have at least go v1.17 +1. Ensure you have at least go v1.21 ``` $ go version -go version go1.17.6 darwin/arm64 +go version go1.21.0 darwin/arm64 ``` -2. Ensure your `$GOPATH` exists and is in your `$PATH` +2. Ensure `$GOPATH/bin` is in your `$PATH` ``` -export GOPATH=$HOME/go -export PATH=$PATH:$GOROOT/bin:$GOPATH/bin +export PATH=$PATH:$(go env GOPATH)/bin +``` +3. Clone the repo and build: +``` +git clone https://github.com/bwhaley/ssmsh.git +cd ssmsh +make ``` -3. Run `go get github.com/bwhaley/ssmsh` -4. Run `cd $GOPATH/src/github.com/bwhaley/ssmsh && make` to build and install the binary to `$GOPATH/bin/ssmsh` ## Related tools @@ -304,5 +307,5 @@ Tool | Description Library | Use ------- | ----- [abiosoft/ishell](https://github.com/abiosoft/ishell) | The interactive shell for golang -[aws-sdk-go](https://github.com/aws/aws-sdk-go) | The AWS SDK for Go +[aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) | The AWS SDK for Go [mattn/go-shellwords](github.com/mattn/go-shellwords) | Parsing for the shell made easy diff --git a/aws/aws.go b/aws/aws.go index 36bbf21b..7746b286 100644 --- a/aws/aws.go +++ b/aws/aws.go @@ -1,20 +1,19 @@ package aws import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" ) -func NewSession(region, profile string) *session.Session { - return session.Must( - session.NewSessionWithOptions( - session.Options{ - SharedConfigState: session.SharedConfigEnable, - Config: aws.Config{ - Region: aws.String(region), - }, - Profile: profile, - }, - ), +func LoadConfig(region, profile string) aws.Config { + cfg, err := config.LoadDefaultConfig(context.TODO(), + config.WithRegion(region), + config.WithSharedConfigProfile(profile), ) + if err != nil { + panic("unable to load AWS config: " + err.Error()) + } + return cfg } diff --git a/commands/commands.go b/commands/commands.go index 8d6bbcfc..f6172774 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/abiosoft/ishell" - "github.com/bwhaley/ssmsh/config" "github.com/bwhaley/ssmsh/parameterstore" ) @@ -14,14 +13,12 @@ type fn func(*ishell.Context) var ( shell *ishell.Shell ps *parameterstore.ParameterStore - cfg *config.Config ) // Init initializes the ssmsh subcommands -func Init(iShell *ishell.Shell, iPs *parameterstore.ParameterStore, iCfg *config.Config) { +func Init(iShell *ishell.Shell, iPs *parameterstore.ParameterStore) { shell = iShell ps = iPs - cfg = iCfg registerCommand("cd", "change your relative location within the parameter store", cd, cdUsage) registerCommand("cp", "copy source to dest", cp, cpUsage) registerCommand("decrypt", "toggle parameter decryption", decrypt, decryptUsage) @@ -99,20 +96,45 @@ func trim(with []string) (without []string) { return without } -func printResult(result interface{}) { - switch cfg.Default.Output { - case "json": - printJSON(result) - default: - shell.Printf("%+v\n", result) +func printResult(result any) { + raw, err := json.Marshal(result) + if err != nil { + shell.Println("Error with result: ", err) + return } -} - -func printJSON(result interface{}) { - resultJSON, err := json.MarshalIndent(result, "", " ") + cleaned := stripNulls(raw) + indented, err := json.MarshalIndent(cleaned, "", " ") if err != nil { shell.Println("Error with result: ", err) - } else { - shell.Println(string(resultJSON)) + return + } + shell.Println(string(indented)) +} + +func stripNulls(data []byte) any { + var raw any + if err := json.Unmarshal(data, &raw); err != nil { + return raw + } + return cleanValue(raw) +} + +func cleanValue(v any) any { + switch val := v.(type) { + case map[string]any: + cleaned := make(map[string]any) + for k, v := range val { + if v != nil { + cleaned[k] = cleanValue(v) + } + } + return cleaned + case []any: + for i, item := range val { + val[i] = cleanValue(item) + } + return val + default: + return v } } diff --git a/commands/key.go b/commands/key.go index 9b9a70a4..2c7e6f06 100644 --- a/commands/key.go +++ b/commands/key.go @@ -1,11 +1,12 @@ package commands import ( + "context" "fmt" "github.com/abiosoft/ishell" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/kms" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/kms" saws "github.com/bwhaley/ssmsh/aws" ) @@ -26,15 +27,15 @@ func key(c *ishell.Context) { } func checkKey(key string) (err error) { - client := kms.New(saws.NewSession(ps.Region, ps.Profile)) + client := kms.NewFromConfig(saws.LoadConfig(ps.Region, ps.Profile)) input := kms.ListKeysInput{} for { - resp, err := client.ListKeys(&input) + resp, err := client.ListKeys(context.TODO(), &input) if err != nil { return err } for _, keyEntry := range resp.Keys { - if aws.StringValue(keyEntry.KeyId) == key || aws.StringValue(keyEntry.KeyArn) == key { + if aws.ToString(keyEntry.KeyId) == key || aws.ToString(keyEntry.KeyArn) == key { return nil } } diff --git a/commands/policy.go b/commands/policy.go index 28652396..1fbf722d 100644 --- a/commands/policy.go +++ b/commands/policy.go @@ -167,7 +167,7 @@ func createPolicy(policyName string, policyArgs []string) (err error) { } policy.noChangeNotification = append(policy.noChangeNotification, *p) default: - return fmt.Errorf("Unable to parse policy type %s with attributes %s", policyType, policyAttributes) + return fmt.Errorf("unable to parse policy type %s with attributes %s", policyType, policyAttributes) } } policies[policyName] = policy diff --git a/commands/put.go b/commands/put.go index 935fdfc3..56e336c9 100644 --- a/commands/put.go +++ b/commands/put.go @@ -9,8 +9,9 @@ import ( "strings" "github.com/abiosoft/ishell" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ssm" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ssm" + "github.com/aws/aws-sdk-go-v2/service/ssm/types" "github.com/bwhaley/ssmsh/parameterstore" ) @@ -64,7 +65,7 @@ func put(c *ishell.Context) { if putParamInput.Name == nil || putParamInput.Value == nil || - putParamInput.Type == nil { + putParamInput.Type == "" { shell.Println("Error: name, type and value are required.") return } @@ -73,20 +74,20 @@ func put(c *ishell.Context) { if err != nil { shell.Println("Error: ", err) } else { - version := strconv.Itoa(int(aws.Int64Value(resp.Version))) + version := strconv.Itoa(int(resp.Version)) if version != "" { - shell.Println("Put " + aws.StringValue(putParamInput.Name) + " version " + version) + shell.Println("Put " + aws.ToString(putParamInput.Name) + " version " + version) } } } // setDefaults sets parameter settings according to the defaults func setDefaults(param *ssm.PutParameterInput) (err error) { - param.SetOverwrite(ps.Overwrite) + param.Overwrite = aws.Bool(ps.Overwrite) if ps.Key != "" { - param.SetKeyId(ps.Key) + param.KeyId = aws.String(ps.Key) } - param.SetType(ps.Type) + param.Type = types.ParameterType(ps.Type) err = validateType(ps.Type) if err != nil { return err @@ -168,11 +169,11 @@ func validateType(s string) (err error) { validTypes := []string{"String", "StringList", "SecureString"} for i := 0; i < len(validTypes); i++ { if strings.EqualFold(s, validTypes[i]) { // Case insensitive validation of type field - putParamInput.Type = aws.String(validTypes[i]) + putParamInput.Type = types.ParameterType(validTypes[i]) return nil } } - return fmt.Errorf("Invalid type %s", s) + return fmt.Errorf("invalid type %s", s) } func validateValue(s string) (err error) { @@ -194,27 +195,27 @@ func trimSpaces(s string) string { func validateName(s string) (err error) { if strings.HasPrefix(s, parameterstore.Delimiter) { - putParamInput.SetName(s) + putParamInput.Name = aws.String(s) } else { - putParamInput.SetName(ps.Cwd + parameterstore.Delimiter + s) + putParamInput.Name = aws.String(ps.Cwd + parameterstore.Delimiter + s) } return nil } func validateDescription(s string) (err error) { - putParamInput.SetDescription(s) + putParamInput.Description = aws.String(s) return nil } // TODO validate key func validateKey(s string) (err error) { - putParamInput.SetKeyId(s) + putParamInput.KeyId = aws.String(s) return nil } // TODO validate pattern func validatePattern(s string) (err error) { - putParamInput.SetAllowedPattern(s) + putParamInput.AllowedPattern = aws.String(s) return nil } @@ -224,7 +225,7 @@ func validateOverwrite(s string) (err error) { shell.Println("overwrite must be true or false") return err } - putParamInput.SetOverwrite(overwrite) + putParamInput.Overwrite = aws.Bool(overwrite) return nil } @@ -233,17 +234,16 @@ func validateRegion(s string) (err error) { return nil } -const ( - StandardTier = "Standard" - AdvancedTier = "Advanced" -) - func validateTier(s string) (err error) { - if strings.ToLower(s) == StandardTier || strings.ToLower(s) == AdvancedTier { - putParamInput.Tier = aws.String(strings.Title(s)) - return nil + switch strings.ToLower(s) { + case "standard": + putParamInput.Tier = types.ParameterTierStandard + case "advanced": + putParamInput.Tier = types.ParameterTierAdvanced + default: + return errors.New("tier must be standard or advanced") } - return errors.New("tier must be standard or advanced") + return nil } func validatePolicies(s string) (err error) { @@ -274,6 +274,6 @@ func validatePolicies(s string) (err error) { return err } putParamInput.Policies = aws.String(string(policyBytes)) - putParamInput.Tier = aws.String(AdvancedTier) + putParamInput.Tier = types.ParameterTierAdvanced return nil } diff --git a/go.mod b/go.mod index 92eb5c40..e8ef3e4b 100644 --- a/go.mod +++ b/go.mod @@ -1,27 +1,37 @@ module github.com/bwhaley/ssmsh -go 1.17 +go 1.25.0 require ( github.com/abiosoft/ishell v2.0.1-0.20181228190644-8b8aa74a8512+incompatible - github.com/aws/aws-sdk-go v1.50.16 + github.com/aws/aws-sdk-go-v2 v1.41.4 + github.com/aws/aws-sdk-go-v2/config v1.32.12 + github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 + github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3 github.com/mattn/go-shellwords v1.0.12 gopkg.in/gcfg.v1 v1.2.3 ) require ( github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect - github.com/chzyer/logex v1.1.10 // indirect - github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fatih/color v1.10.0 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect + github.com/aws/smithy-go v1.24.2 // indirect + github.com/chzyer/test v1.0.0 // indirect + github.com/fatih/color v1.19.0 // indirect github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect - github.com/stretchr/testify v1.6.1 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/stretchr/testify v1.11.1 // indirect + golang.org/x/sys v0.42.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 1f513095..300d97f0 100644 --- a/go.sum +++ b/go.sum @@ -2,66 +2,64 @@ github.com/abiosoft/ishell v2.0.1-0.20181228190644-8b8aa74a8512+incompatible h1: github.com/abiosoft/ishell v2.0.1-0.20181228190644-8b8aa74a8512+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8= github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= -github.com/aws/aws-sdk-go v1.19.20-0.20190426182805-4dbd475cffe6 h1:F7zGMMZffSQwMRJ7UYIEZ4WZDt1yaqeNEQm26C8t3G4= -github.com/aws/aws-sdk-go v1.19.20-0.20190426182805-4dbd475cffe6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.50.16 h1:/KuHK+Sadp9BKXWWtMhPtBdj+PLIFCnQZxQnsuLhxKc= -github.com/aws/aws-sdk-go v1.50.16/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= +github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= +github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3 h1:bBoWhx8lsFLTXintRX64ZBXcmFZbGqUmaPUrjXECqIc= +github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3/go.mod h1:rcRkKbUJ2437WuXdq9fbj+MjTudYWzY9Ct8kiBbN8a8= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-shellwords v1.0.5 h1:JhhFTIOslh5ZsPrpa3Wdg8bF0WI3b44EMblmU9wIsXc= -github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 h1:bNEHhJCnrwMKNMmOx3yAynp5vs5/gRy+XWFtZFu7NBM= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/parameterstore/parameterstore.go b/parameterstore/parameterstore.go index c2bc9d89..4c1b6a96 100644 --- a/parameterstore/parameterstore.go +++ b/parameterstore/parameterstore.go @@ -1,15 +1,16 @@ package parameterstore import ( + "context" "errors" "fmt" "os" spath "path" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ssm" - "github.com/aws/aws-sdk-go/service/ssm/ssmiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ssm" + "github.com/aws/aws-sdk-go-v2/service/ssm/types" saws "github.com/bwhaley/ssmsh/aws" "github.com/bwhaley/ssmsh/config" ) @@ -20,16 +21,25 @@ const ( DefaultParameterType = "SecureString" ) +type SSMAPI interface { + GetParametersByPath(ctx context.Context, params *ssm.GetParametersByPathInput, optFns ...func(*ssm.Options)) (*ssm.GetParametersByPathOutput, error) + GetParameters(ctx context.Context, params *ssm.GetParametersInput, optFns ...func(*ssm.Options)) (*ssm.GetParametersOutput, error) + GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) + GetParameterHistory(ctx context.Context, params *ssm.GetParameterHistoryInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterHistoryOutput, error) + PutParameter(ctx context.Context, params *ssm.PutParameterInput, optFns ...func(*ssm.Options)) (*ssm.PutParameterOutput, error) + DeleteParameters(ctx context.Context, params *ssm.DeleteParametersInput, optFns ...func(*ssm.Options)) (*ssm.DeleteParametersOutput, error) +} + // ParameterStore represents the current state and preferences of the shell type ParameterStore struct { - Cwd string // The current working directory in the hierarchy - Decrypt bool // Decrypt values retrieved from Get - Type string // Default parameter type (String, SecureString, StringList) - Key string // The KMS key to use for SecureString parameters - Region string // AWS region on which to operate - Overwrite bool // Whether or not to overwrite parameters - Profile string // Profile to use from .aws/[config|credentials] - Clients map[string]ssmiface.SSMAPI // per-region SSM clients + Cwd string // The current working directory in the hierarchy + Decrypt bool // Decrypt values retrieved from Get + Type string // Default parameter type (String, SecureString, StringList) + Key string // The KMS key to use for SecureString parameters + Region string // AWS region on which to operate + Overwrite bool // Whether or not to overwrite parameters + Profile string // Profile to use from .aws/[config|credentials] + Clients map[string]SSMAPI // per-region SSM clients } // SetConfig sets the shels configuration state @@ -72,8 +82,8 @@ func (ps *ParameterStore) SetDefaults(cfg config.Config) { func (ps *ParameterStore) NewParameterStore(checkCredentials bool) error { ps.Cwd = Delimiter - ps.Clients = make(map[string]ssmiface.SSMAPI) - ps.Clients[ps.Region] = ssm.New(saws.NewSession(ps.Region, ps.Profile)) + ps.Clients = make(map[string]SSMAPI) + ps.Clients[ps.Region] = ssm.NewFromConfig(saws.LoadConfig(ps.Region, ps.Profile)) if checkCredentials { // Check for a non-existent parameter to validate credentials & permissions @@ -87,7 +97,7 @@ func (ps *ParameterStore) NewParameterStore(checkCredentials bool) error { // InitClient initializes an SSM client in a given region func (ps *ParameterStore) InitClient(region string) { - ps.Clients[region] = ssm.New(saws.NewSession(region, ps.Profile)) + ps.Clients[region] = ssm.NewFromConfig(saws.LoadConfig(region, ps.Profile)) } // ParameterPath abstracts a parameter to include some metadata @@ -106,7 +116,7 @@ func (ps *ParameterStore) SetCwd(path ParameterPath) error { if ps.isPath(path) { ps.Cwd = path.Name } else { - return errors.New("No such path") + return errors.New("no such path") } return nil } @@ -143,14 +153,14 @@ func (ps *ParameterStore) List(ppath ParameterPath, recurse bool, lr chan ListRe return default: } - resp, err := ps.Clients[region].GetParametersByPath(params) + resp, err := ps.Clients[region].GetParametersByPath(context.TODO(), params) if err != nil { lr <- ListResult{nil, err} } for _, p := range resp.Parameters { - results = append(results, aws.StringValue(p.Name)) + results = append(results, aws.ToString(p.Name)) } - if aws.StringValue(resp.NextToken) == "" { + if resp.NextToken == nil { break } params.NextToken = resp.NextToken @@ -166,7 +176,7 @@ func (ps *ParameterStore) List(ppath ParameterPath, recurse bool, lr chan ListRe return } if len(param) == 1 { - pathParam := aws.StringValue(param[0].Name) + pathParam := aws.ToString(param[0].Name) results = append(results, pathParam) } @@ -190,7 +200,7 @@ func (ps *ParameterStore) Remove(params []ParameterPath, recurse bool) (err erro return fmt.Errorf("tried to delete path %s but recursive not requested", param.Name) } } else { - return fmt.Errorf("No path or parameter %s was found, aborting", param.Name) + return fmt.Errorf("no path or parameter %s was found, aborting", param.Name) } } return ps.deleteByRegion(parametersToDelete) @@ -204,17 +214,17 @@ func (ps *ParameterStore) recursiveDelete(path ParameterPath) (err error) { Recursive: aws.Bool(true), } for { - resp, err := ps.Clients[path.Region].GetParametersByPath(additionalParams) + resp, err := ps.Clients[path.Region].GetParametersByPath(context.TODO(), additionalParams) if err != nil { return err } for _, r := range resp.Parameters { parametersToDelete = append(parametersToDelete, ParameterPath{ - Name: aws.StringValue(r.Name), + Name: aws.ToString(r.Name), Region: path.Region, }) } - if aws.StringValue(resp.NextToken) == "" { + if resp.NextToken == nil { break } additionalParams.NextToken = resp.NextToken @@ -253,13 +263,11 @@ func (ps *ParameterStore) delete(params []string, region string) (err error) { ssmParams := &ssm.DeleteParametersInput{ Names: ps.inputPaths(deleteBatch), } - resp, err := ps.Clients[region].DeleteParameters(ssmParams) + resp, err := ps.Clients[region].DeleteParameters(context.TODO(), ssmParams) if err != nil { return err } - for _, r := range resp.InvalidParameters { - invalidParams = append(invalidParams, aws.StringValue(r)) - } + invalidParams = append(invalidParams, resp.InvalidParameters...) } if len(invalidParams) > 0 { return errors.New("Could not delete invalid parameters " + strings.Join(invalidParams, ",")) @@ -268,20 +276,18 @@ func (ps *ParameterStore) delete(params []string, region string) (err error) { } // GetHistory returns the parameter history -func (ps *ParameterStore) GetHistory(param ParameterPath) (r []ssm.ParameterHistory, err error) { +func (ps *ParameterStore) GetHistory(param ParameterPath) (r []types.ParameterHistory, err error) { history := &ssm.GetParameterHistoryInput{ Name: aws.String(fqp(param.Name, ps.Cwd)), WithDecryption: aws.Bool(ps.Decrypt), } for { - resp, err := ps.Clients[param.Region].GetParameterHistory(history) + resp, err := ps.Clients[param.Region].GetParameterHistory(context.TODO(), history) if err != nil { return nil, err } - for _, p := range resp.Parameters { - r = append(r, *p) - } - if aws.StringValue(resp.NextToken) == "" { + r = append(r, resp.Parameters...) + if resp.NextToken == nil { break } history.NextToken = resp.NextToken @@ -290,24 +296,22 @@ func (ps *ParameterStore) GetHistory(param ParameterPath) (r []ssm.ParameterHist } // Get retrieves one or more parameters -func (ps *ParameterStore) Get(params []string, region string) (r []ssm.Parameter, err error) { +func (ps *ParameterStore) Get(params []string, region string) (r []types.Parameter, err error) { ssmParams := &ssm.GetParametersInput{ Names: ps.inputPaths(params), WithDecryption: aws.Bool(ps.Decrypt), } - resp, err := ps.Clients[region].GetParameters(ssmParams) + resp, err := ps.Clients[region].GetParameters(context.TODO(), ssmParams) if err != nil { return nil, err } - for _, p := range resp.Parameters { - r = append(r, *p) - } + r = append(r, resp.Parameters...) return r, nil } // Put creates or updates a parameter func (ps *ParameterStore) Put(param *ssm.PutParameterInput, region string) (resp *ssm.PutParameterOutput, err error) { - resp, err = ps.Clients[region].PutParameter(param) + resp, err = ps.Clients[region].PutParameter(context.TODO(), param) if err != nil { return resp, err } @@ -358,7 +362,7 @@ func (ps *ParameterStore) Copy(src, dst ParameterPath, recurse bool) error { } else if srcIsParameter && dstIsPath { return ps.copyParameterToPath(src, dst) } else if srcIsPath && dstIsParameter { - return fmt.Errorf("Cannot copy path (%s) to parameter (%s)", src, dst) + return fmt.Errorf("cannot copy path (%s) to parameter (%s)", src, dst) } else if srcIsPath { if !recurse { return fmt.Errorf("%s and %s are both paths but recursion not requested. Use -R", src, dst) @@ -419,7 +423,7 @@ func (ps *ParameterStore) copyPathToPath(newPath bool, srcPath, dstPath Paramete Recursive: aws.Bool(true), } for { - resp, err := ps.Clients[srcPath.Region].GetParametersByPath(params) + resp, err := ps.Clients[srcPath.Region].GetParametersByPath(context.TODO(), params) if err != nil { return err } @@ -430,7 +434,7 @@ func (ps *ParameterStore) copyPathToPath(newPath bool, srcPath, dstPath Paramete return err } } - if aws.StringValue(resp.NextToken) == "" { + if resp.NextToken == nil { break } params.NextToken = resp.NextToken @@ -439,11 +443,11 @@ func (ps *ParameterStore) copyPathToPath(newPath bool, srcPath, dstPath Paramete } // makeParameterMap returns a map of source param name to dest param name -func makeParameterMap(params []*ssm.Parameter, newPath bool, srcPath, dstPath ParameterPath) (sourceToDst map[ParameterPath]ParameterPath) { +func makeParameterMap(params []types.Parameter, newPath bool, srcPath, dstPath ParameterPath) (sourceToDst map[ParameterPath]ParameterPath) { sourceToDst = make(map[ParameterPath]ParameterPath) for _, p := range params { srcParam := ParameterPath{ - Name: aws.StringValue(p.Name), + Name: aws.ToString(p.Name), Region: srcPath.Region, } srcPathElements := strings.Split(srcPath.Name, Delimiter) @@ -476,11 +480,11 @@ func makeParameterMap(params []*ssm.Parameter, newPath bool, srcPath, dstPath Pa // inputPaths cleans a list of parameter paths and returns strings // suitable for use as ssm.Parameters -func (ps *ParameterStore) inputPaths(paths []string) []*string { - var _paths []*string +func (ps *ParameterStore) inputPaths(paths []string) []string { + var _paths []string for i, p := range paths { paths[i] = fqp(p, ps.Cwd) - _paths = append(_paths, aws.String(paths[i])) + _paths = append(_paths, paths[i]) } return _paths } @@ -505,7 +509,7 @@ func (ps *ParameterStore) isParameter(param ParameterPath) bool { p := &ssm.GetParameterInput{ Name: aws.String(param.Name), } - _, err := ps.Clients[param.Region].GetParameter(p) + _, err := ps.Clients[param.Region].GetParameter(context.TODO(), p) return err == nil } @@ -516,7 +520,7 @@ func (ps *ParameterStore) isPath(path ParameterPath) bool { Path: aws.String(path.Name), Recursive: aws.Bool(true), } - resp, err := ps.Clients[path.Region].GetParametersByPath(params) + resp, err := ps.Clients[path.Region].GetParametersByPath(context.TODO(), params) if err != nil { return false } diff --git a/parameterstore/parameterstore_test.go b/parameterstore/parameterstore_test.go index 8526fe25..19b98e9d 100644 --- a/parameterstore/parameterstore_test.go +++ b/parameterstore/parameterstore_test.go @@ -1,60 +1,60 @@ package parameterstore_test import ( + "context" "errors" "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ssm" - "github.com/aws/aws-sdk-go/service/ssm/ssmiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ssm" + "github.com/aws/aws-sdk-go-v2/service/ssm/types" "github.com/bwhaley/ssmsh/parameterstore" ) -var EddardStark = &ssm.Parameter{ +var EddardStark = types.Parameter{ Name: aws.String("/House/Stark/EddardStark"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("Lord"), } -var CatelynStark = &ssm.Parameter{ +var CatelynStark = types.Parameter{ Name: aws.String("/House/Stark/CatelynStark"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("Lady"), } -var RobStark = &ssm.Parameter{ +var RobStark = types.Parameter{ Name: aws.String("/House/Stark/RobStark"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("Noble"), } -var JonSnow = &ssm.Parameter{ +var JonSnow = types.Parameter{ Name: aws.String("/House/Stark/JonSnow"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("Bastard"), } -var DaenerysTargaryen = &ssm.Parameter{ +var DaenerysTargaryen = types.Parameter{ Name: aws.String("/House/Targaryen/DaenerysTargaryen"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("Noble"), } -var HouseStark = []*ssm.Parameter{ +var HouseStark = []types.Parameter{ EddardStark, CatelynStark, RobStark, } -var HouseTargaryen = []*ssm.Parameter{ +var HouseTargaryen = []types.Parameter{ DaenerysTargaryen, } const NextToken = "A1B2C3D4" type mockedSSM struct { - ssmiface.SSMAPI GetParametersByPathResp ssm.GetParametersByPathOutput GetParametersByPathNext ssm.GetParametersByPathOutput GetParameterHistoryResp ssm.GetParameterHistoryOutput @@ -64,48 +64,48 @@ type mockedSSM struct { PutParameterResp ssm.PutParameterOutput } -func (m mockedSSM) GetParametersByPath(in *ssm.GetParametersByPathInput) (*ssm.GetParametersByPathOutput, error) { - if aws.StringValue(in.NextToken) != "" { +func (m mockedSSM) GetParametersByPath(_ context.Context, in *ssm.GetParametersByPathInput, _ ...func(*ssm.Options)) (*ssm.GetParametersByPathOutput, error) { + if in.NextToken != nil { return &m.GetParametersByPathNext, nil } return &m.GetParametersByPathResp, nil } -func (m mockedSSM) DeleteParameters(in *ssm.DeleteParametersInput) (*ssm.DeleteParametersOutput, error) { +func (m mockedSSM) DeleteParameters(_ context.Context, in *ssm.DeleteParametersInput, _ ...func(*ssm.Options)) (*ssm.DeleteParametersOutput, error) { return &m.DeleteParametersResp, nil } -func (m mockedSSM) GetParameter(in *ssm.GetParameterInput) (*ssm.GetParameterOutput, error) { - parameterName := aws.StringValue(in.Name) +func (m mockedSSM) GetParameter(_ context.Context, in *ssm.GetParameterInput, _ ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { + parameterName := aws.ToString(in.Name) for _, param := range m.GetParameterResp { - if aws.StringValue(param.Parameter.Name) == parameterName { + if aws.ToString(param.Parameter.Name) == parameterName { return ¶m, nil } } return nil, errors.New("Parameter not found") } -func (m mockedSSM) GetParameterHistory(in *ssm.GetParameterHistoryInput) (*ssm.GetParameterHistoryOutput, error) { +func (m mockedSSM) GetParameterHistory(_ context.Context, in *ssm.GetParameterHistoryInput, _ ...func(*ssm.Options)) (*ssm.GetParameterHistoryOutput, error) { return &m.GetParameterHistoryResp, nil } -func (m mockedSSM) GetParameters(in *ssm.GetParametersInput) (*ssm.GetParametersOutput, error) { +func (m mockedSSM) GetParameters(_ context.Context, in *ssm.GetParametersInput, _ ...func(*ssm.Options)) (*ssm.GetParametersOutput, error) { for _, n := range in.Names { input := &ssm.GetParameterInput{ - Name: n, + Name: aws.String(n), WithDecryption: aws.Bool(true), } - parameter, err := m.GetParameter(input) + parameter, err := m.GetParameter(context.TODO(), input) if err != nil { m.GetParametersResp.InvalidParameters = append(m.GetParametersResp.InvalidParameters, n) } else { - m.GetParametersResp.Parameters = append(m.GetParametersResp.Parameters, parameter.Parameter) + m.GetParametersResp.Parameters = append(m.GetParametersResp.Parameters, *parameter.Parameter) } } return &m.GetParametersResp, nil } -func (m mockedSSM) PutParameter(in *ssm.PutParameterInput) (*ssm.PutParameterOutput, error) { +func (m mockedSSM) PutParameter(_ context.Context, in *ssm.PutParameterInput, _ ...func(*ssm.Options)) (*ssm.PutParameterOutput, error) { return &m.PutParameterResp, nil } @@ -119,21 +119,21 @@ func TestPut(t *testing.T) { p.Cwd = parameterstore.Delimiter p.Clients[p.Region] = mockedSSM{ PutParameterResp: ssm.PutParameterOutput{ - Version: aws.Int64(expectedVersion), + Version: expectedVersion, }, } putParameterInput := ssm.PutParameterInput{ Name: aws.String("/House/Stark/EddardStark"), Value: aws.String("Lord"), Description: aws.String("Lord of Winterfell in Season 1"), - Type: aws.String("String"), + Type: types.ParameterTypeString, } resp, err := p.Put(&putParameterInput, p.Region) if err != nil { t.Fatal("Error putting parameter", err) } else { - if aws.Int64Value(resp.Version) != expectedVersion { - msg := fmt.Errorf("expected %d, got %d", expectedVersion, aws.Int64Value(resp.Version)) + if resp.Version != expectedVersion { + msg := fmt.Errorf("expected %d, got %d", expectedVersion, resp.Version) t.Fatal(msg) } } @@ -158,35 +158,35 @@ func TestMoveParameter(t *testing.T) { p.Clients[p.Region] = mockedSSM{ GetParameterResp: []ssm.GetParameterOutput{ { - Parameter: &ssm.Parameter{ + Parameter: &types.Parameter{ Name: aws.String(srcParam.Name), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("Noble"), }, }, { - Parameter: &ssm.Parameter{ + Parameter: &types.Parameter{ Name: aws.String(dstParam.Name), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("Noble"), }, }, }, GetParameterHistoryResp: ssm.GetParameterHistoryOutput{ - Parameters: []*ssm.ParameterHistory{ + Parameters: []types.ParameterHistory{ { Name: aws.String(srcParam.Name), Value: aws.String("Noble"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Description: aws.String("Eldest daughter of House Stark, bethrothed to Tyrion Lannister"), - Version: aws.Int64(2), + Version: 2, }, { Name: aws.String(srcParam.Name), Value: aws.String("Noble"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Description: aws.String("Eldest daughter of House Stark"), - Version: aws.Int64(1), + Version: 1, }, }, }, @@ -198,9 +198,9 @@ func TestMoveParameter(t *testing.T) { p.Clients[p.Region] = mockedSSM{ GetParameterResp: []ssm.GetParameterOutput{ { - Parameter: &ssm.Parameter{ + Parameter: &types.Parameter{ Name: aws.String(dstParam.Name), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("Noble"), }, }, @@ -244,28 +244,26 @@ func TestCopyPath(t *testing.T) { bothHouses := append(HouseStark, HouseTargaryen...) p.Clients[p.Region] = mockedSSM{ GetParameterResp: []ssm.GetParameterOutput{ - {Parameter: EddardStark}, - {Parameter: CatelynStark}, - {Parameter: RobStark}, - {Parameter: JonSnow}, - {Parameter: DaenerysTargaryen}, + {Parameter: &EddardStark}, + {Parameter: &CatelynStark}, + {Parameter: &RobStark}, + {Parameter: &JonSnow}, + {Parameter: &DaenerysTargaryen}, }, GetParametersByPathResp: ssm.GetParametersByPathOutput{ Parameters: bothHouses, NextToken: aws.String(NextToken), }, GetParametersByPathNext: ssm.GetParametersByPathOutput{ - Parameters: []*ssm.Parameter{JonSnow}, - NextToken: aws.String(""), + Parameters: []types.Parameter{JonSnow}, }, GetParameterHistoryResp: ssm.GetParameterHistoryOutput{ - Parameters: []*ssm.ParameterHistory{ + Parameters: []types.ParameterHistory{ { Name: aws.String("/House/Stark/EddardStark"), - Version: aws.Int64(2), + Version: 2, }, }, - NextToken: aws.String(""), }, } err = p.Copy(srcPath, dstPath, true) @@ -281,7 +279,7 @@ func TestCopyPath(t *testing.T) { t.Fatal("Error getting history: ", err) } if len(resp) != 1 { - msg := fmt.Errorf("Expected history of length 1, got %s", resp) + msg := fmt.Errorf("Expected history of length 1, got %v", resp) t.Fatal(msg) } } @@ -305,35 +303,35 @@ func TestCopyParameter(t *testing.T) { p.Clients[p.Region] = mockedSSM{ GetParameterResp: []ssm.GetParameterOutput{ { - Parameter: &ssm.Parameter{ + Parameter: &types.Parameter{ Name: aws.String("/House/Stark/JonSnow"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("King"), }, }, { - Parameter: &ssm.Parameter{ + Parameter: &types.Parameter{ Name: aws.String("/House/Targaryen/JonSnow"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("King"), }, }, }, GetParameterHistoryResp: ssm.GetParameterHistoryOutput{ - Parameters: []*ssm.ParameterHistory{ + Parameters: []types.ParameterHistory{ { Name: aws.String("/House/Stark/JonSnow"), Value: aws.String("King"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Description: aws.String("King of the north"), - Version: aws.Int64(2), + Version: 2, }, { Name: aws.String("/House/Stark/JonSnow"), Value: aws.String("Bastard"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Description: aws.String("Bastard of Winterfell"), - Version: aws.Int64(1), + Version: 1, }, }, }, @@ -350,8 +348,8 @@ func TestCopyParameter(t *testing.T) { Name: "/House/Targaryen/JonSnow", Region: "region", } - if aws.StringValue(resp[0].Name) != expectedName.Name { - msg := fmt.Errorf("expected %s, got %s", expectedName.Name, aws.StringValue(resp[0].Name)) + if aws.ToString(resp[0].Name) != expectedName.Name { + msg := fmt.Errorf("expected %s, got %s", expectedName.Name, aws.ToString(resp[0].Name)) t.Fatal(msg) } } @@ -369,14 +367,13 @@ func TestCwd(t *testing.T) { { Path: "/House/Stark/..///Deceased", GetParametersByPathResp: ssm.GetParametersByPathOutput{ - Parameters: []*ssm.Parameter{ + Parameters: []types.Parameter{ { Name: aws.String("/House/Stark/EddardStark"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("Lord"), }, }, - NextToken: aws.String(""), }, Expected: "/House/Deceased", }, @@ -435,12 +432,12 @@ func TestDelete(t *testing.T) { }, } deleteParametersOutput := ssm.DeleteParametersOutput{ - DeletedParameters: []*string{ - aws.String("/House/Stark/EddardStark"), - aws.String("/House/Stark/CatelynStark"), + DeletedParameters: []string{ + "/House/Stark/EddardStark", + "/House/Stark/CatelynStark", }, - InvalidParameters: []*string{ - aws.String("/House/Stark/TyrionLannister"), + InvalidParameters: []string{ + "/House/Stark/TyrionLannister", }, } @@ -466,17 +463,16 @@ func TestGetHistory(t *testing.T) { Region: "region", } getHistoryOutput := ssm.GetParameterHistoryOutput{ - Parameters: []*ssm.ParameterHistory{ + Parameters: []types.ParameterHistory{ { Name: aws.String("/House/Stark/EddardStark"), - Version: aws.Int64(2), + Version: 2, }, { Name: aws.String("/House/Stark/EddardStark"), - Version: aws.Int64(1), + Version: 1, }, }, - NextToken: aws.String(""), } var p parameterstore.ParameterStore p.Region = "region" @@ -493,7 +489,7 @@ func TestGetHistory(t *testing.T) { t.Fatal(msg) } if len(resp) != 2 { - msg := fmt.Errorf("Expected history of length 2, got %s", resp) + msg := fmt.Errorf("Expected history of length 2, got %v", resp) t.Fatal(msg) } } @@ -514,17 +510,16 @@ func TestList(t *testing.T) { }, Recurse: false, GetParametersByPathResp: ssm.GetParametersByPathOutput{ - Parameters: []*ssm.Parameter{}, - NextToken: aws.String(""), + Parameters: []types.Parameter{}, }, Expected: []string{ "/House/Stark/EddardStark", }, GetParametersResp: ssm.GetParametersOutput{ - Parameters: []*ssm.Parameter{ + Parameters: []types.Parameter{ { Name: aws.String("/House/Stark/EddardStark"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("Lord"), }, }, @@ -539,10 +534,10 @@ func TestList(t *testing.T) { "root", }, GetParametersResp: ssm.GetParametersOutput{ - Parameters: []*ssm.Parameter{ + Parameters: []types.Parameter{ { Name: aws.String("root"), - Type: aws.String("String"), + Type: types.ParameterTypeString, Value: aws.String("A root parameter"), }, }, @@ -556,7 +551,6 @@ func TestList(t *testing.T) { Recurse: false, GetParametersByPathResp: ssm.GetParametersByPathOutput{ Parameters: HouseStark, - NextToken: aws.String(""), }, Expected: []string{ "EddardStark", @@ -575,8 +569,7 @@ func TestList(t *testing.T) { NextToken: aws.String(NextToken), }, GetParametersByPathNext: ssm.GetParametersByPathOutput{ - Parameters: []*ssm.Parameter{JonSnow, DaenerysTargaryen}, - NextToken: aws.String(""), + Parameters: []types.Parameter{JonSnow, DaenerysTargaryen}, }, Expected: []string{ "/House/Stark/EddardStark", diff --git a/ssmsh.go b/ssmsh.go index 426eb83d..0aeaf2cc 100644 --- a/ssmsh.go +++ b/ssmsh.go @@ -3,7 +3,7 @@ package main import ( "flag" "fmt" - "io/ioutil" + "io" "os" "strings" @@ -34,6 +34,7 @@ func main() { } shell := ishell.New() + shell.SetHomeHistoryPath(".ssmsh_history") var ps parameterstore.ParameterStore ps.SetDefaults(cfg) err = ps.NewParameterStore(true) @@ -41,7 +42,7 @@ func main() { shell.Println("Error initializing session. Is your authentication correct?", err) os.Exit(1) } - commands.Init(shell, &ps, &cfg) + commands.Init(shell, &ps) if *file == "-" { processStdin(shell) @@ -61,7 +62,7 @@ func main() { } func processStdin(shell *ishell.Shell) { - data, err := ioutil.ReadAll(os.Stdin) + data, err := io.ReadAll(os.Stdin) if err != nil { shell.Println("Error reading from stdin:", err) os.Exit(1) @@ -70,7 +71,7 @@ func processStdin(shell *ishell.Shell) { } func processFile(shell *ishell.Shell, fn string) { - data, err := ioutil.ReadFile(fn) + data, err := os.ReadFile(fn) if err != nil { shell.Println("Error reading from file:", err) } @@ -85,13 +86,13 @@ func processData(shell *ishell.Shell, data string) { } args, err := shellwords.Parse(line) if err != nil { - msg := fmt.Errorf("Error parsing %s: %v", line, err) + msg := fmt.Errorf("error parsing %s: %v", line, err) shell.Println(msg) os.Exit(1) } err = shell.Process(args...) if err != nil { - msg := fmt.Errorf("Error executing %s: %v", line, err) + msg := fmt.Errorf("error executing %s: %v", line, err) shell.Println(msg) os.Exit(1) }