Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dac83e23b6 | ||
|
|
86d383c7dd | ||
|
|
5ba86d5b84 | ||
|
|
39f8f290fc | ||
|
|
fcfe267a3f | ||
|
|
f263eefbba |
No files matched your search
@@ -48,7 +48,7 @@ The plugin fetches hosts through the
|
|||||||
[DescribeInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html)
|
[DescribeInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html)
|
||||||
call.
|
call.
|
||||||
|
|
||||||
[Getting Started](https://github.com/hashicorp/boundary-plugin-aws/plugin/service/host/README.md)
|
[Getting Started](plugin/service/host/README.md)
|
||||||
|
|
||||||
## Storage Bucket
|
## Storage Bucket
|
||||||
|
|
||||||
@@ -76,4 +76,4 @@ The plugin fetches metadata about the files through the
|
|||||||
[HeadObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html)
|
[HeadObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html)
|
||||||
call.
|
call.
|
||||||
|
|
||||||
[Getting Started](https://github.com/hashicorp/boundary-plugin-aws/plugin/service/storage/README.md)
|
[Getting Started](plugin/service/storage/README.md)
|
||||||
@@ -1,65 +1,65 @@
|
|||||||
module github.com/hashicorp/boundary-plugin-aws
|
module github.com/hashicorp/boundary-plugin-aws
|
||||||
|
|
||||||
go 1.20
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go v1.44.80
|
github.com/aws/aws-sdk-go v1.44.80
|
||||||
github.com/aws/aws-sdk-go-v2 v1.18.0
|
github.com/aws/aws-sdk-go-v2 v1.20.1
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.18.19
|
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.18
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0
|
github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1
|
github.com/aws/aws-sdk-go-v2/service/iam v1.22.2
|
||||||
github.com/aws/smithy-go v1.13.5
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.2
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.21.2
|
||||||
|
github.com/aws/smithy-go v1.14.1
|
||||||
github.com/google/go-cmp v0.5.9
|
github.com/google/go-cmp v0.5.9
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.1
|
||||||
github.com/hashicorp/boundary/sdk v0.0.33
|
github.com/hashicorp/boundary/sdk v0.0.41-0.20231117205527-08045ddb050e
|
||||||
github.com/hashicorp/go-secure-stdlib/awsutil v0.2.2
|
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.0.0
|
||||||
github.com/hashicorp/terraform-json v0.14.0
|
github.com/hashicorp/terraform-json v0.14.0
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.8.4
|
||||||
google.golang.org/grpc v1.53.0
|
google.golang.org/grpc v1.59.0
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.31.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 // indirect
|
github.com/aws/aws-sdk-go-v2/config v1.18.33 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect
|
github.com/aws/aws-sdk-go-v2/credentials v1.13.32 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.1 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.33 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.1 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.13.2 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/fatih/color v1.14.1 // indirect
|
github.com/fatih/color v1.15.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/eventlogger v0.1.2-0.20230428153751-cca445805f24 // indirect
|
github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0 // indirect
|
||||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20230428153751-cca445805f24 // indirect
|
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||||
github.com/hashicorp/go-kms-wrapping/v2 v2.0.9 // indirect
|
github.com/hashicorp/go-kms-wrapping/v2 v2.0.14 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||||
github.com/hashicorp/go-version v1.5.0 // indirect
|
github.com/hashicorp/go-version v1.5.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/pointerstructure v1.2.1 // indirect
|
github.com/mitchellh/pointerstructure v1.2.1 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/zclconf/go-cty v1.10.0 // indirect
|
github.com/zclconf/go-cty v1.10.0 // indirect
|
||||||
golang.org/x/crypto v0.8.0 // indirect
|
golang.org/x/crypto v0.14.0 // indirect
|
||||||
golang.org/x/net v0.9.0 // indirect
|
golang.org/x/net v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.7.0 // indirect
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
golang.org/x/text v0.9.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
@@ -1,132 +1,153 @@
|
|||||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||||
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||||
github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/aws/aws-sdk-go v1.44.80 h1:jEXGecSgPdvM5KnyDsSgFhZSm7WwaTp4h544Im4SfhI=
|
github.com/aws/aws-sdk-go v1.44.80 h1:jEXGecSgPdvM5KnyDsSgFhZSm7WwaTp4h544Im4SfhI=
|
||||||
github.com/aws/aws-sdk-go v1.44.80/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
github.com/aws/aws-sdk-go v1.44.80/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
|
||||||
github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY=
|
|
||||||
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs=
|
github.com/aws/aws-sdk-go-v2 v1.20.1 h1:rZBf5DWr7YGrnlTK4kgDQGn1ltqOg5orCYb/UhOFZkg=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno=
|
github.com/aws/aws-sdk-go-v2 v1.20.1/go.mod h1:NU06lETsFm8fUC6ZjhgDpVBcGZTFQ6XM+LZWZxMI4ac=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.18.19 h1:AqFK6zFNtq4i1EYu+eC7lcKHYnZagMn6SW171la0bGw=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12 h1:lN6L3LrYHeZ6xCxaIYtoWCx4GMLk4nRknsh29OMSqHY=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.18.19/go.mod h1:XvTmGMY8d52ougvakOv1RpiTLPz9dlG/OQHsKU/cMmY=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12/go.mod h1:TDCkEAkMTXxTs0oLBGBKpBZbk3NLh8EvAfF0Q3x8/0c=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.18 h1:EQMdtHwz0ILTW1hoP+EwuWhwCG1hD6l3+RWFQABET4c=
|
github.com/aws/aws-sdk-go-v2/config v1.18.33 h1:JKcw5SFxFW/rpM4mOPjv0VQ11E2kxW13F3exWOy7VZU=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.18/go.mod h1:vnwlwjIe+3XJPBYKu1et30ZPABG3VaXJYr8ryohpIyM=
|
github.com/aws/aws-sdk-go-v2/config v1.18.33/go.mod h1:hXO/l9pgY3K5oZJldamP0pbZHdPqqk+4/maa7DSD3cA=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 h1:gt57MN3liKiyGopcqgNzJb2+d9MJaKT/q1OksHNXVE4=
|
github.com/aws/aws-sdk-go-v2/credentials v1.13.32 h1:lIH1eKPcCY1ylR4B6PkBGRWMHO3aVenOKJHWiS4/G2w=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1/go.mod h1:lfUx8puBRdM5lVVMQlwt2v+ofiG/X6Ms+dy0UkG/kXw=
|
github.com/aws/aws-sdk-go-v2/credentials v1.13.32/go.mod h1:lL8U3v/Y79YRG69WlAho0OHIKUXCyFvSXaIvfo81sls=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8 h1:DK/9C+UN/X+1+Wm8pqaDksQr2tSLzq+8X1/rI/ZxKEQ=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8/go.mod h1:ce7BgLQfYr5hQFdy67oX2svto3ufGtm6oBvmsHScI1Q=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 h1:c8ed/T9T2K5I+h/JzmF5tpI46+OODQ74dzmdo+QnaMg=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38/go.mod h1:qggunOChCMu9ZF/UkAfhTz25+U2rLVb3ya0Ua6TTfCA=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 h1:p5luUImdIqywn6JpQsW3tq5GNOxKmOnEpybzPx+d1lk=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 h1:hNeAAymUY5gu11WrrmFb3CVIp9Dar9hbo44yzzcQpzA=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32/go.mod h1:0ZXSqrty4FtQ7p8TEuRde/SZm9X05KT18LAUlR40Ln0=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 h1:DWYZIsyqagnWL00f8M/SOr9fN063OEQWn9LLTbdYXsk=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39 h1:fc0ukRAiP1syoSGZYu+DaE+FulSYhTiJ8WpVu5jElU4=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23/go.mod h1:uIiFgURZbACBEQJfqTZPb/jxO7R+9LeoHUFudtIdeQI=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39/go.mod h1:WLAW8PT7+JhjZfLSWe7WEJaJu0GNo0cKc2Zyo003RBs=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.1 h1:vUh7dBFNS3oFCtVv6CiYKh5hP9ls8+kIpKLeFruIBLk=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.1/go.mod h1:sFMeinkhj/SZKQM8BxtvNtSPjJEo0Xrz+w3g2e4FSKI=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0 h1:NXi4pNJWjAaiI56P1Rl8DC9A4jMNRE00WNBsDua5WRg=
|
github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0 h1:NXi4pNJWjAaiI56P1Rl8DC9A4jMNRE00WNBsDua5WRg=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0/go.mod h1:L3ZT0N/vBsw77mOAawXmRnREpEjcHd2v5Hzf7AkIH8M=
|
github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0/go.mod h1:L3ZT0N/vBsw77mOAawXmRnREpEjcHd2v5Hzf7AkIH8M=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA=
|
github.com/aws/aws-sdk-go-v2/service/iam v1.22.2 h1:DPFxx/6Zwes/MiadlDteVqDKov7yQ5v9vuwfhZuJm1s=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8=
|
github.com/aws/aws-sdk-go-v2/service/iam v1.22.2/go.mod h1:cQTMNdo/Z5t1DDRsUnx0a2j6cPnytMBidUYZw2zks28=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 h1:CeuSeq/8FnYpPtnuIeLQEEvDv9zUjneuYi8EghMBdwQ=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13 h1:iV/W5OMBys+66OeXJi/7xIRrKZNsu0ylsLGu+6nbmQE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26/go.mod h1:2UqAAwMUXKeRkAHIlDJqvMVgOWkUi/AUXPk/YIe+Dg4=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13/go.mod h1:ReJb6xYmtGyu9KoFtRreWegbN9dZqvZIIv4vWnhcsyI=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.33 h1:QviNkc+vGSuEHx8P+pVNKOdWLXBPIwMFv7p0fphgE4U=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.33/go.mod h1:fABTUmOrAgAalG2i9WJpjBvlnk7UK8YmnYaxN+Q2CwE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 h1:e2ooMhpYGhDnBfSvIyusvAwX7KexuZaHbQY2Dyei7VU=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32 h1:dGAseBFEYxth10V23b5e2mAS+tX7oVbfYHD6dnDdAsg=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0/go.mod h1:bh2E0CXKZsQN+faiKVqC40vfNMAWheoULBCnEgO9K+8=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32/go.mod h1:4jwAWKEkCR0anWk5+1RbfSg1R5Gzld7NLiuaq5bTR/Y=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1 h1:PJH4I+qYjPXclKRbVCW47iYUvtXEh1u6YmDhn5J8VQE=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.1 h1:PT6PBCycRwhpEW5hJnRiceCeoWJ+r3bdgXtV+VKG7Pk=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1/go.mod h1:ncltU6n4Nof5uJttDtcNQ537uNuwYqsZZQcpkd2/GUQ=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.1/go.mod h1:TqoxCLwT2nrxrBGA+z7t6OWM7LBkgRckK3gOjYE+7JA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 h1:5V7DWLBd7wTELVz5bPpwzYy/sikk0gsgZfj40X+l5OI=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.2 h1:v346f1h8sUBKXnEbrv43L37MTBlFHyKXQPIZHNAaghA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.6/go.mod h1:Y1VOmit/Fn6Tz1uFAeCO6Q7M2fmfXSCLeL5INVYsLuY=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.2/go.mod h1:cwCATiyNrXK9P2FsWdZ89g9mpsYv2rhk0UA/KByl5fY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 h1:B8cauxOH1W1v7rd8RdI/MWnoR4Ze0wIHWrb90qczxj4=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.13.2 h1:A2RlEMo4SJSwbNoUUgkxTAEMduAy/8wG3eB2b2lP4gY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6/go.mod h1:Lh/bc9XUf8CfOY6Jp5aIkQtN+j1mc+nExc+KXj9jx2s=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.13.2/go.mod h1:ju+nNXUunfIFamXUIZQiICjnO/TPlOmWcYhZcSy7xaE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 h1:bWNgNdRko2x6gqa0blfATqAZKZokPIeM1vfmQt2pnvM=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2 h1:OJELEgyaT2kmaBGZ+myyZbTTLobfe3ox3FSh5eYK9Qs=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.7/go.mod h1:JuTnSoeePXmMVe9G8NcjjwgOKEfZ4cOjMuT2IBT/2eI=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2/go.mod h1:ubDBBaDFs1GHijSOTi8ljppML15GLG0HxhILtbjNNYQ=
|
||||||
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.21.2 h1:ympg1+Lnq33XLhcK/xTG4yZHPs1Oyxu+6DEWbl7qOzA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.21.2/go.mod h1:FQ/DQcOfESELfJi5ED+IPPAjI5xC6nxtSolVVB773jM=
|
||||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||||
|
github.com/aws/smithy-go v1.14.1 h1:EFKMUmH/iHMqLiwoEDx2rRjRQpI1YCn5jTysoaDujFs=
|
||||||
|
github.com/aws/smithy-go v1.14.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||||
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
|
||||||
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
|
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
|
||||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/boundary/sdk v0.0.33 h1:kHaVZ3b9j1QvYlG+vhAgrbXh8/+Rq2qlUIs8fKMQH7k=
|
github.com/hashicorp/boundary/sdk v0.0.41-0.20231117205527-08045ddb050e h1:PzZklmCjnaqQq9JdZRI9SVPSd9xo0iXLlmOHoQX68aM=
|
||||||
github.com/hashicorp/boundary/sdk v0.0.33/go.mod h1:jn9j5mM8v2pOk8aLeJNIszdm7WBf4gRSP68iy8Iu5Q0=
|
github.com/hashicorp/boundary/sdk v0.0.41-0.20231117205527-08045ddb050e/go.mod h1:+XTDYf9YNeKIbGOPJwy7hlO2Le4zgzCtHCG/u+z4THI=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/eventlogger v0.1.2-0.20230428153751-cca445805f24 h1:vtCJgcxwDMFrpUgVMfjtwXY1YqvHbj8pNA7GsXYEoLg=
|
github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0 h1:f9oX8/3zxiQrfrWnBeyjDm4S02GAU02OBtCRoZOUwlo=
|
||||||
github.com/hashicorp/eventlogger v0.1.2-0.20230428153751-cca445805f24/go.mod h1://CHt6/j+Q2lc0NlUB5af4aS2M0c0aVBg9/JfcpAyhM=
|
github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0/go.mod h1://CHt6/j+Q2lc0NlUB5af4aS2M0c0aVBg9/JfcpAyhM=
|
||||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20230428153751-cca445805f24 h1:mopKPTxSaVr24uSysyRwywTDs85L5b05qqZvEkWa0qc=
|
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0 h1:iAb287bq0TaWTnhDYuN/zVqdD2EwanQg9ncVelC60Xc=
|
||||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20230428153751-cca445805f24/go.mod h1:EQPLoX6CONA9BSYUovTQBHfPGE91g7wOxv03sO29FzY=
|
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0/go.mod h1:tMywUTIvdB/FXhwm6HMTt61C8/eODY6gitCHhXtyojg=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||||
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.4 h1:8z+Zi0uLYXONkxwWb/D7O5t1IUucB6Ft3uvSOa950bM=
|
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.5 h1:jrnDfQm2hCQ0/hEselgqzV4fK16gpZoY0OWGZpVPNHM=
|
||||||
github.com/hashicorp/go-kms-wrapping/v2 v2.0.9 h1:JpCvi97NMA+saNqO8ovQcGoRbBq6P5ZZlJqvOsW5ick=
|
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.5/go.mod h1:psh1qKep5ukvuNobFY/hCybuudlkkACpmazOsCgX5Rg=
|
||||||
github.com/hashicorp/go-kms-wrapping/v2 v2.0.9/go.mod h1:NtMaPhqSlfQ72XWDD2g80o8HI8RKkowIB8/WZHMyPY4=
|
github.com/hashicorp/go-kms-wrapping/v2 v2.0.14 h1:1ZuhfnZgRnLK8S0KovJkoTCRIQId5pv3sDR7pG5VQBw=
|
||||||
|
github.com/hashicorp/go-kms-wrapping/v2 v2.0.14/go.mod h1:0dWtzl2ilqKpavgM3id/kFK9L3tjo6fS4OhbVPSYpnQ=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-plugin v1.4.9 h1:ESiK220/qE0aGxWdzKIvRH69iLiuN/PjoLTm69RoWtU=
|
github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y=
|
||||||
github.com/hashicorp/go-secure-stdlib/awsutil v0.2.2 h1:kWg2vyKl7BRXrNxYziqDJ55n+vtOQ1QsGORjzoeB+uM=
|
github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4=
|
||||||
github.com/hashicorp/go-secure-stdlib/awsutil v0.2.2/go.mod h1:oKHSQs4ivIfZ3fbXGQOop1XuDfdSb8RIsWTGaAanSfg=
|
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.0.0 h1:ca5TSI4AgaOncPpyzLDtCGjVEtKukONpeM95vFxXCOQ=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.0.0/go.mod h1:7CUvZtfTp2U0CYQCLzMtS2ngckjAZePSfwrE2aeDP1M=
|
||||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
|
||||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng=
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng=
|
||||||
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.9 h1:YZ01/aiARPneDUryzjiPGvr3pmjCSC+1WBZKI1P7I7k=
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
|
||||||
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.6 h1:rOmnqOSUBn5opme1Kh9OXO8qWd0KztO+f02vvCwCc/w=
|
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11 h1:uPW2Wn0YlmI9RGSkZpcIplnVRwJ7BCiGpk1vnF2TMw4=
|
||||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs=
|
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11/go.mod h1:uis9dCmOzXuOaRyXq+1Foh31kcvXKoWogjNnhfjHfW8=
|
||||||
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.4 h1:IU2iGcvthrJ53rPbQU6B4+iVs/cLPO89ti5dd2XEj3k=
|
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.9 h1:0S0ctJ7Ra8O7ap+/3fZUnzJ3VzJyirWS/WnNCuOYtZY=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.9/go.mod h1:TNNdgtjLgVDbrgFcyCKrlAicIl3dZF94swJltyGUX2M=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8/go.mod h1:aiJI+PIApBRQG7FZTEBx5GiiX+HbOHilUdNxUZi4eV0=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.6 h1:ZYv2XA+tEfFXIToR2jmBgVqQU9gERt0APbWqmUoNGnY=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.6/go.mod h1:ggFN8dlaLWS2R1gymBbCrvXM/bkZP7hEAa4seqDwhyg=
|
||||||
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI=
|
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0=
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||||
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2 h1:phcbL8urUzF/kxA/Oj6awENaRwfWsjP59GW7u2qlDyY=
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3 h1:xbrxd0U9XQW8qL1BAz2XrAjAF/P2vcqUTAues9c24B8=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3/go.mod h1:LWq2Sy8UoKKuK4lFuCNWSjJj57MhNNf2zzBWMtkAIX4=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
|
||||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-version v1.5.0 h1:O293SZ2Eg+AAYijkVK3jR786Am1bhDEh2GHT0tIVE5E=
|
github.com/hashicorp/go-version v1.5.0 h1:O293SZ2Eg+AAYijkVK3jR786Am1bhDEh2GHT0tIVE5E=
|
||||||
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s=
|
github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s=
|
||||||
github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM=
|
github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM=
|
||||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||||
|
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
||||||
|
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||||
|
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||||
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f h1:E87tDTVS5W65euzixn7clSzK66puSt1H4I5SC0EmHH4=
|
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f h1:E87tDTVS5W65euzixn7clSzK66puSt1H4I5SC0EmHH4=
|
||||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f/go.mod h1:3J2qVK16Lq8V+wfiL2lPeDZ7UWMxk5LemerHa1p6N00=
|
||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
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 v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
@@ -139,12 +160,14 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng=
|
github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng=
|
||||||
|
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
@@ -153,31 +176,28 @@ github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8oh
|
|||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||||
github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0=
|
github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0=
|
||||||
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
|
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
|
||||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||||
|
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||||
|
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||||
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||||
@@ -186,16 +206,16 @@ github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q
|
|||||||
github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0=
|
github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0=
|
||||||
github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||||
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
|
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
|
||||||
go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo=
|
|
||||||
go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
|
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||||
|
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
@@ -204,12 +224,11 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -224,41 +243,42 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
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=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 h1:QQF+HdiI4iocoxUjjpLgvTYDHKm99C/VtTBFnfiCJos=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
|
||||||
google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
|
||||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws/endpoints"
|
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/errors"
|
"github.com/hashicorp/boundary-plugin-aws/internal/errors"
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/values"
|
"github.com/hashicorp/boundary-plugin-aws/internal/values"
|
||||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -68,25 +68,45 @@ func GetCredentialsConfig(secrets *structpb.Struct, attrs *CredentialAttributes,
|
|||||||
badFields[fmt.Sprintf("secrets.%s", s)] = "unrecognized field"
|
badFields[fmt.Sprintf("secrets.%s", s)] = "unrecognized field"
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(badFields) > 0 {
|
opts := []awsutil.Option{}
|
||||||
return nil, errors.InvalidArgumentError("Error in the secrets provided", badFields)
|
// logic for parsing the credential type
|
||||||
}
|
// supported types:
|
||||||
|
// - user static credential
|
||||||
opts := []awsutil.Option{
|
// - ec2 assume role dynamic credential
|
||||||
awsutil.WithEnvironmentCredentials(true),
|
// - enviornment variables credential
|
||||||
}
|
switch {
|
||||||
if accessKey != "" && secretKey != "" {
|
// static credentials and dynamic credentials cannot be used together
|
||||||
|
case accessKey != "" && secretKey != "" && attrs.RoleArn != "":
|
||||||
|
badFields[fmt.Sprintf("secrets.%s", ConstAccessKeyId)] = "conflicts with role_arn value"
|
||||||
|
badFields[fmt.Sprintf("secrets.%s", ConstSecretAccessKey)] = "conflicts with role_arn value"
|
||||||
|
badFields[fmt.Sprintf("attributes.%s", ConstRoleArn)] = "conflicts with access_key_id and secret_access_key values"
|
||||||
|
// static credential is missing it's secret_access_key
|
||||||
|
case accessKey != "" && secretKey == "":
|
||||||
|
badFields[fmt.Sprintf("secrets.%s", ConstSecretAccessKey)] = "missing required value"
|
||||||
|
// static credential is missing it's access_key_id
|
||||||
|
case accessKey == "" && secretKey != "":
|
||||||
|
badFields[fmt.Sprintf("secrets.%s", ConstAccessKeyId)] = "missing required value"
|
||||||
|
// dynamic credentials and credential rotation is not supported
|
||||||
|
case len(attrs.RoleArn) > 0 && !attrs.DisableCredentialRotation:
|
||||||
|
badFields[fmt.Sprintf("attributes.%s", ConstDisableCredentialRotation)] = "disable_credential_rotation attribute is required when providing a role_arn"
|
||||||
|
// add static credentials
|
||||||
|
case accessKey != "" && secretKey != "":
|
||||||
opts = append(opts,
|
opts = append(opts,
|
||||||
awsutil.WithAccessKey(accessKey),
|
awsutil.WithAccessKey(accessKey),
|
||||||
awsutil.WithSecretKey(secretKey),
|
awsutil.WithSecretKey(secretKey),
|
||||||
)
|
)
|
||||||
|
// add dynamic credentials
|
||||||
|
case attrs.RoleArn != "":
|
||||||
|
opts = append(opts, awsutil.WithRoleArn(attrs.RoleArn))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(badFields) > 0 {
|
||||||
|
return nil, errors.InvalidArgumentError("Error in the secrets provided", badFields)
|
||||||
|
}
|
||||||
|
|
||||||
if attrs.Region != "" {
|
if attrs.Region != "" {
|
||||||
opts = append(opts, awsutil.WithRegion(attrs.Region))
|
opts = append(opts, awsutil.WithRegion(attrs.Region))
|
||||||
}
|
}
|
||||||
if attrs.RoleArn != "" {
|
|
||||||
opts = append(opts, awsutil.WithRoleArn(attrs.RoleArn))
|
|
||||||
}
|
|
||||||
if attrs.RoleExternalId != "" {
|
if attrs.RoleExternalId != "" {
|
||||||
opts = append(opts, awsutil.WithRoleExternalId(attrs.RoleExternalId))
|
opts = append(opts, awsutil.WithRoleExternalId(attrs.RoleExternalId))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package credential
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
@@ -118,7 +118,7 @@ func TestGetCredentialsConfig(t *testing.T) {
|
|||||||
expectedErrContains string
|
expectedErrContains string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no secrets",
|
name: "no credentials",
|
||||||
attrs: &CredentialAttributes{
|
attrs: &CredentialAttributes{
|
||||||
Region: "us-west-2",
|
Region: "us-west-2",
|
||||||
},
|
},
|
||||||
@@ -130,7 +130,7 @@ func TestGetCredentialsConfig(t *testing.T) {
|
|||||||
name: "with static credentials",
|
name: "with static credentials",
|
||||||
secrets: &structpb.Struct{
|
secrets: &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
|
||||||
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -138,7 +138,7 @@ func TestGetCredentialsConfig(t *testing.T) {
|
|||||||
Region: "us-west-2",
|
Region: "us-west-2",
|
||||||
},
|
},
|
||||||
expected: &awsutil.CredentialsConfig{
|
expected: &awsutil.CredentialsConfig{
|
||||||
AccessKey: "foobar",
|
AccessKey: "AKIAfoobar",
|
||||||
SecretKey: "bazqux",
|
SecretKey: "bazqux",
|
||||||
Region: "us-west-2",
|
Region: "us-west-2",
|
||||||
},
|
},
|
||||||
@@ -160,7 +160,7 @@ func TestGetCredentialsConfig(t *testing.T) {
|
|||||||
name: "missing secret key",
|
name: "missing secret key",
|
||||||
secrets: &structpb.Struct{
|
secrets: &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
@@ -173,7 +173,7 @@ func TestGetCredentialsConfig(t *testing.T) {
|
|||||||
name: "unknown fields",
|
name: "unknown fields",
|
||||||
secrets: &structpb.Struct{
|
secrets: &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
|
||||||
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||||
"foo": structpb.NewBoolValue(true),
|
"foo": structpb.NewBoolValue(true),
|
||||||
"bar": structpb.NewBoolValue(true),
|
"bar": structpb.NewBoolValue(true),
|
||||||
@@ -188,7 +188,7 @@ func TestGetCredentialsConfig(t *testing.T) {
|
|||||||
name: "valid ignore creds_last_rotated_time",
|
name: "valid ignore creds_last_rotated_time",
|
||||||
secrets: &structpb.Struct{
|
secrets: &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
|
||||||
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||||
ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
||||||
},
|
},
|
||||||
@@ -197,7 +197,7 @@ func TestGetCredentialsConfig(t *testing.T) {
|
|||||||
Region: "us-west-2",
|
Region: "us-west-2",
|
||||||
},
|
},
|
||||||
expected: &awsutil.CredentialsConfig{
|
expected: &awsutil.CredentialsConfig{
|
||||||
AccessKey: "foobar",
|
AccessKey: "AKIAfoobar",
|
||||||
SecretKey: "bazqux",
|
SecretKey: "bazqux",
|
||||||
Region: "us-west-2",
|
Region: "us-west-2",
|
||||||
},
|
},
|
||||||
@@ -215,6 +215,7 @@ func TestGetCredentialsConfig(t *testing.T) {
|
|||||||
RoleTags: map[string]string{
|
RoleTags: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
|
DisableCredentialRotation: true,
|
||||||
},
|
},
|
||||||
expected: &awsutil.CredentialsConfig{
|
expected: &awsutil.CredentialsConfig{
|
||||||
Region: "us-west-2",
|
Region: "us-west-2",
|
||||||
@@ -230,7 +231,7 @@ func TestGetCredentialsConfig(t *testing.T) {
|
|||||||
name: "with static credential & assume role",
|
name: "with static credential & assume role",
|
||||||
secrets: &structpb.Struct{
|
secrets: &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
|
||||||
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -243,17 +244,21 @@ func TestGetCredentialsConfig(t *testing.T) {
|
|||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: &awsutil.CredentialsConfig{
|
expectedErrContains: "attributes.role_arn: conflicts with access_key_id and secret_access_key values, secrets.access_key_id: conflicts with role_arn value, secrets.secret_access_key: conflicts with role_arn value",
|
||||||
AccessKey: "foobar",
|
},
|
||||||
SecretKey: "bazqux",
|
{
|
||||||
|
name: "with dynamic credentials and no disable credential rotation",
|
||||||
|
secrets: &structpb.Struct{Fields: map[string]*structpb.Value{}},
|
||||||
|
attrs: &CredentialAttributes{
|
||||||
Region: "us-west-2",
|
Region: "us-west-2",
|
||||||
RoleARN: "arn:aws:iam::123456789012:role/S3Access",
|
RoleArn: "arn:aws:iam::123456789012:role/S3Access",
|
||||||
RoleExternalId: "1234567890",
|
RoleExternalId: "1234567890",
|
||||||
RoleSessionName: "test-session",
|
RoleSessionName: "test-session",
|
||||||
RoleTags: map[string]string{
|
RoleTags: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
expectedErrContains: "attributes.disable_credential_rotation: disable_credential_rotation attribute is required when providing a role_arn",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,19 +4,41 @@
|
|||||||
package credential
|
package credential
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
iamTypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/values"
|
"github.com/hashicorp/boundary-plugin-aws/internal/values"
|
||||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CredentialType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// StaticAWS denotes an Access Key Id that begins with "AKIA". These are
|
||||||
|
// long-term access keys, provided by AWS, for an IAM user or an AWS account
|
||||||
|
// root user.
|
||||||
|
// https://docs.aws.amazon.com/IAM/latest/UserGuide/security-creds.html#sec-access-keys-and-secret-access-keys
|
||||||
|
StaticAWS CredentialType = iota
|
||||||
|
|
||||||
|
// DynamicAWS denotes the presence of a RoleARN, or an Access Key Id that
|
||||||
|
// begins with "ASIA". The latter are temporary credentials access keys that
|
||||||
|
// are created using AWS STS operations.
|
||||||
|
DynamicAWS
|
||||||
|
|
||||||
|
// StaticOther denotes the presence of an Access Key Id that does not follow
|
||||||
|
// the AKIA/ASIA convention outlined by AWS.
|
||||||
|
StaticOther
|
||||||
|
|
||||||
|
// Unknown is a catch-all for everything else.
|
||||||
|
Unknown
|
||||||
|
)
|
||||||
|
|
||||||
// rotationWaitTimeout controls the time we wait for credential rotation to
|
// rotationWaitTimeout controls the time we wait for credential rotation to
|
||||||
// succeed. This is important to ensure that rotated credentials can be used
|
// succeed. This is important to ensure that rotated credentials can be used
|
||||||
// right away.
|
// right away.
|
||||||
@@ -85,11 +107,11 @@ func NewAwsCredentialPersistedState(opts ...AwsCredentialPersistedStateOption) (
|
|||||||
// ValidateCreds takes the credentials configuration from the persisted state
|
// ValidateCreds takes the credentials configuration from the persisted state
|
||||||
// and runs sts.GetCallerIdentity for the current credentials, which is done
|
// and runs sts.GetCallerIdentity for the current credentials, which is done
|
||||||
// to check that the credentials are valid.
|
// to check that the credentials are valid.
|
||||||
func (s *AwsCredentialPersistedState) ValidateCreds() error {
|
func (s *AwsCredentialPersistedState) ValidateCreds(ctx context.Context) error {
|
||||||
if s.CredentialsConfig == nil {
|
if s.CredentialsConfig == nil {
|
||||||
return errors.New("missing credentials config")
|
return errors.New("missing credentials config")
|
||||||
}
|
}
|
||||||
if _, err := s.CredentialsConfig.GetCallerIdentity(s.testOpts...); err != nil {
|
if _, err := s.CredentialsConfig.GetCallerIdentity(ctx, s.testOpts...); err != nil {
|
||||||
return fmt.Errorf("error validating credentials: %w", err)
|
return fmt.Errorf("error validating credentials: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -100,11 +122,14 @@ func (s *AwsCredentialPersistedState) ValidateCreds() error {
|
|||||||
// written into the credentials config and the persisted state. On any error, the old credentials are not overwritten.
|
// written into the credentials config and the persisted state. On any error, the old credentials are not overwritten.
|
||||||
// This ensures that any generated new secret key never leaves this function in case of an error, even though it will
|
// This ensures that any generated new secret key never leaves this function in case of an error, even though it will
|
||||||
// still result in an extraneous access key existing.
|
// still result in an extraneous access key existing.
|
||||||
func (s *AwsCredentialPersistedState) RotateCreds() error {
|
func (s *AwsCredentialPersistedState) RotateCreds(ctx context.Context) error {
|
||||||
if s.CredentialsConfig == nil {
|
if s.CredentialsConfig == nil {
|
||||||
return errors.New("missing credentials config")
|
return errors.New("missing credentials config")
|
||||||
}
|
}
|
||||||
if err := s.CredentialsConfig.RotateKeys(append([]awsutil.Option{
|
if GetCredentialType(s.CredentialsConfig) != StaticAWS {
|
||||||
|
return errors.New("invalid credential type")
|
||||||
|
}
|
||||||
|
if err := s.CredentialsConfig.RotateKeys(ctx, append([]awsutil.Option{
|
||||||
awsutil.WithValidityCheckTimeout(rotationWaitTimeout),
|
awsutil.WithValidityCheckTimeout(rotationWaitTimeout),
|
||||||
}, s.testOpts...)...); err != nil {
|
}, s.testOpts...)...); err != nil {
|
||||||
return fmt.Errorf("error rotating credentials: %w", err)
|
return fmt.Errorf("error rotating credentials: %w", err)
|
||||||
@@ -116,7 +141,7 @@ func (s *AwsCredentialPersistedState) RotateCreds() error {
|
|||||||
// ReplaceCreds replaces the access key in the state with a new key.
|
// ReplaceCreds replaces the access key in the state with a new key.
|
||||||
// If the existing key was rotated at any point in time, it is
|
// If the existing key was rotated at any point in time, it is
|
||||||
// deleted first, otherwise it's left alone.
|
// deleted first, otherwise it's left alone.
|
||||||
func (s *AwsCredentialPersistedState) ReplaceCreds(credentialsConfig *awsutil.CredentialsConfig) error {
|
func (s *AwsCredentialPersistedState) ReplaceCreds(ctx context.Context, credentialsConfig *awsutil.CredentialsConfig) error {
|
||||||
if credentialsConfig == nil {
|
if credentialsConfig == nil {
|
||||||
return errors.New("missing new credentials config")
|
return errors.New("missing new credentials config")
|
||||||
}
|
}
|
||||||
@@ -124,11 +149,11 @@ func (s *AwsCredentialPersistedState) ReplaceCreds(credentialsConfig *awsutil.Cr
|
|||||||
return errors.New("missing credentials config")
|
return errors.New("missing credentials config")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.CredsLastRotatedTime.IsZero() {
|
// Delete old/existing credentials. This action is only possible for static credential types.
|
||||||
// Delete old/existing credentials. This is done with the same
|
// This is done with the same credentials to ensure that it has the proper permissions to do it.
|
||||||
// credentials to ensure that it has the proper permissions to do
|
ct := GetCredentialType(s.CredentialsConfig)
|
||||||
// it.
|
if !s.CredsLastRotatedTime.IsZero() && ct == StaticAWS {
|
||||||
if err := s.DeleteCreds(); err != nil {
|
if err := s.DeleteCreds(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,21 +167,22 @@ func (s *AwsCredentialPersistedState) ReplaceCreds(credentialsConfig *awsutil.Cr
|
|||||||
// DeleteCreds deletes the credentials in the state. The access key
|
// DeleteCreds deletes the credentials in the state. The access key
|
||||||
// ID, secret access key, and rotation time fields are zeroed out in
|
// ID, secret access key, and rotation time fields are zeroed out in
|
||||||
// the state just to ensure that they cannot be re-used after.
|
// the state just to ensure that they cannot be re-used after.
|
||||||
func (s *AwsCredentialPersistedState) DeleteCreds() error {
|
func (s *AwsCredentialPersistedState) DeleteCreds(ctx context.Context) error {
|
||||||
if s.CredentialsConfig == nil {
|
if s.CredentialsConfig == nil {
|
||||||
return errors.New("missing credentials config")
|
return errors.New("missing credentials config")
|
||||||
}
|
}
|
||||||
|
if GetCredentialType(s.CredentialsConfig) != StaticAWS {
|
||||||
|
return errors.New("invalid credential type")
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.CredentialsConfig.DeleteAccessKey(s.CredentialsConfig.AccessKey, s.testOpts...); err != nil {
|
if err := s.CredentialsConfig.DeleteAccessKey(ctx, s.CredentialsConfig.AccessKey, s.testOpts...); err != nil {
|
||||||
// Determine if the deletion error was due to a missing
|
// Determine if the deletion error was due to a missing
|
||||||
// resource. If it was, just pass it.
|
// resource. If it was, just pass it.
|
||||||
var awsErr awserr.Error
|
var awsErr *iamTypes.NoSuchEntityException
|
||||||
if errors.As(err, &awsErr) {
|
if errors.As(err, &awsErr) && awsErr.ErrorCode() == "NoSuchEntity" {
|
||||||
if awsErr.Code() == iam.ErrCodeNoSuchEntityException {
|
s.CredentialsConfig = nil
|
||||||
s.CredentialsConfig = nil
|
s.CredsLastRotatedTime = time.Time{}
|
||||||
s.CredsLastRotatedTime = time.Time{}
|
return nil
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise treat it as an actual error.
|
// Otherwise treat it as an actual error.
|
||||||
@@ -168,10 +194,15 @@ func (s *AwsCredentialPersistedState) DeleteCreds() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSession returns a configured AWS session for the credentials in
|
// GenerateCredentialChain returns a AWS configuration for the credentials in the state.
|
||||||
// the state.
|
func (s *AwsCredentialPersistedState) GenerateCredentialChain(ctx context.Context) (*aws.Config, error) {
|
||||||
func (s *AwsCredentialPersistedState) GetSession() (*session.Session, error) {
|
// Default `WithSharedCredentials` is `true` - This disables the ability to
|
||||||
return s.CredentialsConfig.GetSession(s.testOpts...)
|
// read credentials from environment variables because we set the default
|
||||||
|
// profile options, so we need to set it to false (see
|
||||||
|
// awsutil@v2.(*CredentialsConfig).generateAwsConfigOptions) for extra
|
||||||
|
// details.
|
||||||
|
opts := append(s.testOpts, awsutil.WithSharedCredentials(false))
|
||||||
|
return s.CredentialsConfig.GenerateCredentialChain(ctx, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToMap returns a map of the credentials stored in the persisted state.
|
// ToMap returns a map of the credentials stored in the persisted state.
|
||||||
@@ -179,7 +210,15 @@ func (s *AwsCredentialPersistedState) GetSession() (*session.Session, error) {
|
|||||||
// return a map for long-term credentials with following keys:
|
// return a map for long-term credentials with following keys:
|
||||||
// access_key_id, secret_access_key & creds_last_rotated_time
|
// access_key_id, secret_access_key & creds_last_rotated_time
|
||||||
func (s *AwsCredentialPersistedState) ToMap() map[string]any {
|
func (s *AwsCredentialPersistedState) ToMap() map[string]any {
|
||||||
if HasDynamicCredentials(s.CredentialsConfig.AccessKey) {
|
ct := GetCredentialType(s.CredentialsConfig)
|
||||||
|
// Dynamic AWS credentials are temporary credentials that expire within an hour.
|
||||||
|
// ToMap() returns an empty map here so that Boundary does not store temporary
|
||||||
|
// credentials into the database.
|
||||||
|
//
|
||||||
|
// Uknown credentials are not aws compatiable secrets that may have been provided
|
||||||
|
// for a non-aws s3 compatiable service. These credentials will not be returned
|
||||||
|
// because the plugin does not know how to manage non-aws credentials.
|
||||||
|
if ct == DynamicAWS || ct == Unknown {
|
||||||
return map[string]any{}
|
return map[string]any{}
|
||||||
}
|
}
|
||||||
return map[string]any{
|
return map[string]any{
|
||||||
@@ -256,18 +295,22 @@ func AwsCredentialPersistedStateFromProto(secrets *structpb.Struct, attrs *Crede
|
|||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasDynamicCredentials returns true if the access key is a dynamic credential type.
|
// GetCredentialType returns the credential type based on the given
|
||||||
//
|
// AccessKey/RoleARN. See CredentialType definition for more information.
|
||||||
// https://docs.aws.amazon.com/IAM/latest/UserGuide/security-creds.html#sec-access-keys-and-secret-access-keys
|
func GetCredentialType(cc *awsutil.CredentialsConfig) CredentialType {
|
||||||
// Access key IDs beginning with ASIA are temporary credentials access keys that you create using AWS STS operations.
|
if cc == nil {
|
||||||
func HasDynamicCredentials(accessKey string) bool {
|
return Unknown
|
||||||
return strings.HasPrefix(accessKey, "ASIA")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// HasStaticCredentials returns true if the access key is a static credential type.
|
if len(cc.RoleARN) > 0 || strings.HasPrefix(cc.AccessKey, "ASIA") {
|
||||||
//
|
return DynamicAWS
|
||||||
// https://docs.aws.amazon.com/IAM/latest/UserGuide/security-creds.html#sec-access-keys-and-secret-access-keys
|
}
|
||||||
// Access key IDs beginning with AKIA are long-term access keys for an IAM user or an AWS account root user.
|
if strings.HasPrefix(cc.AccessKey, "AKIA") {
|
||||||
func HasStaticCredentials(accessKey string) bool {
|
return StaticAWS
|
||||||
return strings.HasPrefix(accessKey, "AKIA")
|
}
|
||||||
|
if len(cc.AccessKey) > 0 {
|
||||||
|
return StaticOther
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unknown
|
||||||
}
|
}
|
||||||
@@ -4,15 +4,16 @@
|
|||||||
package credential
|
package credential
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go-v2/service/iam"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
iamTypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
|
||||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
)
|
)
|
||||||
@@ -383,7 +384,7 @@ func TestAwsCatalogPersistedState_ValidateCreds(t *testing.T) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
err := tc.in.ValidateCreds()
|
err := tc.in.ValidateCreds(context.Background())
|
||||||
if tc.expectedErr != "" {
|
if tc.expectedErr != "" {
|
||||||
require.EqualError(err, tc.expectedErr)
|
require.EqualError(err, tc.expectedErr)
|
||||||
return
|
return
|
||||||
@@ -407,11 +408,33 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
|
|||||||
in: &AwsCredentialPersistedState{},
|
in: &AwsCredentialPersistedState{},
|
||||||
expectedErr: "missing credentials config",
|
expectedErr: "missing credentials config",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "cannot delete dynamic credential type",
|
||||||
|
in: &AwsCredentialPersistedState{
|
||||||
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
|
AccessKey: "ASIAfoobar",
|
||||||
|
SecretKey: "bazqux",
|
||||||
|
},
|
||||||
|
CredsLastRotatedTime: time.Now(),
|
||||||
|
},
|
||||||
|
expectedErr: "invalid credential type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cannot delete unknown credential type",
|
||||||
|
in: &AwsCredentialPersistedState{
|
||||||
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
|
AccessKey: "DNEfoobar",
|
||||||
|
SecretKey: "bazqux",
|
||||||
|
},
|
||||||
|
CredsLastRotatedTime: time.Now(),
|
||||||
|
},
|
||||||
|
expectedErr: "invalid credential type",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "rotation error",
|
name: "rotation error",
|
||||||
in: &AwsCredentialPersistedState{
|
in: &AwsCredentialPersistedState{
|
||||||
CredentialsConfig: &awsutil.CredentialsConfig{
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
AccessKey: "foobar",
|
AccessKey: "AKIAfoobar",
|
||||||
SecretKey: "bazqux",
|
SecretKey: "bazqux",
|
||||||
Region: "us-west-2",
|
Region: "us-west-2",
|
||||||
},
|
},
|
||||||
@@ -423,13 +446,13 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedErr: fmt.Sprintf("error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr),
|
expectedErr: fmt.Sprintf("error rotating credentials: error calling CreateAccessKey: error calling iam.GetUser: %s", testGetUserErr),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "good",
|
name: "good",
|
||||||
in: &AwsCredentialPersistedState{
|
in: &AwsCredentialPersistedState{
|
||||||
CredentialsConfig: &awsutil.CredentialsConfig{
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
AccessKey: "foobar",
|
AccessKey: "AKIAfoobar",
|
||||||
SecretKey: "bazqux",
|
SecretKey: "bazqux",
|
||||||
Region: "us-west-2",
|
Region: "us-west-2",
|
||||||
},
|
},
|
||||||
@@ -441,7 +464,7 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
|
|||||||
awsutil.NewMockIAM(
|
awsutil.NewMockIAM(
|
||||||
awsutil.WithGetUserOutput(
|
awsutil.WithGetUserOutput(
|
||||||
&iam.GetUserOutput{
|
&iam.GetUserOutput{
|
||||||
User: &iam.User{
|
User: &iamTypes.User{
|
||||||
Arn: aws.String("arn:aws:iam::123456789012:user/JohnDoe"),
|
Arn: aws.String("arn:aws:iam::123456789012:user/JohnDoe"),
|
||||||
UserId: aws.String("AIDAJQABLZS4A3QDU576Q"),
|
UserId: aws.String("AIDAJQABLZS4A3QDU576Q"),
|
||||||
UserName: aws.String("JohnDoe"),
|
UserName: aws.String("JohnDoe"),
|
||||||
@@ -450,7 +473,7 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
|
|||||||
),
|
),
|
||||||
awsutil.WithCreateAccessKeyOutput(
|
awsutil.WithCreateAccessKeyOutput(
|
||||||
&iam.CreateAccessKeyOutput{
|
&iam.CreateAccessKeyOutput{
|
||||||
AccessKey: &iam.AccessKey{
|
AccessKey: &iamTypes.AccessKey{
|
||||||
AccessKeyId: aws.String("one"),
|
AccessKeyId: aws.String("one"),
|
||||||
SecretAccessKey: aws.String("two"),
|
SecretAccessKey: aws.String("two"),
|
||||||
UserName: aws.String("JohnDoe"),
|
UserName: aws.String("JohnDoe"),
|
||||||
@@ -474,7 +497,7 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
err := tc.in.RotateCreds()
|
err := tc.in.RotateCreds(context.Background())
|
||||||
if tc.expectedErr != "" {
|
if tc.expectedErr != "" {
|
||||||
require.EqualError(err, tc.expectedErr)
|
require.EqualError(err, tc.expectedErr)
|
||||||
return
|
return
|
||||||
@@ -514,7 +537,7 @@ func TestAwsCatalogPersistedState_ReplaceCreds(t *testing.T) {
|
|||||||
name: "deletion error",
|
name: "deletion error",
|
||||||
in: &AwsCredentialPersistedState{
|
in: &AwsCredentialPersistedState{
|
||||||
CredentialsConfig: &awsutil.CredentialsConfig{
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
AccessKey: "foobar",
|
AccessKey: "AKIAfoobar",
|
||||||
SecretKey: "bazqux",
|
SecretKey: "bazqux",
|
||||||
},
|
},
|
||||||
CredsLastRotatedTime: time.Now(),
|
CredsLastRotatedTime: time.Now(),
|
||||||
@@ -533,7 +556,7 @@ func TestAwsCatalogPersistedState_ReplaceCreds(t *testing.T) {
|
|||||||
name: "good with delete of old rotated",
|
name: "good with delete of old rotated",
|
||||||
in: &AwsCredentialPersistedState{
|
in: &AwsCredentialPersistedState{
|
||||||
CredentialsConfig: &awsutil.CredentialsConfig{
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
AccessKey: "foobar",
|
AccessKey: "AKIAfoobar",
|
||||||
SecretKey: "bazqux",
|
SecretKey: "bazqux",
|
||||||
},
|
},
|
||||||
CredsLastRotatedTime: time.Now(),
|
CredsLastRotatedTime: time.Now(),
|
||||||
@@ -557,7 +580,7 @@ func TestAwsCatalogPersistedState_ReplaceCreds(t *testing.T) {
|
|||||||
name: "good without delete of old rotated",
|
name: "good without delete of old rotated",
|
||||||
in: &AwsCredentialPersistedState{
|
in: &AwsCredentialPersistedState{
|
||||||
CredentialsConfig: &awsutil.CredentialsConfig{
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
AccessKey: "foobar",
|
AccessKey: "AKIAfoobar",
|
||||||
SecretKey: "bazqux",
|
SecretKey: "bazqux",
|
||||||
},
|
},
|
||||||
testOpts: []awsutil.Option{
|
testOpts: []awsutil.Option{
|
||||||
@@ -583,7 +606,7 @@ func TestAwsCatalogPersistedState_ReplaceCreds(t *testing.T) {
|
|||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
state.Reset()
|
state.Reset()
|
||||||
err := tc.in.ReplaceCreds(tc.credentialConfig)
|
err := tc.in.ReplaceCreds(context.Background(), tc.credentialConfig)
|
||||||
if tc.expectedErr != "" {
|
if tc.expectedErr != "" {
|
||||||
require.EqualError(err, tc.expectedErr)
|
require.EqualError(err, tc.expectedErr)
|
||||||
return
|
return
|
||||||
@@ -609,11 +632,33 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
|
|||||||
in: &AwsCredentialPersistedState{},
|
in: &AwsCredentialPersistedState{},
|
||||||
expectedErr: "missing credentials config",
|
expectedErr: "missing credentials config",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "cannot delete dynamic credential type",
|
||||||
|
in: &AwsCredentialPersistedState{
|
||||||
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
|
AccessKey: "ASIAfoobar",
|
||||||
|
SecretKey: "bazqux",
|
||||||
|
},
|
||||||
|
CredsLastRotatedTime: time.Now(),
|
||||||
|
},
|
||||||
|
expectedErr: "invalid credential type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cannot delete unknown credential type",
|
||||||
|
in: &AwsCredentialPersistedState{
|
||||||
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
|
AccessKey: "DNEfoobar",
|
||||||
|
SecretKey: "bazqux",
|
||||||
|
},
|
||||||
|
CredsLastRotatedTime: time.Now(),
|
||||||
|
},
|
||||||
|
expectedErr: "invalid credential type",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "deletion error",
|
name: "deletion error",
|
||||||
in: &AwsCredentialPersistedState{
|
in: &AwsCredentialPersistedState{
|
||||||
CredentialsConfig: &awsutil.CredentialsConfig{
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
AccessKey: "foobar",
|
AccessKey: "AKIAfoobar",
|
||||||
SecretKey: "bazqux",
|
SecretKey: "bazqux",
|
||||||
},
|
},
|
||||||
testOpts: []awsutil.Option{
|
testOpts: []awsutil.Option{
|
||||||
@@ -630,14 +675,14 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
|
|||||||
name: "deletion error, but OK because key was just gone",
|
name: "deletion error, but OK because key was just gone",
|
||||||
in: &AwsCredentialPersistedState{
|
in: &AwsCredentialPersistedState{
|
||||||
CredentialsConfig: &awsutil.CredentialsConfig{
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
AccessKey: "foobar",
|
AccessKey: "AKIAfoobar",
|
||||||
SecretKey: "bazqux",
|
SecretKey: "bazqux",
|
||||||
},
|
},
|
||||||
testOpts: []awsutil.Option{
|
testOpts: []awsutil.Option{
|
||||||
awsutil.WithIAMAPIFunc(
|
awsutil.WithIAMAPIFunc(
|
||||||
awsutil.NewMockIAM(
|
awsutil.NewMockIAM(
|
||||||
awsutil.WithDeleteAccessKeyError(
|
awsutil.WithDeleteAccessKeyError(
|
||||||
awserr.New(iam.ErrCodeNoSuchEntityException, "", nil),
|
&iamTypes.NoSuchEntityException{},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -648,7 +693,7 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
|
|||||||
name: "good",
|
name: "good",
|
||||||
in: &AwsCredentialPersistedState{
|
in: &AwsCredentialPersistedState{
|
||||||
CredentialsConfig: &awsutil.CredentialsConfig{
|
CredentialsConfig: &awsutil.CredentialsConfig{
|
||||||
AccessKey: "foobar",
|
AccessKey: "AKIAfoobar",
|
||||||
SecretKey: "bazqux",
|
SecretKey: "bazqux",
|
||||||
},
|
},
|
||||||
testOpts: []awsutil.Option{
|
testOpts: []awsutil.Option{
|
||||||
@@ -664,7 +709,7 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
err := tc.in.DeleteCreds()
|
err := tc.in.DeleteCreds(context.Background())
|
||||||
if tc.expectedErr != "" {
|
if tc.expectedErr != "" {
|
||||||
require.EqualError(err, tc.expectedErr)
|
require.EqualError(err, tc.expectedErr)
|
||||||
return
|
return
|
||||||
@@ -677,7 +722,7 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAwsCatalogPersistedState_GetSession(t *testing.T) {
|
func TestAwsCatalogPersistedState_GenerateCredentialChain(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -693,7 +738,7 @@ func TestAwsCatalogPersistedState_GetSession(t *testing.T) {
|
|||||||
},
|
},
|
||||||
testOpts: []awsutil.Option{awsutil.MockOptionErr(errors.New(testOptionErr))},
|
testOpts: []awsutil.Option{awsutil.MockOptionErr(errors.New(testOptionErr))},
|
||||||
},
|
},
|
||||||
expectedErr: fmt.Sprintf("error reading options in GetSession: %s", testOptionErr),
|
expectedErr: fmt.Sprintf("error reading options in GenerateCredentialChain: %s", testOptionErr),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "static credentials",
|
name: "static credentials",
|
||||||
@@ -736,7 +781,7 @@ func TestAwsCatalogPersistedState_GetSession(t *testing.T) {
|
|||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
_, err := tc.in.GetSession()
|
_, err := tc.in.GenerateCredentialChain(context.Background())
|
||||||
if tc.expectedErr != "" {
|
if tc.expectedErr != "" {
|
||||||
require.EqualError(err, tc.expectedErr)
|
require.EqualError(err, tc.expectedErr)
|
||||||
return
|
return
|
||||||
@@ -746,74 +791,53 @@ func TestAwsCatalogPersistedState_GetSession(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHasStaticCredentials(t *testing.T) {
|
func TestGetCredentialType(t *testing.T) {
|
||||||
require := require.New(t)
|
tests := []struct {
|
||||||
cases := []struct {
|
name string
|
||||||
name string
|
credConfig *awsutil.CredentialsConfig
|
||||||
accessKey string
|
expectedCredType CredentialType
|
||||||
expected bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "dynamic credential",
|
name: "nil credential config",
|
||||||
accessKey: "ASIAEXAMPLE",
|
credConfig: nil,
|
||||||
expected: false,
|
expectedCredType: Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "static credential",
|
name: "empty credential config",
|
||||||
accessKey: "AKIAEXAMPLE",
|
credConfig: &awsutil.CredentialsConfig{},
|
||||||
expected: true,
|
expectedCredType: Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "other credential",
|
name: "static aws",
|
||||||
accessKey: "EXAMPLE",
|
credConfig: &awsutil.CredentialsConfig{AccessKey: "AKIAfoobar"},
|
||||||
expected: false,
|
expectedCredType: StaticAWS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "static other",
|
||||||
accessKey: "",
|
credConfig: &awsutil.CredentialsConfig{AccessKey: "cK3kNFa24foobar"},
|
||||||
expected: false,
|
expectedCredType: StaticOther,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dynamic aws - role arn",
|
||||||
|
credConfig: &awsutil.CredentialsConfig{RoleARN: "arn:aws:iam::123456789012:role/S3Access"},
|
||||||
|
expectedCredType: DynamicAWS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dynamic aws - access key",
|
||||||
|
credConfig: &awsutil.CredentialsConfig{AccessKey: "ASIAfoobar"},
|
||||||
|
expectedCredType: DynamicAWS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dynamic aws - role arn and access key",
|
||||||
|
credConfig: &awsutil.CredentialsConfig{AccessKey: "ASIAfoobar", RoleARN: "arn:aws:iam::123456789012:role/S3Access"},
|
||||||
|
expectedCredType: DynamicAWS,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range cases {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
require.Equal(tc.expected, HasStaticCredentials(tc.accessKey))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHasDynamicCredentials(t *testing.T) {
|
for _, tc := range tests {
|
||||||
require := require.New(t)
|
|
||||||
cases := []struct {
|
|
||||||
name string
|
|
||||||
accessKey string
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "dynamic credential",
|
|
||||||
accessKey: "ASIAEXAMPLE",
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "static credential",
|
|
||||||
accessKey: "AKIAEXAMPLE",
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "other credential",
|
|
||||||
accessKey: "EXAMPLE",
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty",
|
|
||||||
accessKey: "",
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tc := range cases {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
require.Equal(tc.expected, HasDynamicCredentials(tc.accessKey))
|
ct := GetCredentialType(tc.credConfig)
|
||||||
|
require.Equal(t, tc.expectedCredType, ct)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,11 @@
|
|||||||
package credential
|
package credential
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"context"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
|
||||||
"github.com/aws/aws-sdk-go/service/iam/iamiface"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
"github.com/aws/aws-sdk-go-v2/service/iam"
|
||||||
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,25 +28,25 @@ func (s *testMockIAMState) Reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type testMockIAM struct {
|
type testMockIAM struct {
|
||||||
iamiface.IAMAPI
|
awsutil.IAMClient
|
||||||
|
|
||||||
State *testMockIAMState
|
State *testMockIAMState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *testMockIAM) DeleteAccessKey(input *iam.DeleteAccessKeyInput) (*iam.DeleteAccessKeyOutput, error) {
|
func (m *testMockIAM) DeleteAccessKey(ctx context.Context, input *iam.DeleteAccessKeyInput, opts ...func(*iam.Options)) (*iam.DeleteAccessKeyOutput, error) {
|
||||||
m.State.DeleteAccessKeyCalled = true
|
m.State.DeleteAccessKeyCalled = true
|
||||||
return m.IAMAPI.DeleteAccessKey(input)
|
return m.IAMClient.DeleteAccessKey(ctx, input, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestMockIAM(state *testMockIAMState, opts ...awsutil.MockIAMOption) awsutil.IAMAPIFunc {
|
func newTestMockIAM(state *testMockIAMState, opts ...awsutil.MockIAMOption) awsutil.IAMAPIFunc {
|
||||||
return func(sess *session.Session) (iamiface.IAMAPI, error) {
|
return func(awsConfig *aws.Config) (awsutil.IAMClient, error) {
|
||||||
m := &testMockIAM{
|
m := &testMockIAM{
|
||||||
State: state,
|
State: state,
|
||||||
}
|
}
|
||||||
f := awsutil.NewMockIAM(opts...)
|
f := awsutil.NewMockIAM(opts...)
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
m.IAMAPI, err = f(sess)
|
m.IAMClient, err = f(awsConfig)
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,15 +83,14 @@ func (p *HostPlugin) OnCreateCatalog(ctx context.Context, req *pb.OnCreateCatalo
|
|||||||
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to rotate static credentials
|
if cred.GetCredentialType(credState.CredentialsConfig) == cred.StaticAWS {
|
||||||
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) {
|
|
||||||
if !catalogAttributes.DisableCredentialRotation {
|
if !catalogAttributes.DisableCredentialRotation {
|
||||||
if err := credState.RotateCreds(); err != nil {
|
if err := credState.RotateCreds(ctx); err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Simply validate if we aren't rotating.
|
// Simply validate if we aren't rotating.
|
||||||
if err := credState.ValidateCreds(); err != nil {
|
if err := credState.ValidateCreds(ctx); err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential validation: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error during credential validation: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +159,7 @@ func (p *HostPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalo
|
|||||||
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) {
|
if cred.GetCredentialType(credState.CredentialsConfig) == cred.StaticAWS {
|
||||||
if catalogAttributes.DisableCredentialRotation && !updateSecrets {
|
if catalogAttributes.DisableCredentialRotation && !updateSecrets {
|
||||||
// This is a validate check to make sure that we aren't disabling
|
// This is a validate check to make sure that we aren't disabling
|
||||||
// rotation for credentials currently being managed by rotation.
|
// rotation for credentials currently being managed by rotation.
|
||||||
@@ -179,7 +178,7 @@ func (p *HostPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalo
|
|||||||
// Replace the credentials. This checks the timestamp on the last
|
// Replace the credentials. This checks the timestamp on the last
|
||||||
// rotation time as well and deletes the credentials if we are
|
// rotation time as well and deletes the credentials if we are
|
||||||
// managing them (ie: if we've rotated them before).
|
// managing them (ie: if we've rotated them before).
|
||||||
if err := credState.ReplaceCreds(catalogSecrets); err != nil {
|
if err := credState.ReplaceCreds(ctx, catalogSecrets); err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,7 +186,7 @@ func (p *HostPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalo
|
|||||||
if !catalogAttributes.DisableCredentialRotation && credState.CredsLastRotatedTime.IsZero() {
|
if !catalogAttributes.DisableCredentialRotation && credState.CredsLastRotatedTime.IsZero() {
|
||||||
// If we're enabling rotation now but didn't before, or have
|
// If we're enabling rotation now but didn't before, or have
|
||||||
// freshly replaced credentials, we can rotate here.
|
// freshly replaced credentials, we can rotate here.
|
||||||
if err := credState.RotateCreds(); err != nil {
|
if err := credState.RotateCreds(ctx); err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,12 +243,12 @@ func (p *HostPlugin) OnDeleteCatalog(ctx context.Context, req *pb.OnDeleteCatalo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try to delete static credentials
|
// try to delete static credentials
|
||||||
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) {
|
if cred.GetCredentialType(credState.CredentialsConfig) == cred.StaticAWS {
|
||||||
if !credState.CredsLastRotatedTime.IsZero() {
|
if !credState.CredsLastRotatedTime.IsZero() {
|
||||||
// Delete old/existing credentials. This is done with the same
|
// Delete old/existing credentials. This is done with the same
|
||||||
// credentials to ensure that it has the proper permissions to do
|
// credentials to ensure that it has the proper permissions to do
|
||||||
// it.
|
// it.
|
||||||
if err := credState.DeleteCreds(); err != nil {
|
if err := credState.DeleteCreds(ctx); err != nil {
|
||||||
return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during catalog deletion: %s", err)
|
return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during catalog deletion: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,11 +331,7 @@ func (p *HostPlugin) OnCreateSet(ctx context.Context, req *pb.OnCreateSetRequest
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := []ec2Option{}
|
ec2Client, err := catalogState.EC2Client(ctx)
|
||||||
if catalogAttributes.Region != "" {
|
|
||||||
opts = append(opts, WithRegion(catalogAttributes.Region))
|
|
||||||
}
|
|
||||||
ec2Client, err := catalogState.EC2Client(ctx, opts...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
|
||||||
}
|
}
|
||||||
@@ -409,11 +404,7 @@ func (p *HostPlugin) OnUpdateSet(ctx context.Context, req *pb.OnUpdateSetRequest
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := []ec2Option{}
|
ec2Client, err := catalogState.EC2Client(ctx)
|
||||||
if catalogAttributes.Region != "" {
|
|
||||||
opts = append(opts, WithRegion(catalogAttributes.Region))
|
|
||||||
}
|
|
||||||
ec2Client, err := catalogState.EC2Client(ctx, opts...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
|
||||||
}
|
}
|
||||||
@@ -516,11 +507,7 @@ func (p *HostPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := []ec2Option{}
|
ec2Client, err := catalogState.EC2Client(ctx)
|
||||||
if catalogAttributes.Region != "" {
|
|
||||||
opts = append(opts, WithRegion(catalogAttributes.Region))
|
|
||||||
}
|
|
||||||
ec2Client, err := catalogState.EC2Client(ctx, opts...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
|
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
|
||||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets"
|
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets"
|
||||||
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
@@ -153,7 +153,7 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr),
|
expectedErrContains: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling iam.GetUser: %s", testGetUserErr),
|
||||||
expectedErrCode: codes.InvalidArgument,
|
expectedErrCode: codes.InvalidArgument,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -411,7 +411,7 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr),
|
expectedErrContains: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling iam.GetUser: %s", testGetUserErr),
|
||||||
expectedErrCode: codes.InvalidArgument,
|
expectedErrCode: codes.InvalidArgument,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go-v2/config"
|
|
||||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
|
||||||
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
||||||
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||||
@@ -88,75 +86,16 @@ func (s *awsCatalogPersistedState) toProto() (*pb.HostCatalogPersisted, error) {
|
|||||||
|
|
||||||
// EC2Client returns a configured EC2 client based on the session
|
// EC2Client returns a configured EC2 client based on the session
|
||||||
// information stored in the state.
|
// information stored in the state.
|
||||||
func (s *awsCatalogPersistedState) EC2Client(ctx context.Context, opt ...ec2Option) (EC2API, error) {
|
func (s *awsCatalogPersistedState) EC2Client(ctx context.Context) (EC2API, error) {
|
||||||
opts, err := getOpts(opt...)
|
awsCfg, err := s.GenerateCredentialChain(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing options when fetching EC2 client: %w", err)
|
return nil, fmt.Errorf("error getting AWS configuration when fetching EC2 client: %w", err)
|
||||||
}
|
}
|
||||||
|
if awsCfg == nil {
|
||||||
awsConfigOpts := []func(*config.LoadOptions) error{}
|
return nil, fmt.Errorf("nil aws configuration")
|
||||||
sess, err := s.GetSession()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error getting AWS session when fetching EC2 client: %w", err)
|
|
||||||
}
|
}
|
||||||
creds, err := sess.Config.Credentials.Get()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error getting AWS session when fetching EC2 client: %w", err)
|
|
||||||
}
|
|
||||||
customCredentials := config.WithCredentialsProvider(
|
|
||||||
credentials.NewStaticCredentialsProvider(creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken),
|
|
||||||
)
|
|
||||||
awsConfigOpts = append(awsConfigOpts, customCredentials)
|
|
||||||
if sess.Config.Region != nil {
|
|
||||||
awsConfigOpts = append(awsConfigOpts, config.WithRegion(*sess.Config.Region))
|
|
||||||
}
|
|
||||||
if opts.withRegion != "" {
|
|
||||||
awsConfigOpts = append(awsConfigOpts, config.WithRegion(opts.withRegion))
|
|
||||||
}
|
|
||||||
cfg, err := config.LoadDefaultConfig(ctx, awsConfigOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error loading aws configuration when fetching EC2 client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.testEC2APIFunc != nil {
|
if s.testEC2APIFunc != nil {
|
||||||
return s.testEC2APIFunc(cfg)
|
return s.testEC2APIFunc(*awsCfg)
|
||||||
}
|
|
||||||
|
|
||||||
cfg.HTTPClient = customClient
|
|
||||||
|
|
||||||
return ec2.NewFromConfig(cfg), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getOpts iterates the inbound ec2Options and returns a struct
|
|
||||||
func getOpts(opt ...ec2Option) (ec2Options, error) {
|
|
||||||
opts := getDefaultEC2Options()
|
|
||||||
for _, o := range opt {
|
|
||||||
if o == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := o(&opts); err != nil {
|
|
||||||
return ec2Options{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return opts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ec2Option - how ec2Options are passed as arguments
|
|
||||||
type ec2Option func(*ec2Options) error
|
|
||||||
|
|
||||||
// options = how options are represented
|
|
||||||
type ec2Options struct {
|
|
||||||
withRegion string
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultEC2Options() ec2Options {
|
|
||||||
return ec2Options{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithRegion contains the region to use
|
|
||||||
func WithRegion(with string) ec2Option {
|
|
||||||
return func(o *ec2Options) error {
|
|
||||||
o.withRegion = with
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
return ec2.NewFromConfig(*awsCfg), nil
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
||||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ func TestAwsCatalogPersistedStateEC2Client(t *testing.T) {
|
|||||||
)
|
)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
clientRaw, err := state.EC2Client(context.Background(), WithRegion("us-west-2"))
|
clientRaw, err := state.EC2Client(context.Background())
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NotNil(clientRaw)
|
require.NotNil(clientRaw)
|
||||||
client, ok := clientRaw.(*testMockEC2)
|
client, ok := clientRaw.(*testMockEC2)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go-v2/aws/retry"
|
"github.com/aws/aws-sdk-go-v2/aws/retry"
|
||||||
@@ -76,17 +77,10 @@ func (p *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCre
|
|||||||
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to rotate static credentials
|
// Try to rotate AWS static credentials
|
||||||
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) {
|
if cred.GetCredentialType(credConfig) == cred.StaticAWS && !storageAttributes.DisableCredentialRotation {
|
||||||
if !storageAttributes.DisableCredentialRotation {
|
if err := credState.RotateCreds(ctx); err != nil {
|
||||||
if err := credState.RotateCreds(); err != nil {
|
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Simply validate if we aren't rotating.
|
|
||||||
if err := credState.ValidateCreds(); err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential validation: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,76 +113,100 @@ func (p *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUpd
|
|||||||
// For storage buckets we only need to really validate what is
|
// For storage buckets we only need to really validate what is
|
||||||
// currently in the new copy of the storage bucket data, so skip
|
// currently in the new copy of the storage bucket data, so skip
|
||||||
// fetching the new copy to keep things a little less messy.
|
// fetching the new copy to keep things a little less messy.
|
||||||
bucket := req.GetNewBucket()
|
newBucket := req.GetNewBucket()
|
||||||
if bucket == nil {
|
if newBucket == nil {
|
||||||
return nil, status.Error(codes.InvalidArgument, "new bucket is required")
|
return nil, status.Error(codes.InvalidArgument, "new bucket is required")
|
||||||
}
|
}
|
||||||
if bucket.BucketName == "" {
|
if newBucket.BucketName == "" {
|
||||||
return nil, status.Error(codes.InvalidArgument, "new bucketName is required")
|
return nil, status.Error(codes.InvalidArgument, "new bucketName is required")
|
||||||
}
|
}
|
||||||
|
currentBucket := req.GetCurrentBucket()
|
||||||
var updateSecrets bool
|
if currentBucket == nil {
|
||||||
secrets := bucket.GetSecrets()
|
return nil, status.Error(codes.InvalidArgument, "current bucket is required")
|
||||||
if secrets != nil {
|
|
||||||
// We will be updating secrets this run, but what exactly that
|
|
||||||
// means will be determined later.
|
|
||||||
updateSecrets = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs := bucket.GetAttributes()
|
oldAttrs := currentBucket.GetAttributes()
|
||||||
if attrs == nil {
|
if oldAttrs == nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "current bucket attributes is required")
|
||||||
|
}
|
||||||
|
oldStorageAttributes, err := getStorageAttributes(oldAttrs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newAttrs := newBucket.GetAttributes()
|
||||||
|
if newAttrs == nil {
|
||||||
return nil, status.Error(codes.InvalidArgument, "new bucket attributes is required")
|
return nil, status.Error(codes.InvalidArgument, "new bucket attributes is required")
|
||||||
}
|
}
|
||||||
|
newStorageAttributes, err := getStorageAttributes(newAttrs)
|
||||||
storageAttributes, err := getStorageAttributes(attrs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the persisted data.
|
|
||||||
// NOTE: We might need to change this at a later time. I'm not too
|
|
||||||
// sure *exactly* what scenarios we might encounter that would
|
|
||||||
// ultimately mean that we would have to handle an empty or missing
|
|
||||||
// state in update, but what we are ultimately assuming here
|
|
||||||
// (implicitly through awsStoragePersistedStateFromProto) is that
|
|
||||||
// the state will exist and be populated. Personally I think this
|
|
||||||
// is fine and important, but this may change in the future.
|
|
||||||
credState, err := cred.AwsCredentialPersistedStateFromProto(
|
credState, err := cred.AwsCredentialPersistedStateFromProto(
|
||||||
req.GetPersisted().GetData(),
|
req.GetPersisted().GetData(),
|
||||||
storageAttributes.CredentialAttributes,
|
oldStorageAttributes.CredentialAttributes,
|
||||||
p.testCredStateOpts...)
|
p.testCredStateOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) {
|
// Verify the incoming credentials are valid and return any errors to the
|
||||||
if storageAttributes.DisableCredentialRotation && !updateSecrets {
|
// user if they're not. Note this doesn't validate the credentials against
|
||||||
// This is a validate check to make sure that we aren't disabling
|
// AWS - it only does logical validation on the fields.
|
||||||
// rotation for credentials currently being managed by rotation.
|
updatedCredentials, err := cred.GetCredentialsConfig(newBucket.GetSecrets(), newStorageAttributes.CredentialAttributes, false)
|
||||||
// This is not allowed.
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will be updating credentials when one of two changes occur:
|
||||||
|
// 1. the new bucket has secrets for static credentials, in which case we need to delete the old static credentials
|
||||||
|
// 2. the new bucket has a different roleARN value for dynamic credentials
|
||||||
|
if newBucket.GetSecrets() != nil || newStorageAttributes.RoleArn != oldStorageAttributes.RoleArn {
|
||||||
|
// Ensure the incoming credentials are valid for interaction with the S3
|
||||||
|
// Bucket before replacing them.
|
||||||
|
newCredState, err := cred.NewAwsCredentialPersistedState(
|
||||||
|
append([]cred.AwsCredentialPersistedStateOption{
|
||||||
|
cred.WithCredentialsConfig(updatedCredentials),
|
||||||
|
}, p.testCredStateOpts...)...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "error setting up new credential persisted state: %s", err)
|
||||||
|
}
|
||||||
|
newStorageState, err := newAwsStoragePersistedState(
|
||||||
|
append([]awsStoragePersistedStateOption{
|
||||||
|
withCredentials(newCredState),
|
||||||
|
}, p.testStorageStateOpts...)...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
|
||||||
|
}
|
||||||
|
if err := dryRunValidation(ctx, newStorageState, newStorageAttributes, newBucket); err != nil {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "failed to validate incoming credentials: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the existing credential state.
|
||||||
|
// This checks the timestamp on the last rotation time as well
|
||||||
|
// and deletes the credentials if we are managing them
|
||||||
|
// (ie: if we've rotated them before).
|
||||||
|
if err := credState.ReplaceCreds(ctx, updatedCredentials); err != nil {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cred.GetCredentialType(credState.CredentialsConfig) == cred.StaticAWS {
|
||||||
|
// This is a validate check to make sure that we aren't disabling
|
||||||
|
// rotation for credentials currently being managed by rotation.
|
||||||
|
// This is not allowed.
|
||||||
|
if newStorageAttributes.DisableCredentialRotation && newBucket.GetSecrets() == nil {
|
||||||
if !credState.CredsLastRotatedTime.IsZero() {
|
if !credState.CredsLastRotatedTime.IsZero() {
|
||||||
return nil, status.Error(codes.FailedPrecondition, "cannot disable rotation for already-rotated credentials")
|
return nil, status.Error(codes.FailedPrecondition, "cannot disable rotation for already-rotated credentials")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if updateSecrets {
|
// If we're enabling rotation now but didn't before, or have
|
||||||
storageSecrets, err := cred.GetCredentialsConfig(secrets, storageAttributes.CredentialAttributes, false)
|
// freshly replaced credentials, we can rotate here.
|
||||||
if err != nil {
|
if !newStorageAttributes.DisableCredentialRotation && credState.CredsLastRotatedTime.IsZero() {
|
||||||
return nil, err
|
if err := credState.RotateCreds(ctx); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
// Replace the credentials. This checks the timestamp on the last
|
|
||||||
// rotation time as well and deletes the credentials if we are
|
|
||||||
// managing them (ie: if we've rotated them before).
|
|
||||||
if err := credState.ReplaceCreds(storageSecrets); err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !storageAttributes.DisableCredentialRotation && credState.CredsLastRotatedTime.IsZero() {
|
|
||||||
// If we're enabling rotation now but didn't before, or have
|
|
||||||
// freshly replaced credentials, we can rotate here.
|
|
||||||
if err := credState.RotateCreds(); err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,7 +222,7 @@ func (p *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUpd
|
|||||||
}
|
}
|
||||||
|
|
||||||
// perform dry run to ensure we can interact with the bucket as expected.
|
// perform dry run to ensure we can interact with the bucket as expected.
|
||||||
if err := dryRunValidation(ctx, storageState, storageAttributes, bucket); err != nil {
|
if err := dryRunValidation(ctx, storageState, newStorageAttributes, newBucket); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,12 +277,12 @@ func (p *StoragePlugin) OnDeleteStorageBucket(ctx context.Context, req *pb.OnDel
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try to delete static credentials
|
// try to delete static credentials
|
||||||
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) {
|
if cred.GetCredentialType(credState.CredentialsConfig) == cred.StaticAWS {
|
||||||
if !credState.CredsLastRotatedTime.IsZero() {
|
if !credState.CredsLastRotatedTime.IsZero() {
|
||||||
// Delete old/existing credentials. This is done with the same
|
// Delete old/existing credentials. This is done with the same
|
||||||
// credentials to ensure that it has the proper permissions to do
|
// credentials to ensure that it has the proper permissions to do
|
||||||
// it.
|
// it.
|
||||||
if err := credState.DeleteCreds(); err != nil {
|
if err := credState.DeleteCreds(ctx); err != nil {
|
||||||
return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during storage bucket deletion: %s", err)
|
return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during storage bucket deletion: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,9 +341,6 @@ func (p *StoragePlugin) HeadObject(ctx context.Context, req *pb.HeadObjectReques
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts := []s3Option{}
|
opts := []s3Option{}
|
||||||
if storageAttributes.Region != "" {
|
|
||||||
opts = append(opts, WithRegion(storageAttributes.Region))
|
|
||||||
}
|
|
||||||
if storageAttributes.EndpointUrl != "" {
|
if storageAttributes.EndpointUrl != "" {
|
||||||
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
||||||
}
|
}
|
||||||
@@ -452,9 +467,6 @@ func (p *StoragePlugin) GetObject(req *pb.GetObjectRequest, stream pb.StoragePlu
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts := []s3Option{}
|
opts := []s3Option{}
|
||||||
if storageAttributes.Region != "" {
|
|
||||||
opts = append(opts, WithRegion(storageAttributes.Region))
|
|
||||||
}
|
|
||||||
if storageAttributes.EndpointUrl != "" {
|
if storageAttributes.EndpointUrl != "" {
|
||||||
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
||||||
}
|
}
|
||||||
@@ -566,9 +578,6 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts := []s3Option{}
|
opts := []s3Option{}
|
||||||
if storageAttributes.Region != "" {
|
|
||||||
opts = append(opts, WithRegion(storageAttributes.Region))
|
|
||||||
}
|
|
||||||
if storageAttributes.EndpointUrl != "" {
|
if storageAttributes.EndpointUrl != "" {
|
||||||
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
||||||
}
|
}
|
||||||
@@ -614,6 +623,129 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteObjects is used to delete one or many objects from an s3 bucket.
|
||||||
|
func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjectsRequest) (*pb.DeleteObjectsResponse, error) {
|
||||||
|
if req.GetKeyPrefix() == "" {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "key prefix is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := req.GetBucket()
|
||||||
|
if bucket == nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "bucket is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if bucket.GetBucketName() == "" {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "bucketName is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs := bucket.GetAttributes()
|
||||||
|
if attrs == nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "attributes is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
storageAttributes, err := getStorageAttributes(attrs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the creds in our persisted state.
|
||||||
|
credState, err := cred.NewAwsCredentialPersistedState(
|
||||||
|
append([]cred.AwsCredentialPersistedStateOption{
|
||||||
|
cred.WithCredentialsConfig(credConfig),
|
||||||
|
}, p.testCredStateOpts...)...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
storageState, err := newAwsStoragePersistedState(
|
||||||
|
append([]awsStoragePersistedStateOption{
|
||||||
|
withCredentials(credState),
|
||||||
|
}, p.testStorageStateOpts...)...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := []s3Option{}
|
||||||
|
if storageAttributes.EndpointUrl != "" {
|
||||||
|
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
||||||
|
}
|
||||||
|
client, err := storageState.S3Client(ctx, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "error getting S3 client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := path.Join(bucket.GetBucketPrefix(), req.GetKeyPrefix())
|
||||||
|
if strings.HasSuffix(req.GetKeyPrefix(), "/") {
|
||||||
|
// path.Join ends by "cleaning" the path, including removing a trailing slash, if
|
||||||
|
// it exists. given that a slash is used to denote a folder, it is required here.
|
||||||
|
prefix += "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !req.Recursive {
|
||||||
|
_, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||||
|
Bucket: aws.String(bucket.GetBucketName()),
|
||||||
|
Key: aws.String(prefix),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, parseAWSErrCode("error deleting S3 object", err)
|
||||||
|
}
|
||||||
|
return &pb.DeleteObjectsResponse{
|
||||||
|
ObjectsDeleted: uint32(1),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxkeys = 1000
|
||||||
|
|
||||||
|
objects := []types.ObjectIdentifier{}
|
||||||
|
var conToken *string
|
||||||
|
truncated := true
|
||||||
|
for truncated {
|
||||||
|
res, err := client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
|
||||||
|
Bucket: aws.String(bucket.GetBucketName()),
|
||||||
|
Prefix: aws.String(prefix),
|
||||||
|
MaxKeys: maxkeys,
|
||||||
|
ContinuationToken: conToken,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, parseAWSErrCode("error iterating S3 bucket contents", err)
|
||||||
|
}
|
||||||
|
truncated = res.IsTruncated
|
||||||
|
conToken = res.NextContinuationToken
|
||||||
|
for _, o := range res.Contents {
|
||||||
|
objects = append(objects, types.ObjectIdentifier{
|
||||||
|
Key: o.Key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleted := 0
|
||||||
|
|
||||||
|
for i := 0; i < len(objects); i += maxkeys {
|
||||||
|
toDelete := objects[i:min(len(objects), i+maxkeys)] // min is required to avoid an out of bounds panic
|
||||||
|
res, err := client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||||
|
Bucket: aws.String(bucket.GetBucketName()),
|
||||||
|
Delete: &types.Delete{
|
||||||
|
Objects: toDelete,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, parseAWSErrCode("error deleting S3 object(s)", err)
|
||||||
|
}
|
||||||
|
deleted += len(res.Deleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.DeleteObjectsResponse{
|
||||||
|
ObjectsDeleted: uint32(deleted),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// dryRunValidation validates that the IAM role policy attached to the given secrets of the
|
// dryRunValidation validates that the IAM role policy attached to the given secrets of the
|
||||||
// storage bucket has the minimum required permissions needed for this plugin to function
|
// storage bucket has the minimum required permissions needed for this plugin to function
|
||||||
// as expected. This function will create an object in the s3 bucket to validate it has
|
// as expected. This function will create an object in the s3 bucket to validate it has
|
||||||
@@ -632,9 +764,6 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts := []s3Option{}
|
opts := []s3Option{}
|
||||||
if attrs.Region != "" {
|
|
||||||
opts = append(opts, WithRegion(attrs.Region))
|
|
||||||
}
|
|
||||||
if attrs.EndpointUrl != "" {
|
if attrs.EndpointUrl != "" {
|
||||||
opts = append(opts, WithEndpoint(attrs.EndpointUrl))
|
opts = append(opts, WithEndpoint(attrs.EndpointUrl))
|
||||||
}
|
}
|
||||||
@@ -667,13 +796,21 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
|
|||||||
return parseAWSErrCode("error failed to get head object", err)
|
return parseAWSErrCode("error failed to get head object", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to delete the test object created for the dry run validation,
|
if res, err := client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
|
||||||
// this step is allowed to fail because DeleteObject is not a required
|
Bucket: aws.String(bucket.GetBucketName()),
|
||||||
// operation for the plugin.
|
Prefix: aws.String(objectKey),
|
||||||
client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
}); err != nil {
|
||||||
|
return parseAWSErrCode("error failed to list objects", err)
|
||||||
|
} else if res == nil || len(res.Contents) != 1 || *res.Contents[0].Key != objectKey {
|
||||||
|
return status.Errorf(codes.InvalidArgument, "list response did not contain the expected key: %+v", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||||
Bucket: aws.String(bucket.GetBucketName()),
|
Bucket: aws.String(bucket.GetBucketName()),
|
||||||
Key: aws.String(objectKey),
|
Key: aws.String(objectKey),
|
||||||
})
|
}); err != nil {
|
||||||
|
return parseAWSErrCode("error failed to delete object", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -9,12 +9,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go-v2/config"
|
|
||||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
|
smithyEndpoints "github.com/aws/smithy-go/endpoints"
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
||||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
|
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
@@ -25,6 +25,8 @@ type S3API interface {
|
|||||||
GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error)
|
GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error)
|
||||||
HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error)
|
HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error)
|
||||||
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
|
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
|
||||||
|
DeleteObjects(ctx context.Context, params *s3.DeleteObjectsInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectsOutput, error)
|
||||||
|
ListObjectsV2(ctx context.Context, params *s3.ListObjectsV2Input, optFns ...func(*s3.Options)) (*s3.ListObjectsV2Output, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var customClient = &http.Client{
|
var customClient = &http.Client{
|
||||||
@@ -97,55 +99,23 @@ func (s *awsStoragePersistedState) S3Client(ctx context.Context, opt ...s3Option
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing options when fetching S3 client: %w", err)
|
return nil, fmt.Errorf("error parsing options when fetching S3 client: %w", err)
|
||||||
}
|
}
|
||||||
|
awsCfg, err := s.GenerateCredentialChain(ctx)
|
||||||
awsConfigOpts := []func(*config.LoadOptions) error{}
|
|
||||||
sess, err := s.GetSession()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting AWS session when fetching S3 client: %w", err)
|
return nil, fmt.Errorf("error getting AWS configuration when fetching S3 client: %w", err)
|
||||||
}
|
}
|
||||||
creds, err := sess.Config.Credentials.Get()
|
if awsCfg == nil {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("nil aws configuration")
|
||||||
return nil, fmt.Errorf("error getting AWS session when fetching S3 client: %w", err)
|
|
||||||
}
|
|
||||||
customCredentials := config.WithCredentialsProvider(
|
|
||||||
credentials.NewStaticCredentialsProvider(creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken),
|
|
||||||
)
|
|
||||||
awsConfigOpts = append(awsConfigOpts, customCredentials)
|
|
||||||
if sess.Config.Region != nil {
|
|
||||||
awsConfigOpts = append(awsConfigOpts, config.WithRegion(*sess.Config.Region))
|
|
||||||
}
|
|
||||||
if opts.withRegion != "" {
|
|
||||||
awsConfigOpts = append(awsConfigOpts, config.WithRegion(opts.withRegion))
|
|
||||||
}
|
}
|
||||||
|
var s3Opts []func(*s3.Options)
|
||||||
if opts.withEndpoint != "" {
|
if opts.withEndpoint != "" {
|
||||||
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
|
s3Opts = append(s3Opts, s3.WithEndpointResolverV2(&endpointResolver{
|
||||||
if service == s3.ServiceID {
|
endpoint: opts.withEndpoint,
|
||||||
if opts.withRegion != "" {
|
}))
|
||||||
region = opts.withRegion
|
|
||||||
}
|
|
||||||
return aws.Endpoint{
|
|
||||||
PartitionID: "aws",
|
|
||||||
URL: opts.withEndpoint,
|
|
||||||
SigningRegion: region,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
// returning EndpointNotFoundError will allow the service to fallback to it's default resolution
|
|
||||||
return aws.Endpoint{}, &aws.EndpointNotFoundError{}
|
|
||||||
})
|
|
||||||
awsConfigOpts = append(awsConfigOpts, config.WithEndpointResolverWithOptions(customResolver))
|
|
||||||
}
|
}
|
||||||
cfg, err := config.LoadDefaultConfig(ctx, awsConfigOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error loading aws configuration when fetching S3 client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.testS3APIFunc != nil {
|
if s.testS3APIFunc != nil {
|
||||||
return s.testS3APIFunc(cfg)
|
return s.testS3APIFunc(*awsCfg)
|
||||||
}
|
}
|
||||||
|
return s3.NewFromConfig(*awsCfg, s3Opts...), nil
|
||||||
cfg.HTTPClient = customClient
|
|
||||||
|
|
||||||
return s3.NewFromConfig(cfg), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOpts iterates the inbound s3Options and returns a struct
|
// getOpts iterates the inbound s3Options and returns a struct
|
||||||
@@ -167,7 +137,6 @@ type s3Option func(*s3Options) error
|
|||||||
|
|
||||||
// options = how options are represented
|
// options = how options are represented
|
||||||
type s3Options struct {
|
type s3Options struct {
|
||||||
withRegion string
|
|
||||||
withEndpoint string
|
withEndpoint string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,14 +144,6 @@ func getDefaultS3Options() s3Options {
|
|||||||
return s3Options{}
|
return s3Options{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithRegion contains the region to use
|
|
||||||
func WithRegion(with string) s3Option {
|
|
||||||
return func(o *s3Options) error {
|
|
||||||
o.withRegion = with
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithEndpoint contains the endpoint to use
|
// WithEndpoint contains the endpoint to use
|
||||||
func WithEndpoint(with string) s3Option {
|
func WithEndpoint(with string) s3Option {
|
||||||
return func(o *s3Options) error {
|
return func(o *s3Options) error {
|
||||||
@@ -190,3 +151,31 @@ func WithEndpoint(with string) s3Option {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type endpointResolver struct {
|
||||||
|
endpoint string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *endpointResolver) ResolveEndpoint(ctx context.Context, params s3.EndpointParameters) (resolver smithyEndpoints.Endpoint, err error) {
|
||||||
|
var uri *url.URL
|
||||||
|
resolver, err = s3.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e.endpoint == "" {
|
||||||
|
// TODO: not sure if we should set an error or ignore the missing endpoint value and continue with the default resolver
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uri, err = url.Parse(e.endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if uri == nil {
|
||||||
|
// TODO: not sure if we should set an error or ignore the missing uri value and continue with the default resolver
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolver = smithyEndpoints.Endpoint{
|
||||||
|
URI: *uri,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -7,9 +7,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
||||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,7 +42,6 @@ func TestAwsStoragePersistedStateS3Client(t *testing.T) {
|
|||||||
withTestS3APIFunc(newTestMockS3(nil)),
|
withTestS3APIFunc(newTestMockS3(nil)),
|
||||||
},
|
},
|
||||||
s3Opts: []s3Option{
|
s3Opts: []s3Option{
|
||||||
WithRegion("us-west-2"),
|
|
||||||
WithEndpoint("0.0.0.0"),
|
WithEndpoint("0.0.0.0"),
|
||||||
},
|
},
|
||||||
expectedRegion: "us-west-2",
|
expectedRegion: "us-west-2",
|
||||||
@@ -73,14 +71,6 @@ func TestAwsStoragePersistedStateS3Client(t *testing.T) {
|
|||||||
require.True(ok)
|
require.True(ok)
|
||||||
require.NotNil(client)
|
require.NotNil(client)
|
||||||
require.Equal(tc.expectedRegion, client.Region)
|
require.Equal(tc.expectedRegion, client.Region)
|
||||||
|
|
||||||
if tc.expectedEndpoint != "" {
|
|
||||||
endpoint, err := client.Endpoint.ResolveEndpoint(s3.ServiceID, client.Region)
|
|
||||||
require.NoError(err)
|
|
||||||
require.Equal(tc.expectedEndpoint, endpoint.URL)
|
|
||||||
require.Equal(tc.expectedRegion, endpoint.SigningRegion)
|
|
||||||
require.Equal("aws", endpoint.PartitionID)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,11 +13,11 @@ import (
|
|||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
"github.com/aws/aws-sdk-go/service/sts"
|
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
||||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
|
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
|
||||||
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
@@ -31,6 +31,9 @@ const (
|
|||||||
testGetObjectErr = "test error for GetObject"
|
testGetObjectErr = "test error for GetObject"
|
||||||
testPutObjectErr = "test error for PutObject"
|
testPutObjectErr = "test error for PutObject"
|
||||||
testHeadObjectErr = "test error for HeadObject"
|
testHeadObjectErr = "test error for HeadObject"
|
||||||
|
testListObjectV2Err = "test error for ListObjectV2"
|
||||||
|
testDeleteObjectErr = "test error for DeleteObject"
|
||||||
|
testDeleteObjectsErr = "test error for DeleteObjects"
|
||||||
)
|
)
|
||||||
|
|
||||||
// throttleErr is a mocked error used for testing the aws s3 client
|
// throttleErr is a mocked error used for testing the aws s3 client
|
||||||
@@ -54,6 +57,14 @@ type testMockS3State struct {
|
|||||||
|
|
||||||
HeadObjectCalled bool
|
HeadObjectCalled bool
|
||||||
HeadObjectInputParams *s3.HeadObjectInput
|
HeadObjectInputParams *s3.HeadObjectInput
|
||||||
|
|
||||||
|
ListObjectsV2Called bool
|
||||||
|
ListObjectsV2InputParams *s3.ListObjectsV2Input
|
||||||
|
|
||||||
|
DeleteObjectCalled bool
|
||||||
|
DeleteObjectInputParams *s3.DeleteObjectInput
|
||||||
|
DeleteObjectsCalled bool
|
||||||
|
DeleteObjectsInputParams *s3.DeleteObjectsInput
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *testMockS3State) Reset() {
|
func (s *testMockS3State) Reset() {
|
||||||
@@ -64,14 +75,19 @@ func (s *testMockS3State) Reset() {
|
|||||||
s.PutObjectBody = nil
|
s.PutObjectBody = nil
|
||||||
s.HeadObjectCalled = false
|
s.HeadObjectCalled = false
|
||||||
s.HeadObjectInputParams = nil
|
s.HeadObjectInputParams = nil
|
||||||
|
s.ListObjectsV2Called = false
|
||||||
|
s.ListObjectsV2InputParams = nil
|
||||||
|
s.DeleteObjectCalled = false
|
||||||
|
s.DeleteObjectInputParams = nil
|
||||||
|
s.DeleteObjectsCalled = false
|
||||||
|
s.DeleteObjectsInputParams = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type testMockS3 struct {
|
type testMockS3 struct {
|
||||||
S3API
|
S3API
|
||||||
|
|
||||||
State *testMockS3State
|
State *testMockS3State
|
||||||
Region string
|
Region string
|
||||||
Endpoint aws.EndpointResolverWithOptions
|
|
||||||
|
|
||||||
// mocked responses for getObject
|
// mocked responses for getObject
|
||||||
GetObjectOutput *s3.GetObjectOutput
|
GetObjectOutput *s3.GetObjectOutput
|
||||||
@@ -84,6 +100,18 @@ type testMockS3 struct {
|
|||||||
// mocked responses for headObject
|
// mocked responses for headObject
|
||||||
HeadObjectOutput *s3.HeadObjectOutput
|
HeadObjectOutput *s3.HeadObjectOutput
|
||||||
HeadObjectErr error
|
HeadObjectErr error
|
||||||
|
|
||||||
|
// mocked responses for ListObjectsV2 (needed for delete)
|
||||||
|
ListObjectsV2Output *s3.ListObjectsV2Output
|
||||||
|
ListObjectsV2Err error
|
||||||
|
// sometimes we need to create a wildcard output
|
||||||
|
ListObjectsV2OutputFunc func(*s3.ListObjectsV2Input) *s3.ListObjectsV2Output
|
||||||
|
|
||||||
|
// mocked responses for DeleteObject(s)
|
||||||
|
DeleteObjectOutput *s3.DeleteObjectOutput
|
||||||
|
DeleteObjectErr error
|
||||||
|
DeleteObjectsOutput *s3.DeleteObjectsOutput
|
||||||
|
DeleteObjectsErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
type testMockS3Option func(m *testMockS3) error
|
type testMockS3Option func(m *testMockS3) error
|
||||||
@@ -130,6 +158,55 @@ func testMockS3WithHeadObjectError(e error) testMockS3Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testMockS3WithListObjectsV2Output(o *s3.ListObjectsV2Output) testMockS3Option {
|
||||||
|
return func(m *testMockS3) error {
|
||||||
|
m.ListObjectsV2Output = o
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMockS3WithListObjectsV2Error(e error) testMockS3Option {
|
||||||
|
return func(m *testMockS3) error {
|
||||||
|
m.ListObjectsV2Err = e
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMockS3WithListObjectsV2OutputFunc(o func(*s3.ListObjectsV2Input) *s3.ListObjectsV2Output) testMockS3Option {
|
||||||
|
return func(m *testMockS3) error {
|
||||||
|
m.ListObjectsV2OutputFunc = o
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMockS3WithDeleteObjectOutput(o *s3.DeleteObjectOutput) testMockS3Option {
|
||||||
|
return func(m *testMockS3) error {
|
||||||
|
m.DeleteObjectOutput = o
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMockS3WithDeleteObjectError(e error) testMockS3Option {
|
||||||
|
return func(m *testMockS3) error {
|
||||||
|
m.DeleteObjectErr = e
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMockS3WithDeleteObjectsOutput(o *s3.DeleteObjectsOutput) testMockS3Option {
|
||||||
|
return func(m *testMockS3) error {
|
||||||
|
m.DeleteObjectsOutput = o
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMockS3WithDeleteObjectsError(e error) testMockS3Option {
|
||||||
|
return func(m *testMockS3) error {
|
||||||
|
m.DeleteObjectsErr = e
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newTestMockS3(state *testMockS3State, opts ...testMockS3Option) s3APIFunc {
|
func newTestMockS3(state *testMockS3State, opts ...testMockS3Option) s3APIFunc {
|
||||||
return func(cfgs ...aws.Config) (S3API, error) {
|
return func(cfgs ...aws.Config) (S3API, error) {
|
||||||
m := &testMockS3{
|
m := &testMockS3{
|
||||||
@@ -147,9 +224,6 @@ func newTestMockS3(state *testMockS3State, opts ...testMockS3Option) s3APIFunc {
|
|||||||
if cfg.Region != "" {
|
if cfg.Region != "" {
|
||||||
m.Region = cfg.Region
|
m.Region = cfg.Region
|
||||||
}
|
}
|
||||||
if cfg.EndpointResolverWithOptions != nil {
|
|
||||||
m.Endpoint = cfg.EndpointResolverWithOptions
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
@@ -197,8 +271,47 @@ func (m *testMockS3) PutObject(ctx context.Context, params *s3.PutObjectInput, o
|
|||||||
return m.PutObjectOutput, nil
|
return m.PutObjectOutput, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *testMockS3) ListObjectsV2(ctx context.Context, params *s3.ListObjectsV2Input, optFns ...func(*s3.Options)) (*s3.ListObjectsV2Output, error) {
|
||||||
|
if m.State != nil {
|
||||||
|
m.State.ListObjectsV2Called = true
|
||||||
|
m.State.ListObjectsV2InputParams = params
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.ListObjectsV2Err != nil {
|
||||||
|
return nil, m.ListObjectsV2Err
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.ListObjectsV2OutputFunc != nil {
|
||||||
|
return m.ListObjectsV2OutputFunc(params), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.ListObjectsV2Output, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *testMockS3) DeleteObject(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectOutput, error) {
|
func (m *testMockS3) DeleteObject(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectOutput, error) {
|
||||||
return &s3.DeleteObjectOutput{}, nil
|
if m.State != nil {
|
||||||
|
m.State.DeleteObjectCalled = true
|
||||||
|
m.State.DeleteObjectInputParams = params
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.DeleteObjectErr != nil {
|
||||||
|
return nil, m.DeleteObjectErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.DeleteObjectOutput, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *testMockS3) DeleteObjects(ctx context.Context, params *s3.DeleteObjectsInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectsOutput, error) {
|
||||||
|
if m.State != nil {
|
||||||
|
m.State.DeleteObjectsCalled = true
|
||||||
|
m.State.DeleteObjectsInputParams = params
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.DeleteObjectsErr != nil {
|
||||||
|
return nil, m.DeleteObjectsErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.DeleteObjectsOutput, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *testMockS3) HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error) {
|
func (m *testMockS3) HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error) {
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func TestStoragePlugin(t *testing.T) {
|
|||||||
iamSecretAccessKeyMissingPutObject, err := tf.GetOutputString("iam_secret_access_key_missing_put_obj")
|
iamSecretAccessKeyMissingPutObject, err := tf.GetOutputString("iam_secret_access_key_missing_put_obj")
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// Start the workflow now. Set up the host catalog. Note that this
|
// Start the workflow now. Set up the storage bucket. Note that this
|
||||||
// will cause the state to go out of drift above in the sense that
|
// will cause the state to go out of drift above in the sense that
|
||||||
// the access key ID/secret access key will no longer be valid. We
|
// the access key ID/secret access key will no longer be valid. We
|
||||||
// will assert this through the returned state.
|
// will assert this through the returned state.
|
||||||
|
|||||||
+7
-7
@@ -13,10 +13,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/smithy-go"
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/values"
|
"github.com/hashicorp/boundary-plugin-aws/internal/values"
|
||||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||||
tfjson "github.com/hashicorp/terraform-json"
|
tfjson "github.com/hashicorp/terraform-json"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
@@ -293,7 +293,7 @@ func requireCredentialsInvalid(t *testing.T, accessKeyId, secretAccessKey string
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
waitErr:
|
waitErr:
|
||||||
for {
|
for {
|
||||||
_, err = c.GetCallerIdentity()
|
_, err = c.GetCallerIdentity(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -308,9 +308,9 @@ waitErr:
|
|||||||
}
|
}
|
||||||
|
|
||||||
require.NotNil(err)
|
require.NotNil(err)
|
||||||
awsErr, ok := err.(awserr.Error)
|
var ae smithy.APIError
|
||||||
require.True(ok)
|
require.True(errors.As(err, &ae))
|
||||||
require.Equal("InvalidClientTokenId", awsErr.Code())
|
require.Equal("InvalidClientTokenId", ae.ErrorCode())
|
||||||
}
|
}
|
||||||
|
|
||||||
func requireCredentialsValid(t *testing.T, accessKeyId, secretAccessKey string) {
|
func requireCredentialsValid(t *testing.T, accessKeyId, secretAccessKey string) {
|
||||||
@@ -322,7 +322,7 @@ func requireCredentialsValid(t *testing.T, accessKeyId, secretAccessKey string)
|
|||||||
awsutil.WithSecretKey(secretAccessKey),
|
awsutil.WithSecretKey(secretAccessKey),
|
||||||
)
|
)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
_, err = c.GetCallerIdentity()
|
_, err = c.GetCallerIdentity(context.Background())
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user