Compare commits

..
Author SHA1 Message Date
Louis Ruch 716052077b s 2026-05-13 20:38:49 -07:00
hashicorp-copywrite[bot] 5b7b669b41 [COMPLIANCE] Add/Update Copyright Headers (#68)
Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2026-05-11 22:37:05 -07:00
dependabot[bot] 7b4ee7f403 build(deps): bump github.com/aws/aws-sdk-go-v2/service/s3 (#66)
Bumps [github.com/aws/aws-sdk-go-v2/service/s3](https://github.com/aws/aws-sdk-go-v2) from 1.69.0 to 1.97.3.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.69.0...service/s3/v1.97.3)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/service/s3
  dependency-version: 1.97.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-22 21:14:09 -07:00
dependabot[bot] 916324ee64 build(deps): bump google.golang.org/grpc from 1.68.0 to 1.79.3 (#65)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.68.0 to 1.79.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.68.0...v1.79.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.79.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-22 21:09:52 -07:00
dependabot[bot] 11d3d6d523 build(deps): bump golang.org/x/crypto from 0.36.0 to 0.45.0 (#62)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.36.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.36.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-25 22:10:57 -08:00
oss-core-libraries-dashboard[bot]andhashicorp-copywrite[bot] 4758d5d9cd [COMPLIANCE] Update Copyright and License Headers (Batch 1 of 1) (#64)
Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2026-01-16 15:56:19 -08:00
Emilia 42e2126217 fix: Allow shared credentials in GenerateCredentialChain (#63)
* allow shared creds

* fix non-const printf format strings for go 1.24

* add .env and .envrc to .gitignore
2025-12-04 12:46:02 -05:00
Katrina Schwark 5d098ac71d Add PCI review checklist to PR template (#61) 2025-06-30 11:57:23 -06:00
dependabot[bot] 949976ec19 build(deps): bump golang.org/x/net from 0.36.0 to 0.38.0 (#60)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 16:23:17 -07:00
dependabot[bot] 49f692e7a8 build(deps): bump golang.org/x/net from 0.33.0 to 0.36.0 (#59)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.33.0 to 0.36.0.
- [Commits](https://github.com/golang/net/compare/v0.33.0...v0.36.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-17 09:58:09 -07:00
daniandDanielle Miu a75ac95465 Implement NormalizeStorageBucketData and begin normalizing endpoint URLs (#58)
Co-authored-by: Danielle Miu <29378233+DanielleMiu@users.noreply.github.com>
2025-03-14 15:19:20 +00:00
Irena Rindos 7fd6d3f3e5 chore: update deps (#57) 2025-01-07 12:45:10 -05:00
Louis Ruch bdd27e2562 feat(storage): Add s3Client cache (#55)
* feat(storage): Add s3Client cache

* Update awsutil deps
2024-11-25 21:32:37 -08:00
Johan Brandhorst-Satzkorn f17f05ec82 Update CODEOWNERS (#54)
Make it effective with a wildcard
2024-11-18 16:23:19 -08:00
Damian Debkowski 899c62ce06 feat: support dual stack requests for aws clients (#52)
* feat: enable dual stack in aws clients

* refactor: optionally set custom endpoint resolver

* feat: update sts & iam client to utilize dualstack

* update aws dependencies to remove role policy bug
2024-10-28 11:50:18 -07:00
50 changed files with 1680 additions and 310 deletions

No files matched your search

+11
View File
@@ -0,0 +1,11 @@
## PCI review checklist
<!-- heimdall_github_prtemplate:grc-pci_dss-2024-01-05 -->
- [ ] I have documented a clear reason for, and description of, the change I am making.
- [ ] If applicable, I've documented a plan to revert these changes if they require more than reverting the pull request.
- [ ] If applicable, I've documented the impact of any changes to security controls.
Examples of changes to security controls include using new access control methods, adding or removing logging pipelines, etc.
+5
View File
@@ -8,3 +8,8 @@ plan.tfplan
# IntelliJ IDEA project files
.idea
*.iprg
.DS_Store
.env
.envrc
+1 -2
View File
@@ -1,5 +1,4 @@
# These owners will be the default owners for everything in
# the repo, unless a later match takes precedence.
@hashicorp/boundary
* @hashicorp/boundary
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2021 HashiCorp, Inc.
Copyright IBM Corp. 2021, 2026
Mozilla Public License Version 2.0
==================================
+48 -45
View File
@@ -1,66 +1,69 @@
module github.com/hashicorp/boundary-plugin-aws
go 1.21
go 1.24.0
require (
github.com/aws/aws-sdk-go-v2 v1.32.2
github.com/aws/aws-sdk-go-v2/service/ec2 v1.186.0
github.com/aws/aws-sdk-go-v2/service/iam v1.37.2
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.1
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2
github.com/aws/smithy-go v1.22.0
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.4.0
github.com/hashicorp/boundary/sdk v0.0.43-0.20240717182311-a20aae98794a
github.com/aws/aws-sdk-go-v2 v1.41.5
github.com/aws/aws-sdk-go-v2/service/ec2 v1.193.0
github.com/aws/aws-sdk-go-v2/service/iam v1.52.2
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2
github.com/aws/smithy-go v1.24.2
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/hashicorp/boundary/sdk v0.0.51
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.0.0
github.com/hashicorp/terraform-json v0.14.0
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.2
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0
github.com/hashicorp/terraform-json v0.23.0
github.com/mitchellh/mapstructure v1.5.0
github.com/stretchr/testify v1.8.4
google.golang.org/grpc v1.61.0
google.golang.org/protobuf v1.35.1
github.com/stretchr/testify v1.10.0
google.golang.org/grpc v1.79.3
google.golang.org/protobuf v1.36.10
)
require (
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 // indirect
github.com/aws/aws-sdk-go-v2/config v1.18.33 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.32 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 // 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/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.2 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.2 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0 // 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-hclog v1.5.0 // indirect
github.com/hashicorp/go-kms-wrapping/v2 v2.0.14 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-kms-wrapping/v2 v2.0.16 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.5.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/pointerstructure v1.2.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/zclconf/go-cty v1.10.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/zclconf/go-cty v1.15.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+116 -136
View File
@@ -4,83 +4,79 @@ github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7Y
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/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
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/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go-v2 v1.20.1/go.mod h1:NU06lETsFm8fUC6ZjhgDpVBcGZTFQ6XM+LZWZxMI4ac=
github.com/aws/aws-sdk-go-v2 v1.32.2 h1:AkNLZEyYMLnx/Q/mSKkcMqwNFXMAvFto9bNsHqcTduI=
github.com/aws/aws-sdk-go-v2 v1.32.2/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 h1:pT3hpW0cOHRJx8Y0DfJUEQuqPild8jRGmSFmBgvydr0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6/go.mod h1:j/I2++U0xX+cr44QjHay4Cvxj6FUbnxrgmqN3H1jTZA=
github.com/aws/aws-sdk-go-v2/config v1.18.33 h1:JKcw5SFxFW/rpM4mOPjv0VQ11E2kxW13F3exWOy7VZU=
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/credentials v1.13.32 h1:lIH1eKPcCY1ylR4B6PkBGRWMHO3aVenOKJHWiS4/G2w=
github.com/aws/aws-sdk-go-v2/credentials v1.13.32/go.mod h1:lL8U3v/Y79YRG69WlAho0OHIKUXCyFvSXaIvfo81sls=
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/feature/ec2/imds v1.13.8/go.mod h1:ce7BgLQfYr5hQFdy67oX2svto3ufGtm6oBvmsHScI1Q=
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/configsources v1.3.21 h1:UAsR3xA31QGf79WzpG/ixT9FZvQlh5HY1NRqSHBNOCk=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21/go.mod h1:JNr43NFf5L9YaG3eKTm7HQzls9J+A9YYcGI5Quh1r2Y=
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/endpoints/v2 v2.6.21 h1:6jZVETqmYCadGFvrYEQfC5fAQmlo80CeL5psbno6r0s=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21/go.mod h1:1SR0GbLlnN3QUmYaflZNiH1ql+1qrSiB2vwcJ+4UM60=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39 h1:fc0ukRAiP1syoSGZYu+DaE+FulSYhTiJ8WpVu5jElU4=
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.3.21 h1:7edmS3VOBDhK00b/MwGtGglCm7hhwNYnjJs/PgFdMQE=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21/go.mod h1:Q9o5h4HoIWG8XfzxqiuK/CGUbepCJ8uTlaE3bAbxytQ=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.186.0 h1:n2l2WeV+lEABrGwG/4MsE0WFEbd3j7yKsmZzbnEm5CY=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.186.0/go.mod h1:kYXaB4FzyhEJjvrJ84oPnMElLiEAjGxxUunVW2tBSng=
github.com/aws/aws-sdk-go-v2/service/iam v1.37.2 h1:E7vCDUFeDN8uOk8Nb2d4E1howWS1TR4HrKABXsvttIs=
github.com/aws/aws-sdk-go-v2/service/iam v1.37.2/go.mod h1:QzMecFrIFYJ1cyxjlUoIFRzYSDX19gdqYUd0Tyws2J8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 h1:4FMHqLfk0efmTqhXVRL5xYRqlEBNBiRI7N6w4jsEdd4=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2/go.mod h1:LWoqeWlK9OZeJxsROW2RqrSPvQHKTpp69r/iDjwsSaw=
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/internal/presigned-url v1.12.2 h1:s7NA1SOw8q/5c0wr8477yOPp0z+uBaXBnLE0XYb0POA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2/go.mod h1:fnjjWyAW/Pj5HYOxl9LJqWtEwS7W2qgcRLWP+uWbss0=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 h1:t7iUP9+4wdc5lt3E41huP+GvQZJD38WLsgVp4iOtAjg=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2/go.mod h1:/niFCtmuQNxqx9v8WAPq5qh7EH25U4BF6tjoyq9bObM=
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.1 h1:MkQ4unegQEStiQYmfFj+Aq5uTp265ncSmm0XTQwDwi0=
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.1/go.mod h1:cB6oAuus7YXRZhWCc1wIwPywwZ1XwweNp2TVAEGYeB8=
github.com/aws/aws-sdk-go-v2/service/sso v1.13.2 h1:A2RlEMo4SJSwbNoUUgkxTAEMduAy/8wG3eB2b2lP4gY=
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/ssooidc v1.15.2 h1:OJELEgyaT2kmaBGZ+myyZbTTLobfe3ox3FSh5eYK9Qs=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2/go.mod h1:ubDBBaDFs1GHijSOTi8ljppML15GLG0HxhILtbjNNYQ=
github.com/aws/aws-sdk-go-v2/service/sts v1.21.2/go.mod h1:FQ/DQcOfESELfJi5ED+IPPAjI5xC6nxtSolVVB773jM=
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 h1:CiS7i0+FUe+/YY1GvIBLLrR/XNGZ4CtM1Ll0XavNuVo=
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2/go.mod h1:HtaiBI8CjYoNVde8arShXb94UbQQi9L4EMr6D+xGBwo=
github.com/aws/smithy-go v1.14.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
github.com/aws/aws-sdk-go-v2/config v1.32.2 h1:4liUsdEpUUPZs5WVapsJLx5NPmQhQdez7nYFcovrytk=
github.com/aws/aws-sdk-go-v2/config v1.32.2/go.mod h1:l0hs06IFz1eCT+jTacU/qZtC33nvcnLADAPL/XyrkZI=
github.com/aws/aws-sdk-go-v2/credentials v1.19.2 h1:qZry8VUyTK4VIo5aEdUcBjPZHL2v4FyQ3QEOaWcFLu4=
github.com/aws/aws-sdk-go-v2/credentials v1.19.2/go.mod h1:YUqm5a1/kBnoK+/NY5WEiMocZihKSo15/tJdmdXnM5g=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.193.0 h1:RhSoBFT5/8tTmIseJUXM6INTXTQDF8+0oyxWBnozIms=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.193.0/go.mod h1:mzj8EEjIHSN2oZRXiw1Dd+uB4HZTl7hC8nBzX9IZMWw=
github.com/aws/aws-sdk-go-v2/service/iam v1.52.2 h1:li0ooCUfHIivHn8nB3LstP6HgdNefwu5gnXE4MLVz/U=
github.com/aws/aws-sdk-go-v2/service/iam v1.52.2/go.mod h1:PuHz5kGh1jtsNpjezdYhRp7xgn6DzCNJJfQt7O7U9Aw=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 h1:HwxWTbTrIHm5qY+CAEur0s/figc3qwvLWsNkF4RPToo=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2 h1:MxMBdKTYBjPQChlJhi4qlEueqB1p1KcbTEa7tD5aqPs=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5 h1:ksUT5KtgpZd3SAiFJNJ0AFEJVva3gjBmN7eXUZjzUwQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10 h1:GtsxyiF3Nd3JahRBJbxLCCdYW9ltGQYrFWg8XdkGDd8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2 h1:a5UTtD4mHBU3t0o6aHQZFJTNKVfxFWfPX7J0Lr7G+uY=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso=
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
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/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.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.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
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/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
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.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/boundary/sdk v0.0.43-0.20240717182311-a20aae98794a h1:SwVze6sYE5o+J9qdcgj29auY1k6O8oSo9DC2+2Gb9rw=
github.com/hashicorp/boundary/sdk v0.0.43-0.20240717182311-a20aae98794a/go.mod h1:9iOT7kDM6mYcSkKxNuZlv8rP7U5BG1kXoevjLLL8lNQ=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/boundary/sdk v0.0.51 h1:a7XpHNTYaeBXRhJkc8MC0siPtfJELYH/ibjw/0UbJEg=
github.com/hashicorp/boundary/sdk v0.0.51/go.mod h1:gSrMTppD30IPVsUqs4SwQowu+OlR5/+58draey++SkQ=
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/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -90,18 +86,18 @@ github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e
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/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/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.5 h1:jrnDfQm2hCQ0/hEselgqzV4fK16gpZoY0OWGZpVPNHM=
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.14 h1:1ZuhfnZgRnLK8S0KovJkoTCRIQId5pv3sDR7pG5VQBw=
github.com/hashicorp/go-kms-wrapping/v2 v2.0.14/go.mod h1:0dWtzl2ilqKpavgM3id/kFK9L3tjo6fS4OhbVPSYpnQ=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.7 h1:gM4OwbF16Cmfxt2QMkoGMQbRTfYFZLvDMPgU3rM3KIo=
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.7/go.mod h1:7ZMHVluyqgHgEuTADeDzFNWoA9mnyPfdiK8Tk2Bct1c=
github.com/hashicorp/go-kms-wrapping/v2 v2.0.16 h1:WZeXfD26QMWYC35at25KgE021SF9L3u9UMHK8fJAdV0=
github.com/hashicorp/go-kms-wrapping/v2 v2.0.16/go.mod h1:ZiKZctjRTLEppuRwrttWkp71VYMbTTCkazK4xT7U/NQ=
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-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y=
github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4=
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-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg=
github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.2 h1:KlDrZWx/wYpnUcFPB0s9uhPjyNZoeIfhrV2MN/Ly76s=
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.2/go.mod h1:6+rVulOPNCQbL3Xv2iLCqM0JmU2WO2wRzP1C6hBKeB8=
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/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
@@ -109,8 +105,8 @@ github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11 h1:uPW2Wn0YlmI9RGSkZ
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11/go.mod h1:uis9dCmOzXuOaRyXq+1Foh31kcvXKoWogjNnhfjHfW8=
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/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM=
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=
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=
@@ -120,17 +116,17 @@ github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
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-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEyqO4u9I=
github.com/hashicorp/go-sockaddr v1.0.6/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.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
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/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
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/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM=
github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI=
github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c=
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=
@@ -139,10 +135,6 @@ 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/go.mod h1:3J2qVK16Lq8V+wfiL2lPeDZ7UWMxk5LemerHa1p6N00=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
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.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@@ -151,22 +143,18 @@ 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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/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/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/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
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/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@@ -185,49 +173,50 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
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/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/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
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/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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
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/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
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-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
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/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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/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-20190620200207-3b0461eec859/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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -238,16 +227,13 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
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-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -258,28 +244,22 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg=
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k=
google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe h1:0poefMBYvYbs7g5UkjS6HcxBPaTRAmznle9jnxYoAI8=
google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
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.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
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-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package credential
@@ -130,7 +130,7 @@ func GetCredentialsConfig(secrets *structpb.Struct, attrs *CredentialAttributes,
cfg, err := awsutil.NewCredentialsConfig(opts...)
if err != nil {
return nil, errors.BadRequestStatus(err.Error())
return nil, errors.BadRequestStatus("%s", err.Error())
}
return cfg, nil
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package credential
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package credential
+2 -8
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package credential
@@ -206,13 +206,7 @@ func (s *AwsCredentialPersistedState) DeleteCreds(ctx context.Context) error {
// GenerateCredentialChain returns a AWS configuration for the credentials in the state.
func (s *AwsCredentialPersistedState) GenerateCredentialChain(ctx context.Context) (*aws.Config, error) {
// Default `WithSharedCredentials` is `true` - This disables the ability to
// 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...)
return s.CredentialsConfig.GenerateCredentialChain(ctx, s.testOpts...)
}
// ToMap returns a map of the credentials stored in the persisted state.
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package credential
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package credential
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package errors
@@ -73,7 +73,7 @@ func InvalidArgumentError(msg string, f map[string]string) error {
sort.Strings(fieldMsgs)
msg = fmt.Sprintf("%s: [%s]", msg, strings.Join(fieldMsgs, ", "))
}
return BadRequestStatus(msg)
return BadRequestStatus("%s", msg)
}
// ParseAWSError converts an aws service error into a RPC status. This method
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package errors
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package errors
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package values
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package values
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package plugin
@@ -32,6 +32,6 @@ type AwsPlugin struct {
func NewAwsPlugin() *AwsPlugin {
return &AwsPlugin{
HostPlugin: &host.HostPlugin{},
StoragePlugin: &storage.StoragePlugin{},
StoragePlugin: storage.New(),
}
}
+40 -1
View File
@@ -16,6 +16,36 @@ boundary host-catalogs create plugin \
-secret secret_access_key='SECRET'
```
To create a host catalog that returns addresses only from the primary network
interface:
```
boundary host-catalogs create plugin \
-scope-id p_1234567890 \
-name "Example Plugin-Based Host Catalog" \
-description "Description for plugin-based host catalog" \
-plugin-name aws \
-attr region=REGION \
-attr primary_interface_only=true \
-secret access_key_id='KEY' \
-secret secret_access_key='SECRET'
```
To create a host catalog that excludes public IPv4 addresses while keeping
private IPv4 addresses:
```
boundary host-catalogs create plugin \
-scope-id p_1234567890 \
-name "Example Plugin-Based Host Catalog" \
-description "Description for plugin-based host catalog" \
-plugin-name aws \
-attr region=REGION \
-attr exclude_public_ips=true \
-secret access_key_id='KEY' \
-secret secret_access_key='SECRET'
```
To create a host set, filtering the host set based on tag keys `foo` or `bar`
(either tag can be present), ensuring that any targets set to this host set only
connect to external addresses in the `54.0.0.0/8` class A subnet:
@@ -75,7 +105,6 @@ a configured IAM user for this provider:
"Resource": "*"
}
]
}
```
* `iam:GetUser`, `iam:CreateAccessKey`, and `iam:DeleteAccessKey`, configured to
@@ -116,6 +145,16 @@ The following `attributes` are valid on an AWS host catalog resource:
provider.
- `role_tags` (object): The key-value pair tags configured for the `AssumeRole`
provider.
- `primary_interface_only` (bool): If `true`, only addresses from the primary
ENI are returned. When `false` or omitted, addresses from all attached ENIs
are considered.
- `exclude_private_ips` (bool): If `true`, private IPv4 addresses are omitted.
- `exclude_public_ips` (bool): If `true`, public IPv4 addresses are omitted.
If none of the exclusion flags are set, the plugin preserves the historical
behavior and returns all discovered address classes. The exclusion flags apply
to both IPv4 and IPv6 addresses based on whether the IP is classified as public
or private.
The following `secrets` are required on an AWS host catalog resource:
+47 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package host
@@ -22,6 +22,18 @@ type CatalogAttributes struct {
// DualStack is used for configuring how the aws client will resolve requests.
DualStack bool
// PrimaryInterfaceOnly restricts discovery to the instance's primary ENI.
PrimaryInterfaceOnly bool
// ExcludePrivateIps controls whether private IPv4 addresses are omitted.
ExcludePrivateIps bool
// ExcludePublicIps controls whether public IPv4 addresses are omitted.
ExcludePublicIps bool
// ExcludeIpv6 controls whether IPv6 addresses are omitted.
ExcludeIpv6 bool
}
func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
@@ -40,6 +52,36 @@ func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
}
delete(unknownFields, ConstAwsDualStack)
primaryInterfaceOnly, err := values.GetBoolValue(in, ConstPrimaryInterfaceOnly, false)
if err != nil {
badFields[fmt.Sprintf("attributes.%s", ConstPrimaryInterfaceOnly)] = err.Error()
}
delete(unknownFields, ConstPrimaryInterfaceOnly)
excludePrivateIPs, err := values.GetBoolValue(in, ConstExcludePrivateIps, false)
if err != nil {
badFields[fmt.Sprintf("attributes.%s", ConstExcludePrivateIps)] = err.Error()
}
delete(unknownFields, ConstExcludePrivateIps)
excludePublicIPs, err := values.GetBoolValue(in, ConstExcludePublicIps, false)
if err != nil {
badFields[fmt.Sprintf("attributes.%s", ConstExcludePublicIps)] = err.Error()
}
delete(unknownFields, ConstExcludePublicIps)
excludeIpv6, err := values.GetBoolValue(in, ConstExcludeIpv6, false)
if err != nil {
badFields[fmt.Sprintf("attributes.%s", ConstExcludeIpv6)] = err.Error()
}
delete(unknownFields, ConstExcludeIpv6)
if excludePrivateIPs && excludePublicIPs && excludeIpv6 {
badFields[fmt.Sprintf("attributes.%s", ConstExcludePrivateIps)] = "cannot be combined with exclude_public_ips and exclude_ipv6"
badFields[fmt.Sprintf("attributes.%s", ConstExcludePublicIps)] = "cannot be combined with exclude_private_ips and exclude_ipv6"
badFields[fmt.Sprintf("attributes.%s", ConstExcludeIpv6)] = "cannot be combined with exclude_private_ips and exclude_public_ips"
}
for s := range unknownFields {
switch s {
// Ignore knownFields from CredentialAttributes
@@ -67,6 +109,10 @@ func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
return &CatalogAttributes{
CredentialAttributes: credAttributes,
DualStack: dualStack,
PrimaryInterfaceOnly: primaryInterfaceOnly,
ExcludePrivateIps: excludePrivateIPs,
ExcludePublicIps: excludePublicIPs,
ExcludeIpv6: excludeIpv6,
}, nil
}
+101 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package host
@@ -59,6 +59,106 @@ func TestGetCatalogAttributes(t *testing.T) {
DualStack: true,
},
},
{
name: "with primary interface only",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"primary_interface_only": structpb.NewBoolValue(true),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
PrimaryInterfaceOnly: true,
},
},
{
name: "with exclude public ip",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"exclude_public_ips": structpb.NewBoolValue(true),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
ExcludePublicIps: true,
},
},
{
name: "with exclude private ip",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"exclude_private_ips": structpb.NewBoolValue(true),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
ExcludePrivateIps: true,
},
},
{
name: "with mixed attributes",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"dual_stack": structpb.NewBoolValue(true),
"exclude_private_ips": structpb.NewBoolValue(true),
"exclude_ipv6": structpb.NewBoolValue(true),
"primary_interface_only": structpb.NewBoolValue(true),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
DualStack: true,
ExcludePrivateIps: true,
ExcludeIpv6: true,
PrimaryInterfaceOnly: true,
},
},
{
name: "can exclude public and private IPs when IPv6 remains enabled",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"exclude_private_ips": structpb.NewBoolValue(true),
"exclude_public_ips": structpb.NewBoolValue(true),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
ExcludePrivateIps: true,
ExcludePublicIps: true,
},
},
{
name: "cannot exclude public private and IPv6 together",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"exclude_private_ips": structpb.NewBoolValue(true),
"exclude_public_ips": structpb.NewBoolValue(true),
"exclude_ipv6": structpb.NewBoolValue(true),
},
},
expectedErrContains: "attributes.exclude_ipv6: cannot be combined with exclude_private_ips and exclude_public_ips, attributes.exclude_private_ips: cannot be combined with exclude_public_ips and exclude_ipv6, attributes.exclude_public_ips: cannot be combined with exclude_private_ips and exclude_ipv6",
},
{
name: "default",
in: &structpb.Struct{
+13 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package host
@@ -13,4 +13,16 @@ const (
// ConstAwsDualStack is the key for the dualstack flag in the aws s3 client.
ConstAwsDualStack = "dual_stack"
// ConstPrimaryInterfaceOnly limits host discovery to the primary ENI.
ConstPrimaryInterfaceOnly = "primary_interface_only"
// ConstExcludePrivateIps excludes private IPv4 addresses.
ConstExcludePrivateIps = "exclude_private_ips"
// ConstExcludePublicIps excludes public IPv4 addresses associated to ENI IPs.
ConstExcludePublicIps = "exclude_public_ips"
// ConstExcludeIpv6 excludes IPv6 addresses.
ConstExcludeIpv6 = "exclude_ipv6"
)
+44 -15
View File
@@ -1,8 +1,8 @@
package host
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package host
import (
"context"
"fmt"
@@ -93,7 +93,7 @@ func (p *HostPlugin) OnCreateCatalog(ctx context.Context, req *pb.OnCreateCatalo
persistedProto, err := catalogState.toProto()
if err != nil {
return nil, errors.BadRequestStatus(err.Error())
return nil, errors.BadRequestStatus("%s", err.Error())
}
return &pb.OnCreateCatalogResponse{
@@ -227,7 +227,7 @@ func (p *HostPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalo
persistedProto, err := catalogState.toProto()
if err != nil {
return nil, errors.BadRequestStatus(err.Error())
return nil, errors.BadRequestStatus("%s", err.Error())
}
return &pb.OnUpdateCatalogResponse{
@@ -550,7 +550,7 @@ func (p *HostPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*
// set of hosts afterwards (possibly removing duplicates).
for _, reservation := range output.Reservations {
for _, instance := range reservation.Instances {
host, err := awsInstanceToHost(instance)
host, err := awsInstanceToHost(instance, catalogAttributes)
if err != nil {
return nil, errors.BadRequestStatus("error processing host results for host set id %q: %s", query.Id, err)
}
@@ -648,10 +648,13 @@ func buildDescribeInstancesInput(attrs *SetAttributes, dryRun bool) (*ec2.Descri
// awsInstanceToHost processes data from an ec2.Instance and returns
// a ListHostsResponseHost with the ID and network addresses
// populated.
func awsInstanceToHost(instance types.Instance) (*pb.ListHostsResponseHost, error) {
func awsInstanceToHost(instance types.Instance, catalogAttributes *CatalogAttributes) (*pb.ListHostsResponseHost, error) {
if aws.ToString(instance.InstanceId) == "" {
return nil, fmt.Errorf("response integrity error: missing instance id")
}
if catalogAttributes == nil {
return nil, fmt.Errorf("catalog attributes are required")
}
result := new(pb.ListHostsResponseHost)
@@ -666,22 +669,46 @@ func awsInstanceToHost(instance types.Instance) (*pb.ListHostsResponseHost, erro
}
}
result.IpAddresses = appendDistinct(result.IpAddresses, instance.PrivateIpAddress, instance.PublicIpAddress)
result.DnsNames = appendDistinct(result.DnsNames, instance.PrivateDnsName, instance.PublicDnsName)
if !catalogAttributes.ExcludePrivateIps {
result.IpAddresses = appendDistinct(result.IpAddresses, instance.PrivateIpAddress)
result.DnsNames = appendDistinct(result.DnsNames, instance.PrivateDnsName)
}
if !catalogAttributes.ExcludePublicIps {
result.IpAddresses = appendDistinct(result.IpAddresses, instance.PublicIpAddress)
result.DnsNames = appendDistinct(result.DnsNames, instance.PublicDnsName)
}
// Now go through all of the interfaces and log the IP address of
// every interface.
for _, iface := range instance.NetworkInterfaces {
if catalogAttributes.PrimaryInterfaceOnly {
if iface.Attachment == nil || aws.ToInt32(iface.Attachment.DeviceIndex) != 0 {
// We only want to sync the primary interface so skip any others
continue
}
}
// Populate default IP addresses/DNS name similar to how we do
// for the entire instance.
result.IpAddresses = appendDistinct(result.IpAddresses, iface.PrivateIpAddress)
result.DnsNames = appendDistinct(result.DnsNames, iface.PrivateDnsName)
if !catalogAttributes.ExcludePrivateIps {
result.IpAddresses = appendDistinct(result.IpAddresses, iface.PrivateIpAddress)
result.DnsNames = appendDistinct(result.DnsNames, iface.PrivateDnsName)
}
// Iterate through the private IP addresses and log the
// information.
for _, addr := range iface.PrivateIpAddresses {
result.IpAddresses = appendDistinct(result.IpAddresses, addr.PrivateIpAddress)
result.DnsNames = appendDistinct(result.DnsNames, addr.PrivateDnsName)
if !catalogAttributes.ExcludePrivateIps {
result.IpAddresses = appendDistinct(result.IpAddresses, addr.PrivateIpAddress)
result.DnsNames = appendDistinct(result.DnsNames, addr.PrivateDnsName)
}
if catalogAttributes.ExcludePublicIps {
// skip adding public addresses if we are excluding them
continue
}
// PublicIP for iface is only available via an association to the private IP
if addr.Association != nil {
result.IpAddresses = appendDistinct(result.IpAddresses, addr.Association.PublicIp)
result.DnsNames = appendDistinct(result.DnsNames, addr.Association.PublicDnsName)
@@ -689,8 +716,10 @@ func awsInstanceToHost(instance types.Instance) (*pb.ListHostsResponseHost, erro
}
// Add the IPv6 addresses.
for _, addr := range iface.Ipv6Addresses {
result.IpAddresses = appendDistinct(result.IpAddresses, addr.Ipv6Address)
if !catalogAttributes.ExcludeIpv6 {
for _, addr := range iface.Ipv6Addresses {
result.IpAddresses = appendDistinct(result.IpAddresses, addr.Ipv6Address)
}
}
}
+693 -12
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package host
@@ -638,7 +638,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
Fields: map[string]*structpb.Value{
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
}},
},
},
Attrs: &hostcatalogs.HostCatalog_Attributes{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
@@ -664,7 +665,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
Fields: map[string]*structpb.Value{
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
}},
},
},
Attrs: &hostcatalogs.HostCatalog_Attributes{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
@@ -694,7 +696,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
Fields: map[string]*structpb.Value{
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
}},
},
},
Attrs: &hostcatalogs.HostCatalog_Attributes{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
@@ -721,7 +724,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
Fields: map[string]*structpb.Value{
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
}},
},
},
Attrs: &hostcatalogs.HostCatalog_Attributes{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
@@ -2383,10 +2387,11 @@ func TestBuildDescribeInstancesInput(t *testing.T) {
func TestAwsInstanceToHost(t *testing.T) {
cases := []struct {
name string
instance types.Instance
expected *pb.ListHostsResponseHost
expectedErr string
name string
instance types.Instance
catalogAttributes *CatalogAttributes
expected *pb.ListHostsResponseHost
expectedErr string
}{
{
name: "missing instance id",
@@ -2591,14 +2596,45 @@ func TestAwsInstanceToHost(t *testing.T) {
},
Ipv6Addresses: []types.InstanceIpv6Address{
{Ipv6Address: nil}, // Just coverage for nil assertion which is skipped
{Ipv6Address: aws.String("some::fake::address")},
{Ipv6Address: aws.String("2001:db8::1")},
},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "foobar",
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "some::fake::address"},
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "2001:db8::1"},
DnsNames: []string{"test.example.internal", "test.example.com"},
},
},
{
name: "good, single IP w/public addr",
instance: types.Instance{
InstanceId: aws.String("foobar"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("test.example.internal"),
PublicIpAddress: aws.String("1.1.1.1"),
PublicDnsName: aws.String("test.example.com"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("test.example.internal"),
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
Association: &types.InstanceNetworkInterfaceAssociation{
PublicIp: aws.String("1.1.1.1"),
PublicDnsName: aws.String("test.example.com"),
},
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("test.example.internal"),
},
},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "foobar",
IpAddresses: []string{"10.0.0.1", "1.1.1.1"},
DnsNames: []string{"test.example.internal", "test.example.com"},
},
},
@@ -2676,18 +2712,663 @@ func TestAwsInstanceToHost(t *testing.T) {
DnsNames: []string{"test.example.internal", "test.example.com"},
},
},
{
name: "primary_interface_only keeps all addresses on primary ENI",
catalogAttributes: &CatalogAttributes{
PrimaryInterfaceOnly: true,
},
instance: types.Instance{
InstanceId: aws.String("foobar"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("test.example.internal"),
PublicIpAddress: aws.String("1.1.1.1"),
PublicDnsName: aws.String("test.example.com"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
Attachment: &types.InstanceNetworkInterfaceAttachment{
DeviceIndex: aws.Int32(0),
},
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("test.example.internal"),
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
Primary: aws.Bool(true),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("test.example.internal"),
},
// Secondary IPs on primary ENI (e.g. VPC CNI pod IPs).
{
Primary: aws.Bool(false),
PrivateIpAddress: aws.String("10.0.0.42"),
PrivateDnsName: aws.String("pod-a.example.internal"),
},
{
Primary: aws.Bool(false),
PrivateIpAddress: aws.String("10.0.0.43"),
PrivateDnsName: aws.String("pod-b.example.internal"),
},
},
},
// Secondary ENI attached by VPC CNI for additional pod IPs.
{
Attachment: &types.InstanceNetworkInterfaceAttachment{
DeviceIndex: aws.Int32(1),
},
PrivateIpAddress: aws.String("10.0.0.99"),
PrivateDnsName: aws.String("eni1.example.internal"),
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "foobar",
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "10.0.0.42", "10.0.0.43"},
DnsNames: []string{"test.example.internal", "test.example.com", "pod-a.example.internal", "pod-b.example.internal"},
},
},
{
name: "primary_interface_only keeps IPv6 on primary ENI",
catalogAttributes: &CatalogAttributes{
PrimaryInterfaceOnly: true,
},
instance: types.Instance{
InstanceId: aws.String("foobar"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("test.example.internal"),
NetworkInterfaces: []types.InstanceNetworkInterface{
// Secondary ENI listed first to verify ordering does not matter.
{
Attachment: &types.InstanceNetworkInterfaceAttachment{
DeviceIndex: aws.Int32(1),
},
PrivateIpAddress: aws.String("10.0.0.99"),
Ipv6Addresses: []types.InstanceIpv6Address{
{Ipv6Address: aws.String("2001:db8::2")},
},
},
{
Attachment: &types.InstanceNetworkInterfaceAttachment{
DeviceIndex: aws.Int32(0),
},
PrivateIpAddress: aws.String("10.0.0.1"),
Ipv6Addresses: []types.InstanceIpv6Address{
{Ipv6Address: aws.String("2001:db8::3")},
{Ipv6Address: aws.String("2001:db8::4")},
},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "foobar",
IpAddresses: []string{"10.0.0.1", "2001:db8::3", "2001:db8::4"},
DnsNames: []string{"test.example.internal"},
},
},
{
name: "primary_interface_only without interfaces still returns top-level IPs",
catalogAttributes: &CatalogAttributes{
PrimaryInterfaceOnly: true,
},
instance: types.Instance{
InstanceId: aws.String("foobar"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("test.example.internal"),
PublicIpAddress: aws.String("1.1.1.1"),
PublicDnsName: aws.String("test.example.com"),
},
expected: &pb.ListHostsResponseHost{
ExternalId: "foobar",
IpAddresses: []string{"10.0.0.1", "1.1.1.1"},
DnsNames: []string{"test.example.internal", "test.example.com"},
},
},
{
name: "exclude_public_ips excludes public IPv4 sources but keeps IPv6",
catalogAttributes: &CatalogAttributes{
ExcludePublicIps: true,
},
instance: types.Instance{
InstanceId: aws.String("i-1234"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PublicIpAddress: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
Association: &types.InstanceNetworkInterfaceAssociation{
PublicIp: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
},
},
},
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::1")}},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "i-1234",
IpAddresses: []string{"10.0.0.1", "2001:db8::1"},
DnsNames: []string{"host.internal"},
},
},
{
name: "exclude_private_ips excludes private IPv4 sources but keeps IPv6",
catalogAttributes: &CatalogAttributes{
ExcludePrivateIps: true,
},
instance: types.Instance{
InstanceId: aws.String("i-1234"),
PublicIpAddress: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{Association: &types.InstanceNetworkInterfaceAssociation{PublicIp: aws.String("1.2.3.4"), PublicDnsName: aws.String("host.public")}},
},
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::1")}},
},
{
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{Association: &types.InstanceNetworkInterfaceAssociation{PublicIp: aws.String("1.2.3.5"), PublicDnsName: aws.String("svc.public")}},
},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "i-1234",
IpAddresses: []string{"1.2.3.4", "2001:db8::1", "1.2.3.5"},
DnsNames: []string{"host.public", "svc.public"},
},
},
{
name: "exclude_public_ips keeps IPv6 addresses",
catalogAttributes: &CatalogAttributes{
ExcludePublicIps: true,
},
instance: types.Instance{
InstanceId: aws.String("i-1234"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("fd00::1")}},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "i-1234",
IpAddresses: []string{"10.0.0.1", "fd00::1"},
DnsNames: []string{"host.internal"},
},
},
{
name: "exclude_ipv6 omits IPv6 while preserving IPv4 and DNS",
catalogAttributes: &CatalogAttributes{
ExcludeIpv6: true,
},
instance: types.Instance{
InstanceId: aws.String("i-1234"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PublicIpAddress: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
Association: &types.InstanceNetworkInterfaceAssociation{
PublicIp: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
},
},
},
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::1")}},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "i-1234",
IpAddresses: []string{"10.0.0.1", "1.2.3.4"},
DnsNames: []string{"host.internal", "host.public"},
},
},
{
name: "exclude_public_ips with exclude_ipv6 keeps only private IPv4 sources",
catalogAttributes: &CatalogAttributes{
ExcludePublicIps: true,
ExcludeIpv6: true,
},
instance: types.Instance{
InstanceId: aws.String("i-1234"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PublicIpAddress: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
Association: &types.InstanceNetworkInterfaceAssociation{
PublicIp: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
},
},
{
PrivateIpAddress: aws.String("10.0.0.2"),
PrivateDnsName: aws.String("svc.internal"),
},
},
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::1")}},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "i-1234",
IpAddresses: []string{"10.0.0.1", "10.0.0.2"},
DnsNames: []string{"host.internal", "svc.internal"},
},
},
{
name: "exclude_private_ips with exclude_ipv6 keeps only public IPv4 sources",
catalogAttributes: &CatalogAttributes{
ExcludePrivateIps: true,
ExcludeIpv6: true,
},
instance: types.Instance{
InstanceId: aws.String("i-1234"),
PublicIpAddress: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
Association: &types.InstanceNetworkInterfaceAssociation{
PublicIp: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
},
},
{
Association: &types.InstanceNetworkInterfaceAssociation{
PublicIp: aws.String("1.2.3.5"),
PublicDnsName: aws.String("svc.public"),
},
},
},
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("fd00::1")}},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "i-1234",
IpAddresses: []string{"1.2.3.4", "1.2.3.5"},
DnsNames: []string{"host.public", "svc.public"},
},
},
{
name: "primary_interface_only with exclude_ipv6 drops IPv6 from primary ENI",
catalogAttributes: &CatalogAttributes{
PrimaryInterfaceOnly: true,
ExcludeIpv6: true,
},
instance: types.Instance{
InstanceId: aws.String("foobar"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("test.example.internal"),
PublicIpAddress: aws.String("1.1.1.1"),
PublicDnsName: aws.String("test.example.com"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
Attachment: &types.InstanceNetworkInterfaceAttachment{
DeviceIndex: aws.Int32(0),
},
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("test.example.internal"),
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
PrivateIpAddress: aws.String("10.0.0.42"),
PrivateDnsName: aws.String("pod-a.example.internal"),
},
},
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::3")}},
},
{
Attachment: &types.InstanceNetworkInterfaceAttachment{
DeviceIndex: aws.Int32(1),
},
PrivateIpAddress: aws.String("10.0.0.99"),
PrivateDnsName: aws.String("eni1.example.internal"),
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::4")}},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "foobar",
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "10.0.0.42"},
DnsNames: []string{"test.example.internal", "test.example.com", "pod-a.example.internal"},
},
},
{
name: "exclude_ipv6 on IPv6-only interface leaves no addresses",
catalogAttributes: &CatalogAttributes{
ExcludeIpv6: true,
},
instance: types.Instance{
InstanceId: aws.String("i-1234"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
Ipv6Addresses: []types.InstanceIpv6Address{
{Ipv6Address: aws.String("2001:db8::1")},
{Ipv6Address: aws.String("fd00::1")},
},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "i-1234",
},
},
{
name: "deduplicates repeated addresses and DNS names across instance and interfaces",
instance: types.Instance{
InstanceId: aws.String("i-1234"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PublicIpAddress: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
Association: &types.InstanceNetworkInterfaceAssociation{
PublicIp: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
},
},
},
Ipv6Addresses: []types.InstanceIpv6Address{
{Ipv6Address: aws.String("2001:db8::1")},
{Ipv6Address: aws.String("2001:db8::1")},
},
},
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
Association: &types.InstanceNetworkInterfaceAssociation{
PublicIp: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
},
},
},
Ipv6Addresses: []types.InstanceIpv6Address{
{Ipv6Address: aws.String("2001:db8::1")},
},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "i-1234",
IpAddresses: []string{"10.0.0.1", "1.2.3.4", "2001:db8::1"},
DnsNames: []string{"host.internal", "host.public"},
},
},
{
name: "primary_interface_only skips interfaces without attachment metadata",
catalogAttributes: &CatalogAttributes{
PrimaryInterfaceOnly: true,
},
instance: types.Instance{
InstanceId: aws.String("i-1234"),
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PublicIpAddress: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
PrivateIpAddress: aws.String("10.0.0.99"),
PrivateDnsName: aws.String("missing-attachment.internal"),
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
PrivateIpAddress: aws.String("10.0.0.99"),
PrivateDnsName: aws.String("missing-attachment.internal"),
},
},
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::99")}},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "i-1234",
IpAddresses: []string{"10.0.0.1", "1.2.3.4"},
DnsNames: []string{"host.internal", "host.public"},
},
},
{
name: "exclude_private_ips removes private DNS names even when public addresses remain",
catalogAttributes: &CatalogAttributes{
ExcludePrivateIps: true,
},
instance: types.Instance{
InstanceId: aws.String("i-1234"),
PublicIpAddress: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
NetworkInterfaces: []types.InstanceNetworkInterface{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
PrivateIpAddresses: []types.InstancePrivateIpAddress{
{
PrivateIpAddress: aws.String("10.0.0.1"),
PrivateDnsName: aws.String("host.internal"),
Association: &types.InstanceNetworkInterfaceAssociation{
PublicIp: aws.String("1.2.3.4"),
PublicDnsName: aws.String("host.public"),
},
},
{
PrivateIpAddress: aws.String("10.0.0.2"),
PrivateDnsName: aws.String("svc.internal"),
Association: &types.InstanceNetworkInterfaceAssociation{
PublicIp: aws.String("1.2.3.5"),
PublicDnsName: aws.String("svc.public"),
},
},
},
},
},
},
expected: &pb.ListHostsResponseHost{
ExternalId: "i-1234",
IpAddresses: []string{"1.2.3.4", "1.2.3.5"},
DnsNames: []string{"host.public", "svc.public"},
},
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := awsInstanceToHost(tc.instance)
attrs := tc.catalogAttributes
if attrs == nil {
attrs = &CatalogAttributes{}
}
actual, err := awsInstanceToHost(tc.instance, attrs)
if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr)
return
}
require.NoError(err)
require.Equal(tc.expected, actual)
})
}
}
func TestGetCatalogAttributesAddressSelection(t *testing.T) {
cases := []struct {
name string
in *structpb.Struct
expectedErr string
expected *CatalogAttributes
}{
{
name: "valid selectors",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"primary_interface_only": structpb.NewBoolValue(true),
"exclude_public_ips": structpb.NewBoolValue(true),
"exclude_ipv6": structpb.NewBoolValue(true),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
PrimaryInterfaceOnly: true,
ExcludePublicIps: true,
ExcludeIpv6: true,
},
},
{
name: "exclude_private_ips can be combined with exclude_ipv6",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"exclude_private_ips": structpb.NewBoolValue(true),
"exclude_ipv6": structpb.NewBoolValue(true),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
ExcludePrivateIps: true,
ExcludeIpv6: true,
},
},
{
name: "exclude_public_ips can be combined with exclude_ipv6",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"exclude_public_ips": structpb.NewBoolValue(true),
"exclude_ipv6": structpb.NewBoolValue(true),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
ExcludePublicIps: true,
ExcludeIpv6: true,
},
},
{
name: "exclude_private_ips and exclude_public_ips can be combined when IPv6 remains enabled",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"exclude_private_ips": structpb.NewBoolValue(true),
"exclude_public_ips": structpb.NewBoolValue(true),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
ExcludePrivateIps: true,
ExcludePublicIps: true,
},
},
{
name: "exclude_private_ips and exclude_public_ips cannot also exclude_ipv6",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"exclude_private_ips": structpb.NewBoolValue(true),
"exclude_public_ips": structpb.NewBoolValue(true),
"exclude_ipv6": structpb.NewBoolValue(true),
},
},
expectedErr: "attributes.exclude_ipv6: cannot be combined with exclude_private_ips and exclude_public_ips, attributes.exclude_private_ips: cannot be combined with exclude_public_ips and exclude_ipv6, attributes.exclude_public_ips: cannot be combined with exclude_private_ips and exclude_ipv6",
},
{
name: "exclude_ipv6 alone is accepted",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"exclude_ipv6": structpb.NewBoolValue(true),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
ExcludeIpv6: true,
},
},
{
name: "exclude_ipv6 requires a boolean",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
"exclude_ipv6": structpb.NewStringValue("true"),
},
},
expectedErr: "attributes.exclude_ipv6",
},
{
name: "no selectors configured preserves historical behavior",
in: &structpb.Struct{
Fields: map[string]*structpb.Value{
"region": structpb.NewStringValue("us-west-2"),
},
},
expected: &CatalogAttributes{
CredentialAttributes: &credential.CredentialAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
},
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := getCatalogAttributes(tc.in)
if tc.expectedErr != "" {
require.Error(err)
require.Contains(err.Error(), tc.expectedErr)
return
}
require.NoError(err)
require.Equal(tc.expected, actual)
})
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package host
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package host
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package host
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package storage
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package storage
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package storage
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package storage
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package storage
+200 -42
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package storage
@@ -14,6 +14,7 @@ import (
"os"
"path"
"strings"
"sync"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
@@ -24,14 +25,24 @@ import (
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
)
// Ensure that we are implementing StoragePluginServiceServer
var _ pb.StoragePluginServiceServer = (*StoragePlugin)(nil)
// clientCache caches previously initialized S3API clients created with a RoleARN attribute.
type clientCache struct {
// cache is a map of cached s3 clients. The key of the map
// is the public id of the storage bucket
cache map[string]S3API
sync.RWMutex
}
// StoragePlugin implements the StoragePluginServiceServer interface for the
// AWS storage service plugin.
type StoragePlugin struct {
@@ -42,6 +53,137 @@ type StoragePlugin struct {
// testStorageStateOpts are passed in to the stored state to control test behavior
testStorageStateOpts []awsStoragePersistedStateOption
clients *clientCache
}
// New creates a new StoragePlugin
func New() *StoragePlugin {
c := &clientCache{
cache: make(map[string]S3API),
}
return &StoragePlugin{
clients: c,
}
}
// getClient returns an S3API client for the given storage bucket id.
func (p *StoragePlugin) getClient(ctx context.Context,
storageBucketId string,
storageState *awsStoragePersistedState,
opt ...s3Option) (S3API, error) {
if storageBucketId == "" {
// No storage bucket ID to key cache on, create new client and return
client, err := storageState.s3Client(ctx, opt...)
if err != nil {
return nil, errors.BadRequestStatus("error creating S3 client: %s", err)
}
return client, nil
}
opts, err := getOpts(opt...)
if err != nil {
return nil, fmt.Errorf("error parsing options in cache: %w", err)
}
if opts.withCacheRefresh {
// Forced cache refresh
p.clients.Lock()
defer p.clients.Unlock()
client, err := storageState.s3Client(ctx, opt...)
if err != nil {
return nil, errors.BadRequestStatus("error creating S3 client: %s", err)
}
p.clients.cache[storageBucketId] = client
return client, nil
}
p.clients.RLock()
client, ok := p.clients.cache[storageBucketId]
p.clients.RUnlock()
if !ok || client.Credentials().Expired() {
// We got a cache miss or the credentials has expired, time to refresh
p.clients.Lock()
defer p.clients.Unlock()
// Check cache again in case another caller updated it since we got the lock
client, ok = p.clients.cache[storageBucketId]
if ok && !client.Credentials().Expired() {
// Got a cache hit with valid credentials, return cached client
return client, nil
}
// Create new client and cache it
var err error
client, err = storageState.s3Client(ctx, opt...)
if err != nil {
return nil, errors.BadRequestStatus("error creating S3 client: %s", err)
}
p.clients.cache[storageBucketId] = client
}
return client, nil
}
type s3Caller func(client S3API) (any, error)
// call gets the s3client and passes it to the s3caller function, if a permission error is returned
// it forces a cache refresh and tries again.
func (p *StoragePlugin) call(
ctx context.Context,
fn s3Caller,
storageBucketId string,
storageState *awsStoragePersistedState,
opts ...s3Option) (any, error) {
s3Client, err := p.getClient(ctx, storageBucketId, storageState, opts...)
if err != nil {
return nil, errors.BadRequestStatus("error getting S3 client: %s", err)
}
firstAttempt := true
for {
resp, err := fn(s3Client)
if err != nil {
st, _ := errors.ParseAWSError("", err)
if st.Code() == codes.PermissionDenied && firstAttempt {
// We got a permission error, this is the first time so refresh cache and try again
firstAttempt = false
opts = append(opts, WithCacheRefresh(true))
s3Client, err = p.getClient(ctx, storageBucketId, storageState, opts...)
if err != nil {
return nil, errors.BadRequestStatus("error refreshing S3 client: %s", err)
}
continue
}
return nil, err
}
return resp, nil
}
}
// NormalizeStorageBucketData is called by boundary before calling OnCreate or OnUpdate
//
// aws storage supports a total of 8 attributes, 6 as part of credential.CredentialAttributes
// and 2 more from storage.StorageAttributes. of these, only endpoint_url is normalized.
func (sp *StoragePlugin) NormalizeStorageBucketData(ctx context.Context, req *pb.NormalizeStorageBucketDataRequest) (*pb.NormalizeStorageBucketDataResponse, error) {
sa, err := getStorageAttributes(req.GetAttributes())
if err != nil {
return nil, err
}
fields := req.GetAttributes().GetFields()
if _, ok := fields[ConstAwsEndpointUrl]; ok {
if sa.EndpointUrl, err = parseutil.NormalizeAddr(sa.EndpointUrl); err != nil {
return nil, status.New(codes.InvalidArgument, fmt.Sprintf("failed to normalize provided %s: %s", ConstAwsEndpointUrl, err.Error())).Err()
}
fields[ConstAwsEndpointUrl] = structpb.NewStringValue(sa.EndpointUrl)
}
return &pb.NormalizeStorageBucketDataResponse{
Attributes: &structpb.Struct{
Fields: fields,
},
}, nil
}
// OnCreateStorageBucket is called when a storage bucket is created.
@@ -102,7 +244,7 @@ func (p *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCre
persistedProto, err := storageState.toProto()
if err != nil {
return nil, errors.BadRequestStatus(err.Error())
return nil, errors.BadRequestStatus("%s", err.Error())
}
return &pb.OnCreateStorageBucketResponse{
@@ -231,7 +373,7 @@ func (p *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUpd
persistedProto, err := storageState.toProto()
if err != nil {
return nil, errors.BadRequestStatus(err.Error())
return nil, errors.BadRequestStatus("%s", err.Error())
}
return &pb.OnUpdateStorageBucketResponse{
@@ -351,19 +493,20 @@ func (p *StoragePlugin) HeadObject(ctx context.Context, req *pb.HeadObjectReques
if storageAttributes.DualStack {
opts = append(opts, WithDualStack(storageAttributes.DualStack))
}
s3Client, err := storageState.S3Client(ctx, opts...)
if err != nil {
return nil, errors.BadRequestStatus("error getting S3 client: %s", err)
}
objectKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
resp, err := s3Client.HeadObject(ctx, &s3.HeadObjectInput{
Bucket: aws.String(bucket.GetBucketName()),
Key: aws.String(objectKey),
})
headCall := func(s3Client S3API) (any, error) {
return s3Client.HeadObject(ctx, &s3.HeadObjectInput{
Bucket: aws.String(bucket.GetBucketName()),
Key: aws.String(objectKey),
})
}
headResp, err := p.call(ctx, headCall, bucket.GetId(), storageState, opts...)
if err != nil {
return nil, parseS3Error("head object", err, req).Err()
}
resp := headResp.(*s3.HeadObjectOutput)
return &pb.HeadObjectResponse{
ContentLength: aws.ToInt64(resp.ContentLength),
LastModified: timestamppb.New(*resp.LastModified),
@@ -480,19 +623,19 @@ func (p *StoragePlugin) GetObject(req *pb.GetObjectRequest, stream pb.StoragePlu
if storageAttributes.DualStack {
opts = append(opts, WithDualStack(storageAttributes.DualStack))
}
s3Client, err := storageState.S3Client(stream.Context(), opts...)
if err != nil {
return errors.BadRequestStatus("error getting S3 client: %s", err)
}
objectKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
resp, err := s3Client.GetObject(stream.Context(), &s3.GetObjectInput{
Bucket: aws.String(bucket.GetBucketName()),
Key: aws.String(objectKey),
})
getCall := func(s3Client S3API) (any, error) {
return s3Client.GetObject(stream.Context(), &s3.GetObjectInput{
Bucket: aws.String(bucket.GetBucketName()),
Key: aws.String(objectKey),
})
}
getResp, err := p.call(stream.Context(), getCall, bucket.GetId(), storageState, opts...)
if err != nil {
return parseS3Error("get object", err, req).Err()
}
resp := getResp.(*s3.GetObjectOutput)
defer resp.Body.Close()
reader := bufio.NewReader(resp.Body)
@@ -594,10 +737,6 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
if storageAttributes.DualStack {
opts = append(opts, WithDualStack(storageAttributes.DualStack))
}
s3Client, err := storageState.S3Client(ctx, opts...)
if err != nil {
return nil, errors.BadRequestStatus("error getting S3 client: %s", err)
}
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
@@ -609,18 +748,23 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
if err != nil {
return nil, errors.UnknownStatus("failed to rewind file pointer")
}
objectKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
resp, err := s3Client.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(bucket.GetBucketName()),
Key: aws.String(objectKey),
Body: file,
ChecksumAlgorithm: types.ChecksumAlgorithmSha256,
ChecksumSHA256: aws.String(checksum),
})
putCall := func(s3Client S3API) (any, error) {
return s3Client.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(bucket.GetBucketName()),
Key: aws.String(objectKey),
Body: file,
ChecksumAlgorithm: types.ChecksumAlgorithmSha256,
ChecksumSHA256: aws.String(checksum),
})
}
putResp, err := p.call(ctx, putCall, bucket.GetId(), storageState, opts...)
if err != nil {
return nil, parseS3Error("put object", err, req).Err()
}
resp := putResp.(*s3.PutObjectOutput)
if resp.ChecksumSHA256 == nil {
return nil, errors.UnknownStatus("missing checksum response from aws")
}
@@ -689,10 +833,6 @@ func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjects
if storageAttributes.EndpointUrl != "" {
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
}
client, err := storageState.S3Client(ctx, opts...)
if err != nil {
return nil, errors.BadRequestStatus("error getting S3 client: %s", err)
}
prefix := path.Join(bucket.GetBucketPrefix(), req.GetKeyPrefix())
if strings.HasSuffix(req.GetKeyPrefix(), "/") {
@@ -702,10 +842,13 @@ func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjects
}
if !req.Recursive {
_, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{
Bucket: aws.String(bucket.GetBucketName()),
Key: aws.String(prefix),
})
deleteCall := func(s3Client S3API) (any, error) {
return s3Client.DeleteObject(ctx, &s3.DeleteObjectInput{
Bucket: aws.String(bucket.GetBucketName()),
Key: aws.String(prefix),
})
}
_, err := p.call(ctx, deleteCall, bucket.GetId(), storageState, opts...)
if err != nil {
return nil, parseS3Error("delete object", err, req).Err()
}
@@ -715,10 +858,13 @@ func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjects
}
const maxkeys = 1000
objects := []types.ObjectIdentifier{}
client, err := p.getClient(ctx, bucket.GetId(), storageState, opts...)
if err != nil {
return nil, errors.BadRequestStatus("error getting S3 client: %s", err)
}
var conToken *string
truncated := true
truncated, firstAttempt := true, true
for truncated {
res, err := client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
Bucket: aws.String(bucket.GetBucketName()),
@@ -727,6 +873,18 @@ func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjects
ContinuationToken: conToken,
})
if err != nil {
st, _ := errors.ParseAWSError("", err)
if st.Code() == codes.PermissionDenied && firstAttempt {
// This is first attempt and we get a permission error, refresh cache and try again
firstAttempt = false
opts = append(opts, WithCacheRefresh(true))
client, err = p.getClient(ctx, bucket.GetId(), storageState, opts...)
if err != nil {
return nil, errors.BadRequestStatus("error refreshing S3 client: %s", err)
}
continue
}
return nil, parseS3Error("list objects", err, req).Err()
}
truncated = aws.ToBool(res.IsTruncated)
@@ -784,7 +942,7 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
opts = append(opts, WithDualStack(attrs.DualStack))
}
client, err := state.S3Client(ctx, opts...)
client, err := state.s3Client(ctx, opts...)
if err != nil {
return status.New(codes.InvalidArgument, fmt.Sprintf("error getting S3 client: %s", err))
}
+286 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package storage
@@ -26,6 +26,7 @@ import (
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
internal "github.com/hashicorp/boundary-plugin-aws/internal/errors"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
"github.com/hashicorp/boundary/sdk/pbs/plugin"
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
"github.com/stretchr/testify/assert"
@@ -36,6 +37,204 @@ import (
"google.golang.org/protobuf/types/known/timestamppb"
)
func TestStoragePlugin_NormalizeStorageBucketData(t *testing.T) {
ctx := context.Background()
tests := []struct {
name string
req *plugin.NormalizeStorageBucketDataRequest
expected *plugin.NormalizeStorageBucketDataResponse
err string
}{
{
name: "missing attributes",
req: &plugin.NormalizeStorageBucketDataRequest{},
err: "attributes.region: missing required value \"region\"",
},
{
name: "empty attributes",
req: &plugin.NormalizeStorageBucketDataRequest{
Attributes: &structpb.Struct{},
},
err: "attributes.region: missing required value \"region\"",
},
{
name: "missing required field",
req: &plugin.NormalizeStorageBucketDataRequest{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("http://127.0.0.1:9000"),
},
},
},
err: "attributes.region: missing required value \"region\"",
},
{
name: "missing endpoint_url",
req: &plugin.NormalizeStorageBucketDataRequest{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
credential.ConstRegion: structpb.NewStringValue("somewhere"),
},
},
},
expected: &plugin.NormalizeStorageBucketDataResponse{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
credential.ConstRegion: structpb.NewStringValue("somewhere"),
},
},
},
},
{
name: "valid endpoint_url returned unchanged",
req: &plugin.NormalizeStorageBucketDataRequest{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("http://127.0.0.1:9000"),
credential.ConstRegion: structpb.NewStringValue("somewhere"),
},
},
},
expected: &plugin.NormalizeStorageBucketDataResponse{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("http://127.0.0.1:9000"),
credential.ConstRegion: structpb.NewStringValue("somewhere"),
},
},
},
},
{
name: "valid ipv6 endpoint_url returned normalized",
req: &plugin.NormalizeStorageBucketDataRequest{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("https://[2001:BEEF:0:0:0:1:0:0001]"),
credential.ConstRegion: structpb.NewStringValue("somewhere"),
},
},
},
expected: &plugin.NormalizeStorageBucketDataResponse{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("https://[2001:beef::1:0:1]"),
credential.ConstRegion: structpb.NewStringValue("somewhere"),
},
},
},
},
{
name: "valid ipv6 endpoint_url with port returned normalized",
req: &plugin.NormalizeStorageBucketDataRequest{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("https://[2001:BEEF:0:0:0:1:0:0001]:9000"),
credential.ConstRegion: structpb.NewStringValue("somewhere"),
},
},
},
expected: &plugin.NormalizeStorageBucketDataResponse{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("https://[2001:beef::1:0:1]:9000"),
credential.ConstRegion: structpb.NewStringValue("somewhere"),
},
},
},
},
{
name: "invalid ipv6 endpoint_url returns normalization error",
req: &plugin.NormalizeStorageBucketDataRequest{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("https://[2001:BEEF:0:0:0:1:0:0001"),
credential.ConstRegion: structpb.NewStringValue("somewhere"),
},
},
},
err: "failed to normalize provided endpoint_url: failed to parse address",
},
{
name: "invalid ipv6 endpoint_url returns normalization error 2",
req: &plugin.NormalizeStorageBucketDataRequest{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("https://[2001:BEEF:0:0:0:1:0:0001]:"),
credential.ConstRegion: structpb.NewStringValue("somewhere"),
},
},
},
err: "failed to normalize provided endpoint_url: url has malformed host: missing port value after colon",
},
{
name: "valid all fields returns normalized",
req: &plugin.NormalizeStorageBucketDataRequest{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("http://[2001:BEEF:0:0:0:1:0:0001]:9000/test/path"),
credential.ConstRegion: structpb.NewStringValue("somewhere ❤"),
credential.ConstDisableCredentialRotation: structpb.NewBoolValue(false),
credential.ConstRoleArn: structpb.NewStringValue("an arn"),
credential.ConstRoleExternalId: structpb.NewStringValue("id"),
credential.ConstRoleSessionName: structpb.NewStringValue("sesh"),
ConstAwsDualStack: structpb.NewBoolValue(false),
credential.ConstRoleTags: structpb.NewStructValue(&structpb.Struct{
Fields: map[string]*structpb.Value{
"foo": structpb.NewStringValue("bar"),
},
}),
},
},
},
expected: &plugin.NormalizeStorageBucketDataResponse{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAwsEndpointUrl: structpb.NewStringValue("http://[2001:beef::1:0:1]:9000/test/path"),
credential.ConstRegion: structpb.NewStringValue("somewhere ❤"),
credential.ConstDisableCredentialRotation: structpb.NewBoolValue(false),
credential.ConstRoleArn: structpb.NewStringValue("an arn"),
credential.ConstRoleExternalId: structpb.NewStringValue("id"),
credential.ConstRoleSessionName: structpb.NewStringValue("sesh"),
ConstAwsDualStack: structpb.NewBoolValue(false),
credential.ConstRoleTags: structpb.NewStructValue(&structpb.Struct{
Fields: map[string]*structpb.Value{
"foo": structpb.NewStringValue("bar"),
},
}),
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
sp := new(StoragePlugin)
res, err := sp.NormalizeStorageBucketData(ctx, tt.req)
if tt.err != "" {
require.ErrorContains(err, tt.err)
return
}
if err != nil {
if st, ok := status.FromError(err); ok {
require.Nil(err, st.Message())
}
}
require.Nil(err)
for key, val := range tt.expected.GetAttributes().GetFields() {
v, ok := res.GetAttributes().GetFields()[key]
assert.True(ok, "missing expected value: %q", key)
if ok {
assert.Equal(val, v, "expected[%q]: %v, but got: %v", key, val, v)
}
}
})
}
}
func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
validRequest := func() *pb.OnCreateStorageBucketRequest {
return &pb.OnCreateStorageBucketRequest{
@@ -3917,3 +4116,89 @@ func TestStoragePlugin_DeleteObjects(t *testing.T) {
})
}
}
func TestS3ClientCache(t *testing.T) {
ctx := context.Background()
assert, require := assert.New(t), require.New(t)
cred1, err := awsutil.NewCredentialsConfig(
awsutil.WithRegion("us-east-1"),
awsutil.WithAccessKey("cred1"),
awsutil.WithSecretKey("secret1"),
)
require.NoError(err)
cred2, err := awsutil.NewCredentialsConfig(
awsutil.WithRegion("us-east-1"),
awsutil.WithAccessKey("cred2"),
awsutil.WithSecretKey("secret2"),
)
require.NoError(err)
cred3, err := awsutil.NewCredentialsConfig(
awsutil.WithRegion("us-east-1"),
awsutil.WithAccessKey("cred3"),
awsutil.WithSecretKey("secret3"),
)
require.NoError(err)
p := New()
cred, err := credential.NewAwsCredentialPersistedState(
credential.WithCredentialsConfig(cred1),
)
require.NoError(err)
state, err := newAwsStoragePersistedState(withCredentials(cred))
require.NoError(err)
// Cache should be empty before first call
assert.Empty(p.clients.cache)
client, err := p.getClient(ctx, "id", state)
require.NoError(err)
require.NotNil(client)
assert.Equal(cred1.AccessKey, client.Credentials().AccessKeyID)
assert.Equal(cred1.SecretKey, client.Credentials().SecretAccessKey)
// Update state with new credentials
cred, err = credential.NewAwsCredentialPersistedState(
credential.WithCredentialsConfig(cred2),
)
require.NoError(err)
state, err = newAwsStoragePersistedState(withCredentials(cred))
require.NoError(err)
// Client is cached and creds valid so it should still grab initial client
client, err = p.getClient(ctx, "id", state)
require.NoError(err)
require.NotNil(client)
assert.Equal(cred1.AccessKey, client.Credentials().AccessKeyID)
assert.Equal(cred1.SecretKey, client.Credentials().SecretAccessKey)
// Force credentials to expire
c := client.(*s3Client)
c.creds.CanExpire = true
p.clients.Lock()
p.clients.cache["id"] = c
p.clients.Unlock()
// Getting client should now create new client on cred2 since the credentials have expired
client, err = p.getClient(ctx, "id", state)
require.NoError(err)
require.NotNil(client)
assert.Equal(cred2.AccessKey, client.Credentials().AccessKeyID)
assert.Equal(cred2.SecretKey, client.Credentials().SecretAccessKey)
// Update state with new credentials again
cred, err = credential.NewAwsCredentialPersistedState(
credential.WithCredentialsConfig(cred3),
)
require.NoError(err)
state, err = newAwsStoragePersistedState(withCredentials(cred))
require.NoError(err)
// Getting client and force cache refresh should return cred3 based client
client, err = p.getClient(ctx, "id", state, WithCacheRefresh(true))
require.NoError(err)
require.NotNil(client)
assert.Equal(cred3.AccessKey, client.Credentials().AccessKeyID)
assert.Equal(cred3.SecretKey, client.Credentials().SecretAccessKey)
}
+34 -6
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package storage
@@ -24,6 +24,16 @@ type S3API interface {
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)
Credentials() aws.Credentials
}
type s3Client struct {
*s3.Client
creds aws.Credentials
}
func (c *s3Client) Credentials() aws.Credentials {
return c.creds
}
type awsStoragePersistedState struct {
@@ -77,9 +87,9 @@ func (s *awsStoragePersistedState) toProto() (*storagebuckets.StorageBucketPersi
return &storagebuckets.StorageBucketPersisted{Data: data}, nil
}
// S3Client returns a configured S3 client based on the session
// s3Client returns a configured S3 client based on the session
// information stored in the state.
func (s *awsStoragePersistedState) S3Client(ctx context.Context, opt ...s3Option) (S3API, error) {
func (s *awsStoragePersistedState) s3Client(ctx context.Context, opt ...s3Option) (S3API, error) {
opts, err := getOpts(opt...)
if err != nil {
return nil, fmt.Errorf("error parsing options when fetching S3 client: %w", err)
@@ -101,7 +111,16 @@ func (s *awsStoragePersistedState) S3Client(ctx context.Context, opt ...s3Option
if s.testS3APIFunc != nil {
return s.testS3APIFunc(*awsCfg)
}
return s3.NewFromConfig(*awsCfg, s3Opts...), nil
// Retrieve the credentials provider from the client
credsProvider := awsCfg.Credentials
creds, err := credsProvider.Retrieve(ctx)
if err != nil {
return nil, fmt.Errorf("failed to retrieve credentials: %v\n", err)
}
c := s3.NewFromConfig(*awsCfg, s3Opts...)
return &s3Client{Client: c, creds: creds}, nil
}
// getOpts iterates the inbound s3Options and returns a struct
@@ -123,14 +142,23 @@ type s3Option func(*s3Options) error
// options = how options are represented
type s3Options struct {
withEndpoint string
withDualStack bool
withEndpoint string
withDualStack bool
withCacheRefresh bool
}
func getDefaultS3Options() s3Options {
return s3Options{}
}
// WithCacheRefresh controls if the cache should be forced to refresh
func WithCacheRefresh(refresh bool) s3Option {
return func(o *s3Options) error {
o.withCacheRefresh = refresh
return nil
}
}
// WithEndpoint contains the endpoint to use
func WithEndpoint(with string) s3Option {
return func(o *s3Options) error {
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package storage
@@ -64,7 +64,7 @@ func TestAwsStoragePersistedStateS3Client(t *testing.T) {
)
require.NoError(err)
clientRaw, err := state.S3Client(context.Background(), tc.s3Opts...)
clientRaw, err := state.s3Client(context.Background(), tc.s3Opts...)
require.NoError(err)
require.NotNil(clientRaw)
client, ok := clientRaw.(*testMockS3)
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package storage
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package testing
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package testing
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright (c) HashiCorp, Inc.
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: MPL-2.0
variable "iam_user_prefix" {
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright (c) HashiCorp, Inc.
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: MPL-2.0
terraform {
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright (c) HashiCorp, Inc.
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: MPL-2.0
variable "instance_tags" {
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright (c) HashiCorp, Inc.
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: MPL-2.0
resource "random_id" "foo" {
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright (c) HashiCorp, Inc.
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: MPL-2.0
resource "aws_iam_role" "valid" {
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright (c) HashiCorp, Inc.
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: MPL-2.0
terraform {
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright (c) HashiCorp, Inc.
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: MPL-2.0
output "bucket_name" {
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright (c) HashiCorp, Inc.
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: MPL-2.0
resource "aws_s3_bucket" "test" {
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright (c) HashiCorp, Inc.
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: MPL-2.0
data "aws_caller_identity" "current" {}
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package testing
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) HashiCorp, Inc.
// Copyright IBM Corp. 2021, 2026
// SPDX-License-Identifier: MPL-2.0
package testing