From 017c942dc589ed8411f7cb6f49bc2b57b7b3ce76 Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:12:18 +0100 Subject: [PATCH 01/15] refactor AWSSecretsManager to check region configuration after loading SDK config --- book/src/lib/client/aws_secrets_manager.md | 19 +++++++++---------- framework/secretsmanager.go | 12 +++++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/book/src/lib/client/aws_secrets_manager.md b/book/src/lib/client/aws_secrets_manager.md index 54cf899d8..07d16c850 100644 --- a/book/src/lib/client/aws_secrets_manager.md +++ b/book/src/lib/client/aws_secrets_manager.md @@ -10,11 +10,10 @@ Creating a new instance is straight-forward. You should either use environment v > [!NOTE] > Environment variables take precedence over shared credentials. -## Using environment variables -You can pass required configuration as following environment variables: -* `AWS_ACCESS_KEY_ID` -* `AWS_SECRET_ACCESS_KEY` -* `AWS_REGION` +Once you have an instance of AWS Secrets Manager you gain access to following functions: +* `CreateSecret(key string, val string, override bool) error` +* `GetSecret(key string) (AWSSecret, error)` +* `RemoveSecret(key string, noRecovery bool) error` ## Using shared credentials If you have shared credentials stored in `.aws/credentials` file, then the easiest way to configure the client is by setting @@ -23,11 +22,11 @@ If you have shared credentials stored in `.aws/credentials` file, then the easie > [!WARNING] > Remember, that most probably you will need to manually create a new session for that profile before running your application. - > [!NOTE] > You can read more about configuring the AWS SDK [here](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html). -Once you have an instance of AWS Secrets Manager you gain access to following functions: -* `CreateSecret(key string, val string, override bool) error` -* `GetSecret(key string) (AWSSecret, error)` -* `RemoveSecret(key string, noRecovery bool) error` \ No newline at end of file +## Using environment variables +You can pass required configuration as following environment variables: +* `AWS_ACCESS_KEY_ID` +* `AWS_SECRET_ACCESS_KEY` +* `AWS_REGION` diff --git a/framework/secretsmanager.go b/framework/secretsmanager.go index 2406397a0..6e5a5ae5e 100644 --- a/framework/secretsmanager.go +++ b/framework/secretsmanager.go @@ -66,14 +66,16 @@ type AWSSecretsManager struct { // NewAWSSecretsManager create a new connection to AWS Secrets Manager func NewAWSSecretsManager(requestTimeout time.Duration) (*AWSSecretsManager, error) { cfg, err := config.LoadDefaultConfig(context.TODO()) - region := os.Getenv("AWS_REGION") - if region == "" { - return nil, fmt.Errorf("region is required for AWSSecretsManager, use env variable: export AWS_REGION=...: %w", err) - } - cfg.Region = region if err != nil { return nil, fmt.Errorf("unable to load AWS SDK config, %v", err) } + if cfg.Region == "" { + region := os.Getenv("AWS_REGION") + if region == "" { + return nil, fmt.Errorf("region is required for AWSSecretsManager, use env variable: export AWS_REGION") + } + cfg.Region = region + } l := log.Logger.With().Str("Component", "AWSSecretsManager").Logger() l.Info().Msg("Connecting to AWS Secrets Manager") return &AWSSecretsManager{ From 648a4ea7dbfbe1b2bcb860ae88e9bd1f6aab76d0 Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:12:28 +0100 Subject: [PATCH 02/15] enhance ghsecrets tool to support AWS backend for managing test secrets --- tools/ghsecrets/go.mod | 82 ++++++++++++- tools/ghsecrets/go.sum | 251 ++++++++++++++++++++++++++++++++++++++++ tools/ghsecrets/main.go | 103 +++++++++++++---- 3 files changed, 412 insertions(+), 24 deletions(-) diff --git a/tools/ghsecrets/go.mod b/tools/ghsecrets/go.mod index 665c36a85..6c62c4a8c 100644 --- a/tools/ghsecrets/go.mod +++ b/tools/ghsecrets/go.mod @@ -2,11 +2,91 @@ module github.com/smartcontractkit/chainlink-testing-framework/tools/ghsecrets go 1.22.5 -require github.com/spf13/cobra v1.8.1 +require ( + github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.1 + github.com/spf13/cobra v1.8.1 +) require ( + dario.cat/mergo v1.0.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/aws/aws-sdk-go-v2 v1.31.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.39 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.37 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.23.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.31.3 // indirect + github.com/aws/smithy-go v1.21.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/containerd/containerd v1.7.18 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v27.1.1+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.6 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.22.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/klauspost/compress v1.17.4 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/rs/zerolog v1.33.0 // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/testcontainers/testcontainers-go v0.34.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) retract [v1.999.0-test-release, v1.999.999-test-release] + +replace github.com/smartcontractkit/chainlink-testing-framework/framework => ../../framework diff --git a/tools/ghsecrets/go.sum b/tools/ghsecrets/go.sum index 912390a78..c18a1ff83 100644 --- a/tools/ghsecrets/go.sum +++ b/tools/ghsecrets/go.sum @@ -1,10 +1,261 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U= +github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA= +github.com/aws/aws-sdk-go-v2/config v1.27.39 h1:FCylu78eTGzW1ynHcongXK9YHtoXD5AiiUqq3YfJYjU= +github.com/aws/aws-sdk-go-v2/config v1.27.39/go.mod h1:wczj2hbyskP4LjMKBEZwPRO1shXY+GsQleab+ZXT2ik= +github.com/aws/aws-sdk-go-v2/credentials v1.17.37 h1:G2aOH01yW8X373JK419THj5QVqu9vKEwxSEsGxihoW0= +github.com/aws/aws-sdk-go-v2/credentials v1.17.37/go.mod h1:0ecCjlb7htYCptRD45lXJ6aJDQac6D2NlKGpZqyTG6A= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20/go.mod h1:oAfOFzUB14ltPZj1rWwRc3d/6OgD76R8KlvU3EqM9Fg= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3 h1:W2M3kQSuN1+FXgV2wMv1JMWPxw/37wBN87QHYDuTV0Y= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3/go.mod h1:WyLS5qwXHtjKAONYZq/4ewdd+hcVsa3LBu77Ow5uj3k= +github.com/aws/aws-sdk-go-v2/service/sso v1.23.3 h1:rs4JCczF805+FDv2tRhZ1NU0RB2H6ryAvsWPanAr72Y= +github.com/aws/aws-sdk-go-v2/service/sso v1.23.3/go.mod h1:XRlMvmad0ZNL+75C5FYdMvbbLkd6qiqz6foR1nA1PXY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.3 h1:S7EPdMVZod8BGKQQPTBK+FcX9g7bKR7c4+HxWqHP7Vg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.3/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E= +github.com/aws/aws-sdk-go-v2/service/sts v1.31.3 h1:VzudTFrDCIDakXtemR7l6Qzt2+JYsVqo2MxBPt5k8T8= +github.com/aws/aws-sdk-go-v2/service/sts v1.31.3/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI= +github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA= +github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= +github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= +github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +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/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= +github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/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-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= +github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +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-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +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/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= +github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/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/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 h1:vlzZttNJGVqTsRFU9AmdnrcO1Znh8Ew9kCD//yjigk0= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +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= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/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= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index b7954eaef..7eb2bfe09 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -9,44 +9,70 @@ import ( "path/filepath" "strings" + "time" + + "github.com/smartcontractkit/chainlink-testing-framework/framework" + "github.com/spf13/cobra" ) func main() { var filePath string var customSecretID string + var backend string // Backend: GitHub or AWS var setCmd = &cobra.Command{ Use: "set", - Short: "Set test secrets in GitHub", + Short: "Set test secrets in GitHub or AWS", Run: func(cmd *cobra.Command, args []string) { - if !isGHInstalled() { - fmt.Println("GitHub CLI not found. Please go to https://cli.github.com/ and install it to use this tool.") - return - } - + // Validate file if err := validateFile(filePath); err != nil { fmt.Println(err) return } - secretID, err := getSecretID(customSecretID) - if err != nil { - log.Fatalf("Failed to obtain secret ID: %s", err) + var secretID string + var err error + + if customSecretID != "" { + secretID = customSecretID + } else { + if !isGHInstalled() { + fmt.Println("GitHub CLI not found. Please go to https://cli.github.com/ and install it to use this tool.") + return + } + secretID, err = generateSecretIDFromGithubUsername() + if err != nil { + log.Fatalf("Failed to generate secret ID: %s", err) + } } - setSecret(filePath, secretID) + // Set secret according to backend + switch strings.ToLower(backend) { + case "github": + if err := setGitHubSecret(filePath, secretID); err != nil { + log.Fatalf("Failed to set GitHub secret: %s", err) + } + case "aws": + if err := setAWSSecret(filePath, secretID); err != nil { + log.Fatalf("Failed to set AWS secret: %s", err) + } + default: + log.Fatalf("Unsupported backend: %s. Valid backends are 'github' or 'aws'.", backend) + } }, } var rootCmd = &cobra.Command{ Use: "ghsecrets", - Short: "A tool for managing GitHub test secrets", + Short: "A tool for managing GitHub or AWS test secrets", } rootCmd.AddCommand(setCmd) - setCmd.PersistentFlags().StringVarP(&filePath, "file", "f", defaultSecretsPath(), "path to dotenv file with test secrets") - setCmd.PersistentFlags().StringVarP(&customSecretID, "secret-id", "s", "", "custom secret ID. Do not use unless you know what you are doing") + + setCmd.PersistentFlags().StringVarP(&filePath, "file", "f", defaultSecretsPath(), "path to file with test secrets") + setCmd.PersistentFlags().StringVarP(&customSecretID, "secret-id", "s", "", "custom secret ID") + setCmd.PersistentFlags().StringVarP(&backend, "backend", "b", "aws", "Backend to use for storing secrets. Options: github, aws") if err := rootCmd.Execute(); err != nil { fmt.Println(err) @@ -78,10 +104,8 @@ func validateFile(filePath string) error { return nil } -func getSecretID(customID string) (string, error) { - if customID != "" { - return customID, nil - } +// generateSecretIDFromGithubUsername generates a secret ID based on the GitHub username +func generateSecretIDFromGithubUsername() (string, error) { usernameCmd := exec.Command("gh", "api", "user", "--jq", ".login") usernameOutput, err := usernameCmd.CombinedOutput() if err != nil { @@ -92,11 +116,13 @@ func getSecretID(customID string) (string, error) { return strings.ToUpper(secretID), nil } -func setSecret(filePath, secretID string) { - // Read the file content +// =========================== +// GitHub Secrets Logic +// =========================== +func setGitHubSecret(filePath, secretID string) error { data, err := os.ReadFile(filePath) if err != nil { - log.Fatalf("Failed to read file: %s", err) + return fmt.Errorf("failed to read file: %w", err) } // Base64 encode the file content @@ -109,13 +135,44 @@ func setSecret(filePath, secretID string) { // Execute the command to set the secret output, err := setSecretCmd.CombinedOutput() if err != nil { - log.Fatalf("Failed to set secret: %s\nOutput: %s", err, string(output)) + return fmt.Errorf("failed to set secret: %s\nOutput: %s", err, string(output)) } fmt.Printf( - "Test secret set successfully in Github with key: %s\n\n"+ - "To run a Github workflow with the test secrets, use the 'test_secrets_override_key' flag.\n"+ + "Test secret set successfully in GitHub with key: %s\n\n"+ + "To run a GitHub workflow with the test secrets, use the 'test_secrets_override_key' flag.\n"+ "Example: gh workflow run ${workflow_name} -f test_secrets_override_key=%s\n", secretID, secretID, ) + + return nil +} + +// =========================== +// AWS Secrets Manager Logic +// =========================== +func setAWSSecret(filePath, secretID string) error { + // 1. Read the file content + data, err := os.ReadFile(filePath) + if err != nil { + return fmt.Errorf("failed to read file: %w", err) + } + + // 2. Base64 encode the file content (or skip if you prefer raw) + encoded := base64.StdEncoding.EncodeToString(data) + + // 3. Create a new AWS Secrets Manager client + sm, err := framework.NewAWSSecretsManager(10 * time.Second) + if err != nil { + return fmt.Errorf("failed to initialize AWS Secrets Manager: %w", err) + } + + // 4. Create (or override) the secret + err = sm.CreateSecret(secretID, encoded, true) + if err != nil { + return fmt.Errorf("failed to create (or override) AWS secret: %w", err) + } + + fmt.Printf("Test secret set successfully in AWS with key: %s\n", secretID) + return nil } From 62d4c281ab965a218b8eb00b1323e855f53c7cf9 Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:21:22 +0100 Subject: [PATCH 03/15] add 'get' command to ghsecrets tool for retrieving secrets from AWS Secrets Manager --- tools/ghsecrets/main.go | 79 ++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index 7eb2bfe09..2a380e077 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -8,19 +8,19 @@ import ( "os/exec" "path/filepath" "strings" - "time" "github.com/smartcontractkit/chainlink-testing-framework/framework" - "github.com/spf13/cobra" ) func main() { var filePath string - var customSecretID string + var secretID string var backend string // Backend: GitHub or AWS + var decode bool // Decode flag for `get` + // Set Command var setCmd = &cobra.Command{ Use: "set", Short: "Set test secrets in GitHub or AWS", @@ -31,16 +31,12 @@ func main() { return } - var secretID string - var err error - - if customSecretID != "" { - secretID = customSecretID - } else { + if secretID == "" { if !isGHInstalled() { fmt.Println("GitHub CLI not found. Please go to https://cli.github.com/ and install it to use this tool.") return } + var err error secretID, err = generateSecretIDFromGithubUsername() if err != nil { log.Fatalf("Failed to generate secret ID: %s", err) @@ -63,17 +59,42 @@ func main() { }, } + // Get Command + var getCmd = &cobra.Command{ + Use: "get", + Short: "Retrieve a secret from AWS Secrets Manager", + Run: func(cmd *cobra.Command, args []string) { + if strings.ToLower(backend) != "aws" { + log.Fatalf("The 'get' command only supports the AWS backend.") + } + + if secretID == "" { + log.Fatalf("You must specify a secret ID using the --secret-id flag.") + } + + // Retrieve the secret from AWS Secrets Manager + if err := getAWSSecret(secretID, decode); err != nil { + log.Fatalf("Failed to retrieve AWS secret: %s", err) + } + }, + } + var rootCmd = &cobra.Command{ Use: "ghsecrets", Short: "A tool for managing GitHub or AWS test secrets", } rootCmd.AddCommand(setCmd) + rootCmd.AddCommand(getCmd) setCmd.PersistentFlags().StringVarP(&filePath, "file", "f", defaultSecretsPath(), "path to file with test secrets") - setCmd.PersistentFlags().StringVarP(&customSecretID, "secret-id", "s", "", "custom secret ID") + setCmd.PersistentFlags().StringVarP(&secretID, "secret-id", "s", "", "ID of the secret to set") setCmd.PersistentFlags().StringVarP(&backend, "backend", "b", "aws", "Backend to use for storing secrets. Options: github, aws") + getCmd.PersistentFlags().StringVarP(&secretID, "secret-id", "s", "", "ID of the secret to retrieve") + getCmd.PersistentFlags().StringVarP(&backend, "backend", "b", "aws", "Backend to use for retrieving secrets. Only 'aws' is supported for this command.") + getCmd.PersistentFlags().BoolVarP(&decode, "decode", "d", false, "Decode the Base64-encoded secret value") + if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) @@ -116,9 +137,7 @@ func generateSecretIDFromGithubUsername() (string, error) { return strings.ToUpper(secretID), nil } -// =========================== -// GitHub Secrets Logic -// =========================== +// setGitHubSecret sets a test secret in GitHub Secrets func setGitHubSecret(filePath, secretID string) error { data, err := os.ReadFile(filePath) if err != nil { @@ -148,31 +167,43 @@ func setGitHubSecret(filePath, secretID string) error { return nil } -// =========================== -// AWS Secrets Manager Logic -// =========================== +// getAWSSecret retrieves a test secret from AWS Secrets Manager func setAWSSecret(filePath, secretID string) error { - // 1. Read the file content data, err := os.ReadFile(filePath) if err != nil { return fmt.Errorf("failed to read file: %w", err) } - - // 2. Base64 encode the file content (or skip if you prefer raw) encoded := base64.StdEncoding.EncodeToString(data) - - // 3. Create a new AWS Secrets Manager client sm, err := framework.NewAWSSecretsManager(10 * time.Second) if err != nil { return fmt.Errorf("failed to initialize AWS Secrets Manager: %w", err) } - - // 4. Create (or override) the secret err = sm.CreateSecret(secretID, encoded, true) if err != nil { return fmt.Errorf("failed to create (or override) AWS secret: %w", err) } - fmt.Printf("Test secret set successfully in AWS with key: %s\n", secretID) return nil } + +// getAWSSecret retrieves a test secret from AWS Secrets Manager +func getAWSSecret(secretID string, decode bool) error { + sm, err := framework.NewAWSSecretsManager(10 * time.Second) + if err != nil { + return fmt.Errorf("failed to initialize AWS Secrets Manager: %w", err) + } + secret, err := sm.GetSecret(secretID) + if err != nil { + return fmt.Errorf("failed to retrieve AWS secret: %w", err) + } + value := secret.Value() + if decode { + decoded, err := base64.StdEncoding.DecodeString(value) + if err != nil { + return fmt.Errorf("failed to decode secret value: %w", err) + } + value = string(decoded) + } + fmt.Println(value) + return nil +} From 699d16b58109f14ded1a858ffbb0f58638dbbba1 Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:48:56 +0100 Subject: [PATCH 04/15] enhance ghsecrets tool to support sharing secrets with specified IAM ARNs in AWS --- tools/ghsecrets/go.mod | 72 +------------ tools/ghsecrets/go.sum | 223 ---------------------------------------- tools/ghsecrets/main.go | 223 +++++++++++++++++++++++++++++----------- 3 files changed, 168 insertions(+), 350 deletions(-) diff --git a/tools/ghsecrets/go.mod b/tools/ghsecrets/go.mod index 6c62c4a8c..9e9625f53 100644 --- a/tools/ghsecrets/go.mod +++ b/tools/ghsecrets/go.mod @@ -3,16 +3,14 @@ module github.com/smartcontractkit/chainlink-testing-framework/tools/ghsecrets go 1.22.5 require ( - github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.1 + github.com/aws/aws-sdk-go-v2 v1.31.0 + github.com/aws/aws-sdk-go-v2/config v1.27.39 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3 + github.com/aws/aws-sdk-go-v2/service/sts v1.31.3 github.com/spf13/cobra v1.8.1 ) require ( - dario.cat/mergo v1.0.0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/aws/aws-sdk-go-v2 v1.31.0 // indirect - github.com/aws/aws-sdk-go-v2/config v1.27.39 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.37 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect @@ -20,73 +18,11 @@ require ( github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.23.3 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.31.3 // indirect github.com/aws/smithy-go v1.21.0 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/containerd/containerd v1.7.18 // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/containerd/platforms v0.2.1 // indirect - github.com/cpuguy83/dockercfg v0.3.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/distribution/reference v0.6.0 // indirect - github.com/docker/docker v27.1.1+incompatible // indirect - github.com/docker/go-connections v0.5.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/gabriel-vasile/mimetype v1.4.6 // indirect - github.com/go-logr/logr v1.4.1 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.22.1 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/patternmatcher v0.6.0 // indirect - github.com/moby/sys/sequential v0.5.0 // indirect - github.com/moby/sys/user v0.1.0 // indirect - github.com/moby/term v0.5.0 // indirect - github.com/morikuni/aec v1.0.0 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/rs/zerolog v1.33.0 // indirect - github.com/shirou/gopsutil/v3 v3.23.12 // indirect - github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.9.0 // indirect - github.com/testcontainers/testcontainers-go v0.34.0 // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.24.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/net v0.30.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) retract [v1.999.0-test-release, v1.999.999-test-release] - -replace github.com/smartcontractkit/chainlink-testing-framework/framework => ../../framework diff --git a/tools/ghsecrets/go.sum b/tools/ghsecrets/go.sum index c18a1ff83..d3a6e00a2 100644 --- a/tools/ghsecrets/go.sum +++ b/tools/ghsecrets/go.sum @@ -1,11 +1,3 @@ -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U= github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA= github.com/aws/aws-sdk-go-v2/config v1.27.39 h1:FCylu78eTGzW1ynHcongXK9YHtoXD5AiiUqq3YfJYjU= @@ -34,228 +26,13 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.31.3 h1:VzudTFrDCIDakXtemR7l6Qzt2+JY github.com/aws/aws-sdk-go-v2/service/sts v1.31.3/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI= github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA= github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= -github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= -github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= -github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= -github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -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/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= -github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= -github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/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-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= -github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= -github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -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-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -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/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= -github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= -github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= -github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= -github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= -github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= -github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= -go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= -go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/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/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 h1:vlzZttNJGVqTsRFU9AmdnrcO1Znh8Ew9kCD//yjigk0= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= -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= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/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= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index 2a380e077..28b84a134 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -1,24 +1,31 @@ package main import ( + "context" "encoding/base64" + "encoding/json" + "errors" "fmt" "log" "os" "os/exec" "path/filepath" "strings" - "time" - "github.com/smartcontractkit/chainlink-testing-framework/framework" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types" + "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/spf13/cobra" ) func main() { var filePath string var secretID string - var backend string // Backend: GitHub or AWS - var decode bool // Decode flag for `get` + var backend string // Backend: GitHub or AWS + var decode bool // Decode flag for `get` + var sharedWith []string // List of ARNs to share the secret with // Set Command var setCmd = &cobra.Command{ @@ -43,14 +50,13 @@ func main() { } } - // Set secret according to backend switch strings.ToLower(backend) { case "github": if err := setGitHubSecret(filePath, secretID); err != nil { log.Fatalf("Failed to set GitHub secret: %s", err) } case "aws": - if err := setAWSSecret(filePath, secretID); err != nil { + if err := setAWSSecret(filePath, secretID, sharedWith); err != nil { log.Fatalf("Failed to set AWS secret: %s", err) } default: @@ -72,7 +78,6 @@ func main() { log.Fatalf("You must specify a secret ID using the --secret-id flag.") } - // Retrieve the secret from AWS Secrets Manager if err := getAWSSecret(secretID, decode); err != nil { log.Fatalf("Failed to retrieve AWS secret: %s", err) } @@ -87,9 +92,10 @@ func main() { rootCmd.AddCommand(setCmd) rootCmd.AddCommand(getCmd) - setCmd.PersistentFlags().StringVarP(&filePath, "file", "f", defaultSecretsPath(), "path to file with test secrets") + setCmd.PersistentFlags().StringVarP(&filePath, "file", "f", defaultSecretsPath(), "Path to file with test secrets") setCmd.PersistentFlags().StringVarP(&secretID, "secret-id", "s", "", "ID of the secret to set") setCmd.PersistentFlags().StringVarP(&backend, "backend", "b", "aws", "Backend to use for storing secrets. Options: github, aws") + setCmd.PersistentFlags().StringSliceVar(&sharedWith, "shared-with", []string{}, "Comma-separated list of IAM ARNs to share the secret with") getCmd.PersistentFlags().StringVarP(&secretID, "secret-id", "s", "", "ID of the secret to retrieve") getCmd.PersistentFlags().StringVarP(&backend, "backend", "b", "aws", "Backend to use for retrieving secrets. Only 'aws' is supported for this command.") @@ -101,43 +107,7 @@ func main() { } } -func defaultSecretsPath() string { - homeDir, err := os.UserHomeDir() - if err != nil { - log.Fatalf("Failed to get user home directory: %s", err) - } - return filepath.Join(homeDir, ".testsecrets") -} - -func isGHInstalled() bool { - _, err := exec.LookPath("gh") - return err == nil -} - -func validateFile(filePath string) error { - info, err := os.Stat(filePath) - if os.IsNotExist(err) { - return fmt.Errorf("file '%s' does not exist", filePath) - } - if info.Size() == 0 { - return fmt.Errorf("file '%s' is empty", filePath) - } - return nil -} - -// generateSecretIDFromGithubUsername generates a secret ID based on the GitHub username -func generateSecretIDFromGithubUsername() (string, error) { - usernameCmd := exec.Command("gh", "api", "user", "--jq", ".login") - usernameOutput, err := usernameCmd.CombinedOutput() - if err != nil { - return "", fmt.Errorf("failed to execute command: %s, output: %s", err, usernameOutput) - } - trimmedUsername := strings.TrimSpace(string(usernameOutput)) - secretID := fmt.Sprintf("BASE64_TESTSECRETS_%s", trimmedUsername) - return strings.ToUpper(secretID), nil -} - -// setGitHubSecret sets a test secret in GitHub Secrets +// setGitHubSecret creates or updates a secret in GitHub func setGitHubSecret(filePath, secretID string) error { data, err := os.ReadFile(filePath) if err != nil { @@ -151,7 +121,6 @@ func setGitHubSecret(filePath, secretID string) error { setSecretCmd := exec.Command("gh", "secret", "set", secretID, "--body", encoded) setSecretCmd.Stdin = strings.NewReader(encoded) - // Execute the command to set the secret output, err := setSecretCmd.CombinedOutput() if err != nil { return fmt.Errorf("failed to set secret: %s\nOutput: %s", err, string(output)) @@ -163,40 +132,137 @@ func setGitHubSecret(filePath, secretID string) error { "Example: gh workflow run ${workflow_name} -f test_secrets_override_key=%s\n", secretID, secretID, ) - return nil } -// getAWSSecret retrieves a test secret from AWS Secrets Manager -func setAWSSecret(filePath, secretID string) error { +// setAWSSecret creates or updates a secret in AWS Secrets Manager +func setAWSSecret(filePath, secretID string, sharedWith []string) error { data, err := os.ReadFile(filePath) if err != nil { return fmt.Errorf("failed to read file: %w", err) } encoded := base64.StdEncoding.EncodeToString(data) - sm, err := framework.NewAWSSecretsManager(10 * time.Second) + + // 1) Load AWS config + cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { - return fmt.Errorf("failed to initialize AWS Secrets Manager: %w", err) + return fmt.Errorf("failed to load AWS config: %w", err) } - err = sm.CreateSecret(secretID, encoded, true) + + // 2) Create Secrets Manager client + smClient := secretsmanager.NewFromConfig(cfg) + + // 3) Try creating the secret + _, err = smClient.CreateSecret(context.TODO(), &secretsmanager.CreateSecretInput{ + Name: aws.String(secretID), + SecretString: aws.String(encoded), + Description: aws.String("Secret created by ghsecrets CLI"), + }) if err != nil { - return fmt.Errorf("failed to create (or override) AWS secret: %w", err) + // If the secret already exists, update it instead + var resourceExistsErr *types.ResourceExistsException + if errors.As(err, &resourceExistsErr) { + fmt.Printf("Secret %s already exists, updating its value...\n", secretID) + _, err = smClient.UpdateSecret(context.TODO(), &secretsmanager.UpdateSecretInput{ + SecretId: aws.String(secretID), + SecretString: aws.String(encoded), + Description: aws.String("Secret updated by ghsecrets CLI"), + }) + if err != nil { + return fmt.Errorf("failed to update AWS secret: %w", err) + } + } else { + return fmt.Errorf("failed to create AWS secret: %w", err) + } + } else { + fmt.Printf("Secret %s created successfully.\n", secretID) + } + + // 4) Update sharing policy if necessary + if len(sharedWith) > 0 { + err = updateAWSSecretAccessPolicy(secretID, sharedWith) + if err != nil { + return fmt.Errorf("failed to update secret sharing policy: %w", err) + } } - fmt.Printf("Test secret set successfully in AWS with key: %s\n", secretID) + return nil +} + +// updateAWSSecretAccessPolicy updates the sharing policy for a secret in AWS Secrets Manager +func updateAWSSecretAccessPolicy(secretID string, sharedWith []string) error { + // 1) Load AWS config + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + return fmt.Errorf("failed to load AWS config: %w", err) + } + + // 2) Create an STS client to find your AWS Account ID + stsClient := sts.NewFromConfig(cfg) + callerIdentity, err := stsClient.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{}) + if err != nil { + return fmt.Errorf("failed to get caller identity: %w", err) + } + accountID := aws.ToString(callerIdentity.Account) + + // 3) Build a list of principals to allow access + principals := []string{ + fmt.Sprintf("arn:aws:iam::%s:root", accountID), // Your AWS account + } + principals = append(principals, sharedWith...) // Add additional ARNs + + // 4) Build the policy + statements := []map[string]interface{}{ + { + "Sid": "AllowAccessToSpecificPrincipals", + "Effect": "Allow", + "Action": "secretsmanager:GetSecretValue", + "Resource": fmt.Sprintf("arn:aws:secretsmanager:%s:%s:secret:%s", + cfg.Region, accountID, secretID), + "Principal": map[string]interface{}{ + "AWS": principals, + }, + }, + } + policyDoc := map[string]interface{}{ + "Version": "2012-10-17", + "Statement": statements, + } + + policyBytes, err := json.Marshal(policyDoc) + if err != nil { + return fmt.Errorf("failed to marshal resource policy: %w", err) + } + + // 5) Attach the resource policy to the secret + smClient := secretsmanager.NewFromConfig(cfg) + _, err = smClient.PutResourcePolicy(context.TODO(), &secretsmanager.PutResourcePolicyInput{ + SecretId: aws.String(secretID), + ResourcePolicy: aws.String(string(policyBytes)), + }) + if err != nil { + return fmt.Errorf("failed to attach resource policy: %w", err) + } + + fmt.Printf("Updated sharing policy for secret: %s\n", secretID) return nil } // getAWSSecret retrieves a test secret from AWS Secrets Manager func getAWSSecret(secretID string, decode bool) error { - sm, err := framework.NewAWSSecretsManager(10 * time.Second) + cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { - return fmt.Errorf("failed to initialize AWS Secrets Manager: %w", err) + return fmt.Errorf("failed to load AWS config: %w", err) } - secret, err := sm.GetSecret(secretID) + + smClient := secretsmanager.NewFromConfig(cfg) + out, err := smClient.GetSecretValue(context.TODO(), &secretsmanager.GetSecretValueInput{ + SecretId: aws.String(secretID), + }) if err != nil { return fmt.Errorf("failed to retrieve AWS secret: %w", err) } - value := secret.Value() + + value := aws.ToString(out.SecretString) if decode { decoded, err := base64.StdEncoding.DecodeString(value) if err != nil { @@ -204,6 +270,45 @@ func getAWSSecret(secretID string, decode bool) error { } value = string(decoded) } - fmt.Println(value) + + fmt.Printf("Retrieved secret value:\n%s\n", value) + return nil +} + +// ======================================================================= +// Utility Functions +// ======================================================================= +func defaultSecretsPath() string { + homeDir, err := os.UserHomeDir() + if err != nil { + log.Fatalf("Failed to get user home directory: %s", err) + } + return filepath.Join(homeDir, ".testsecrets") +} + +func isGHInstalled() bool { + _, err := exec.LookPath("gh") + return err == nil +} + +func validateFile(filePath string) error { + info, err := os.Stat(filePath) + if os.IsNotExist(err) { + return fmt.Errorf("file '%s' does not exist", filePath) + } + if info.Size() == 0 { + return fmt.Errorf("file '%s' is empty", filePath) + } return nil } + +func generateSecretIDFromGithubUsername() (string, error) { + usernameCmd := exec.Command("gh", "api", "user", "--jq", ".login") + usernameOutput, err := usernameCmd.CombinedOutput() + if err != nil { + return "", fmt.Errorf("failed to execute command: %s, output: %s", err, usernameOutput) + } + trimmedUsername := strings.TrimSpace(string(usernameOutput)) + secretID := fmt.Sprintf("BASE64_TESTSECRETS_%s", trimmedUsername) + return strings.ToUpper(secretID), nil +} From 4164fbf237373ded003d09b819c81962989c5627 Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:24:47 +0100 Subject: [PATCH 05/15] Handle not authenticated --- tools/ghsecrets/main.go | 67 +++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index 28b84a134..db81e24ba 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -34,33 +34,37 @@ func main() { Run: func(cmd *cobra.Command, args []string) { // Validate file if err := validateFile(filePath); err != nil { - fmt.Println(err) + exitWithError(err, "Failed to validate file") return } if secretID == "" { if !isGHInstalled() { - fmt.Println("GitHub CLI not found. Please go to https://cli.github.com/ and install it to use this tool.") + exitWithError(nil, "GitHub CLI not found. Please go to https://cli.github.com/ and install it to use this tool.") return } var err error secretID, err = generateSecretIDFromGithubUsername() if err != nil { - log.Fatalf("Failed to generate secret ID: %s", err) + exitWithError(err, "Failed to generate secret ID") + return } } switch strings.ToLower(backend) { case "github": if err := setGitHubSecret(filePath, secretID); err != nil { - log.Fatalf("Failed to set GitHub secret: %s", err) + exitWithError(err, "Failed to set GitHub secret") + return } case "aws": if err := setAWSSecret(filePath, secretID, sharedWith); err != nil { - log.Fatalf("Failed to set AWS secret: %s", err) + exitWithError(err, "Failed to set AWS secret") + return } default: - log.Fatalf("Unsupported backend: %s. Valid backends are 'github' or 'aws'.", backend) + exitWithError(nil, "Unsupported backend. Valid backends are 'github' or 'aws'.") + return } }, } @@ -70,16 +74,8 @@ func main() { Use: "get", Short: "Retrieve a secret from AWS Secrets Manager", Run: func(cmd *cobra.Command, args []string) { - if strings.ToLower(backend) != "aws" { - log.Fatalf("The 'get' command only supports the AWS backend.") - } - - if secretID == "" { - log.Fatalf("You must specify a secret ID using the --secret-id flag.") - } - if err := getAWSSecret(secretID, decode); err != nil { - log.Fatalf("Failed to retrieve AWS secret: %s", err) + exitWithError(err, "Failed to retrieve AWS secret") } }, } @@ -98,12 +94,13 @@ func main() { setCmd.PersistentFlags().StringSliceVar(&sharedWith, "shared-with", []string{}, "Comma-separated list of IAM ARNs to share the secret with") getCmd.PersistentFlags().StringVarP(&secretID, "secret-id", "s", "", "ID of the secret to retrieve") - getCmd.PersistentFlags().StringVarP(&backend, "backend", "b", "aws", "Backend to use for retrieving secrets. Only 'aws' is supported for this command.") getCmd.PersistentFlags().BoolVarP(&decode, "decode", "d", false, "Decode the Base64-encoded secret value") + // Make secretID a required flag for the set command + getCmd.MarkPersistentFlagRequired("secret-id") + if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) + exitWithError(err, "Failed to execute command") } } @@ -156,7 +153,7 @@ func setAWSSecret(filePath, secretID string, sharedWith []string) error { _, err = smClient.CreateSecret(context.TODO(), &secretsmanager.CreateSecretInput{ Name: aws.String(secretID), SecretString: aws.String(encoded), - Description: aws.String("Secret created by ghsecrets CLI"), + Description: aws.String("Chainlink Test Secret created by CTF/ghsecrets CLI"), }) if err != nil { // If the secret already exists, update it instead @@ -169,9 +166,24 @@ func setAWSSecret(filePath, secretID string, sharedWith []string) error { Description: aws.String("Secret updated by ghsecrets CLI"), }) if err != nil { + // Check for the SSO token expiration error + if strings.Contains(err.Error(), "InvalidGrantException") { + return fmt.Errorf( + "Your AWS SSO session has likely expired. Please re-authenticate by running:\n\n aws sso login --profile \n\nThen try again.\n\nOriginal error: %w", + err, + ) + } return fmt.Errorf("failed to update AWS secret: %w", err) } + fmt.Printf("Secret %s updated successfully.\n", secretID) } else { + // Check for the SSO token expiration error + if strings.Contains(err.Error(), "InvalidGrantException") { + return fmt.Errorf( + "Your AWS SSO session has likely expired. Please re-authenticate by running:\n\n aws sso login --profile \n\nThen try again.\n\nOriginal error: %w", + err, + ) + } return fmt.Errorf("failed to create AWS secret: %w", err) } } else { @@ -247,6 +259,7 @@ func updateAWSSecretAccessPolicy(secretID string, sharedWith []string) error { return nil } +// getAWSSecret retrieves a test secret from AWS Secrets Manager // getAWSSecret retrieves a test secret from AWS Secrets Manager func getAWSSecret(secretID string, decode bool) error { cfg, err := config.LoadDefaultConfig(context.TODO()) @@ -259,6 +272,13 @@ func getAWSSecret(secretID string, decode bool) error { SecretId: aws.String(secretID), }) if err != nil { + // Check if the error is due to an expired SSO token + if strings.Contains(err.Error(), "InvalidGrantException") { + return fmt.Errorf( + "Your AWS SSO session has likely expired. Please re-authenticate by running:\n\n aws sso login --profile \n\nThen try again.\n\nOriginal error: %w", + err, + ) + } return fmt.Errorf("failed to retrieve AWS secret: %w", err) } @@ -312,3 +332,12 @@ func generateSecretIDFromGithubUsername() (string, error) { secretID := fmt.Sprintf("BASE64_TESTSECRETS_%s", trimmedUsername) return strings.ToUpper(secretID), nil } + +func exitWithError(err error, msg string) { + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %v\n", msg, err) + } else { + fmt.Fprintf(os.Stderr, "%s\n", msg) + } + os.Exit(1) +} From b1b028012e831d60f113858250df1e2bd993ab2d Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:54:19 +0100 Subject: [PATCH 06/15] Ensure secretID starts with "testsecrets/" prefix and improve success message for AWS Secrets Manager --- tools/ghsecrets/main.go | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index db81e24ba..bac6ae06f 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -32,7 +32,6 @@ func main() { Use: "set", Short: "Set test secrets in GitHub or AWS", Run: func(cmd *cobra.Command, args []string) { - // Validate file if err := validateFile(filePath); err != nil { exitWithError(err, "Failed to validate file") return @@ -51,6 +50,9 @@ func main() { } } + // Ensure secretID starts with "testsecrets/" + secretID = ensurePrefix(secretID, "testsecrets/") + switch strings.ToLower(backend) { case "github": if err := setGitHubSecret(filePath, secretID); err != nil { @@ -134,29 +136,27 @@ func setGitHubSecret(filePath, secretID string) error { // setAWSSecret creates or updates a secret in AWS Secrets Manager func setAWSSecret(filePath, secretID string, sharedWith []string) error { + secretID = ensurePrefix(secretID, "testsecrets/") // Ensure prefix + data, err := os.ReadFile(filePath) if err != nil { return fmt.Errorf("failed to read file: %w", err) } encoded := base64.StdEncoding.EncodeToString(data) - // 1) Load AWS config cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { return fmt.Errorf("failed to load AWS config: %w", err) } - // 2) Create Secrets Manager client smClient := secretsmanager.NewFromConfig(cfg) - // 3) Try creating the secret _, err = smClient.CreateSecret(context.TODO(), &secretsmanager.CreateSecretInput{ Name: aws.String(secretID), SecretString: aws.String(encoded), Description: aws.String("Chainlink Test Secret created by CTF/ghsecrets CLI"), }) if err != nil { - // If the secret already exists, update it instead var resourceExistsErr *types.ResourceExistsException if errors.As(err, &resourceExistsErr) { fmt.Printf("Secret %s already exists, updating its value...\n", secretID) @@ -166,7 +166,6 @@ func setAWSSecret(filePath, secretID string, sharedWith []string) error { Description: aws.String("Secret updated by ghsecrets CLI"), }) if err != nil { - // Check for the SSO token expiration error if strings.Contains(err.Error(), "InvalidGrantException") { return fmt.Errorf( "Your AWS SSO session has likely expired. Please re-authenticate by running:\n\n aws sso login --profile \n\nThen try again.\n\nOriginal error: %w", @@ -175,9 +174,7 @@ func setAWSSecret(filePath, secretID string, sharedWith []string) error { } return fmt.Errorf("failed to update AWS secret: %w", err) } - fmt.Printf("Secret %s updated successfully.\n", secretID) } else { - // Check for the SSO token expiration error if strings.Contains(err.Error(), "InvalidGrantException") { return fmt.Errorf( "Your AWS SSO session has likely expired. Please re-authenticate by running:\n\n aws sso login --profile \n\nThen try again.\n\nOriginal error: %w", @@ -186,17 +183,22 @@ func setAWSSecret(filePath, secretID string, sharedWith []string) error { } return fmt.Errorf("failed to create AWS secret: %w", err) } - } else { - fmt.Printf("Secret %s created successfully.\n", secretID) } - // 4) Update sharing policy if necessary if len(sharedWith) > 0 { err = updateAWSSecretAccessPolicy(secretID, sharedWith) if err != nil { return fmt.Errorf("failed to update secret sharing policy: %w", err) } } + + // Success message with AWS-specific instructions + fmt.Printf( + "Test secret set successfully in AWS Secrets Manager with key: %s\n\n"+ + "To use this secret in a GitHub workflow, set the 'test_secrets_override_key' flag with the 'aws:' prefix. Example:\n"+ + "gh workflow run ${workflow_name} -f test_secrets_override_key=aws:%s\n", + secretID, secretID, + ) return nil } @@ -333,6 +335,13 @@ func generateSecretIDFromGithubUsername() (string, error) { return strings.ToUpper(secretID), nil } +func ensurePrefix(secretID, prefix string) string { + if !strings.HasPrefix(secretID, prefix) { + return prefix + secretID + } + return secretID +} + func exitWithError(err error, msg string) { if err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", msg, err) From 632660c917e774a6c1073c01c9fad60358b9994e Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:56:53 +0100 Subject: [PATCH 07/15] revert some changes --- book/src/lib/client/aws_secrets_manager.md | 19 ++++++++++--------- framework/secretsmanager.go | 12 +++++------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/book/src/lib/client/aws_secrets_manager.md b/book/src/lib/client/aws_secrets_manager.md index 07d16c850..54cf899d8 100644 --- a/book/src/lib/client/aws_secrets_manager.md +++ b/book/src/lib/client/aws_secrets_manager.md @@ -10,10 +10,11 @@ Creating a new instance is straight-forward. You should either use environment v > [!NOTE] > Environment variables take precedence over shared credentials. -Once you have an instance of AWS Secrets Manager you gain access to following functions: -* `CreateSecret(key string, val string, override bool) error` -* `GetSecret(key string) (AWSSecret, error)` -* `RemoveSecret(key string, noRecovery bool) error` +## Using environment variables +You can pass required configuration as following environment variables: +* `AWS_ACCESS_KEY_ID` +* `AWS_SECRET_ACCESS_KEY` +* `AWS_REGION` ## Using shared credentials If you have shared credentials stored in `.aws/credentials` file, then the easiest way to configure the client is by setting @@ -22,11 +23,11 @@ If you have shared credentials stored in `.aws/credentials` file, then the easie > [!WARNING] > Remember, that most probably you will need to manually create a new session for that profile before running your application. + > [!NOTE] > You can read more about configuring the AWS SDK [here](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html). -## Using environment variables -You can pass required configuration as following environment variables: -* `AWS_ACCESS_KEY_ID` -* `AWS_SECRET_ACCESS_KEY` -* `AWS_REGION` +Once you have an instance of AWS Secrets Manager you gain access to following functions: +* `CreateSecret(key string, val string, override bool) error` +* `GetSecret(key string) (AWSSecret, error)` +* `RemoveSecret(key string, noRecovery bool) error` \ No newline at end of file diff --git a/framework/secretsmanager.go b/framework/secretsmanager.go index 6e5a5ae5e..2406397a0 100644 --- a/framework/secretsmanager.go +++ b/framework/secretsmanager.go @@ -66,16 +66,14 @@ type AWSSecretsManager struct { // NewAWSSecretsManager create a new connection to AWS Secrets Manager func NewAWSSecretsManager(requestTimeout time.Duration) (*AWSSecretsManager, error) { cfg, err := config.LoadDefaultConfig(context.TODO()) + region := os.Getenv("AWS_REGION") + if region == "" { + return nil, fmt.Errorf("region is required for AWSSecretsManager, use env variable: export AWS_REGION=...: %w", err) + } + cfg.Region = region if err != nil { return nil, fmt.Errorf("unable to load AWS SDK config, %v", err) } - if cfg.Region == "" { - region := os.Getenv("AWS_REGION") - if region == "" { - return nil, fmt.Errorf("region is required for AWSSecretsManager, use env variable: export AWS_REGION") - } - cfg.Region = region - } l := log.Logger.With().Str("Component", "AWSSecretsManager").Logger() l.Info().Msg("Connecting to AWS Secrets Manager") return &AWSSecretsManager{ From 807cf961824d5501b7b1eb2de6e27e1577202632 Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:58:43 +0100 Subject: [PATCH 08/15] Fix --- tools/ghsecrets/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index bac6ae06f..f0b00f3b5 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -50,9 +50,6 @@ func main() { } } - // Ensure secretID starts with "testsecrets/" - secretID = ensurePrefix(secretID, "testsecrets/") - switch strings.ToLower(backend) { case "github": if err := setGitHubSecret(filePath, secretID); err != nil { @@ -60,6 +57,9 @@ func main() { return } case "aws": + // Ensure AWS secretID starts with "testsecrets/" prefix + // GHA IAM role has a policy that restricts access to secrets with this prefix + secretID = ensurePrefix(secretID, "testsecrets/") if err := setAWSSecret(filePath, secretID, sharedWith); err != nil { exitWithError(err, "Failed to set AWS secret") return From 1373324724b5fb8c57a23d116d3830a855e7ae67 Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:09:14 +0100 Subject: [PATCH 09/15] Update readme --- tools/ghsecrets/README.md | 163 ++++++++++++++++++++++++++++++++------ 1 file changed, 137 insertions(+), 26 deletions(-) diff --git a/tools/ghsecrets/README.md b/tools/ghsecrets/README.md index cb7c1b533..8dde4dd22 100644 --- a/tools/ghsecrets/README.md +++ b/tools/ghsecrets/README.md @@ -1,58 +1,169 @@ # ghsecrets -ghsecrets is a command-line tool designed to manage and set test secrets in GitHub via the GitHub CLI. +`ghsecrets` is a command-line tool designed to manage and set test secrets in either: + +- **GitHub** (via the GitHub CLI), or +- **AWS Secrets Manager**. + +This tool helps streamline the process of storing test secrets which can be referenced by your workflows or other services. + +--- ## Installation -To install ghsecrets CLI, you need to have Go installed on your machine. With Go installed, run the following command: +To install the `ghsecrets` CLI, ensure you have Go installed. Then run: ```sh go install github.com/smartcontractkit/chainlink-testing-framework/tools/ghsecrets@latest ``` -Please install GitHub CLI to use this tool - https://cli.github.com/ +Note: If you plan to set secrets in GitHub, please also install the GitHub CLI (gh). ## Usage -Set default test secrets from ~/.testsecrets file: +### 1. Setting Secrets + +By default, `ghsecrets set` assumes you want to store secrets in AWS Secrets Manager, using a file from `~/.testsecrets` (if not specified). You can change the backend to GitHub, specify a custom file path, or share the AWS secret with other IAM principals. Below are common examples: + +#### a) Set secrets in AWS (default) + +This will read from `~/.testsecrets` (by default) and create/update a secret in AWS Secrets Manager: ```sh ghsecrets set ``` +If you’d like to specify a different file: + +```sh +ghsecrets set --file /path/to/mysecrets.env +``` + +If you’d like to specify a custom secret name: + +```sh +ghsecrets set --secret-id my-custom-secret +``` + +Note: For AWS backend, the tool automatically adds the `testsecrets/` prefix if it is missing. This ensures consistency and allows GitHub Actions to access all secrets with this designated prefix. + +If you’d like to share this secret with additional AWS IAM principals (e.g., a collaborator’s account): + +```sh +ghsecrets set --shared-with arn:aws:iam::123456789012:role/SomeRole +``` + +You can specify multiple ARNs using commas: + +```sh +ghsecrets set --shared-with arn:aws:iam::123456789012:role/SomeRole,arn:aws:iam::345678901234:root +``` + +#### b) Set secrets in GitHub + +```sh +ghsecrets set --backend github +``` + +This will: +1. Read from the default file (`~/.testsecrets`) unless `--file` is specified. +2. Base64-encode the content. +3. Create/update a GitHub secret using the GitHub CLI. + +### 2. Retrieving Secrets (AWS Only) + +If you want to retrieve an existing secret from AWS Secrets Manager, use: + +```sh +ghsecrets get --secret-id testsecrets/MySecretName +``` + +By default, it prints out the Base64-encoded string. To decode it automatically: + +```sh +ghsecrets get --secret-id testsecrets/MySecretName --decode +``` + ## FAQ -### Q: What should I do if I get "command not found: ghsecrets" after installation? +
+Q: I get "command not found: ghsecrets" after installation. How do I fix this? + +This error typically means the directory where Go installs its binaries is not in your system’s PATH. The binaries are usually installed in `$GOPATH/bin` or `$GOBIN`. + +Steps to fix: +1. If you use `asdf`, run: + + ```sh + asdf reshim golang + ``` + +2. Otherwise, add your Go bin directory to PATH manually: + - Find your Go bin directory: + + ```sh + echo $(go env GOPATH)/bin + ``` + + - Add it to your shell config (e.g., `~/.bashrc`, `~/.zshrc`): + + ```sh + export PATH="$PATH:" + ``` + + - Reload your shell: -This error typically means that the directory where Go installs its binaries is not included in your system's PATH. The binaries are usually installed in $GOPATH/bin or $GOBIN. Here's how you can resolve this issue: + ```sh + source ~/.bashrc # or .zshrc, etc. + ``` -1. If you use `asdf` run `asdf reshim golang` +3. Alternatively, run the tool using its full path without modifying PATH: -2. Or, add Go bin directory to PATH: + ```sh + $(go env GOPATH)/bin/ghsecrets set + ``` -- First, find out where your Go bin directory is by running: +
- ```sh - echo $(go env GOPATH)/bin - ``` +
+Q: What if my AWS SSO session expires? + +If you see errors like `InvalidGrantException` when setting or retrieving secrets from AWS, your SSO session may have expired. Re-authenticate using: + +```sh +aws sso login --profile +``` + +Then try running `ghsecrets` again. + +
+ +
+Q: What if I get an error that says "GitHub CLI not found"? + +For GitHub secrets, this tool requires the GitHub CLI. Please install it first: + +```sh +brew install gh +# or +sudo apt-get install gh +``` + +Then run: + +```sh +gh auth login +``` - This command will print the path where Go binaries are installed, typically something like /home/username/go/bin +and follow the prompts to authenticate. -- Add the following line at the end of your shell config file (`.bashrc`, `.zshrc`), usually located at `~/`: +
- ```sh - export PATH="$PATH:" - ``` +## Contributing -- Apply the changes by sourcing the file: - ```sh - source ~/.bashrc # Use the appropriate file like .zshrc if needed - ``` +Pull requests are welcome! For major changes, please open an issue first to discuss what you would like to change. -3. Alternatively, run using the full path: +## License - If you prefer not to alter your PATH, or if you are troubleshooting temporarily, you can run the tool directly using its full path: +This project is licensed under the MIT License. Feel free to use, modify, and distribute it as needed. - ```sh - $(go env GOPATH)/bin/ghsecrets set - ``` From 85b48a95b3e6caa522b93edf203002780c7f34de Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:16:25 +0100 Subject: [PATCH 10/15] update readme --- tools/ghsecrets/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/ghsecrets/README.md b/tools/ghsecrets/README.md index 8dde4dd22..bf3aa84ac 100644 --- a/tools/ghsecrets/README.md +++ b/tools/ghsecrets/README.md @@ -29,6 +29,13 @@ By default, `ghsecrets set` assumes you want to store secrets in AWS Secrets Man This will read from `~/.testsecrets` (by default) and create/update a secret in AWS Secrets Manager: +> **⚠️ Note:** Ensure you authenticate with AWS before using the tool: +> +> ```sh +> aws sso login --profile +> ``` +> By default, use the `SDLC` profile + ```sh ghsecrets set ``` From d475cae0c8930dcc1df960d07ea7e42564153c6b Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:17:54 +0100 Subject: [PATCH 11/15] Update get --- tools/ghsecrets/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index f0b00f3b5..f149ef2e0 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -76,6 +76,7 @@ func main() { Use: "get", Short: "Retrieve a secret from AWS Secrets Manager", Run: func(cmd *cobra.Command, args []string) { + secretID = ensurePrefix(secretID, "testsecrets/") if err := getAWSSecret(secretID, decode); err != nil { exitWithError(err, "Failed to retrieve AWS secret") } From 9a8c67ad8bb0fad864f7899121ff2111ec6b50df Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:43:16 +0100 Subject: [PATCH 12/15] Add profile flag --- tools/ghsecrets/README.md | 22 ++--- tools/ghsecrets/main.go | 175 +++++++++++++++++++------------------- 2 files changed, 99 insertions(+), 98 deletions(-) diff --git a/tools/ghsecrets/README.md b/tools/ghsecrets/README.md index bf3aa84ac..87f74b8ff 100644 --- a/tools/ghsecrets/README.md +++ b/tools/ghsecrets/README.md @@ -27,29 +27,29 @@ By default, `ghsecrets set` assumes you want to store secrets in AWS Secrets Man #### a) Set secrets in AWS (default) -This will read from `~/.testsecrets` (by default) and create/update a secret in AWS Secrets Manager: - > **⚠️ Note:** Ensure you authenticate with AWS before using the tool: > > ```sh -> aws sso login --profile +> aws sso login --profile > ``` -> By default, use the `SDLC` profile +> By default, use the SDLC profile + +This will read from `~/.testsecrets` (by default) and create/update a secret in AWS Secrets Manager: ```sh -ghsecrets set +ghsecrets set --profile ``` If you’d like to specify a different file: ```sh -ghsecrets set --file /path/to/mysecrets.env +ghsecrets set --file /path/to/mysecrets.env --profile ``` If you’d like to specify a custom secret name: ```sh -ghsecrets set --secret-id my-custom-secret +ghsecrets set --secret-id my-custom-secret --profile ``` Note: For AWS backend, the tool automatically adds the `testsecrets/` prefix if it is missing. This ensures consistency and allows GitHub Actions to access all secrets with this designated prefix. @@ -57,13 +57,13 @@ Note: For AWS backend, the tool automatically adds the `testsecrets/` prefix if If you’d like to share this secret with additional AWS IAM principals (e.g., a collaborator’s account): ```sh -ghsecrets set --shared-with arn:aws:iam::123456789012:role/SomeRole +ghsecrets set --shared-with arn:aws:iam::123456789012:role/SomeRole --profile ``` You can specify multiple ARNs using commas: ```sh -ghsecrets set --shared-with arn:aws:iam::123456789012:role/SomeRole,arn:aws:iam::345678901234:root +ghsecrets set --shared-with arn:aws:iam::123456789012:role/SomeRole,arn:aws:iam::345678901234:root --profile ``` #### b) Set secrets in GitHub @@ -82,13 +82,13 @@ This will: If you want to retrieve an existing secret from AWS Secrets Manager, use: ```sh -ghsecrets get --secret-id testsecrets/MySecretName +ghsecrets get --secret-id testsecrets/MySecretName --profile ``` By default, it prints out the Base64-encoded string. To decode it automatically: ```sh -ghsecrets get --secret-id testsecrets/MySecretName --decode +ghsecrets get --secret-id testsecrets/MySecretName --decode --profile ``` ## FAQ diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index f149ef2e0..aedb5f8b3 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -25,6 +25,7 @@ func main() { var secretID string var backend string // Backend: GitHub or AWS var decode bool // Decode flag for `get` + var profile string // AWS profile to use var sharedWith []string // List of ARNs to share the secret with // Set Command @@ -57,10 +58,13 @@ func main() { return } case "aws": + if profile == "" { + exitWithError(nil, "AWS profile is required when using the AWS backend. Use the --profile flag to specify it.") + return + } // Ensure AWS secretID starts with "testsecrets/" prefix - // GHA IAM role has a policy that restricts access to secrets with this prefix secretID = ensurePrefix(secretID, "testsecrets/") - if err := setAWSSecret(filePath, secretID, sharedWith); err != nil { + if err := setAWSSecret(filePath, secretID, profile, sharedWith); err != nil { exitWithError(err, "Failed to set AWS secret") return } @@ -76,8 +80,12 @@ func main() { Use: "get", Short: "Retrieve a secret from AWS Secrets Manager", Run: func(cmd *cobra.Command, args []string) { + if profile == "" { + exitWithError(nil, "AWS profile is required when using the AWS backend. Use the --profile flag to specify it.") + return + } secretID = ensurePrefix(secretID, "testsecrets/") - if err := getAWSSecret(secretID, decode); err != nil { + if err := getAWSSecret(secretID, decode, profile); err != nil { exitWithError(err, "Failed to retrieve AWS secret") } }, @@ -94,12 +102,13 @@ func main() { setCmd.PersistentFlags().StringVarP(&filePath, "file", "f", defaultSecretsPath(), "Path to file with test secrets") setCmd.PersistentFlags().StringVarP(&secretID, "secret-id", "s", "", "ID of the secret to set") setCmd.PersistentFlags().StringVarP(&backend, "backend", "b", "aws", "Backend to use for storing secrets. Options: github, aws") + setCmd.PersistentFlags().StringVar(&profile, "profile", "", "AWS profile to use for credentials (required for AWS backend)") setCmd.PersistentFlags().StringSliceVar(&sharedWith, "shared-with", []string{}, "Comma-separated list of IAM ARNs to share the secret with") getCmd.PersistentFlags().StringVarP(&secretID, "secret-id", "s", "", "ID of the secret to retrieve") getCmd.PersistentFlags().BoolVarP(&decode, "decode", "d", false, "Decode the Base64-encoded secret value") + getCmd.PersistentFlags().StringVar(&profile, "profile", "", "AWS profile to use for credentials (required for AWS backend)") - // Make secretID a required flag for the set command getCmd.MarkPersistentFlagRequired("secret-id") if err := rootCmd.Execute(); err != nil { @@ -114,10 +123,8 @@ func setGitHubSecret(filePath, secretID string) error { return fmt.Errorf("failed to read file: %w", err) } - // Base64 encode the file content encoded := base64.StdEncoding.EncodeToString(data) - // Construct the GitHub CLI command to set the secret setSecretCmd := exec.Command("gh", "secret", "set", secretID, "--body", encoded) setSecretCmd.Stdin = strings.NewReader(encoded) @@ -136,18 +143,16 @@ func setGitHubSecret(filePath, secretID string) error { } // setAWSSecret creates or updates a secret in AWS Secrets Manager -func setAWSSecret(filePath, secretID string, sharedWith []string) error { - secretID = ensurePrefix(secretID, "testsecrets/") // Ensure prefix - +func setAWSSecret(filePath, secretID, profile string, sharedWith []string) error { data, err := os.ReadFile(filePath) if err != nil { return fmt.Errorf("failed to read file: %w", err) } encoded := base64.StdEncoding.EncodeToString(data) - cfg, err := config.LoadDefaultConfig(context.TODO()) + cfg, err := loadAWSConfig(profile) if err != nil { - return fmt.Errorf("failed to load AWS config: %w", err) + return handleAWSSSOError(err) } smClient := secretsmanager.NewFromConfig(cfg) @@ -167,33 +172,20 @@ func setAWSSecret(filePath, secretID string, sharedWith []string) error { Description: aws.String("Secret updated by ghsecrets CLI"), }) if err != nil { - if strings.Contains(err.Error(), "InvalidGrantException") { - return fmt.Errorf( - "Your AWS SSO session has likely expired. Please re-authenticate by running:\n\n aws sso login --profile \n\nThen try again.\n\nOriginal error: %w", - err, - ) - } - return fmt.Errorf("failed to update AWS secret: %w", err) + return handleAWSSSOError(err) } } else { - if strings.Contains(err.Error(), "InvalidGrantException") { - return fmt.Errorf( - "Your AWS SSO session has likely expired. Please re-authenticate by running:\n\n aws sso login --profile \n\nThen try again.\n\nOriginal error: %w", - err, - ) - } - return fmt.Errorf("failed to create AWS secret: %w", err) + return handleAWSSSOError(err) } } if len(sharedWith) > 0 { - err = updateAWSSecretAccessPolicy(secretID, sharedWith) + err = updateAWSSecretAccessPolicy(secretID, sharedWith, profile) if err != nil { return fmt.Errorf("failed to update secret sharing policy: %w", err) } } - // Success message with AWS-specific instructions fmt.Printf( "Test secret set successfully in AWS Secrets Manager with key: %s\n\n"+ "To use this secret in a GitHub workflow, set the 'test_secrets_override_key' flag with the 'aws:' prefix. Example:\n"+ @@ -203,10 +195,38 @@ func setAWSSecret(filePath, secretID string, sharedWith []string) error { return nil } +// getAWSSecret retrieves a test secret from AWS Secrets Manager +func getAWSSecret(secretID string, decode bool, profile string) error { + cfg, err := loadAWSConfig(profile) + if err != nil { + return handleAWSSSOError(err) + } + + smClient := secretsmanager.NewFromConfig(cfg) + out, err := smClient.GetSecretValue(context.TODO(), &secretsmanager.GetSecretValueInput{ + SecretId: aws.String(secretID), + }) + if err != nil { + return handleAWSSSOError(err) + } + + value := aws.ToString(out.SecretString) + if decode { + decoded, err := base64.StdEncoding.DecodeString(value) + if err != nil { + return fmt.Errorf("failed to decode secret value: %w", err) + } + value = string(decoded) + } + + fmt.Printf("Retrieved secret value:\n%s\n", value) + return nil +} + // updateAWSSecretAccessPolicy updates the sharing policy for a secret in AWS Secrets Manager -func updateAWSSecretAccessPolicy(secretID string, sharedWith []string) error { +func updateAWSSecretAccessPolicy(secretID string, sharedWith []string, profile string) error { // 1) Load AWS config - cfg, err := config.LoadDefaultConfig(context.TODO()) + cfg, err := loadAWSConfig(profile) if err != nil { return fmt.Errorf("failed to load AWS config: %w", err) } @@ -262,56 +282,19 @@ func updateAWSSecretAccessPolicy(secretID string, sharedWith []string) error { return nil } -// getAWSSecret retrieves a test secret from AWS Secrets Manager -// getAWSSecret retrieves a test secret from AWS Secrets Manager -func getAWSSecret(secretID string, decode bool) error { - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - return fmt.Errorf("failed to load AWS config: %w", err) - } - - smClient := secretsmanager.NewFromConfig(cfg) - out, err := smClient.GetSecretValue(context.TODO(), &secretsmanager.GetSecretValueInput{ - SecretId: aws.String(secretID), - }) - if err != nil { - // Check if the error is due to an expired SSO token - if strings.Contains(err.Error(), "InvalidGrantException") { - return fmt.Errorf( - "Your AWS SSO session has likely expired. Please re-authenticate by running:\n\n aws sso login --profile \n\nThen try again.\n\nOriginal error: %w", - err, - ) - } - return fmt.Errorf("failed to retrieve AWS secret: %w", err) - } - - value := aws.ToString(out.SecretString) - if decode { - decoded, err := base64.StdEncoding.DecodeString(value) - if err != nil { - return fmt.Errorf("failed to decode secret value: %w", err) - } - value = string(decoded) - } - - fmt.Printf("Retrieved secret value:\n%s\n", value) - return nil -} - -// ======================================================================= // Utility Functions -// ======================================================================= -func defaultSecretsPath() string { - homeDir, err := os.UserHomeDir() - if err != nil { - log.Fatalf("Failed to get user home directory: %s", err) - } - return filepath.Join(homeDir, ".testsecrets") +func loadAWSConfig(profile string) (aws.Config, error) { + return config.LoadDefaultConfig( + context.TODO(), + config.WithSharedConfigProfile(profile), + ) } -func isGHInstalled() bool { - _, err := exec.LookPath("gh") - return err == nil +func ensurePrefix(secretID, prefix string) string { + if !strings.HasPrefix(secretID, prefix) { + return prefix + secretID + } + return secretID } func validateFile(filePath string) error { @@ -325,6 +308,27 @@ func validateFile(filePath string) error { return nil } +func handleAWSSSOError(err error) error { + if strings.Contains(err.Error(), "SSO session has expired") || strings.Contains(err.Error(), "InvalidGrantException") { + return fmt.Errorf( + "AWS SSO session has expired or is invalid. Please re-authenticate by running:\n\n"+ + " aws sso login --profile \n\n"+ + "Then try again with --profile flag.\n\nOriginal error: %w", + err, + ) + } + return fmt.Errorf("AWS operation failed: %w", err) +} + +func exitWithError(err error, msg string) { + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %v\n", msg, err) + } else { + fmt.Fprintf(os.Stderr, "%s\n", msg) + } + os.Exit(1) +} + func generateSecretIDFromGithubUsername() (string, error) { usernameCmd := exec.Command("gh", "api", "user", "--jq", ".login") usernameOutput, err := usernameCmd.CombinedOutput() @@ -336,18 +340,15 @@ func generateSecretIDFromGithubUsername() (string, error) { return strings.ToUpper(secretID), nil } -func ensurePrefix(secretID, prefix string) string { - if !strings.HasPrefix(secretID, prefix) { - return prefix + secretID +func defaultSecretsPath() string { + homeDir, err := os.UserHomeDir() + if err != nil { + log.Fatalf("Failed to get user home directory: %s", err) } - return secretID + return filepath.Join(homeDir, ".testsecrets") } -func exitWithError(err error, msg string) { - if err != nil { - fmt.Fprintf(os.Stderr, "%s: %v\n", msg, err) - } else { - fmt.Fprintf(os.Stderr, "%s\n", msg) - } - os.Exit(1) +func isGHInstalled() bool { + _, err := exec.LookPath("gh") + return err == nil } From ad8c4758a6255ad9b0032da4023730ce303f2480 Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 15 Jan 2025 12:04:33 +0100 Subject: [PATCH 13/15] Update readme --- tools/ghsecrets/README.md | 18 +++++++++--------- tools/ghsecrets/main.go | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/ghsecrets/README.md b/tools/ghsecrets/README.md index 87f74b8ff..a71576d2d 100644 --- a/tools/ghsecrets/README.md +++ b/tools/ghsecrets/README.md @@ -30,26 +30,26 @@ By default, `ghsecrets set` assumes you want to store secrets in AWS Secrets Man > **⚠️ Note:** Ensure you authenticate with AWS before using the tool: > > ```sh -> aws sso login --profile +> aws sso login --profile > ``` -> By default, use the SDLC profile +> Use the **SDLC** profile in AWS This will read from `~/.testsecrets` (by default) and create/update a secret in AWS Secrets Manager: ```sh -ghsecrets set --profile +ghsecrets set --profile ``` If you’d like to specify a different file: ```sh -ghsecrets set --file /path/to/mysecrets.env --profile +ghsecrets set --file /path/to/mysecrets.env --profile ``` If you’d like to specify a custom secret name: ```sh -ghsecrets set --secret-id my-custom-secret --profile +ghsecrets set --secret-id my-custom-secret --profile ``` Note: For AWS backend, the tool automatically adds the `testsecrets/` prefix if it is missing. This ensures consistency and allows GitHub Actions to access all secrets with this designated prefix. @@ -57,13 +57,13 @@ Note: For AWS backend, the tool automatically adds the `testsecrets/` prefix if If you’d like to share this secret with additional AWS IAM principals (e.g., a collaborator’s account): ```sh -ghsecrets set --shared-with arn:aws:iam::123456789012:role/SomeRole --profile +ghsecrets set --shared-with arn:aws:iam::123456789012:role/SomeRole --profile ``` You can specify multiple ARNs using commas: ```sh -ghsecrets set --shared-with arn:aws:iam::123456789012:role/SomeRole,arn:aws:iam::345678901234:root --profile +ghsecrets set --shared-with arn:aws:iam::123456789012:role/SomeRole,arn:aws:iam::345678901234:root --profile ``` #### b) Set secrets in GitHub @@ -82,13 +82,13 @@ This will: If you want to retrieve an existing secret from AWS Secrets Manager, use: ```sh -ghsecrets get --secret-id testsecrets/MySecretName --profile +ghsecrets get --secret-id testsecrets/MySecretName --profile ``` By default, it prints out the Base64-encoded string. To decode it automatically: ```sh -ghsecrets get --secret-id testsecrets/MySecretName --decode --profile +ghsecrets get --secret-id testsecrets/MySecretName --decode --profile ``` ## FAQ diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index aedb5f8b3..db8b53160 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -312,8 +312,8 @@ func handleAWSSSOError(err error) error { if strings.Contains(err.Error(), "SSO session has expired") || strings.Contains(err.Error(), "InvalidGrantException") { return fmt.Errorf( "AWS SSO session has expired or is invalid. Please re-authenticate by running:\n\n"+ - " aws sso login --profile \n\n"+ - "Then try again with --profile flag.\n\nOriginal error: %w", + " aws sso login --profile \n\n"+ + "Then try again with --profile flag.\n\nOriginal error: %w", err, ) } From a83d40eacc1a0971eaae2d045271df7854e30fd4 Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:37:29 +0100 Subject: [PATCH 14/15] Decode base64 test secrets by default when fetching test secrets --- tools/ghsecrets/README.md | 4 ++-- tools/ghsecrets/main.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/ghsecrets/README.md b/tools/ghsecrets/README.md index a71576d2d..75bfbc3b1 100644 --- a/tools/ghsecrets/README.md +++ b/tools/ghsecrets/README.md @@ -85,10 +85,10 @@ If you want to retrieve an existing secret from AWS Secrets Manager, use: ghsecrets get --secret-id testsecrets/MySecretName --profile ``` -By default, it prints out the Base64-encoded string. To decode it automatically: +By default, it tries to decode a Base64-encoded test secret. To disable decoding use `--decode false` flag: ```sh -ghsecrets get --secret-id testsecrets/MySecretName --decode --profile +ghsecrets get --secret-id testsecrets/MySecretName --decode false --profile ``` ## FAQ diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index db8b53160..6a95d077e 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -106,7 +106,7 @@ func main() { setCmd.PersistentFlags().StringSliceVar(&sharedWith, "shared-with", []string{}, "Comma-separated list of IAM ARNs to share the secret with") getCmd.PersistentFlags().StringVarP(&secretID, "secret-id", "s", "", "ID of the secret to retrieve") - getCmd.PersistentFlags().BoolVarP(&decode, "decode", "d", false, "Decode the Base64-encoded secret value") + getCmd.PersistentFlags().BoolVarP(&decode, "decode", "d", true, "Decode the Base64-encoded secret value") getCmd.PersistentFlags().StringVar(&profile, "profile", "", "AWS profile to use for credentials (required for AWS backend)") getCmd.MarkPersistentFlagRequired("secret-id") From dcbc2169170f2c7c62a23fd6c400372454c2a7ba Mon Sep 17 00:00:00 2001 From: lukaszcl <120112546+lukaszcl@users.noreply.github.com> Date: Thu, 16 Jan 2025 14:25:54 +0100 Subject: [PATCH 15/15] Add env flag for GitHub secrets management --- tools/ghsecrets/main.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tools/ghsecrets/main.go b/tools/ghsecrets/main.go index 6a95d077e..2defd95ef 100644 --- a/tools/ghsecrets/main.go +++ b/tools/ghsecrets/main.go @@ -27,6 +27,7 @@ func main() { var decode bool // Decode flag for `get` var profile string // AWS profile to use var sharedWith []string // List of ARNs to share the secret with + var env string // Environment name for GitHub secrets // Set Command var setCmd = &cobra.Command{ @@ -53,7 +54,7 @@ func main() { switch strings.ToLower(backend) { case "github": - if err := setGitHubSecret(filePath, secretID); err != nil { + if err := setGitHubSecret(filePath, secretID, env); err != nil { exitWithError(err, "Failed to set GitHub secret") return } @@ -104,6 +105,7 @@ func main() { setCmd.PersistentFlags().StringVarP(&backend, "backend", "b", "aws", "Backend to use for storing secrets. Options: github, aws") setCmd.PersistentFlags().StringVar(&profile, "profile", "", "AWS profile to use for credentials (required for AWS backend)") setCmd.PersistentFlags().StringSliceVar(&sharedWith, "shared-with", []string{}, "Comma-separated list of IAM ARNs to share the secret with") + setCmd.PersistentFlags().StringVar(&env, "env", "", "Optional environment name (for GitHub Secrets)") getCmd.PersistentFlags().StringVarP(&secretID, "secret-id", "s", "", "ID of the secret to retrieve") getCmd.PersistentFlags().BoolVarP(&decode, "decode", "d", true, "Decode the Base64-encoded secret value") @@ -117,7 +119,7 @@ func main() { } // setGitHubSecret creates or updates a secret in GitHub -func setGitHubSecret(filePath, secretID string) error { +func setGitHubSecret(filePath, secretID, env string) error { data, err := os.ReadFile(filePath) if err != nil { return fmt.Errorf("failed to read file: %w", err) @@ -125,7 +127,15 @@ func setGitHubSecret(filePath, secretID string) error { encoded := base64.StdEncoding.EncodeToString(data) - setSecretCmd := exec.Command("gh", "secret", "set", secretID, "--body", encoded) + // Build the gh command + args := []string{"secret", "set", secretID, "--body", encoded} + + // If --env was provided, add the environment argument + if env != "" { + args = append(args, "--env", env) + } + + setSecretCmd := exec.Command("gh", args...) setSecretCmd.Stdin = strings.NewReader(encoded) output, err := setSecretCmd.CombinedOutput() @@ -134,7 +144,7 @@ func setGitHubSecret(filePath, secretID string) error { } fmt.Printf( - "Test secret set successfully in GitHub with key: %s\n\n"+ + "Test secret set successfully in GitHub Secrets with key: %s\n\n"+ "To run a GitHub workflow with the test secrets, use the 'test_secrets_override_key' flag.\n"+ "Example: gh workflow run ${workflow_name} -f test_secrets_override_key=%s\n", secretID, secretID,