From 4b87df4602ff6898a155b99621a2e84895b3bb06 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 1 Nov 2023 10:36:10 +0100 Subject: [PATCH 1/9] generate cli docs --- docs/reference/ocm.md | 10 ++++++++++ docs/reference/ocm_attributes.md | 10 ++++++++++ docs/reference/ocm_download_resources.md | 1 + docs/reference/ocm_ocm-downloadhandlers.md | 1 + 4 files changed, 22 insertions(+) diff --git a/docs/reference/ocm.md b/docs/reference/ocm.md index 489f623e2b..321e5082b9 100644 --- a/docs/reference/ocm.md +++ b/docs/reference/ocm.md @@ -253,6 +253,16 @@ The value can be a simple type or a JSON/YAML string for complex values The are temporarily stored in the filesystem, instead of the memory, to avoid blowing up the memory consumption. +- ocm.software/compositionmode [compositionmode]: *bool* (default: false + + Composition mode decouples a component version provided by a repository + implemention from the backened persistence. Added local blobs will + and other changes witll not be forwarded to the backend repository until + an AddVersion is called on the component. + If composition mode is disabled blobs will directly be forwarded to + the backend and descriptor updated will be persisted on AddVersion + or closing a provided existing component version. + - ocm.software/signing/sigstore [sigstore]: *sigstore config* Configuration to use for sigstore based signing. The following fields are used. diff --git a/docs/reference/ocm_attributes.md b/docs/reference/ocm_attributes.md index bd243a16fc..772c17f214 100644 --- a/docs/reference/ocm_attributes.md +++ b/docs/reference/ocm_attributes.md @@ -163,6 +163,16 @@ OCM library: The are temporarily stored in the filesystem, instead of the memory, to avoid blowing up the memory consumption. +- ocm.software/compositionmode [compositionmode]: *bool* (default: false + + Composition mode decouples a component version provided by a repository + implemention from the backened persistence. Added local blobs will + and other changes witll not be forwarded to the backend repository until + an AddVersion is called on the component. + If composition mode is disabled blobs will directly be forwarded to + the backend and descriptor updated will be persisted on AddVersion + or closing a provided existing component version. + - ocm.software/signing/sigstore [sigstore]: *sigstore config* Configuration to use for sigstore based signing. The following fields are used. diff --git a/docs/reference/ocm_download_resources.md b/docs/reference/ocm_download_resources.md index 46fab78cb9..b48beb3071 100644 --- a/docs/reference/ocm_download_resources.md +++ b/docs/reference/ocm_download_resources.md @@ -181,6 +181,7 @@ The downloader name may be a path expression with the following possibilities: - application/vnd.docker.distribution.manifest.v2+tar+gzip - application/vnd.gardener.landscaper.blueprint.layer.v1.tar - application/vnd.gardener.landscaper.blueprint.layer.v1.tar+gzip + - application/vnd.gardener.landscaper.blueprint.v1+tar - application/vnd.gardener.landscaper.blueprint.v1+tar+gzip - application/vnd.oci.image.manifest.v1+tar - application/vnd.oci.image.manifest.v1+tar+gzip diff --git a/docs/reference/ocm_ocm-downloadhandlers.md b/docs/reference/ocm_ocm-downloadhandlers.md index 4ea3ef1eb0..1bbb7c16c6 100644 --- a/docs/reference/ocm_ocm-downloadhandlers.md +++ b/docs/reference/ocm_ocm-downloadhandlers.md @@ -93,6 +93,7 @@ The following handler names are possible: - application/vnd.docker.distribution.manifest.v2+tar+gzip - application/vnd.gardener.landscaper.blueprint.layer.v1.tar - application/vnd.gardener.landscaper.blueprint.layer.v1.tar+gzip + - application/vnd.gardener.landscaper.blueprint.v1+tar - application/vnd.gardener.landscaper.blueprint.v1+tar+gzip - application/vnd.oci.image.manifest.v1+tar - application/vnd.oci.image.manifest.v1+tar+gzip From 1eff7279a24d21ba13c45e427b27ec8f15e72a0c Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Mon, 30 Oct 2023 15:01:39 +0100 Subject: [PATCH 2/9] first version for vault credential provider --- docs/reference/ocm_credential-handling.md | 1 + docs/reference/ocm_get_credentials.md | 1 + go.mod | 4 + go.sum | 79 +++++ .../credentials/identity/hostpath/identity.go | 27 +- pkg/contexts/credentials/interface.go | 8 + .../credentials/repositories/vault/auth.go | 129 +++++++++ .../credentials/repositories/vault/cache.go | 47 +++ .../repositories/vault/identity/identity.go | 121 ++++++++ .../credentials/repositories/vault/logging.go | 14 + .../credentials/repositories/vault/options.go | 81 ++++++ .../repositories/vault/provider.go | 270 ++++++++++++++++++ .../repositories/vault/repository.go | 69 +++++ .../repositories/vault/suite_test.go | 17 ++ .../credentials/repositories/vault/type.go | 73 +++++ pkg/errors/required.go | 39 +++ 16 files changed, 967 insertions(+), 13 deletions(-) create mode 100644 pkg/contexts/credentials/repositories/vault/auth.go create mode 100644 pkg/contexts/credentials/repositories/vault/cache.go create mode 100644 pkg/contexts/credentials/repositories/vault/identity/identity.go create mode 100644 pkg/contexts/credentials/repositories/vault/logging.go create mode 100644 pkg/contexts/credentials/repositories/vault/options.go create mode 100644 pkg/contexts/credentials/repositories/vault/provider.go create mode 100644 pkg/contexts/credentials/repositories/vault/repository.go create mode 100644 pkg/contexts/credentials/repositories/vault/suite_test.go create mode 100644 pkg/contexts/credentials/repositories/vault/type.go create mode 100644 pkg/errors/required.go diff --git a/docs/reference/ocm_credential-handling.md b/docs/reference/ocm_credential-handling.md index 5ec9b49591..27ba87c386 100644 --- a/docs/reference/ocm_credential-handling.md +++ b/docs/reference/ocm_credential-handling.md @@ -92,6 +92,7 @@ behaviours are described in the following list: - *type* (required if set in pattern): the identity type - *hostname* (required if set in pattern): the hostname of a server + - *scheme* (optional): the URL scheme of a server - *port* (optional): the port of a server - *pathprefix* (optional): a path prefix to match. The element with the most matching path components is selected (separator is /). diff --git a/docs/reference/ocm_get_credentials.md b/docs/reference/ocm_get_credentials.md index 64541bc777..9040b89f8e 100644 --- a/docs/reference/ocm_get_credentials.md +++ b/docs/reference/ocm_get_credentials.md @@ -92,6 +92,7 @@ The following standard identity matchers are supported: - *type* (required if set in pattern): the identity type - *hostname* (required if set in pattern): the hostname of a server + - *scheme* (optional): the URL scheme of a server - *port* (optional): the port of a server - *pathprefix* (optional): a path prefix to match. The element with the most matching path components is selected (separator is /). diff --git a/go.mod b/go.mod index 5f7aa0a630..8ec753dc0e 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/ghodss/yaml v1.0.0 github.com/go-logr/logr v1.2.4 + github.com/hashicorp/vault-client-go v0.4.2 github.com/mandelsoft/filepath v0.0.0-20230412200429-36b1eb66bd27 github.com/mandelsoft/spiff v1.7.0-beta-5 github.com/modern-go/reflect2 v1.0.2 @@ -202,6 +203,8 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.2 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.15 // indirect @@ -253,6 +256,7 @@ require ( github.com/rivo/uniseg v0.4.2 // indirect github.com/rubenv/sql-migrate v1.3.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect github.com/segmentio/ksuid v1.0.4 // indirect diff --git a/go.sum b/go.sum index 7aa2ddff03..beb1bfe3c4 100644 --- a/go.sum +++ b/go.sum @@ -37,7 +37,9 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94= +cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= cloud.google.com/go/kms v1.12.1 h1:xZmZuwy2cwzsocmKDOPu4BL7umg8QXagQx6fKVmf45U= +cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -56,6 +58,7 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230329111138-12e09aba5ebd h1:1tbEqR4NyQLgiod7vLXSswHteGetAVZrMGCqrJxLKRs= +github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230329111138-12e09aba5ebd/go.mod h1:0vOOKsOMKPThRu9lQMAxcQ8D60f8U+wHXl07SyUw0+U= github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 h1:8+4G8JaejP8Xa6W46PzJEwisNgBXMvFcz78N6zG/ARw= github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0/go.mod h1:GgeIE+1be8Ivm7Sh4RgwI42aTtC9qrcj+Y9Y6CjJhJs= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -63,10 +66,15 @@ github.com/Azure/azure-sdk-for-go v46.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 h1:SEy2xmstIphdPwNBUi7uhvjyjhVKISfwjfOJmuy7kg4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v0.12.0 h1:4Kynh6Hn2ekyIsBgNQJb3dn1+/MyvzfUJebti2emB/A= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v0.12.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= @@ -107,6 +115,7 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -114,6 +123,7 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/gostackparse v0.6.0 h1:egCGQviIabPwsyoWpGvIBGrEnNWez35aEO7OJ1vBI4o= github.com/DataDog/gostackparse v0.6.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= @@ -154,6 +164,7 @@ github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01 github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim v0.9.3/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8= +github.com/Microsoft/hcsshim v0.10.0-rc.7/go.mod h1:ILuwjA+kNW+MrN/w5un7n3mTqkwsFu4Bp05/okFUZlE= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -225,12 +236,14 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.44.288 h1:Ln7fIao/nl0ACtelgR1I4AiEw/GLNkKcXfCaHupUW5Q= +github.com/aws/aws-sdk-go v1.44.288/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250= github.com/aws/aws-sdk-go-v2 v1.14.0/go.mod h1:ZA3Y8V0LrlWj63MQAnRHgKf/5QB//LSZCPNWlWrNGLU= github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= @@ -286,6 +299,7 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 h1:HfVVR1vItaG6le+Bpw6P4midjBDMKnjMyZnw9MXYUcE= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17/go.mod h1:YqMdV+gEKCQ59NrB7rzrJdALeBIsYiVi8Inj3+KcqHI= github.com/aws/aws-sdk-go-v2/service/kms v1.22.2 h1:jwmtdM1/l1DRNy5jQrrYpsQm8zwetkgeqhAqefDr1yI= +github.com/aws/aws-sdk-go-v2/service/kms v1.22.2/go.mod h1:aNfh11Smy55o65PB3MyKbkM8BFyFUcZmj1k+4g8eNfg= github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 h1:3/gm/JTX9bX8CpzTgIlrtYpB3EVBDxyg/GY/QdcIEZw= github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11/go.mod h1:fmgDANqTUCxciViKl9hb/zD5LFbvPINFRgWhDbR+vZo= github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM= @@ -308,6 +322,7 @@ github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355- github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795/go.mod h1:8vJsEZ4iRqG+Vx6pKhWK6U00qcj0KC37IsfszMkY6UE= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -325,6 +340,7 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= @@ -338,6 +354,7 @@ github.com/buildkite/agent/v3 v3.49.0 h1:FSmRQz8YFhaCXg4MfE7JucPcY7mQ/HWM55ir1j3 github.com/buildkite/agent/v3 v3.49.0/go.mod h1:iasSyh3KPjOPCnyvnZB1trkkX7jrdL8PnLBgjdVJxgU= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= +github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -373,6 +390,7 @@ github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtM github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudfoundry-incubator/candiedyaml v0.0.0-20170901234223-a41693b7b7af h1:6Cpkahw28+gcBdnXQL7LcMTX488+6jl6hfoTMRT6Hm4= +github.com/cloudfoundry-incubator/candiedyaml v0.0.0-20170901234223-a41693b7b7af/go.mod h1:dOLSIXcRQJiDS1vlrYFNJicoHNZLsBKideE+70hGdV4= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -386,6 +404,7 @@ github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= +github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= @@ -404,6 +423,7 @@ github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTF github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= @@ -438,6 +458,7 @@ github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EX github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= @@ -527,6 +548,7 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 h1:vU+EP9ZuFUCYE0NYLwTSob+3LNEJATzNfP/DC7SWGWI= github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= @@ -545,6 +567,7 @@ github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xb github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936 h1:foGzavPWwtoyBvjWyKJYDYsyzy+23iBV7NKTwdk+LRY= +github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936/go.mod h1:ttKPnOepYt4LLzD+loXQ1rT6EmpyIYHro7TAJuIIlHo= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= @@ -557,6 +580,7 @@ github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= +github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE= @@ -622,8 +646,11 @@ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2Vvl github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= +github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= github.com/facebookgo/limitgroup v0.0.0-20150612190941-6abd8d71ec01 h1:IeaD1VDVBPlx3viJT9Md8if8IxxJnO+x0JCGb054heg= +github.com/facebookgo/limitgroup v0.0.0-20150612190941-6abd8d71ec01/go.mod h1:ypD5nozFk9vcGw1ATYefw6jHe/jZP++Z15/+VTMcWhc= github.com/facebookgo/muster v0.0.0-20150708232844-fd3d7953fd52 h1:a4DFiKFJiDRGFD1qIcqGLX/WlUMD9dyLSLDt+9QZgt8= +github.com/facebookgo/muster v0.0.0-20150708232844-fd3d7953fd52/go.mod h1:yIquW87NGRw1FU5p5lEkpnt/QxoH5uPAOUlOVkAUuMg= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -631,15 +658,18 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fluxcd/pkg/ssa v0.24.1 h1:0dn5FqyYdGa+VuDp5EJrkLbPq5xhhSAAkMgGUeMpOM0= github.com/fluxcd/pkg/ssa v0.24.1/go.mod h1:nEOUOwGotBlNZkTkO6GHPlI0U0BmHTavFd1Jk+TzsGw= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= +github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= @@ -688,6 +718,7 @@ github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jT 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-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= @@ -739,6 +770,7 @@ github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9F github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 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.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 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= @@ -749,9 +781,11 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-rod/rod v0.113.3 h1:oLiKZW721CCMwA5g7977cWfcAKQ+FuosP47Zf1QiDrA= +github.com/go-rod/rod v0.113.3/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684Mthw= github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -854,6 +888,7 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= @@ -918,6 +953,7 @@ github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkj github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= +github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -929,6 +965,7 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= +github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= @@ -937,6 +974,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -970,6 +1008,7 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= @@ -980,10 +1019,14 @@ github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUD github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -991,15 +1034,21 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/vault-client-go v0.4.2 h1:XeUXb5jnDuCUhC8HRpkdGPLh1XtzXmiOnF0mXEbARxI= +github.com/hashicorp/vault-client-go v0.4.2/go.mod h1:4tDw7Uhq5XOxS1fO+oMtotHL7j4sB9cp0T7U6m4FzDY= github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as= +github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8= github.com/honeycombio/beeline-go v1.10.0 h1:cUDe555oqvw8oD76BQJ8alk7FP0JZ/M/zXpNvOEDLDc= +github.com/honeycombio/beeline-go v1.10.0/go.mod h1:Zz5WMeQCJzFt2Mvf8t6HC1X8RLskLVR/e8rvcmXB1G8= github.com/honeycombio/libhoney-go v1.16.0 h1:kPpqoz6vbOzgp7jC6SR7SkNj7rua7rgxvznI6M3KdHc= +github.com/honeycombio/libhoney-go v1.16.0/go.mod h1:izP4fbREuZ3vqC4HlCAmPrcPT9gxyxejRjGtCYpmBn0= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -1027,6 +1076,7 @@ github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbB github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b h1:ZGiXF8sz7PDk6RgkP+A/SFfUD0ZR/AgG6SpRNEDKZy8= github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b/go.mod h1:hQmNrgofl+IY/8L+n20H6E6PWBBTokdsv+q49j0QhsU= github.com/jellydator/ttlcache/v3 v3.0.1 h1:cHgCSMS7TdQcoprXnWUptJZzyFsqs18Lt8VVhRuZYVU= +github.com/jellydator/ttlcache/v3 v3.0.1/go.mod h1:WwTaEmcXQ3MTjOm4bsZoDFiCu/hMvNWLO1w67RXz6h4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE= github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= @@ -1040,6 +1090,7 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548 h1:dYTbLf4m0a5u0KLmPfB6mgxbcV7588bOCx79hxa5Sr4= +github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= @@ -1098,6 +1149,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= @@ -1174,6 +1226,7 @@ github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOq github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -1181,6 +1234,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -1350,7 +1404,9 @@ github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNc github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= +github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1422,6 +1478,7 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 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/rubenv/sql-migrate v1.3.1 h1:Vx+n4Du8X8VTYuXbhNxdEUoh6wiJERA0GlWocR5FrbA= github.com/rubenv/sql-migrate v1.3.1/go.mod h1:YzG/Vh82CwyhTFXy+Mf5ahAiiEOpAlHurg+23VEzcsk= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= @@ -1430,6 +1487,7 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= @@ -1449,6 +1507,7 @@ github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -1464,9 +1523,13 @@ github.com/sigstore/rekor v1.2.2-0.20230530122220-67cc9e58bd23/go.mod h1:h1tOLhl github.com/sigstore/sigstore v1.7.1 h1:fCATemikcBK0cG4+NcM940MfoIgmioY1vC6E66hXxks= github.com/sigstore/sigstore v1.7.1/go.mod h1:0PmMzfJP2Y9+lugD0wer4e7TihR5tM7NcIs3bQNk5xg= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.7.1 h1:rDHrG/63b3nBq3G9plg7iYnWN6lBhOfq/XultlCZgII= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.7.1/go.mod h1:hl0LRidnJG1uL1lLSHGEjcs+MxLjT65NJ7pX/TQDIsk= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.7.1 h1:X3ezwolP+b1jP3R6XPOWhUU0TZKONiv6EIRuySlZGrY= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.7.1/go.mod h1:SG2NPEdX2Vi7CBp/o93kJqXrovkis/T9ou9oxZONyEA= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.7.1 h1:mj1KhdzzP1me994bt1UXhq5KZGSR1SoqxTqcT+hfPMk= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.7.1/go.mod h1:Z7LFrKKfj5ZPhy0YS9HcI4H6kbUQzBsE3e3hR+R3YY8= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.7.1 h1:fhOToGY5fC5TY101an8i/oDYpoLzUJ1nUFwhnHA1+XY= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.7.1/go.mod h1:SN4QZHHDs2VqXh5bRXrIi8vqLbOijIp2XoSlmV/WJ/c= github.com/sigstore/timestamp-authority v1.1.1 h1:EldrdeBED0edNzDMvYZDf5CyWgtSchtR9DKYyksNR8M= github.com/sigstore/timestamp-authority v1.1.1/go.mod h1:cEDLEHl/L3ppqKDaiZ3Cg4ikcaYleuq90I/BFNePzF0= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= @@ -1484,6 +1547,7 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= +github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -1562,6 +1626,7 @@ github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4D github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= @@ -1599,7 +1664,9 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xanzy/go-gitlab v0.86.0 h1:jR8V9cK9jXRQDb46KOB20NCF3ksY09luaG0IfXE6p7w= @@ -1622,10 +1689,15 @@ github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= +github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= +github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= github.com/ysmood/got v0.34.1 h1:IrV2uWLs45VXNvZqhJ6g2nIhY+pgIG1CUoOcqfXFl1s= +github.com/ysmood/got v0.34.1/go.mod h1:yddyjq/PmAf08RMLSwDjPyCvHvYed+WjHnQxpH851LM= github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= +github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/leakless v0.8.0 h1:BzLrVoiwxikpgEQR0Lk8NyBN5Cit2b1z+u0mgL4ZJak= +github.com/ysmood/leakless v0.8.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1689,6 +1761,7 @@ go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= @@ -1709,6 +1782,7 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -2142,6 +2216,7 @@ golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNq golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -2229,7 +2304,9 @@ google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -2283,6 +2360,7 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVCwMc= +gopkg.in/alexcesaro/statsd.v2 v2.0.0/go.mod h1:i0ubccKGzBVNBpdGV5MocxyA/XlLUJzA7SLonnE4drU= gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2336,6 +2414,7 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= helm.sh/helm/v3 v3.12.2 h1:kFyDBr/mgJUlyGzVTCieG4wW0zmo7fcNRWK0+FKkxqU= helm.sh/helm/v3 v3.12.2/go.mod h1:v1PMayudIfZAvec3Wp4wAErensvK/rv5fu/xCiE6t3I= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/contexts/credentials/identity/hostpath/identity.go b/pkg/contexts/credentials/identity/hostpath/identity.go index 1813f290e2..612512af50 100644 --- a/pkg/contexts/credentials/identity/hostpath/identity.go +++ b/pkg/contexts/credentials/identity/hostpath/identity.go @@ -36,6 +36,7 @@ This matcher works on the following properties: - *`+ID_TYPE+`* (required if set in pattern): the identity type - *`+ID_HOSTNAME+`* (required if set in pattern): the hostname of a server +- *`+ID_SCHEME+`* (optional): the URL scheme of a server - *`+ID_PORT+`* (optional): the port of a server - *`+ID_PATHPREFIX+`* (optional): a path prefix to match. The element with the most matching path components is selected (separator is /). @@ -45,37 +46,37 @@ This matcher works on the following properties: var Matcher = IdentityMatcher("") func IdentityMatcher(identityType string) cpi.IdentityMatcher { - return func(pattern, cur, id cpi.ConsumerIdentity) bool { - if pattern[ID_TYPE] != "" && pattern[ID_TYPE] != id[ID_TYPE] { + return func(request, cur, id cpi.ConsumerIdentity) bool { + if request[ID_TYPE] != "" && request[ID_TYPE] != id[ID_TYPE] { return false } - if identityType != "" && pattern[ID_TYPE] != "" && identityType != pattern[ID_TYPE] { + if identityType != "" && request[ID_TYPE] != "" && identityType != request[ID_TYPE] { return false } - if pattern[ID_HOSTNAME] != "" && id[ID_HOSTNAME] != "" && pattern[ID_HOSTNAME] != id[ID_HOSTNAME] { + if request[ID_HOSTNAME] != "" && id[ID_HOSTNAME] != "" && request[ID_HOSTNAME] != id[ID_HOSTNAME] { return false } - if pattern[ID_PORT] != "" { - if id[ID_PORT] != "" && id[ID_PORT] != pattern[ID_PORT] { + if request[ID_PORT] != "" { + if id[ID_PORT] != "" && id[ID_PORT] != request[ID_PORT] { return false } } - if pattern[ID_SCHEME] != "" { - if id[ID_SCHEME] != "" && id[ID_SCHEME] != pattern[ID_SCHEME] { + if request[ID_SCHEME] != "" { + if id[ID_SCHEME] != "" && id[ID_SCHEME] != request[ID_SCHEME] { return false } } - if pattern[ID_PATHPREFIX] != "" { + if request[ID_PATHPREFIX] != "" { if id[ID_PATHPREFIX] != "" { - if len(id[ID_PATHPREFIX]) > len(pattern[ID_PATHPREFIX]) { + if len(id[ID_PATHPREFIX]) > len(request[ID_PATHPREFIX]) { return false } - pcomps := strings.Split(pattern[ID_PATHPREFIX], "/") + pcomps := strings.Split(request[ID_PATHPREFIX], "/") icomps := strings.Split(id[ID_PATHPREFIX], "/") if len(icomps) > len(pcomps) { return false @@ -100,10 +101,10 @@ func IdentityMatcher(identityType string) cpi.IdentityMatcher { if cur[ID_HOSTNAME] == "" && id[ID_HOSTNAME] != "" { return true } - if cur[ID_PORT] == "" && (id[ID_PORT] != "" && pattern[ID_PORT] != "") { + if cur[ID_PORT] == "" && (id[ID_PORT] != "" && request[ID_PORT] != "") { return true } - if cur[ID_SCHEME] == "" && (id[ID_SCHEME] != "" && pattern[ID_SCHEME] != "") { + if cur[ID_SCHEME] == "" && (id[ID_SCHEME] != "" && request[ID_SCHEME] != "") { return true } diff --git a/pkg/contexts/credentials/interface.go b/pkg/contexts/credentials/interface.go index eb4037bb2b..054260cd2c 100644 --- a/pkg/contexts/credentials/interface.go +++ b/pkg/contexts/credentials/interface.go @@ -84,6 +84,14 @@ func NewCredentials(props common.Properties) Credentials { return internal.NewCredentials(props) } +func CredentialsFromList(props ...string) Credentials { + creds := DirectCredentials{} + for i := 1; i < len(props); i += 2 { + creds[props[i-1]] = props[i] + } + return creds +} + func ToGenericCredentialsSpec(spec CredentialsSpec) (*GenericCredentialsSpec, error) { return internal.ToGenericCredentialsSpec(spec) } diff --git a/pkg/contexts/credentials/repositories/vault/auth.go b/pkg/contexts/credentials/repositories/vault/auth.go new file mode 100644 index 0000000000..c646e4c2f5 --- /dev/null +++ b/pkg/contexts/credentials/repositories/vault/auth.go @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package vault + +import ( + "context" + "sort" + "sync" + + "github.com/hashicorp/vault-client-go" + "github.com/hashicorp/vault-client-go/schema" + "golang.org/x/exp/maps" + + "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" + "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/vault/identity" + "github.com/open-component-model/ocm/pkg/errors" +) + +type AuthMethod interface { + GetName() string + Validate(creds cpi.Credentials) error + GetToken(ctx context.Context, client *vault.Client, ns string, creds cpi.Credentials) (string, error) +} + +type AuthMethods struct { + lock sync.Mutex + methods map[string]AuthMethod +} + +var methods = NewAuthMethods() + +func NewAuthMethods() *AuthMethods { + return &AuthMethods{ + methods: map[string]AuthMethod{}, + } +} + +func (r *AuthMethods) Register(m AuthMethod) { + r.lock.Lock() + defer r.lock.Unlock() + + r.methods[m.GetName()] = m +} + +func (r *AuthMethods) Get(name string) AuthMethod { + r.lock.Lock() + defer r.lock.Unlock() + + return r.methods[name] +} + +func (r *AuthMethods) Names() []string { + r.lock.Lock() + defer r.lock.Unlock() + + names := maps.Keys(r.methods) + sort.Strings(names) + return names +} + +func RegisterAuthMethod(m AuthMethod) { + methods.Register(m) +} + +//////////////////////////////////////////////////////////////////////////////// + +func init() { + RegisterAuthMethod(&approle{}) + RegisterAuthMethod(&token{}) +} + +//////////////////////////////////////////////////////////////////////////////// + +type approle struct{} + +var _ AuthMethod = (*approle)(nil) + +func (a *approle) GetName() string { + return identity.AUTH_APPROLE +} + +func (a *approle) Validate(creds cpi.Credentials) error { + if !creds.ExistsProperty(identity.ATTR_ROLEID) { + return errors.ErrRequired("credential property", identity.ATTR_ROLEID, a.GetName()) + } + if !creds.ExistsProperty(identity.ATTR_SECRETID) { + return errors.ErrRequired("credential property", identity.ATTR_SECRETID, a.GetName()) + } + return nil +} + +func (a *approle) GetToken(ctx context.Context, client *vault.Client, ns string, creds cpi.Credentials) (string, error) { + req := schema.AppRoleLoginRequest{ + RoleId: creds.GetProperty(identity.ATTR_ROLEID), + SecretId: creds.GetProperty(identity.ATTR_SECRETID), + } + resp, err := client.Auth.AppRoleLogin( + ctx, + req, + vault.WithNamespace(ns), + ) + if err != nil { + return "", err + } + return resp.Auth.ClientToken, nil +} + +//////////////////////////////////////////////////////////////////////////////// + +type token struct{} + +var _ AuthMethod = (*token)(nil) + +func (a *token) GetName() string { + return identity.AUTH_TOKEN +} + +func (a *token) Validate(creds cpi.Credentials) error { + if !creds.ExistsProperty(identity.ATTR_TOKEN) { + return errors.ErrRequired("credential property", identity.ATTR_TOKEN, a.GetName()) + } + return nil +} + +func (a *token) GetToken(ctx context.Context, client *vault.Client, ns string, creds cpi.Credentials) (string, error) { + return creds.GetProperty(identity.ATTR_TOKEN), nil +} diff --git a/pkg/contexts/credentials/repositories/vault/cache.go b/pkg/contexts/credentials/repositories/vault/cache.go new file mode 100644 index 0000000000..1b2d700bb5 --- /dev/null +++ b/pkg/contexts/credentials/repositories/vault/cache.go @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package vault + +import ( + "sync" + + "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" + "github.com/open-component-model/ocm/pkg/contexts/datacontext" + "github.com/open-component-model/ocm/pkg/errors" +) + +const ATTR_REPOS = "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/vault" + +type Repositories struct { + lock sync.Mutex + repos map[cpi.ProviderIdentity]*Repository +} + +func newRepositories(datacontext.Context) interface{} { + return &Repositories{ + repos: map[cpi.ProviderIdentity]*Repository{}, + } +} + +func (r *Repositories) GetRepository(ctx cpi.Context, spec *RepositorySpec) (*Repository, error) { + var repo *Repository + + if spec.ServerURL == "" { + return nil, errors.ErrInvalid("server url") + } + r.lock.Lock() + defer r.lock.Unlock() + + var err error + key := spec.GetKey() + repo = r.repos[key] + if repo == nil { + repo, err = NewRepository(ctx, spec) + if err == nil { + r.repos[key] = repo + } + } + return repo, err +} diff --git a/pkg/contexts/credentials/repositories/vault/identity/identity.go b/pkg/contexts/credentials/repositories/vault/identity/identity.go new file mode 100644 index 0000000000..d6dfed6723 --- /dev/null +++ b/pkg/contexts/credentials/repositories/vault/identity/identity.go @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package identity + +import ( + "net" + "net/url" + "path" + "strings" + + "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" + "github.com/open-component-model/ocm/pkg/contexts/credentials/identity/hostpath" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/listformat" +) + +const CONSUMER_TYPE = "HashiCorpVault" + +// identity properties. +const ( + ID_HOSTNAME = hostpath.ID_HOSTNAME + ID_SCHEMA = hostpath.ID_SCHEME + ID_PORT = hostpath.ID_PORT + ID_PATHPREFIX = hostpath.ID_PATHPREFIX + ID_NAMESPACE = "namespace" +) + +// credential properties. +const ( + ATTR_AUTHMETH = "authmeth" + ATTR_TOKEN = cpi.ATTR_TOKEN + ATTR_ROLEID = "roleid" + ATTR_SECRETID = "secretid" +) + +const ( + AUTH_APPROLE = "approle" + AUTH_TOKEN = "token" +) + +var identityMatcher = hostpath.IdentityMatcher(CONSUMER_TYPE) + +func IdentityMatcher(request, cur, id cpi.ConsumerIdentity) bool { + if id[ID_NAMESPACE] != request[ID_NAMESPACE] { + return false + } + return identityMatcher(request, cur, id) +} + +func init() { + attrs := listformat.FormatListElements("", listformat.StringElementDescriptionList{ + ATTR_AUTHMETH, "auth method", + ATTR_TOKEN, "vault token", + ATTR_ROLEID, "applrole role id", + ATTR_SECRETID, "applrole secret id", + ATTR_SECRETID, "applrole secret id", + }) + ids := listformat.FormatListElements("", listformat.StringElementDescriptionList{ + ID_HOSTNAME, "vault server host", + ID_SCHEMA, "(optional) URL scheme", + ID_PORT, "(optional) server port", + ID_NAMESPACE, "vault namespace", + ID_PATHPREFIX, "path prefix for secret", + }) + cpi.RegisterStandardIdentity(CONSUMER_TYPE, identityMatcher, + `HashiCorp Vault credential matcher + +This matcher matches credentials for a HashiCorp vault instance. +It uses the following identity attributes:\n`+ids, + attrs+` +The oly supported auth methods, so far, are token and approle. +`) +} + +func GetConsumerId(serverurl string, namespace string, secretpath ...string) (cpi.ConsumerIdentity, error) { + if serverurl == "" { + return nil, errors.Newf("server address must be given") + } + u, err := url.Parse(serverurl) + if err != nil { + return nil, errors.ErrInvalidWrap(err, "server url", serverurl) + } + + host, port, err := net.SplitHostPort(u.Host) + if err != nil { + if strings.LastIndex(host, ":") >= 0 { + return nil, errors.ErrInvalidWrap(err, "server url", serverurl) + } + host = u.Host + } + + id := cpi.ConsumerIdentity{ + cpi.ID_TYPE: CONSUMER_TYPE, + ID_HOSTNAME: host, + } + if u.Scheme != "" { + id[ID_SCHEMA] = u.Scheme + } + if port != "" { + id[ID_PORT] = port + } + if namespace != "" { + id[ID_NAMESPACE] = namespace + } + + p := path.Join(secretpath...) + if p != "" { + id[ID_PATHPREFIX] = p + } + return id, nil +} + +func GetCredentials(ctx cpi.ContextProvider, serverurl, namespace string, secretpath ...string) (cpi.Credentials, error) { + id, err := GetConsumerId(serverurl, namespace, secretpath...) + if err != nil { + return nil, err + } + return cpi.CredentialsForConsumer(ctx.CredentialsContext(), id, IdentityMatcher) +} diff --git a/pkg/contexts/credentials/repositories/vault/logging.go b/pkg/contexts/credentials/repositories/vault/logging.go new file mode 100644 index 0000000000..d75d785db6 --- /dev/null +++ b/pkg/contexts/credentials/repositories/vault/logging.go @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package vault + +import ( + ocmlog "github.com/open-component-model/ocm/pkg/logging" +) + +var ( + REALM = ocmlog.DefineSubRealm("HashiCorp Vault Access", "credentials", "vault") + log = ocmlog.DynamicLogger(REALM) +) diff --git a/pkg/contexts/credentials/repositories/vault/options.go b/pkg/contexts/credentials/repositories/vault/options.go new file mode 100644 index 0000000000..910ac047be --- /dev/null +++ b/pkg/contexts/credentials/repositories/vault/options.go @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package vault + +import ( + "golang.org/x/exp/slices" + + "github.com/open-component-model/ocm/pkg/utils" +) + +type Option interface { + ApplyTo(*Options) +} + +type Options struct { + Namespace string `json:"namespace"` + SearchEngine string `json:"searchEngine,omitempty"` + Path string `json:"path,omitempty"` + Secrets []string `json:"secrets"` + PropgateConsumerIdentity bool `json:"propagateConsumerIdentity,omitempty"` +} + +type ns string + +func (o ns) ApplyTo(opts *Options) { + opts.Namespace = string(o) +} + +func WithNamespace(s string) Option { + return ns(s) +} + +//////////////////////////////////////////////////////////////////////////////// + +type se string + +func (o se) ApplyTo(opts *Options) { + opts.SearchEngine = string(o) +} + +func WithSearchEngine(s string) Option { + return se(s) +} + +//////////////////////////////////////////////////////////////////////////////// + +type p string + +func (o p) ApplyTo(opts *Options) { + opts.Path = string(o) +} + +func WithPath(s string) Option { + return p(s) +} + +//////////////////////////////////////////////////////////////////////////////// + +type sec []string + +func (o sec) ApplyTo(opts *Options) { + opts.Secrets = append(opts.Secrets, []string(o)...) +} + +func WithSecrets(s ...string) Option { + return sec(slices.Clone(s)) +} + +//////////////////////////////////////////////////////////////////////////////// + +type pr bool + +func (o pr) ApplyTo(opts *Options) { + opts.PropgateConsumerIdentity = bool(o) +} + +func WithPropagation(b ...bool) Option { + return pr(utils.OptionalDefaultedBool(true, b...)) +} diff --git a/pkg/contexts/credentials/repositories/vault/provider.go b/pkg/contexts/credentials/repositories/vault/provider.go new file mode 100644 index 0000000000..2e883c41e2 --- /dev/null +++ b/pkg/contexts/credentials/repositories/vault/provider.go @@ -0,0 +1,270 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package vault + +import ( + "context" + "encoding/json" + "path" + "strings" + "sync" + "time" + + "github.com/hashicorp/vault-client-go" + "golang.org/x/exp/slices" + + "github.com/open-component-model/ocm/pkg/common" + "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" + "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/vault/identity" + "github.com/open-component-model/ocm/pkg/errors" +) + +const PROVIDER = "ocm.software/credentialprovider/" + Type + +type mapping struct { + Id cpi.ConsumerIdentity + Name string +} + +type ConsumerProvider struct { + lock sync.Mutex + credentials map[string]cpi.DirectCredentials + repository *Repository + creds cpi.CredentialsSource + consumer []*mapping + + updated bool +} + +var _ cpi.ConsumerProvider = (*ConsumerProvider)(nil) + +func NewConsumerProvider(repo *Repository) (*ConsumerProvider, error) { + src, err := repo.ctx.GetCredentialsForConsumer(repo.id) + if err != nil { + return nil, err + } + return &ConsumerProvider{ + creds: src, + repository: repo, + credentials: map[string]cpi.DirectCredentials{}, + }, nil +} + +func (p *ConsumerProvider) update() error { + var err error + + if p.updated { + return nil + } + p.updated = true + + p.creds, err = p.repository.ctx.GetCredentialsForConsumer(p.repository.id, identity.IdentityMatcher) + if err != nil { + return err + } + creds, err := p.creds.Credentials(p.repository.ctx) + if err != nil { + return err + } + err = p.validateCreds(creds) + if err != nil { + return err + } + + p.credentials = map[string]cpi.DirectCredentials{} + p.consumer = nil + + ctx := context.Background() + + client, err := vault.New( + vault.WithAddress("https://vault.tools.sap"), + vault.WithRequestTimeout(30*time.Second), + ) + if err != nil { + return err + } + + // vault.WithMountPath("piper/PIPELINE-GROUP-4953/PIPELINE-25042/appRoleCredentials"), + token, err := p.getToken(ctx, client, creds) + if err != nil { + return err + } + + if err := client.SetToken(token); err != nil { + return err + } + + secrets := slices.Clone(p.repository.spec.Secrets) + for i := 0; i < len(secrets); i++ { + n := secrets[i] + creds, id, list, err := p.read(ctx, client, n) + p.error(err, "error reading vault secret", n) + if err == nil { + for _, a := range list { + if !slices.Contains(secrets, a) { + secrets = append(secrets, a) + } + } + if len(id) > 0 { + p.consumer = append(p.consumer, &mapping{ + Id: cpi.ConsumerIdentity(id), + Name: n, + }) + } + if len(creds) > 0 { + p.credentials[n] = cpi.DirectCredentials(creds) + } + } + } + return nil +} + +func (p *ConsumerProvider) validateCreds(creds cpi.Credentials) error { + m := creds.GetProperty(identity.ATTR_AUTHMETH) + if m == "" { + return errors.ErrRequired(identity.ATTR_AUTHMETH) + } + meth := methods.Get(m) + if meth == nil { + return errors.ErrInvalid(identity.ATTR_AUTHMETH, m) + } + return meth.Validate(creds) +} + +func (p *ConsumerProvider) getToken(ctx context.Context, client *vault.Client, creds cpi.Credentials) (string, error) { + m := creds.GetProperty(identity.ATTR_AUTHMETH) + return methods.Get(m).GetToken(ctx, client, p.repository.spec.Namespace, creds) +} + +func (p *ConsumerProvider) error(err error, msg string, secret string, keypairs ...interface{}) { + if err == nil { + return + } + log.Error(msg, append(keypairs, + "server", p.repository.spec.ServerURL, + "namespace", p.repository.spec.Namespace, + "engine", p.repository.spec.SearchEngine, + "path", path.Join(p.repository.spec.Path, secret), + "error", err.Error(), + )..., + ) +} + +func (p *ConsumerProvider) read(ctx context.Context, client *vault.Client, secret string) (common.Properties, common.Properties, []string, error) { + // read the secret + + secret = path.Join(p.repository.spec.Path, secret) + s, err := client.Secrets.KvV2Read(ctx, secret, + vault.WithNamespace(p.repository.spec.Namespace), + vault.WithMountPath(p.repository.spec.SearchEngine)) + if err != nil { + return nil, nil, nil, err + } + + var id common.Properties + var list []string + props := getProps(s.Data.Data) + + if meta, ok := s.Data.Metadata["custom_metadata"].(map[string]interface{}); ok { + sub := false + if cid := meta["consumerId"]; cid != nil { + id = common.Properties{} + if err := json.Unmarshal([]byte(cid.(string)), &id); err != nil { + id = nil + } + sub = true + } + if cid := meta["secrets"]; cid != nil { + if s, ok := meta["secrets"].(string); ok { + for _, e := range strings.Split(s, ",") { + e = strings.TrimSpace(e) + if e != "" { + list = append(list, e) + } + } + } + sub = true + } + if _, ok := meta[cpi.ID_TYPE]; !sub && ok { + id = getProps(meta) + } + } + return props, id, list, nil +} + +func getProps(data map[string]interface{}) common.Properties { + props := common.Properties{} + for k, v := range data { + if s, ok := v.(string); ok { + props[k] = s + } + } + return props +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ConsumerProvider interface + +func (p *ConsumerProvider) Unregister(id cpi.ProviderIdentity) { +} + +func (p *ConsumerProvider) Match(req cpi.ConsumerIdentity, cur cpi.ConsumerIdentity, m cpi.IdentityMatcher) (cpi.CredentialsSource, cpi.ConsumerIdentity) { + return p.get(req, cur, m) +} + +func (p *ConsumerProvider) Get(req cpi.ConsumerIdentity) (cpi.CredentialsSource, bool) { + creds, _ := p.get(req, nil, cpi.CompleteMatch) + return creds, creds != nil +} + +func (p *ConsumerProvider) get(req cpi.ConsumerIdentity, cur cpi.ConsumerIdentity, m cpi.IdentityMatcher) (cpi.CredentialsSource, cpi.ConsumerIdentity) { + if req.Equals(p.repository.id) { + return nil, cur + } + + p.lock.Lock() + defer p.lock.Unlock() + + p.update() + var creds cpi.CredentialsSource + + for _, a := range p.consumer { + if m(req, cur, a.Id) { + cur = a.Id + creds = p.credentials[a.Name] + } + } + return creds, cur +} + +//////////////////////////////////////////////////////////////////////////////// +// lookup + +func (c *ConsumerProvider) ExistsCredentials(name string) (bool, error) { + c.lock.Lock() + defer c.lock.Unlock() + + err := c.update() + if err != nil { + return false, err + } + _, ok := c.credentials[name] + return ok, nil +} + +func (c *ConsumerProvider) LookupCredentials(name string) (cpi.Credentials, error) { + c.lock.Lock() + defer c.lock.Unlock() + + err := c.update() + if err != nil { + return nil, err + } + src, ok := c.credentials[name] + if ok { + return src, nil + } + return nil, nil +} diff --git a/pkg/contexts/credentials/repositories/vault/repository.go b/pkg/contexts/credentials/repositories/vault/repository.go new file mode 100644 index 0000000000..f5c8167625 --- /dev/null +++ b/pkg/contexts/credentials/repositories/vault/repository.go @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package vault + +import ( + "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" + "github.com/open-component-model/ocm/pkg/contexts/credentials/internal" + "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/vault/identity" + "github.com/open-component-model/ocm/pkg/errors" +) + +type Repository struct { + ctx cpi.Context + spec *RepositorySpec + id cpi.ConsumerIdentity + provider *ConsumerProvider +} + +var ( + _ cpi.Repository = (*Repository)(nil) + _ cpi.ConsumerIdentityProvider = (*Repository)(nil) +) + +func NewRepository(ctx cpi.Context, spec *RepositorySpec) (*Repository, error) { + id, err := identity.GetConsumerId(spec.ServerURL, spec.Namespace, spec.Path) + if err != nil { + return nil, err + } + r := &Repository{ + ctx: ctx, + spec: spec, + id: id, + } + if spec.ServerURL == "" { + return nil, errors.ErrInvalid("server url") + } + r.provider, err = NewConsumerProvider(r) + if err != nil { + return nil, err + } + if spec.PropgateConsumerIdentity { + ctx.RegisterConsumerProvider(spec.GetKey(), r.provider) + } + return r, err +} + +var _ cpi.Repository = &Repository{} + +func (r *Repository) ExistsCredentials(name string) (bool, error) { + return r.provider.ExistsCredentials(name) +} + +func (r *Repository) LookupCredentials(name string) (cpi.Credentials, error) { + return r.provider.LookupCredentials(name) +} + +func (r *Repository) WriteCredentials(name string, creds cpi.Credentials) (cpi.Credentials, error) { + return nil, errors.ErrNotSupported("write", "credentials", Type) +} + +func (r *Repository) GetConsumerId(uctx ...internal.UsageContext) internal.ConsumerIdentity { + return r.id +} + +func (r *Repository) GetIdentityMatcher() string { + return identity.CONSUMER_TYPE +} diff --git a/pkg/contexts/credentials/repositories/vault/suite_test.go b/pkg/contexts/credentials/repositories/vault/suite_test.go new file mode 100644 index 0000000000..9a64144cc1 --- /dev/null +++ b/pkg/contexts/credentials/repositories/vault/suite_test.go @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package vault_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestConfig(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Vault Credentials Test Suite") +} diff --git a/pkg/contexts/credentials/repositories/vault/type.go b/pkg/contexts/credentials/repositories/vault/type.go new file mode 100644 index 0000000000..59df58e67c --- /dev/null +++ b/pkg/contexts/credentials/repositories/vault/type.go @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package vault + +import ( + "encoding/json" + "fmt" + + "golang.org/x/exp/slices" + + "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" + "github.com/open-component-model/ocm/pkg/runtime" +) + +const ( + Type = "HashiCorpVault" + TypeV1 = Type + runtime.VersionSeparator + "v1" +) + +func init() { + cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](Type)) + cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](TypeV1)) +} + +// RepositorySpec describes a docker config based credential repository interface. +type RepositorySpec struct { + runtime.ObjectVersionedType `json:",inline"` + ServerURL string `json:"serverURL"` + Options `json:",inline"` +} + +// NewRepositorySpec creates a new memory RepositorySpec. +func NewRepositorySpec(url string, opts ...Option) *RepositorySpec { + var eff Options + for _, o := range opts { + o.ApplyTo(&eff) + } + return &RepositorySpec{ + ObjectVersionedType: runtime.NewVersionedTypedObject(Type), + ServerURL: url, + Options: eff, + } +} + +func (a *RepositorySpec) GetType() string { + return Type +} + +func (a *RepositorySpec) Repository(ctx cpi.Context, creds cpi.Credentials) (cpi.Repository, error) { + r := ctx.GetAttributes().GetOrCreateAttribute(ATTR_REPOS, newRepositories) + repos, ok := r.(*Repositories) + if !ok { + return nil, fmt.Errorf("failed to assert type %T to Repositories", r) + } + spec := *a + spec.Secrets = slices.Clone(a.Secrets) + if spec.SearchEngine == "" { + spec.SearchEngine = "secrets" + } + return repos.GetRepository(ctx, &spec) +} + +func (a *RepositorySpec) GetKey() cpi.ProviderIdentity { + spec := *a + spec.PropgateConsumerIdentity = false + data, err := json.Marshal(&spec) + if err == nil { + return cpi.ProviderIdentity(data) + } + return cpi.ProviderIdentity(spec.ServerURL) +} diff --git a/pkg/errors/required.go b/pkg/errors/required.go new file mode 100644 index 0000000000..cd0239459a --- /dev/null +++ b/pkg/errors/required.go @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package errors + +type RequiredError struct { + errinfo +} + +var formatRequired = NewDefaultFormatter("", "required", "for") + +func ErrRequired(spec ...string) error { + return &RequiredError{newErrInfo(formatRequired, spec...)} +} + +func ErrRequiredWrap(err error, spec ...string) error { + return &RequiredError{wrapErrInfo(err, formatRequired, spec...)} +} + +func IsErrNRequired(err error) bool { + return IsA(err, &RequiredError{}) +} + +func IsErrRequiredKind(err error, kind string) bool { + var uerr *RequiredError + if err == nil || !As(err, &uerr) { + return false + } + return uerr.kind == kind +} + +func IsErrRequiredElem(err error, kind, elem string) bool { + var uerr *RequiredError + if err == nil || !As(err, &uerr) { + return false + } + return uerr.kind == kind && uerr.elem != nil && *uerr.elem == elem +} From e18c7b3f98c038b1b9126640d761ee91c9a25ef7 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 1 Nov 2023 09:47:39 +0100 Subject: [PATCH 3/9] option utils --- pkg/optionutils/options.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/optionutils/options.go b/pkg/optionutils/options.go index 80a29e313e..d11173aa38 100644 --- a/pkg/optionutils/options.go +++ b/pkg/optionutils/options.go @@ -10,10 +10,14 @@ type Option[T any] interface { func EvalOptions[O any](opts ...Option[*O]) *O { var eff O - for _, opt := range opts { + ApplyOptions(&eff, opts...) + return &eff +} + +func ApplyOptions[O any](opts *O, list ...Option[*O]) { + for _, opt := range list { if opt != nil { - opt.ApplyTo(&eff) + opt.ApplyTo(opts) } } - return &eff } From 6d287c81f0ff6feca02a23c17d7e8a19a7464db1 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 1 Nov 2023 09:49:41 +0100 Subject: [PATCH 4/9] vault to optionutils --- .../credentials/repositories/vault/options.go | 29 +++++++++++++++---- .../credentials/repositories/vault/type.go | 7 ++--- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/pkg/contexts/credentials/repositories/vault/options.go b/pkg/contexts/credentials/repositories/vault/options.go index 910ac047be..94157d40a7 100644 --- a/pkg/contexts/credentials/repositories/vault/options.go +++ b/pkg/contexts/credentials/repositories/vault/options.go @@ -7,21 +7,40 @@ package vault import ( "golang.org/x/exp/slices" + "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/utils" ) -type Option interface { - ApplyTo(*Options) -} +type Option = optionutils.Option[*Options] type Options struct { - Namespace string `json:"namespace"` + Namespace string `json:"namespace,omitempty"` SearchEngine string `json:"searchEngine,omitempty"` Path string `json:"path,omitempty"` - Secrets []string `json:"secrets"` + Secrets []string `json:"secrets,omitempty"` PropgateConsumerIdentity bool `json:"propagateConsumerIdentity,omitempty"` } +var _ Option = (*Options)(nil) + +func (o *Options) ApplyTo(opts *Options) { + if o.Namespace != "" { + opts.Namespace = o.Namespace + } + if o.SearchEngine != "" { + opts.SearchEngine = o.SearchEngine + } + if o.Path != "" { + opts.Path = o.Path + } + if o.Secrets != nil { + opts.Secrets = slices.Clone(o.Secrets) + } + opts.PropgateConsumerIdentity = o.PropgateConsumerIdentity +} + +//////////////////////////////////////////////////////////////////////////////// + type ns string func (o ns) ApplyTo(opts *Options) { diff --git a/pkg/contexts/credentials/repositories/vault/type.go b/pkg/contexts/credentials/repositories/vault/type.go index 59df58e67c..0b40857d24 100644 --- a/pkg/contexts/credentials/repositories/vault/type.go +++ b/pkg/contexts/credentials/repositories/vault/type.go @@ -11,6 +11,7 @@ import ( "golang.org/x/exp/slices" "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" + "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/runtime" ) @@ -33,14 +34,10 @@ type RepositorySpec struct { // NewRepositorySpec creates a new memory RepositorySpec. func NewRepositorySpec(url string, opts ...Option) *RepositorySpec { - var eff Options - for _, o := range opts { - o.ApplyTo(&eff) - } return &RepositorySpec{ ObjectVersionedType: runtime.NewVersionedTypedObject(Type), ServerURL: url, - Options: eff, + Options: *optionutils.EvalOptions(opts...), } } From 7c6767e8a4abd304bb70f401343089e9bb73cf2d Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 1 Nov 2023 10:34:00 +0100 Subject: [PATCH 5/9] support for descriptive runtime types --- .../flagsets/flagsetscheme/scheme.go | 35 ++-- .../flagsets/flagsetscheme/types.go | 65 +++++++ .../flagsets/flagsetscheme/types_options.go | 47 +++++ pkg/contexts/ocm/cpi/accessspec_options.go | 10 +- pkg/contexts/ocm/cpi/accesstypes.go | 8 +- pkg/contexts/ocm/cpi/clitypes/clitypes.go | 75 ------- .../ocm/cpi/clitypes/clitypes_options.go | 64 ------ pkg/contexts/ocm/internal/accesstypes.go | 4 +- .../labels/routingslip/internal/entrytypes.go | 4 +- .../routingslip/spi/entrytype_options.go | 10 +- .../ocm/labels/routingslip/spi/support.go | 8 +- pkg/contexts/ocm/usage.go | 71 +------ pkg/generics/cast.go | 4 +- pkg/optionutils/options.go | 9 +- pkg/optionutils/target.go | 39 ++++ pkg/runtime/descriptivetype/options.go | 68 +++++++ pkg/runtime/descriptivetype/type.go | 184 ++++++++++++++++++ 17 files changed, 459 insertions(+), 246 deletions(-) create mode 100644 pkg/cobrautils/flagsets/flagsetscheme/types.go create mode 100644 pkg/cobrautils/flagsets/flagsetscheme/types_options.go delete mode 100644 pkg/contexts/ocm/cpi/clitypes/clitypes.go delete mode 100644 pkg/contexts/ocm/cpi/clitypes/clitypes_options.go create mode 100644 pkg/optionutils/target.go create mode 100644 pkg/runtime/descriptivetype/options.go create mode 100644 pkg/runtime/descriptivetype/type.go diff --git a/pkg/cobrautils/flagsets/flagsetscheme/scheme.go b/pkg/cobrautils/flagsets/flagsetscheme/scheme.go index 78bf5cea41..c403fe28d4 100644 --- a/pkg/cobrautils/flagsets/flagsetscheme/scheme.go +++ b/pkg/cobrautils/flagsets/flagsetscheme/scheme.go @@ -8,17 +8,16 @@ import ( "github.com/open-component-model/ocm/pkg/cobrautils/flagsets" "github.com/open-component-model/ocm/pkg/logging" "github.com/open-component-model/ocm/pkg/runtime" + "github.com/open-component-model/ocm/pkg/runtime/descriptivetype" "github.com/open-component-model/ocm/pkg/utils" ) // VersionTypedObjectType is the appropriately extended type interface // based on runtime.VersionTypedObjectType. type VersionTypedObjectType[T runtime.VersionedTypedObject] interface { - runtime.VersionedTypedObjectType[T] + descriptivetype.TypedObjectType[T] ConfigOptionTypeSetHandler() flagsets.ConfigOptionTypeSetHandler - Description() string - Format() string } //////////////////////////////////////////////////////////////////////////////// @@ -26,26 +25,36 @@ type VersionTypedObjectType[T runtime.VersionedTypedObject] interface { // TypeScheme is the appropriately extended scheme interface based on // runtime.TypeScheme. type TypeScheme[T runtime.VersionedTypedObject, R VersionTypedObjectType[T]] interface { - runtime.TypeScheme[T, R] + descriptivetype.TypeScheme[T, R] + CreateConfigTypeSetConfigProvider() flagsets.ConfigTypeOptionSetConfigProvider } -type _typeScheme[T runtime.VersionedTypedObject, R VersionTypedObjectType[T]] runtime.TypeScheme[T, R] +type _typeScheme[T runtime.VersionedTypedObject, R VersionTypedObjectType[T]] interface { + descriptivetype.TypeScheme[T, R] +} type typeScheme[T runtime.VersionedTypedObject, R VersionTypedObjectType[T], S TypeScheme[T, R]] struct { - name string + cfgname string description string group string typeOption string _typeScheme[T, R] } +func flagExtender[T runtime.VersionedTypedObject, R VersionTypedObjectType[T]](ty R) string { + if h := ty.ConfigOptionTypeSetHandler(); h != nil { + return utils.IndentLines(flagsets.FormatConfigOptions(h), " ") + } + return "" +} + // NewTypeScheme provides an TypeScheme implementation based on the interfaces // and the default runtime.TypeScheme implementation. -func NewTypeScheme[T runtime.VersionedTypedObject, R VersionTypedObjectType[T], S TypeScheme[T, R]](name, typeOption, desc, group string, unknown runtime.Unstructured, acceptUnknown bool, base ...S) TypeScheme[T, R] { - scheme := runtime.MustNewDefaultTypeScheme[T, R](unknown, acceptUnknown, nil, utils.Optional(base...)) +func NewTypeScheme[T runtime.VersionedTypedObject, R VersionTypedObjectType[T], S TypeScheme[T, R]](kindname string, cfgname, typeOption, desc, group string, unknown runtime.Unstructured, acceptUnknown bool, base ...S) TypeScheme[T, R] { + scheme := descriptivetype.NewTypeScheme[T, R](kindname, flagExtender[T, R], unknown, acceptUnknown, utils.Optional(base...)) return &typeScheme[T, R, S]{ - name: name, + cfgname: cfgname, description: desc, group: group, typeOption: typeOption, @@ -56,9 +65,9 @@ func NewTypeScheme[T runtime.VersionedTypedObject, R VersionTypedObjectType[T], func (t *typeScheme[T, R, S]) CreateConfigTypeSetConfigProvider() flagsets.ConfigTypeOptionSetConfigProvider { var prov flagsets.ConfigTypeOptionSetConfigProvider if t.typeOption == "" { - prov = flagsets.NewExplicitlyTypedConfigProvider(t.name, t.description, true) + prov = flagsets.NewExplicitlyTypedConfigProvider(t.cfgname, t.description, true) } else { - prov = flagsets.NewTypedConfigProvider(t.name, t.description, t.typeOption, true) + prov = flagsets.NewTypedConfigProvider(t.cfgname, t.description, t.typeOption, true) } if t.group != "" { prov.AddGroups(t.group) @@ -66,7 +75,7 @@ func (t *typeScheme[T, R, S]) CreateConfigTypeSetConfigProvider() flagsets.Confi for _, p := range t.KnownTypes() { err := prov.AddTypeSet(p.ConfigOptionTypeSetHandler()) if err != nil { - logging.Logger().LogError(err, "cannot compose type CLI options", "type", t.name) + logging.Logger().LogError(err, "cannot compose type CLI options", "type", t.cfgname) } } if t.BaseScheme() != nil { @@ -75,7 +84,7 @@ func (t *typeScheme[T, R, S]) CreateConfigTypeSetConfigProvider() flagsets.Confi if prov.GetTypeSet(s.GetName()) == nil { err := prov.AddTypeSet(s) if err != nil { - logging.Logger().LogError(err, "cannot compose type CLI options", "type", t.name) + logging.Logger().LogError(err, "cannot compose type CLI options", "type", t.cfgname) } } } diff --git a/pkg/cobrautils/flagsets/flagsetscheme/types.go b/pkg/cobrautils/flagsets/flagsetscheme/types.go new file mode 100644 index 0000000000..48b8acb25a --- /dev/null +++ b/pkg/cobrautils/flagsets/flagsetscheme/types.go @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package flagsetscheme + +import ( + "github.com/open-component-model/ocm/pkg/cobrautils/flagsets" + "github.com/open-component-model/ocm/pkg/optionutils" + "github.com/open-component-model/ocm/pkg/runtime" + "github.com/open-component-model/ocm/pkg/runtime/descriptivetype" +) + +type additionalTypeInfo interface { + ConfigOptionTypeSetHandler() flagsets.ConfigOptionTypeSetHandler + Description() string + Format() string +} + +type TypedObjectTypeObject[E runtime.VersionedTypedObject] struct { + *descriptivetype.TypedObjectTypeObject[E] + handler flagsets.ConfigOptionTypeSetHandler + validator func(E) error +} + +var _ additionalTypeInfo = (*TypedObjectTypeObject[runtime.VersionedTypedObject])(nil) + +func NewTypedObjectTypeObject[E runtime.VersionedTypedObject](vt runtime.VersionedTypedObjectType[E], opts ...TypeOption) *TypedObjectTypeObject[E] { + t := NewTypeObjectTarget[E](&TypedObjectTypeObject[E]{ + TypedObjectTypeObject: descriptivetype.NewTypedObjectTypeObject[E](vt), + }) + optionutils.ApplyOptions[OptionTarget](t, opts...) + return t.target +} + +func (t *TypedObjectTypeObject[E]) ConfigOptionTypeSetHandler() flagsets.ConfigOptionTypeSetHandler { + return t.handler +} + +func (t *TypedObjectTypeObject[E]) Validate(e E) error { + if t.validator == nil { + return nil + } + return t.validator(e) +} + +//////////////////////////////////////////////////////////////////////////////// + +// TypeObjectTarget is used as target for option functions, it provides +// setters for fields, which should nor be modifiable for a final type object. +type TypeObjectTarget[E runtime.VersionedTypedObject] struct { + *descriptivetype.TypeObjectTarget[E] + target *TypedObjectTypeObject[E] +} + +func NewTypeObjectTarget[E runtime.VersionedTypedObject](target *TypedObjectTypeObject[E]) *TypeObjectTarget[E] { + return &TypeObjectTarget[E]{ + target: target, + TypeObjectTarget: descriptivetype.NewTypeObjectTarget[E](target.TypedObjectTypeObject), + } +} + +func (t TypeObjectTarget[E]) SetConfigHandler(value flagsets.ConfigOptionTypeSetHandler) { + t.target.handler = value +} diff --git a/pkg/cobrautils/flagsets/flagsetscheme/types_options.go b/pkg/cobrautils/flagsets/flagsetscheme/types_options.go new file mode 100644 index 0000000000..104b76eac9 --- /dev/null +++ b/pkg/cobrautils/flagsets/flagsetscheme/types_options.go @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package flagsetscheme + +import ( + "github.com/open-component-model/ocm/pkg/cobrautils/flagsets" + "github.com/open-component-model/ocm/pkg/optionutils" + "github.com/open-component-model/ocm/pkg/runtime/descriptivetype" +) + +//////////////////////////////////////////////////////////////////////////////// +// Access Type Options + +type OptionTarget interface { + descriptivetype.OptionTarget + SetConfigHandler(flagsets.ConfigOptionTypeSetHandler) +} + +type TypeOption = optionutils.Option[OptionTarget] + +//////////////////////////////////////////////////////////////////////////////// +// options derived from descriptivetype. + +func WithFormatSpec(value string) TypeOption { + return optionutils.MapOptionTarget[OptionTarget](descriptivetype.WithFormatSpec(value)) +} + +func WithDescription(value string) TypeOption { + return optionutils.MapOptionTarget[OptionTarget](descriptivetype.WithDescription(value)) +} + +//////////////////////////////////////////////////////////////////////////////// +// additional options. + +type configOption struct { + value flagsets.ConfigOptionTypeSetHandler +} + +func WithConfigHandler(value flagsets.ConfigOptionTypeSetHandler) TypeOption { + return configOption{value} +} + +func (o configOption) ApplyTo(t OptionTarget) { + t.SetConfigHandler(o.value) +} diff --git a/pkg/contexts/ocm/cpi/accessspec_options.go b/pkg/contexts/ocm/cpi/accessspec_options.go index 380dc99de2..718277e4e5 100644 --- a/pkg/contexts/ocm/cpi/accessspec_options.go +++ b/pkg/contexts/ocm/cpi/accessspec_options.go @@ -6,19 +6,19 @@ package cpi import ( "github.com/open-component-model/ocm/pkg/cobrautils/flagsets" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/clitypes" + "github.com/open-component-model/ocm/pkg/cobrautils/flagsets/flagsetscheme" ) -type AccessSpecTypeOption = clitypes.CLITypeOption +type AccessSpecTypeOption = flagsetscheme.TypeOption func WithFormatSpec(value string) AccessSpecTypeOption { - return clitypes.WithFormatSpec(value) + return flagsetscheme.WithFormatSpec(value) } func WithDescription(value string) AccessSpecTypeOption { - return clitypes.WithDescription(value) + return flagsetscheme.WithDescription(value) } func WithConfigHandler(value flagsets.ConfigOptionTypeSetHandler) AccessSpecTypeOption { - return clitypes.WithConfigHandler(value) + return flagsetscheme.WithConfigHandler(value) } diff --git a/pkg/contexts/ocm/cpi/accesstypes.go b/pkg/contexts/ocm/cpi/accesstypes.go index 14024d5267..f573991325 100644 --- a/pkg/contexts/ocm/cpi/accesstypes.go +++ b/pkg/contexts/ocm/cpi/accesstypes.go @@ -5,7 +5,7 @@ package cpi import ( - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/clitypes" + "github.com/open-component-model/ocm/pkg/cobrautils/flagsets/flagsetscheme" "github.com/open-component-model/ocm/pkg/runtime" ) @@ -36,13 +36,13 @@ func MustNewAccessSpecMultiFormatVersion(kind string, formats AccessSpecFormatVe } func NewAccessSpecType[I AccessSpec](name string, opts ...AccessSpecTypeOption) AccessType { - return clitypes.NewCLITypedObjectTypeObject[AccessSpec](runtime.NewVersionedTypedObjectType[AccessSpec, I](name), opts...) + return flagsetscheme.NewTypedObjectTypeObject[AccessSpec](runtime.NewVersionedTypedObjectType[AccessSpec, I](name), opts...) } func NewAccessSpecTypeByConverter[I AccessSpec, V runtime.VersionedTypedObject](name string, converter runtime.Converter[I, V], opts ...AccessSpecTypeOption) AccessType { - return clitypes.NewCLITypedObjectTypeObject[AccessSpec](runtime.NewVersionedTypedObjectTypeByConverter[AccessSpec, I, V](name, converter), opts...) + return flagsetscheme.NewTypedObjectTypeObject[AccessSpec](runtime.NewVersionedTypedObjectTypeByConverter[AccessSpec, I, V](name, converter), opts...) } func NewAccessSpecTypeByFormatVersion(name string, fmt runtime.FormatVersion[AccessSpec], opts ...AccessSpecTypeOption) AccessType { - return clitypes.NewCLITypedObjectTypeObject[AccessSpec](runtime.NewVersionedTypedObjectTypeByFormatVersion[AccessSpec](name, fmt), opts...) + return flagsetscheme.NewTypedObjectTypeObject[AccessSpec](runtime.NewVersionedTypedObjectTypeByFormatVersion[AccessSpec](name, fmt), opts...) } diff --git a/pkg/contexts/ocm/cpi/clitypes/clitypes.go b/pkg/contexts/ocm/cpi/clitypes/clitypes.go deleted file mode 100644 index cff1aa0cdb..0000000000 --- a/pkg/contexts/ocm/cpi/clitypes/clitypes.go +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. -// -// SPDX-License-Identifier: Apache-2.0 - -package clitypes - -import ( - "github.com/open-component-model/ocm/pkg/cobrautils/flagsets" - "github.com/open-component-model/ocm/pkg/runtime" -) - -type additionalCLITypeInfo interface { - ConfigOptionTypeSetHandler() flagsets.ConfigOptionTypeSetHandler - Description() string - Format() string -} - -type CLITypedObjectTypeObject[E runtime.VersionedTypedObject] struct { - runtime.VersionedTypedObjectType[E] - description string - format string - handler flagsets.ConfigOptionTypeSetHandler - validator func(E) error -} - -var _ additionalCLITypeInfo = (*CLITypedObjectTypeObject[runtime.VersionedTypedObject])(nil) - -func NewCLITypedObjectTypeObject[E runtime.VersionedTypedObject](vt runtime.VersionedTypedObjectType[E], opts ...CLITypeOption) *CLITypedObjectTypeObject[E] { - t := CLIObjectTypeTarget[E]{&CLITypedObjectTypeObject[E]{ - VersionedTypedObjectType: vt, - }} - for _, o := range opts { - o.ApplyToCLIOptionTarget(t) - } - return t.target -} - -func (t *CLITypedObjectTypeObject[E]) ConfigOptionTypeSetHandler() flagsets.ConfigOptionTypeSetHandler { - return t.handler -} - -func (t *CLITypedObjectTypeObject[E]) Description() string { - return t.description -} - -func (t *CLITypedObjectTypeObject[E]) Format() string { - return t.format -} - -func (t *CLITypedObjectTypeObject[E]) Validate(e E) error { - if t.validator == nil { - return nil - } - return t.validator(e) -} - -//////////////////////////////////////////////////////////////////////////////// - -// CLIObjectTypeTarget is used as target for option functions, it provides -// setters for fields, which should nor be modifiable for a final type object. -type CLIObjectTypeTarget[E runtime.VersionedTypedObject] struct { - target *CLITypedObjectTypeObject[E] -} - -func (t CLIObjectTypeTarget[E]) SetDescription(value string) { - t.target.description = value -} - -func (t CLIObjectTypeTarget[E]) SetFormat(value string) { - t.target.format = value -} - -func (t CLIObjectTypeTarget[E]) SetConfigHandler(value flagsets.ConfigOptionTypeSetHandler) { - t.target.handler = value -} diff --git a/pkg/contexts/ocm/cpi/clitypes/clitypes_options.go b/pkg/contexts/ocm/cpi/clitypes/clitypes_options.go deleted file mode 100644 index a462552a9a..0000000000 --- a/pkg/contexts/ocm/cpi/clitypes/clitypes_options.go +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. -// -// SPDX-License-Identifier: Apache-2.0 - -package clitypes - -import ( - "github.com/open-component-model/ocm/pkg/cobrautils/flagsets" -) - -//////////////////////////////////////////////////////////////////////////////// -// Access Type Options - -type CLIOptionTarget interface { - SetFormat(string) - SetDescription(string) - SetConfigHandler(flagsets.ConfigOptionTypeSetHandler) -} - -type CLITypeOption interface { - ApplyToCLIOptionTarget(CLIOptionTarget) -} - -//////////////////////////////////////////////////////////////////////////////// - -type formatOption struct { - value string -} - -func WithFormatSpec(value string) CLITypeOption { - return formatOption{value} -} - -func (o formatOption) ApplyToCLIOptionTarget(t CLIOptionTarget) { - t.SetFormat(o.value) -} - -//////////////////////////////////////////////////////////////////////////////// - -type descriptionOption struct { - value string -} - -func WithDescription(value string) CLITypeOption { - return descriptionOption{value} -} - -func (o descriptionOption) ApplyToCLIOptionTarget(t CLIOptionTarget) { - t.SetDescription(o.value) -} - -//////////////////////////////////////////////////////////////////////////////// - -type configOption struct { - value flagsets.ConfigOptionTypeSetHandler -} - -func WithConfigHandler(value flagsets.ConfigOptionTypeSetHandler) CLITypeOption { - return configOption{value} -} - -func (o configOption) ApplyToCLIOptionTarget(t CLIOptionTarget) { - t.SetConfigHandler(o.value) -} diff --git a/pkg/contexts/ocm/internal/accesstypes.go b/pkg/contexts/ocm/internal/accesstypes.go index 02f18adf1a..e912cd4388 100644 --- a/pkg/contexts/ocm/internal/accesstypes.go +++ b/pkg/contexts/ocm/internal/accesstypes.go @@ -100,11 +100,11 @@ type AccessMethodView interface { type AccessTypeScheme flagsetscheme.TypeScheme[AccessSpec, AccessType] func NewAccessTypeScheme(base ...AccessTypeScheme) AccessTypeScheme { - return flagsetscheme.NewTypeScheme[AccessSpec, AccessType, AccessTypeScheme]("access", "accessType", "blob access specification", "Access Specification Options", &UnknownAccessSpec{}, true, base...) + return flagsetscheme.NewTypeScheme[AccessSpec, AccessType, AccessTypeScheme]("Access type", "access", "accessType", "blob access specification", "Access Specification Options", &UnknownAccessSpec{}, true, base...) } func NewStrictAccessTypeScheme(base ...AccessTypeScheme) runtime.VersionedTypeRegistry[AccessSpec, AccessType] { - return flagsetscheme.NewTypeScheme[AccessSpec, AccessType, AccessTypeScheme]("access", "accessType", "blob access specification", "Access Specification Options", nil, false, base...) + return flagsetscheme.NewTypeScheme[AccessSpec, AccessType, AccessTypeScheme]("Access type", "access", "accessType", "blob access specification", "Access Specification Options", nil, false, base...) } // DefaultAccessTypeScheme contains all globally known access serializer. diff --git a/pkg/contexts/ocm/labels/routingslip/internal/entrytypes.go b/pkg/contexts/ocm/labels/routingslip/internal/entrytypes.go index cde0d8496c..932d972c41 100644 --- a/pkg/contexts/ocm/labels/routingslip/internal/entrytypes.go +++ b/pkg/contexts/ocm/labels/routingslip/internal/entrytypes.go @@ -45,11 +45,11 @@ type ( type EntryTypeScheme = flagsetscheme.TypeScheme[Entry, EntryType] func NewEntryTypeScheme(base ...EntryTypeScheme) EntryTypeScheme { - return flagsetscheme.NewTypeScheme[Entry, EntryType, EntryTypeScheme]("entry", "", "routing slip entry specification", "Entry Specification Options", &UnknownEntry{}, true, base...) + return flagsetscheme.NewTypeScheme[Entry, EntryType, EntryTypeScheme]("Entry type", "entry", "", "routing slip entry specification", "Entry Specification Options", &UnknownEntry{}, true, base...) } func NewStrictEntryTypeScheme(base ...EntryTypeScheme) EntryTypeScheme { - return flagsetscheme.NewTypeScheme[Entry, EntryType, EntryTypeScheme]("entry", "", "routing slip entry specification", "Entry Specification Options", nil, false, base...) + return flagsetscheme.NewTypeScheme[Entry, EntryType, EntryTypeScheme]("Entry type", "entry", "", "routing slip entry specification", "Entry Specification Options", nil, false, base...) } func CreateEntry(t runtime.TypedObject) (Entry, error) { diff --git a/pkg/contexts/ocm/labels/routingslip/spi/entrytype_options.go b/pkg/contexts/ocm/labels/routingslip/spi/entrytype_options.go index 127119fbb0..ad727449c7 100644 --- a/pkg/contexts/ocm/labels/routingslip/spi/entrytype_options.go +++ b/pkg/contexts/ocm/labels/routingslip/spi/entrytype_options.go @@ -6,19 +6,19 @@ package spi import ( "github.com/open-component-model/ocm/pkg/cobrautils/flagsets" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/clitypes" + "github.com/open-component-model/ocm/pkg/cobrautils/flagsets/flagsetscheme" ) -type EntryTypeOption = clitypes.CLITypeOption +type EntryTypeOption = flagsetscheme.TypeOption func WithFormatSpec(value string) EntryTypeOption { - return clitypes.WithFormatSpec(value) + return flagsetscheme.WithFormatSpec(value) } func WithDescription(value string) EntryTypeOption { - return clitypes.WithDescription(value) + return flagsetscheme.WithDescription(value) } func WithConfigHandler(value flagsets.ConfigOptionTypeSetHandler) EntryTypeOption { - return clitypes.WithConfigHandler(value) + return flagsetscheme.WithConfigHandler(value) } diff --git a/pkg/contexts/ocm/labels/routingslip/spi/support.go b/pkg/contexts/ocm/labels/routingslip/spi/support.go index a46846a127..2a0307208b 100644 --- a/pkg/contexts/ocm/labels/routingslip/spi/support.go +++ b/pkg/contexts/ocm/labels/routingslip/spi/support.go @@ -5,7 +5,7 @@ package spi import ( - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/clitypes" + "github.com/open-component-model/ocm/pkg/cobrautils/flagsets/flagsetscheme" "github.com/open-component-model/ocm/pkg/runtime" ) @@ -30,15 +30,15 @@ func MustNewEntryMultiFormatVersion(kind string, formats EntryFormatVersionRegis //////////////////////////////////////////////////////////////////////////////// func NewEntryType[I Entry](name string, opts ...EntryTypeOption) EntryType { - return clitypes.NewCLITypedObjectTypeObject[Entry](runtime.NewVersionedTypedObjectType[Entry, I](name), opts...) + return flagsetscheme.NewTypedObjectTypeObject[Entry](runtime.NewVersionedTypedObjectType[Entry, I](name), opts...) } func NewEntryTypeByConverter[I Entry, V runtime.VersionedTypedObject](name string, converter runtime.Converter[I, V], opts ...EntryTypeOption) EntryType { - return clitypes.NewCLITypedObjectTypeObject[Entry](runtime.NewVersionedTypedObjectTypeByConverter[Entry, I, V](name, converter), opts...) + return flagsetscheme.NewTypedObjectTypeObject[Entry](runtime.NewVersionedTypedObjectTypeByConverter[Entry, I, V](name, converter), opts...) } func NewEntryTypeByFormatVersion(name string, fmt runtime.FormatVersion[Entry], opts ...EntryTypeOption) EntryType { - return clitypes.NewCLITypedObjectTypeObject[Entry](runtime.NewVersionedTypedObjectTypeByFormatVersion[Entry](name, fmt), opts...) + return flagsetscheme.NewTypedObjectTypeObject[Entry](runtime.NewVersionedTypedObjectTypeByFormatVersion[Entry](name, fmt), opts...) } //////////////////////////////////////////////////////////////////////////////// diff --git a/pkg/contexts/ocm/usage.go b/pkg/contexts/ocm/usage.go index b3be485652..7ed4e0e10b 100644 --- a/pkg/contexts/ocm/usage.go +++ b/pkg/contexts/ocm/usage.go @@ -4,15 +4,6 @@ package ocm -import ( - "fmt" - "strings" - - "github.com/open-component-model/ocm/pkg/cobrautils/flagsets" - "github.com/open-component-model/ocm/pkg/runtime" - "github.com/open-component-model/ocm/pkg/utils" -) - func AccessUsage(scheme AccessTypeScheme, cli bool) string { s := ` The following list describes the supported access methods, their versions @@ -22,65 +13,5 @@ The access method specification can be put below the access field. If always requires the field type describing the kind and version shown below. ` - type method struct { - desc string - versions map[string]string - options flagsets.ConfigOptionTypeSetHandler - } - - descs := map[string]*method{} - - // gather info for kinds and versions - for _, n := range scheme.KnownTypeNames() { - kind, vers := runtime.KindVersion(n) - - info := descs[kind] - if info == nil { - info = &method{versions: map[string]string{}} - descs[kind] = info - } - - if vers == "" { - vers = "v1" - } - if _, ok := info.versions[vers]; !ok { - info.versions[vers] = "" - } - - t := scheme.GetType(n) - - if t.ConfigOptionTypeSetHandler() != nil { - info.options = t.ConfigOptionTypeSetHandler() - } - desc := t.Description() - if desc != "" { - info.desc = desc - } - - desc = t.Format() - if desc != "" { - info.versions[vers] = desc - } - } - - for _, t := range utils.StringMapKeys(descs) { - info := descs[t] - desc := strings.Trim(info.desc, "\n") - if desc != "" { - s = fmt.Sprintf("%s\n- Access type %s\n\n%s\n\n", s, t, utils.IndentLines(desc, " ")) - - format := "" - for _, f := range utils.StringMapKeys(info.versions) { - desc = strings.Trim(info.versions[f], "\n") - if desc != "" { - format = fmt.Sprintf("%s\n- Version %s\n\n%s\n", format, f, utils.IndentLines(desc, " ")) - } - } - if format != "" { - s += fmt.Sprintf(" The following versions are supported:\n%s\n", strings.Trim(utils.IndentLines(format, " "), "\n")) - } - } - s += utils.IndentLines(flagsets.FormatConfigOptions(info.options), " ") - } - return s + return s + scheme.Describe() } diff --git a/pkg/generics/cast.go b/pkg/generics/cast.go index c27e21d236..3d6336b759 100644 --- a/pkg/generics/cast.go +++ b/pkg/generics/cast.go @@ -18,6 +18,8 @@ func TypeOf[T any]() reflect.Type { return reflect.TypeOf(&ifce).Elem() } +// As casts an element typed by a type parameter +// to a subtype, given by the type parameter T. func As[T any](o interface{}) T { var _nil T if o == nil { @@ -26,7 +28,7 @@ func As[T any](o interface{}) T { return o.(T) } -// CastPointer casts a pointer/error result to an interface/error +// AsE casts a pointer/error result to an interface/error // result. // In Go this cannot be done directly, because returning a nil pinter // for an interface return type, would result is a typed nil value for diff --git a/pkg/optionutils/options.go b/pkg/optionutils/options.go index d11173aa38..13f6e6402c 100644 --- a/pkg/optionutils/options.go +++ b/pkg/optionutils/options.go @@ -8,13 +8,20 @@ type Option[T any] interface { ApplyTo(T) } +// EvalOptions applies options to a new options object +// and returns this object. +// O must be a struct type. func EvalOptions[O any](opts ...Option[*O]) *O { var eff O ApplyOptions(&eff, opts...) return &eff } -func ApplyOptions[O any](opts *O, list ...Option[*O]) { +// ApplyOptions applies options to +// an option target O. O must either +// be a target interface type or a target struct +// pointer type. +func ApplyOptions[O any](opts O, list ...Option[O]) { for _, opt := range list { if opt != nil { opt.ApplyTo(opts) diff --git a/pkg/optionutils/target.go b/pkg/optionutils/target.go new file mode 100644 index 0000000000..003cc69dae --- /dev/null +++ b/pkg/optionutils/target.go @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package optionutils + +import ( + "github.com/open-component-model/ocm/pkg/generics" +) + +/////////////////////////////////////////////////////////////////////////////(// +// if the option target is an interface, it is easily possible to +// provide new targets with more options just by extending the +// target interface. The option consumer the accepts options for +// the target interface. +// To be able to reuse options from the base target interface +// a wrapper option implementation is required which implements +// the extended option interface and maps it to the base option +// interface. +// The following mechanism requires option targets W and B to be +// interface types. + +type targetInterfaceWrapper[B any, W any /*B*/] struct { + option Option[B] +} + +func (w *targetInterfaceWrapper[B, W]) ApplyTo(opts W) { + w.option.ApplyTo(generics.As[B](opts)) +} + +// MapOptionTarget maps the option target interface from +// B to W, hereby, W must be a subtype of B, which cannot be +// expressed with Go generics (Type constraint should be W B). +// If this constraint is not met, there will be a runtime error. +func MapOptionTarget[W, B any](opt Option[B]) Option[W] { + return &targetInterfaceWrapper[B, W]{ + opt, + } +} diff --git a/pkg/runtime/descriptivetype/options.go b/pkg/runtime/descriptivetype/options.go new file mode 100644 index 0000000000..6ed0bd372d --- /dev/null +++ b/pkg/runtime/descriptivetype/options.go @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package descriptivetype + +import ( + "github.com/open-component-model/ocm/pkg/optionutils" + "github.com/open-component-model/ocm/pkg/runtime" +) + +//////////////////////////////////////////////////////////////////////////////// + +// TypeObjectTarget is used as target for option functions, it provides +// setters for fields, which should not be modifiable for a final type object. +type TypeObjectTarget[E runtime.VersionedTypedObject] struct { + target *TypedObjectTypeObject[E] +} + +func NewTypeObjectTarget[E runtime.VersionedTypedObject](target *TypedObjectTypeObject[E]) *TypeObjectTarget[E] { + return &TypeObjectTarget[E]{target} +} + +func (t *TypeObjectTarget[E]) SetDescription(value string) { + t.target.description = value +} + +func (t *TypeObjectTarget[E]) SetFormat(value string) { + t.target.format = value +} + +//////////////////////////////////////////////////////////////////////////////// +// Descriptive Type Options + +type OptionTarget interface { + SetFormat(string) + SetDescription(string) +} + +type Option = optionutils.Option[OptionTarget] + +//////////////////////////////////////////////////////////////////////////////// + +type formatOption struct { + value string +} + +func WithFormatSpec(value string) Option { + return formatOption{value} +} + +func (o formatOption) ApplyTo(t OptionTarget) { + t.SetFormat(o.value) +} + +//////////////////////////////////////////////////////////////////////////////// + +type descriptionOption struct { + value string +} + +func WithDescription(value string) Option { + return descriptionOption{value} +} + +func (o descriptionOption) ApplyTo(t OptionTarget) { + t.SetDescription(o.value) +} diff --git a/pkg/runtime/descriptivetype/type.go b/pkg/runtime/descriptivetype/type.go new file mode 100644 index 0000000000..ff5e3fb637 --- /dev/null +++ b/pkg/runtime/descriptivetype/type.go @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package descriptivetype + +import ( + "fmt" + "strings" + + "github.com/open-component-model/ocm/pkg/runtime" + "github.com/open-component-model/ocm/pkg/utils" +) + +// DescriptionExtender provides an additional descrition for a type object +// which is appended to the format description in the schmeme descrition +// for the type in question. +type DescriptionExtender[T any] func(t T) string + +// TypedObjectType is the appropriately extended type interface +// based on runtime.VersionTypedObjectType providing support for a functional and +// format description. +type TypedObjectType[T runtime.VersionedTypedObject] interface { + runtime.VersionedTypedObjectType[T] + + Description() string + Format() string +} + +//////////////////////////////////////////////////////////////////////////////// + +// TypeScheme is the appropriately extended scheme interface based on +// runtime.TypeScheme. Based on the additional type info a complete +// scheme description can be created calling the Describe method. +type TypeScheme[T runtime.VersionedTypedObject, R TypedObjectType[T]] interface { + runtime.TypeScheme[T, R] + + Describe() string +} + +type _typeScheme[T runtime.VersionedTypedObject, R TypedObjectType[T]] interface { + runtime.TypeScheme[T, R] // for goland to be able to accept extender argument type +} + +type typeScheme[T runtime.VersionedTypedObject, R TypedObjectType[T], S TypeScheme[T, R]] struct { + name string + extender DescriptionExtender[R] + _typeScheme[T, R] +} + +func MustNewDefaultTypeScheme[T runtime.VersionedTypedObject, R TypedObjectType[T], S TypeScheme[T, R]](name string, extender DescriptionExtender[R], unknown runtime.Unstructured, acceptUnknown bool, defaultdecoder runtime.TypedObjectDecoder[T], base ...TypeScheme[T, R]) TypeScheme[T, R] { + scheme := runtime.MustNewDefaultTypeScheme[T, R](unknown, acceptUnknown, defaultdecoder, utils.Optional(base...)) + return &typeScheme[T, R, S]{ + name: name, + extender: extender, + _typeScheme: scheme, + } +} + +// NewTypeScheme provides an TypeScheme implementation based on the interfaces +// and the default runtime.TypeScheme implementation. +func NewTypeScheme[T runtime.VersionedTypedObject, R TypedObjectType[T], S TypeScheme[T, R]](name string, extender DescriptionExtender[R], unknown runtime.Unstructured, acceptUnknown bool, base ...S) TypeScheme[T, R] { + scheme := runtime.MustNewDefaultTypeScheme[T, R](unknown, acceptUnknown, nil, utils.Optional(base...)) + return &typeScheme[T, R, S]{ + name: name, + extender: extender, + _typeScheme: scheme, + } +} + +func (t *typeScheme[T, R, S]) KnownTypes() runtime.KnownTypes[T, R] { + return t._typeScheme.KnownTypes() // Goland +} + +//////////////////////////////////////////////////////////////////////////////// + +func (t *typeScheme[T, R, S]) Describe() string { + s := "" + type method struct { + desc string + versions map[string]string + more string + } + + descs := map[string]*method{} + + // gather info for kinds and versions + for _, n := range t.KnownTypeNames() { + kind, vers := runtime.KindVersion(n) + + info := descs[kind] + if info == nil { + info = &method{versions: map[string]string{}} + descs[kind] = info + } + + if vers == "" { + vers = "v1" + } + if _, ok := info.versions[vers]; !ok { + info.versions[vers] = "" + } + + ty := t.GetType(n) + + if t.extender != nil { + more := t.extender(ty) + if more != "" { + info.more = more + } + } + desc := ty.Description() + if desc != "" { + info.desc = desc + } + + desc = ty.Format() + if desc != "" { + info.versions[vers] = desc + } + } + + for _, tn := range utils.StringMapKeys(descs) { + info := descs[tn] + desc := strings.Trim(info.desc, "\n") + if desc != "" { + s = fmt.Sprintf("%s\n- %s %s\n\n%s\n\n", s, t.name, tn, utils.IndentLines(desc, " ")) + + format := "" + for _, f := range utils.StringMapKeys(info.versions) { + desc = strings.Trim(info.versions[f], "\n") + if desc != "" { + format = fmt.Sprintf("%s\n- Version %s\n\n%s\n", format, f, utils.IndentLines(desc, " ")) + } + } + if format != "" { + s += fmt.Sprintf(" The following versions are supported:\n%s\n", strings.Trim(utils.IndentLines(format, " "), "\n")) + } + } + s += info.more + } + return s +} + +//////////////////////////////////////////////////////////////////////////////// + +type descriptiveTypeInfo interface { + Description() string + Format() string +} + +type TypedObjectTypeObject[T runtime.VersionedTypedObject] struct { + runtime.VersionedTypedObjectType[T] + description string + format string + validator func(T) error +} + +var _ descriptiveTypeInfo = (*TypedObjectTypeObject[runtime.VersionedTypedObject])(nil) + +func NewTypedObjectTypeObject[E runtime.VersionedTypedObject](vt runtime.VersionedTypedObjectType[E], opts ...Option) *TypedObjectTypeObject[E] { + t := NewTypeObjectTarget[E](&TypedObjectTypeObject[E]{ + VersionedTypedObjectType: vt, + }) + for _, o := range opts { + o.ApplyTo(t) + } + return t.target +} + +func (t *TypedObjectTypeObject[T]) Description() string { + return t.description +} + +func (t *TypedObjectTypeObject[T]) Format() string { + return t.format +} + +func (t *TypedObjectTypeObject[T]) Validate(e T) error { + if t.validator == nil { + return nil + } + return t.validator(e) +} From 58f3438d0dc5fb0f6878590c15c003dcbfd0469d Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 1 Nov 2023 17:09:37 +0100 Subject: [PATCH 6/9] credential repository descriptions --- cmds/ocm/topics/common/credentials/topic.go | 110 +++++++-- docs/reference/ocm_credential-handling.md | 221 ++++++++++++++++-- docs/reference/ocm_get_credentials.md | 22 ++ docs/reference/ocm_logging.md | 1 + pkg/contexts/credentials/cpi/repotypes.go | 25 +- .../credentials/internal/repotypes.go | 11 +- .../repositories/directcreds/a_usage.go | 18 ++ .../repositories/directcreds/type.go | 2 +- .../repositories/dockerconfig/a_usage.go | 23 ++ .../repositories/dockerconfig/type.go | 2 +- pkg/contexts/credentials/repositories/init.go | 1 + .../credentials/repositories/vault/a_usage.go | 57 +++++ .../repositories/vault/identity/identity.go | 5 +- .../repositories/vault/provider.go | 13 +- .../credentials/repositories/vault/type.go | 2 +- pkg/contexts/credentials/usage.go | 15 ++ 16 files changed, 471 insertions(+), 57 deletions(-) create mode 100644 pkg/contexts/credentials/repositories/directcreds/a_usage.go create mode 100644 pkg/contexts/credentials/repositories/dockerconfig/a_usage.go create mode 100644 pkg/contexts/credentials/repositories/vault/a_usage.go create mode 100644 pkg/contexts/credentials/usage.go diff --git a/cmds/ocm/topics/common/credentials/topic.go b/cmds/ocm/topics/common/credentials/topic.go index 8c03cd9759..66d53a40d9 100644 --- a/cmds/ocm/topics/common/credentials/topic.go +++ b/cmds/ocm/topics/common/credentials/topic.go @@ -27,32 +27,112 @@ func New(ctx clictx.Context) *cobra.Command { Use: "credential-handling", Short: "Provisioning of credentials for credential consumers", Long: ` -Because of the dynamic nature of the OCM area there are several kinds of +In contrast to libraries intended for a dedicated technical environment, +for example the handling of OCI images in OCI registries, the OCM +ecosystem cannot provide a specialized credential management for a decicated +environment. + +Because of its extensibility working with component versions could +require access to any kind of technical system, either for storing +the model elements in a storage backend, or for accessing content +in any kind of technical storage system. There are several kinds of credential consumers with potentially completely different kinds of credentials. Therefore, a common uniform credential management is required, capable to serve all those use cases. -This is achieved by establishing a credential request mechanism based on -generic consumer identities and credential property sets. -On the one hand every kind of credential consumer uses a dedicated consumer -type (string). Additionally, it defines a set of properties further describing -the target/context credentials are required for. +This credential management brings together various kinds of credential consumers, +for example the access to artifacts in OCI registries or accessing +Git repository content, and credential providers, like +vaults or local files in the filesystem (for example a technology +specific credential source like the docker config json file for +accessing OCI registries). + +The used credential management model is based on four elements: +- *Credentials:* + + Credentials are described property set (key/value pairs). +- *Consumer Ids* + + Because of the extensible nature of the OCM model, credential consumers + must be formally identified. A consumer id described a concrete + access, which must be authorized. + + This is again achieved by a set of simple named attributes. There is only + one defined property, which must always be present, the type attibute. + It denotes the type of the technical environment credentails are required for. + For example, for accessing OCI or Git registries. Additionally, there may + be any number of arbitrary attributes used to describe the concrete + instance of such an environment and access paths in this environment, which + should be accessed (for example the OCI registry URL to describe the instance + and the repository path for the set of objects, which should be accessed) + + There are two use cases for consumer ids: + - *Credential Request.* They are used by a credential consumer to issue a + credential request to the credential management. Hereby, they describe the + concrete element, which should accessed. + - *Credential Assignment.* The credential management allows to assign + credentials to consumer ids + +- *Credential Providers* or repositories + + Credential repositories are dedicated kinds of implementations, which provide + access to names sets of credentials stored in any kind of technical + environment, for example a vault or a credentials somewhere on the local + filesystem. -On the other hand credentials can be defined for such sets of identities -with partial sets of properties (see ocm configfile). A credential -request is then matched against the available credential settings using matchers, -which might be specific for dedicated kinds of requests. For example, a hostpath -matcher matches a path prefix for a pathprefix property. +- *Identity Matchers* -The best matching set of credential properties is then returned to the -credential consumer, which checks for the expected credential properties. + The credential management must resolve crednetial requests against a set + of credential assignments. This is not necessarily a complete attribute match + for the involved consumer ids. There is typically some kind of matching + involved. For example, an assigment is done for an OCI registry with a dedicated + server url and prefix for the repository path (type is OCIRegistry, host is + ghcr.io, prefixpath is open-component-model). The assigned credentials + should be applicable for sub repositories. Som the assignment use a more + general consumer id than the concrete credential request (for example for + repository path open-component-model/ocm/ocmcli) -The following credential consumer types are used: + This kind of matching depend on the used attribute and is therefore in general + type specific. Therefore, every consumer type uses an own identity matcher, + which is then used by the credential management to find the best matching + assignment. + +The general process for a credential management then looks as follows. +- credentials provided by credential repositories are assigned to generalized + consumer ids. +- a concrete access operation for a technical environment calculates + a detailed consumer id for the element, which should be accessed +- it asks the credential management for credentials for this id +- the management examines all defined assignments to find the best + matching one based on the provided matching mechanism. +- it then returns the mapped credentials from the references repository. + +The critical task for a user of the toolset is to define those assignments. +This is basically a manual task, because the credentials stored in vault +(for example) could be usable for any kind of system, which typically +cannot be derived from the credential values. + +But luckily, those could partly be automated: +- there may be credential providers, which are technology specific, for example + the docker config json is used to describe credentials for OCI registries. + Such providers can automatically assign the found credentials to appropriate + consumer ids. +- If the credential store has the possibility to store custom meta data for a + credential set, this metadata can be used to describe the intended consumer + ids. The provider implementation then uses this info create the appropriate + assignments. + +### Consumer Types and Matchers + +The following credential consumer types are used/supported: ` + listformat.FormatListElements("", consumer) + `\ Those consumer types provide their own matchers, which are often based on some standard generic matches. Those generic matchers and their behaviours are described in the following list: ` + listformat.FormatListElements("", standard) + ` -`, + +### Credential Providers + +` + ctx.CredentialsContext().RepositoryTypes().Describe(), } } diff --git a/docs/reference/ocm_credential-handling.md b/docs/reference/ocm_credential-handling.md index 27ba87c386..2222f285c7 100644 --- a/docs/reference/ocm_credential-handling.md +++ b/docs/reference/ocm_credential-handling.md @@ -3,27 +3,104 @@ ### Description -Because of the dynamic nature of the OCM area there are several kinds of +In contrast to libraries intended for a dedicated technical environment, +for example the handling of OCI images in OCI registries, the OCM +ecosystem cannot provide a specialized credential management for a decicated +environment. + +Because of its extensibility working with component versions could +require access to any kind of technical system, either for storing +the model elements in a storage backend, or for accessing content +in any kind of technical storage system. There are several kinds of credential consumers with potentially completely different kinds of credentials. Therefore, a common uniform credential management is required, capable to serve all those use cases. -This is achieved by establishing a credential request mechanism based on -generic consumer identities and credential property sets. -On the one hand every kind of credential consumer uses a dedicated consumer -type (string). Additionally, it defines a set of properties further describing -the target/context credentials are required for. - -On the other hand credentials can be defined for such sets of identities -with partial sets of properties (see [ocm configfile](ocm_configfile.md)). A credential -request is then matched against the available credential settings using matchers, -which might be specific for dedicated kinds of requests. For example, a hostpath -matcher matches a path prefix for a pathprefix property. - -The best matching set of credential properties is then returned to the -credential consumer, which checks for the expected credential properties. - -The following credential consumer types are used: +This credential management brings together various kinds of credential consumers, +for example the access to artifacts in OCI registries or accessing +Git repository content, and credential providers, like +vaults or local files in the filesystem (for example a technology +specific credential source like the docker config json file for +accessing OCI registries). + +The used credential management model is based on four elements: +- *Credentials:* + + Credentials are described property set (key/value pairs). +- *Consumer Ids* + + Because of the extensible nature of the OCM model, credential consumers + must be formally identified. A consumer id described a concrete + access, which must be authorized. + + This is again achieved by a set of simple named attributes. There is only + one defined property, which must always be present, the type attibute. + It denotes the type of the technical environment credentails are required for. + For example, for accessing OCI or Git registries. Additionally, there may + be any number of arbitrary attributes used to describe the concrete + instance of such an environment and access paths in this environment, which + should be accessed (for example the OCI registry URL to describe the instance + and the repository path for the set of objects, which should be accessed) + + There are two use cases for consumer ids: + - *Credential Request.* They are used by a credential consumer to issue a + credential request to the credential management. Hereby, they describe the + concrete element, which should accessed. + - *Credential Assignment.* The credential management allows to assign + credentials to consumer ids + +- *Credential Providers* or repositories + + Credential repositories are dedicated kinds of implementations, which provide + access to names sets of credentials stored in any kind of technical + environment, for example a vault or a credentials somewhere on the local + filesystem. + +- *Identity Matchers* + + The credential management must resolve crednetial requests against a set + of credential assignments. This is not necessarily a complete attribute match + for the involved consumer ids. There is typically some kind of matching + involved. For example, an assigment is done for an OCI registry with a dedicated + server url and prefix for the repository path (type is OCIRegistry, host is + ghcr.io, prefixpath is open-component-model). The assigned credentials + should be applicable for sub repositories. Som the assignment use a more + general consumer id than the concrete credential request (for example for + repository path open-component-model/ocm/ocmcli) + + This kind of matching depend on the used attribute and is therefore in general + type specific. Therefore, every consumer type uses an own identity matcher, + which is then used by the credential management to find the best matching + assignment. + +The general process for a credential management then looks as follows. +- credentials provided by credential repositories are assigned to generalized + consumer ids. +- a concrete access operation for a technical environment calculates + a detailed consumer id for the element, which should be accessed +- it asks the credential management for credentials for this id +- the management examines all defined assignments to find the best + matching one based on the provided matching mechanism. +- it then returns the mapped credentials from the references repository. + +The critical task for a user of the toolset is to define those assignments. +This is basically a manual task, because the credentials stored in vault +(for example) could be usable for any kind of system, which typically +cannot be derived from the credential values. + +But luckily, those could partly be automated: +- there may be credential providers, which are technology specific, for example + the docker config json is used to describe credentials for OCI registries. + Such providers can automatically assign the found credentials to appropriate + consumer ids. +- If the credential store has the possibility to store custom meta data for a + credential set, this metadata can be used to describe the intended consumer + ids. The provider implementation then uses this info create the appropriate + assignments. + +### Consumer Types and Matchers + +The following credential consumer types are used/supported: - Buildcredentials.ocm.software: Gardener config credential matcher It matches the Buildcredentials.ocm.software consumer type and additionally acts like @@ -43,6 +120,28 @@ The following credential consumer types are used: - token: GitHub personal access token + - HashiCorpVault: HashiCorp Vault credential matcher + + This matcher matches credentials for a HashiCorp vault instance. + It uses the following identity attributes: + - hostname: vault server host + - scheme: (optional) URL scheme + - port: (optional) server port + - namespace: vault namespace + - pathprefix: path prefix for secret + + + Credential consumers of the consumer type HashiCorpVault evaluate the following credential properties: + + - authmeth: auth method + - token: vault token + - roleid: applrole role id + - secretid: applrole secret id + - secretid: applrole secret id + + The only supported auth methods, so far, are token and approle. + + - HelmChartRepository: Helm chart repository It matches the HelmChartRepository consumer type and additionally acts like @@ -101,16 +200,94 @@ behaviours are described in the following list: - partial: complete match of given pattern ignoring additional attributes +### Credential Providers -### SEE ALSO -##### Parents +- Credential provider Credentials -* [ocm](ocm.md) — Open Component Model command line client + This repository type can be used to specify a single inline credential + set. The default name is the empty string or Credentials. + + The following versions are supported: + - Version v1 + + The repository specification supports the following fields: + - properties: *map[string]string*: direct credential fields + + +- Credential provider DockerConfig + + This repository type can be used to access credentials stored in a file + following the docker config json format. It take into account the + credentials helper section, also. If enabled, the described + credentials will be automatically assigned to appropriate consumer ids. + + The following versions are supported: + - Version v1 + The repository specification supports the following fields: + - dockerConfigFile: *string*: the file path to a docker config file + - dockerConfig: *json*: an embedded docker config json + - propagateConsumerIdentity: *bool*(optional): enable consumer id propagation -##### Additional Links +- Credential provider HashiCorpVault -* [ocm configfile](ocm_configfile.md) — configuration file + This repository type can be used to access credentials stored in a HashiCorp + Vault. + + It provides access to list of secrets stored under a dedicated path in + a vault namespace. This list can either explicitly be specified, or + it is taken from the metadata of a specified secret. + + The following custom metadata attributes are evaluated: + - secrets this attribute may contain a comma separated list of + vault secrets, which should be exposed by this repository instance. + The names are evaluated under the path prefix used for the repository. + - consumerId this attribute may contain a JSON encoded + consumer id , this secret should be assigned to. + - type if no special attribute is defined this attribute + indicated to use the complete custom metadata as consumer id. + + It uses the HashiCorpVault identity matcher and consumer type + to requests credentials for the access. + + + This matcher matches credentials for a HashiCorp vault instance. + It uses the following identity attributes: + - hostname: vault server host + - scheme: (optional) URL scheme + - port: (optional) server port + - namespace: vault namespace + - pathprefix: path prefix for secret + + + It requires the following credential attributes: + + - authmeth: auth method + - token: vault token + - roleid: applrole role id + - secretid: applrole secret id + - secretid: applrole secret id + + The only supported auth methods, so far, are token and approle. + + The following versions are supported: + - Version v1 + + The repository specification supports the following fields: + - serverURL: *string* (required): the URL of the vault instance + - namespace: *string* (optional): the namespace used to evaluate secrets + - searchEngine: *string* (optional): the search engine to use (default: secrets) + - path: *string* (optional): the path prefix used to lookup secrets + - secrets: *[]string* (optional): list of secrets + - propagateConsumerIdentity: *bool*(optional): evaluate metadata for consumer id propagation + + + +### SEE ALSO + +##### Parents + +* [ocm](ocm.md) — Open Component Model command line client diff --git a/docs/reference/ocm_get_credentials.md b/docs/reference/ocm_get_credentials.md index 9040b89f8e..27483b343f 100644 --- a/docs/reference/ocm_get_credentials.md +++ b/docs/reference/ocm_get_credentials.md @@ -45,6 +45,28 @@ Matchers exist for the following usage contexts or consumer types: - token: GitHub personal access token + - HashiCorpVault: HashiCorp Vault credential matcher + + This matcher matches credentials for a HashiCorp vault instance. + It uses the following identity attributes: + - hostname: vault server host + - scheme: (optional) URL scheme + - port: (optional) server port + - namespace: vault namespace + - pathprefix: path prefix for secret + + + Credential consumers of the consumer type HashiCorpVault evaluate the following credential properties: + + - authmeth: auth method + - token: vault token + - roleid: applrole role id + - secretid: applrole secret id + - secretid: applrole secret id + + The only supported auth methods, so far, are token and approle. + + - HelmChartRepository: Helm chart repository It matches the HelmChartRepository consumer type and additionally acts like diff --git a/docs/reference/ocm_logging.md b/docs/reference/ocm_logging.md index 92e011ac65..4ec16172c2 100644 --- a/docs/reference/ocm_logging.md +++ b/docs/reference/ocm_logging.md @@ -20,6 +20,7 @@ The following *realms* are used by the command line tool: - ocm/accessmethod/ociartifact: access method ociArtifact - ocm/compdesc: component descriptor handling - ocm/credentials/dockerconfig: docker config handling as credential repository + - ocm/credentials/vault: HashiCorp Vault Access - ocm/downloader: Downloaders - ocm/oci/mapping: OCM to OCI Registry Mapping - ocm/oci/ocireg: OCI repository handling diff --git a/pkg/contexts/credentials/cpi/repotypes.go b/pkg/contexts/credentials/cpi/repotypes.go index ba33f6bc81..28c5647b35 100644 --- a/pkg/contexts/credentials/cpi/repotypes.go +++ b/pkg/contexts/credentials/cpi/repotypes.go @@ -9,6 +9,7 @@ package cpi import ( "github.com/open-component-model/ocm/pkg/runtime" + "github.com/open-component-model/ocm/pkg/runtime/descriptivetype" ) type RepositoryTypeVersionScheme = runtime.TypeVersionScheme[RepositorySpec, RepositoryType] @@ -27,14 +28,26 @@ func RegisterRepositoryTypeVersions(s RepositoryTypeVersionScheme) { //////////////////////////////////////////////////////////////////////////////// -func NewRepositoryType[I RepositorySpec](name string) RepositoryType { - return runtime.NewVersionedTypedObjectType[RepositorySpec, I](name) +func NewRepositoryType[I RepositorySpec](name string, opts ...RepositoryOption) RepositoryType { + return descriptivetype.NewTypedObjectTypeObject(runtime.NewVersionedTypedObjectType[RepositorySpec, I](name), opts...) } -func NewRepositoryTypeByConverter[I RepositorySpec, V runtime.TypedObject](name string, converter runtime.Converter[I, V]) RepositoryType { - return runtime.NewVersionedTypedObjectTypeByConverter[RepositorySpec, I](name, converter) +func NewRepositoryTypeByConverter[I RepositorySpec, V runtime.TypedObject](name string, converter runtime.Converter[I, V], opts ...RepositoryOption) RepositoryType { + return descriptivetype.NewTypedObjectTypeObject(runtime.NewVersionedTypedObjectTypeByConverter[RepositorySpec, I](name, converter), opts...) } -func NewRepositoryTypeByFormatVersion(name string, fmt runtime.FormatVersion[RepositorySpec]) RepositoryType { - return runtime.NewVersionedTypedObjectTypeByFormatVersion[RepositorySpec](name, fmt) +func NewRepositoryTypeByFormatVersion(name string, fmt runtime.FormatVersion[RepositorySpec], opts ...RepositoryOption) RepositoryType { + return descriptivetype.NewTypedObjectTypeObject(runtime.NewVersionedTypedObjectTypeByFormatVersion[RepositorySpec](name, fmt), opts...) +} + +//////////////////////////////////////////////////////////////////////////////// + +type RepositoryOption = descriptivetype.Option + +func WithDescription(v string) RepositoryOption { + return descriptivetype.WithDescription(v) +} + +func WithFormatSpec(v string) RepositoryOption { + return descriptivetype.WithFormatSpec(v) } diff --git a/pkg/contexts/credentials/internal/repotypes.go b/pkg/contexts/credentials/internal/repotypes.go index fd92e59aa2..0e96254d71 100644 --- a/pkg/contexts/credentials/internal/repotypes.go +++ b/pkg/contexts/credentials/internal/repotypes.go @@ -12,11 +12,12 @@ import ( "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/generics" "github.com/open-component-model/ocm/pkg/runtime" + "github.com/open-component-model/ocm/pkg/runtime/descriptivetype" "github.com/open-component-model/ocm/pkg/utils" ) type RepositoryType interface { - runtime.VersionedTypedObjectType[RepositorySpec] + descriptivetype.TypedObjectType[RepositorySpec] } type RepositorySpec interface { @@ -31,22 +32,22 @@ type ( ) type RepositoryTypeScheme interface { - runtime.TypeScheme[RepositorySpec, RepositoryType] + descriptivetype.TypeScheme[RepositorySpec, RepositoryType] } -type _Scheme = runtime.TypeScheme[RepositorySpec, RepositoryType] +type _Scheme = descriptivetype.TypeScheme[RepositorySpec, RepositoryType] type repositoryTypeScheme struct { _Scheme } func NewRepositoryTypeScheme(defaultDecoder RepositorySpecDecoder, base ...RepositoryTypeScheme) RepositoryTypeScheme { - scheme := runtime.MustNewDefaultTypeScheme[RepositorySpec, RepositoryType](&UnknownRepositorySpec{}, true, defaultDecoder, utils.Optional(base...)) + scheme := descriptivetype.MustNewDefaultTypeScheme[RepositorySpec, RepositoryType, RepositoryTypeScheme]("Credential provider", nil, &UnknownRepositorySpec{}, true, defaultDecoder, utils.Optional(base...)) return &repositoryTypeScheme{scheme} } func NewStrictRepositoryTypeScheme(base ...RepositoryTypeScheme) runtime.VersionedTypeRegistry[RepositorySpec, RepositoryType] { - scheme := runtime.MustNewDefaultTypeScheme[RepositorySpec, RepositoryType](nil, false, nil, utils.Optional(base...)) + scheme := descriptivetype.MustNewDefaultTypeScheme[RepositorySpec, RepositoryType, RepositoryTypeScheme]("Credential provider", nil, nil, false, nil, utils.Optional(base...)) return &repositoryTypeScheme{scheme} } diff --git a/pkg/contexts/credentials/repositories/directcreds/a_usage.go b/pkg/contexts/credentials/repositories/directcreds/a_usage.go new file mode 100644 index 0000000000..705144e3bf --- /dev/null +++ b/pkg/contexts/credentials/repositories/directcreds/a_usage.go @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package directcreds + +import ( + "github.com/open-component-model/ocm/pkg/listformat" +) + +var usage = ` +This repository type can be used to specify a single inline credential +set. The default name is the empty string or ` + Type + `.` + +var format = `The repository specification supports the following fields: +` + listformat.FormatListElements("", listformat.StringElementDescriptionList{ + "properties", "*map[string]string*: direct credential fields", +}) diff --git a/pkg/contexts/credentials/repositories/directcreds/type.go b/pkg/contexts/credentials/repositories/directcreds/type.go index 54fef494d6..7fde18b079 100644 --- a/pkg/contexts/credentials/repositories/directcreds/type.go +++ b/pkg/contexts/credentials/repositories/directcreds/type.go @@ -17,7 +17,7 @@ const ( func init() { cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](Type)) - cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](TypeV1)) + cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](TypeV1, cpi.WithDescription(usage), cpi.WithFormatSpec(format))) } // RepositorySpec describes a repository interface for single direct credentials. diff --git a/pkg/contexts/credentials/repositories/dockerconfig/a_usage.go b/pkg/contexts/credentials/repositories/dockerconfig/a_usage.go new file mode 100644 index 0000000000..b6f15f95ba --- /dev/null +++ b/pkg/contexts/credentials/repositories/dockerconfig/a_usage.go @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package dockerconfig + +import ( + "github.com/open-component-model/ocm/pkg/listformat" +) + +var usage = ` +This repository type can be used to access credentials stored in a file +following the docker config json format. It take into account the +credentials helper section, also. If enabled, the described +credentials will be automatically assigned to appropriate consumer ids. +` + +var format = `The repository specification supports the following fields: +` + listformat.FormatListElements("", listformat.StringElementDescriptionList{ + "dockerConfigFile", "*string*: the file path to a docker config file", + "dockerConfig", "*json*: an embedded docker config json", + "propagateConsumerIdentity", "*bool*(optional): enable consumer id propagation", +}) diff --git a/pkg/contexts/credentials/repositories/dockerconfig/type.go b/pkg/contexts/credentials/repositories/dockerconfig/type.go index 8968e92b4f..30acfd93c9 100644 --- a/pkg/contexts/credentials/repositories/dockerconfig/type.go +++ b/pkg/contexts/credentials/repositories/dockerconfig/type.go @@ -19,7 +19,7 @@ const ( func init() { cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](Type)) - cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](TypeV1)) + cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](TypeV1, cpi.WithDescription(usage), cpi.WithFormatSpec(format))) } // RepositorySpec describes a docker config based credential repository interface. diff --git a/pkg/contexts/credentials/repositories/init.go b/pkg/contexts/credentials/repositories/init.go index bd9f8e9c7b..84d8ad4bc8 100644 --- a/pkg/contexts/credentials/repositories/init.go +++ b/pkg/contexts/credentials/repositories/init.go @@ -11,4 +11,5 @@ import ( _ "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/gardenerconfig" _ "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/memory" _ "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/memory/config" + _ "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/vault" ) diff --git a/pkg/contexts/credentials/repositories/vault/a_usage.go b/pkg/contexts/credentials/repositories/vault/a_usage.go new file mode 100644 index 0000000000..7150ced7eb --- /dev/null +++ b/pkg/contexts/credentials/repositories/vault/a_usage.go @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package vault + +import ( + "strings" + + "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" + "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/vault/identity" + "github.com/open-component-model/ocm/pkg/listformat" +) + +func init() { + info := cpi.DefaultContext.ConsumerIdentityMatchers().GetInfo(identity.CONSUMER_TYPE) + idx := strings.Index(info.Description, "\n") + desc := ` +This repository type can be used to access credentials stored in a HashiCorp +Vault. + +It provides access to list of secrets stored under a dedicated path in +a vault namespace. This list can either explicitly be specified, or +it is taken from the metadata of a specified secret. + +The following custom metadata attributes are evaluated: +- ` + CUSTOM_SECRETS + ` this attribute may contain a comma separated list of + vault secrets, which should be exposed by this repository instance. + The names are evaluated under the path prefix used for the repository. +- ` + CUSTOM_CONSUMERID + ` this attribute may contain a JSON encoded + consumer id , this secret should be assigned to. +- type if no special attribute is defined this attribute + indicated to use the complete custom metadata as consumer id. + +It uses the ` + identity.CONSUMER_TYPE + ` identity matcher and consumer type +to requests credentials for the access. +` + info.Description[idx:] + ` + +It requires the following credential attributes: + +` + info.CredentialAttributes + + usage = desc +} + +var usage string + +var format = ` +The repository specification supports the following fields: +` + listformat.FormatListElements("", listformat.StringElementDescriptionList{ + "serverURL", "*string* (required): the URL of the vault instance", + "namespace", "*string* (optional): the namespace used to evaluate secrets", + "searchEngine", "*string* (optional): the search engine to use (default: secrets)", + "path", "*string* (optional): the path prefix used to lookup secrets", + "secrets", "*[]string* (optional): list of secrets", + "propagateConsumerIdentity", "*bool*(optional): evaluate metadata for consumer id propagation", +}) diff --git a/pkg/contexts/credentials/repositories/vault/identity/identity.go b/pkg/contexts/credentials/repositories/vault/identity/identity.go index d6dfed6723..9d0518a5a0 100644 --- a/pkg/contexts/credentials/repositories/vault/identity/identity.go +++ b/pkg/contexts/credentials/repositories/vault/identity/identity.go @@ -68,9 +68,10 @@ func init() { `HashiCorp Vault credential matcher This matcher matches credentials for a HashiCorp vault instance. -It uses the following identity attributes:\n`+ids, +It uses the following identity attributes: +`+ids, attrs+` -The oly supported auth methods, so far, are token and approle. +The only supported auth methods, so far, are token and approle. `) } diff --git a/pkg/contexts/credentials/repositories/vault/provider.go b/pkg/contexts/credentials/repositories/vault/provider.go index 2e883c41e2..204fec11d8 100644 --- a/pkg/contexts/credentials/repositories/vault/provider.go +++ b/pkg/contexts/credentials/repositories/vault/provider.go @@ -23,6 +23,11 @@ import ( const PROVIDER = "ocm.software/credentialprovider/" + Type +const ( + CUSTOM_SECRETS = "secrets" + CUSTOM_CONSUMERID = "consumerId" +) + type mapping struct { Id cpi.ConsumerIdentity Name string @@ -79,7 +84,7 @@ func (p *ConsumerProvider) update() error { ctx := context.Background() client, err := vault.New( - vault.WithAddress("https://vault.tools.sap"), + vault.WithAddress(p.repository.spec.ServerURL), vault.WithRequestTimeout(30*time.Second), ) if err != nil { @@ -169,15 +174,15 @@ func (p *ConsumerProvider) read(ctx context.Context, client *vault.Client, secre if meta, ok := s.Data.Metadata["custom_metadata"].(map[string]interface{}); ok { sub := false - if cid := meta["consumerId"]; cid != nil { + if cid := meta[CUSTOM_CONSUMERID]; cid != nil { id = common.Properties{} if err := json.Unmarshal([]byte(cid.(string)), &id); err != nil { id = nil } sub = true } - if cid := meta["secrets"]; cid != nil { - if s, ok := meta["secrets"].(string); ok { + if cid := meta[CUSTOM_SECRETS]; cid != nil { + if s, ok := meta[CUSTOM_SECRETS].(string); ok { for _, e := range strings.Split(s, ",") { e = strings.TrimSpace(e) if e != "" { diff --git a/pkg/contexts/credentials/repositories/vault/type.go b/pkg/contexts/credentials/repositories/vault/type.go index 0b40857d24..5ad59f032e 100644 --- a/pkg/contexts/credentials/repositories/vault/type.go +++ b/pkg/contexts/credentials/repositories/vault/type.go @@ -22,7 +22,7 @@ const ( func init() { cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](Type)) - cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](TypeV1)) + cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](TypeV1, cpi.WithDescription(usage), cpi.WithFormatSpec(format))) } // RepositorySpec describes a docker config based credential repository interface. diff --git a/pkg/contexts/credentials/usage.go b/pkg/contexts/credentials/usage.go new file mode 100644 index 0000000000..feb9081bef --- /dev/null +++ b/pkg/contexts/credentials/usage.go @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package credentials + +func RepositoryUsage(scheme RepositoryTypeScheme) string { + s := ` +The following list describes the supported credential providers +(credential repositories), their specification versions +and formats. Because of the extensible nature of the OCM model, +credential consum +` + return s + scheme.Describe() +} From 8e5410968cbdbda35a55fdf86e3e84a418399900 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 1 Nov 2023 20:06:42 +0100 Subject: [PATCH 7/9] fix naming of secretsEngine --- docs/reference/ocm_credential-handling.md | 5 ++++- .../credentials/repositories/vault/a_usage.go | 7 +++++-- .../credentials/repositories/vault/options.go | 10 +++++----- .../credentials/repositories/vault/provider.go | 17 +++++++++++++++-- .../credentials/repositories/vault/type.go | 4 ++-- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/docs/reference/ocm_credential-handling.md b/docs/reference/ocm_credential-handling.md index 2222f285c7..99e8fe85e3 100644 --- a/docs/reference/ocm_credential-handling.md +++ b/docs/reference/ocm_credential-handling.md @@ -278,11 +278,14 @@ behaviours are described in the following list: The repository specification supports the following fields: - serverURL: *string* (required): the URL of the vault instance - namespace: *string* (optional): the namespace used to evaluate secrets - - searchEngine: *string* (optional): the search engine to use (default: secrets) + - secretsEngine: *string* (optional): the secrets engine to use (default: secrets) - path: *string* (optional): the path prefix used to lookup secrets - secrets: *[]string* (optional): list of secrets - propagateConsumerIdentity: *bool*(optional): evaluate metadata for consumer id propagation + If the secrets list is empty, all secret entries found in the given path + is read. + ### SEE ALSO diff --git a/pkg/contexts/credentials/repositories/vault/a_usage.go b/pkg/contexts/credentials/repositories/vault/a_usage.go index 7150ced7eb..b772496657 100644 --- a/pkg/contexts/credentials/repositories/vault/a_usage.go +++ b/pkg/contexts/credentials/repositories/vault/a_usage.go @@ -50,8 +50,11 @@ The repository specification supports the following fields: ` + listformat.FormatListElements("", listformat.StringElementDescriptionList{ "serverURL", "*string* (required): the URL of the vault instance", "namespace", "*string* (optional): the namespace used to evaluate secrets", - "searchEngine", "*string* (optional): the search engine to use (default: secrets)", + "secretsEngine", "*string* (optional): the secrets engine to use (default: secrets)", "path", "*string* (optional): the path prefix used to lookup secrets", "secrets", "*[]string* (optional): list of secrets", "propagateConsumerIdentity", "*bool*(optional): evaluate metadata for consumer id propagation", -}) +}) + ` +If the secrets list is empty, all secret entries found in the given path +is read. +` diff --git a/pkg/contexts/credentials/repositories/vault/options.go b/pkg/contexts/credentials/repositories/vault/options.go index 94157d40a7..71031496f1 100644 --- a/pkg/contexts/credentials/repositories/vault/options.go +++ b/pkg/contexts/credentials/repositories/vault/options.go @@ -15,7 +15,7 @@ type Option = optionutils.Option[*Options] type Options struct { Namespace string `json:"namespace,omitempty"` - SearchEngine string `json:"searchEngine,omitempty"` + SecretsEngine string `json:"secretsEngine,omitempty"` Path string `json:"path,omitempty"` Secrets []string `json:"secrets,omitempty"` PropgateConsumerIdentity bool `json:"propagateConsumerIdentity,omitempty"` @@ -27,8 +27,8 @@ func (o *Options) ApplyTo(opts *Options) { if o.Namespace != "" { opts.Namespace = o.Namespace } - if o.SearchEngine != "" { - opts.SearchEngine = o.SearchEngine + if o.SecretsEngine != "" { + opts.SecretsEngine = o.SecretsEngine } if o.Path != "" { opts.Path = o.Path @@ -56,10 +56,10 @@ func WithNamespace(s string) Option { type se string func (o se) ApplyTo(opts *Options) { - opts.SearchEngine = string(o) + opts.SecretsEngine = string(o) } -func WithSearchEngine(s string) Option { +func WithSecretsEngine(s string) Option { return se(s) } diff --git a/pkg/contexts/credentials/repositories/vault/provider.go b/pkg/contexts/credentials/repositories/vault/provider.go index 204fec11d8..7782fec78e 100644 --- a/pkg/contexts/credentials/repositories/vault/provider.go +++ b/pkg/contexts/credentials/repositories/vault/provider.go @@ -102,6 +102,19 @@ func (p *ConsumerProvider) update() error { } secrets := slices.Clone(p.repository.spec.Secrets) + if len(secrets) == 0 { + s, err := client.Secrets.KvV2List(ctx, p.repository.spec.Path, + vault.WithNamespace(p.repository.spec.Namespace), + vault.WithMountPath(p.repository.spec.SecretsEngine)) + if err != nil { + return err + } + for _, k := range s.Data.Keys { + if !strings.HasSuffix(k, "/") { + secrets = append(secrets, k) + } + } + } for i := 0; i < len(secrets); i++ { n := secrets[i] creds, id, list, err := p.read(ctx, client, n) @@ -150,7 +163,7 @@ func (p *ConsumerProvider) error(err error, msg string, secret string, keypairs log.Error(msg, append(keypairs, "server", p.repository.spec.ServerURL, "namespace", p.repository.spec.Namespace, - "engine", p.repository.spec.SearchEngine, + "engine", p.repository.spec.SecretsEngine, "path", path.Join(p.repository.spec.Path, secret), "error", err.Error(), )..., @@ -163,7 +176,7 @@ func (p *ConsumerProvider) read(ctx context.Context, client *vault.Client, secre secret = path.Join(p.repository.spec.Path, secret) s, err := client.Secrets.KvV2Read(ctx, secret, vault.WithNamespace(p.repository.spec.Namespace), - vault.WithMountPath(p.repository.spec.SearchEngine)) + vault.WithMountPath(p.repository.spec.SecretsEngine)) if err != nil { return nil, nil, nil, err } diff --git a/pkg/contexts/credentials/repositories/vault/type.go b/pkg/contexts/credentials/repositories/vault/type.go index 5ad59f032e..267ce989af 100644 --- a/pkg/contexts/credentials/repositories/vault/type.go +++ b/pkg/contexts/credentials/repositories/vault/type.go @@ -53,8 +53,8 @@ func (a *RepositorySpec) Repository(ctx cpi.Context, creds cpi.Credentials) (cpi } spec := *a spec.Secrets = slices.Clone(a.Secrets) - if spec.SearchEngine == "" { - spec.SearchEngine = "secrets" + if spec.SecretsEngine == "" { + spec.SecretsEngine = "secrets" } return repos.GetRepository(ctx, &spec) } From a94128e5756bd5d611a8208e31b0c1bddf00f2d4 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Thu, 2 Nov 2023 09:26:32 +0100 Subject: [PATCH 8/9] add secret engine to ID --- docs/reference/ocm_credential-handling.md | 2 ++ docs/reference/ocm_get_credentials.md | 1 + .../repositories/vault/identity/identity.go | 30 +++++++++++-------- .../repositories/vault/provider.go | 6 ++-- .../repositories/vault/repository.go | 2 +- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/docs/reference/ocm_credential-handling.md b/docs/reference/ocm_credential-handling.md index 99e8fe85e3..a0e58ef395 100644 --- a/docs/reference/ocm_credential-handling.md +++ b/docs/reference/ocm_credential-handling.md @@ -128,6 +128,7 @@ The following credential consumer types are used/supported: - scheme: (optional) URL scheme - port: (optional) server port - namespace: vault namespace + - secretEngine: secret engine - pathprefix: path prefix for secret @@ -259,6 +260,7 @@ behaviours are described in the following list: - scheme: (optional) URL scheme - port: (optional) server port - namespace: vault namespace + - secretEngine: secret engine - pathprefix: path prefix for secret diff --git a/docs/reference/ocm_get_credentials.md b/docs/reference/ocm_get_credentials.md index 27483b343f..5512cc261d 100644 --- a/docs/reference/ocm_get_credentials.md +++ b/docs/reference/ocm_get_credentials.md @@ -53,6 +53,7 @@ Matchers exist for the following usage contexts or consumer types: - scheme: (optional) URL scheme - port: (optional) server port - namespace: vault namespace + - secretEngine: secret engine - pathprefix: path prefix for secret diff --git a/pkg/contexts/credentials/repositories/vault/identity/identity.go b/pkg/contexts/credentials/repositories/vault/identity/identity.go index 9d0518a5a0..79ba43d7eb 100644 --- a/pkg/contexts/credentials/repositories/vault/identity/identity.go +++ b/pkg/contexts/credentials/repositories/vault/identity/identity.go @@ -7,7 +7,6 @@ package identity import ( "net" "net/url" - "path" "strings" "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" @@ -20,11 +19,12 @@ const CONSUMER_TYPE = "HashiCorpVault" // identity properties. const ( - ID_HOSTNAME = hostpath.ID_HOSTNAME - ID_SCHEMA = hostpath.ID_SCHEME - ID_PORT = hostpath.ID_PORT - ID_PATHPREFIX = hostpath.ID_PATHPREFIX - ID_NAMESPACE = "namespace" + ID_HOSTNAME = hostpath.ID_HOSTNAME + ID_SCHEMA = hostpath.ID_SCHEME + ID_PORT = hostpath.ID_PORT + ID_PATHPREFIX = hostpath.ID_PATHPREFIX + ID_SECRETENGINE = "secretEngine" + ID_NAMESPACE = "namespace" ) // credential properties. @@ -46,6 +46,9 @@ func IdentityMatcher(request, cur, id cpi.ConsumerIdentity) bool { if id[ID_NAMESPACE] != request[ID_NAMESPACE] { return false } + if id[ID_SECRETENGINE] != "" && id[ID_SECRETENGINE] != request[ID_SECRETENGINE] { + return false + } return identityMatcher(request, cur, id) } @@ -62,6 +65,7 @@ func init() { ID_SCHEMA, "(optional) URL scheme", ID_PORT, "(optional) server port", ID_NAMESPACE, "vault namespace", + ID_SECRETENGINE, "secret engine", ID_PATHPREFIX, "path prefix for secret", }) cpi.RegisterStandardIdentity(CONSUMER_TYPE, identityMatcher, @@ -75,7 +79,7 @@ The only supported auth methods, so far, are token and approl `) } -func GetConsumerId(serverurl string, namespace string, secretpath ...string) (cpi.ConsumerIdentity, error) { +func GetConsumerId(serverurl string, namespace string, secretengine string, secretpath string) (cpi.ConsumerIdentity, error) { if serverurl == "" { return nil, errors.Newf("server address must be given") } @@ -105,16 +109,18 @@ func GetConsumerId(serverurl string, namespace string, secretpath ...string) (cp if namespace != "" { id[ID_NAMESPACE] = namespace } + if secretengine != "" { + id[ID_SECRETENGINE] = secretengine + } - p := path.Join(secretpath...) - if p != "" { - id[ID_PATHPREFIX] = p + if secretpath != "" { + id[ID_PATHPREFIX] = secretpath } return id, nil } -func GetCredentials(ctx cpi.ContextProvider, serverurl, namespace string, secretpath ...string) (cpi.Credentials, error) { - id, err := GetConsumerId(serverurl, namespace, secretpath...) +func GetCredentials(ctx cpi.ContextProvider, serverurl, namespace string, secretengine, secretpath string) (cpi.Credentials, error) { + id, err := GetConsumerId(serverurl, namespace, secretengine, secretpath) if err != nil { return nil, err } diff --git a/pkg/contexts/credentials/repositories/vault/provider.go b/pkg/contexts/credentials/repositories/vault/provider.go index 7782fec78e..1d8c9fb4ec 100644 --- a/pkg/contexts/credentials/repositories/vault/provider.go +++ b/pkg/contexts/credentials/repositories/vault/provider.go @@ -100,11 +100,14 @@ func (p *ConsumerProvider) update() error { if err := client.SetToken(token); err != nil { return err } + if err := client.SetNamespace(p.repository.spec.Namespace); err != nil { + return err + } + // TODO: support for pure path based access for other secret engine types secrets := slices.Clone(p.repository.spec.Secrets) if len(secrets) == 0 { s, err := client.Secrets.KvV2List(ctx, p.repository.spec.Path, - vault.WithNamespace(p.repository.spec.Namespace), vault.WithMountPath(p.repository.spec.SecretsEngine)) if err != nil { return err @@ -175,7 +178,6 @@ func (p *ConsumerProvider) read(ctx context.Context, client *vault.Client, secre secret = path.Join(p.repository.spec.Path, secret) s, err := client.Secrets.KvV2Read(ctx, secret, - vault.WithNamespace(p.repository.spec.Namespace), vault.WithMountPath(p.repository.spec.SecretsEngine)) if err != nil { return nil, nil, nil, err diff --git a/pkg/contexts/credentials/repositories/vault/repository.go b/pkg/contexts/credentials/repositories/vault/repository.go index f5c8167625..c09ed75bde 100644 --- a/pkg/contexts/credentials/repositories/vault/repository.go +++ b/pkg/contexts/credentials/repositories/vault/repository.go @@ -24,7 +24,7 @@ var ( ) func NewRepository(ctx cpi.Context, spec *RepositorySpec) (*Repository, error) { - id, err := identity.GetConsumerId(spec.ServerURL, spec.Namespace, spec.Path) + id, err := identity.GetConsumerId(spec.ServerURL, spec.Namespace, spec.SecretsEngine, spec.Path) if err != nil { return nil, err } From 4d909d1702188abcbffe90204dc663b7f0a739c4 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Fri, 3 Nov 2023 08:22:17 -0700 Subject: [PATCH 9/9] fix docker file --- components/helminstaller/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/helminstaller/Dockerfile b/components/helminstaller/Dockerfile index 02805e6177..d160f60630 100644 --- a/components/helminstaller/Dockerfile +++ b/components/helminstaller/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.19 AS builder +FROM --platform=$BUILDPLATFORM golang:1.21 AS builder ARG COMMIT EFFECTIVE_VERSION GIT_TREE_STATE ARG TARGETOS TARGETARCH