diff --git a/.github/workflows/assets.yaml b/.github/workflows/assets.yaml deleted file mode 100644 index 49b75d04b2..0000000000 --- a/.github/workflows/assets.yaml +++ /dev/null @@ -1,97 +0,0 @@ -# ------------------------------------------------------------ -# Copyright 2023 The Radius Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ------------------------------------------------------------ - -name: assets - -on: - push: - branches: - - main - tags: - - v* -jobs: - publish: - name: Assets - runs-on: ubuntu-latest - if: github.repository == 'radius-project/radius' - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Parse release version and set environment variables - run: python ./.github/scripts/get_release_version.py - # The install scripts are not versioned. They go to tools/rad/install.{sh,ps1} - - name: Upload install.sh - uses: bacongobbler/azure-blob-storage-upload@v3.0.0 - with: - source_dir: 'deploy' - container_name: 'tools' - connection_string: ${{ secrets.ASSETS_STORAGE_CONNECTION_STRING }} - overwrite: 'true' - extra_args: '--destination-path rad --pattern install.sh' - - name: Upload install.ps1 - uses: bacongobbler/azure-blob-storage-upload@v3.0.0 - with: - source_dir: 'deploy' - container_name: 'tools' - connection_string: ${{ secrets.ASSETS_STORAGE_CONNECTION_STRING }} - overwrite: 'true' - extra_args: '--destination-path rad --pattern install.ps1' - - name: Upload Grafana dashboard - uses: bacongobbler/azure-blob-storage-upload@v3.0.0 - with: - source_dir: 'grafana' - container_name: 'tools' - connection_string: ${{ secrets.ASSETS_STORAGE_CONNECTION_STRING }} - overwrite: 'true' - extra_args: '--destination-path grafana --pattern *.json --timeout 300' - - # Logic: If this is a real release (tagged, non-rc) then compare to our existing full - # release and see if it's newer. This prevents a patch release of an older vintage from overwriting - # a newer release - - name: Download version marker file - run: | - curl https://get.radapp.dev/version/stable.txt -o current-stable.txt - if: ${{ success() && env.UPDATE_RELEASE == 'true' }} - - name: Get version - id: setcurrentversion - if: ${{ success() && env.UPDATE_RELEASE == 'true' }} - run: echo "version=$(cat current-stable.txt)" >> $GITHUB_OUTPUT - - name: Compare versions - uses: madhead/semver-utils@latest - if: ${{ success() && env.UPDATE_RELEASE == 'true' }} - id: compare - with: - version: ${{ env.REL_CHANNEL }}.0 - compare-to: ${{ steps.setcurrentversion.outputs.version }}.0 - - name: Print info (for sanity) - if: ${{ success() && env.UPDATE_RELEASE == 'true' }} - run: | - echo "current stable channel: ${{ steps.setcurrentversion.outputs.version }}" - echo "this build channel: ${{ env.REL_CHANNEL }}" - echo "comparison: ${{ steps.compare.outputs.comparison-result }}" - - name: Write stable.txt - if: ${{ success() && steps.compare.outputs.comparison-result == '>' }} - run: | - echo $REL_CHANNEL > stable.txt - - name: 'Update latest version marker' - uses: bacongobbler/azure-blob-storage-upload@v3.0.0 - if: ${{ success() && steps.compare.outputs.comparison-result == '>' }} - with: - container_name: 'version' - connection_string: ${{ secrets.ASSETS_STORAGE_CONNECTION_STRING }} - overwrite: 'true' - source_dir: . - extra_args: '--pattern stable.txt' diff --git a/bicep-types b/bicep-types index f0cd725a8d..99cdcee81e 160000 --- a/bicep-types +++ b/bicep-types @@ -1 +1 @@ -Subproject commit f0cd725a8db482315a44ba70811c4dc4ce4f9e88 +Subproject commit 99cdcee81e5885f77b92a73e6273babc8fb1f001 diff --git a/go.mod b/go.mod index aae07deffe..5b459b80ae 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/radius-project/radius go 1.22.0 require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.2.0 @@ -14,17 +14,17 @@ require ( github.com/Azure/secrets-store-csi-driver-provider-azure v1.5.3 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/agnivade/levenshtein v1.1.1 - github.com/aws/aws-sdk-go-v2 v1.30.3 - github.com/aws/aws-sdk-go-v2/config v1.27.27 - github.com/aws/aws-sdk-go-v2/credentials v1.17.27 - github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.20.3 - github.com/aws/aws-sdk-go-v2/service/cloudformation v1.53.3 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.173.0 - github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 - github.com/aws/smithy-go v1.20.3 - github.com/charmbracelet/bubbles v0.18.0 - github.com/charmbracelet/bubbletea v0.26.6 - github.com/charmbracelet/lipgloss v0.12.1 + github.com/aws/aws-sdk-go-v2 v1.30.4 + github.com/aws/aws-sdk-go-v2/config v1.27.31 + github.com/aws/aws-sdk-go-v2/credentials v1.17.30 + github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.20.5 + github.com/aws/aws-sdk-go-v2/service/cloudformation v1.53.5 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.177.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 + github.com/aws/smithy-go v1.20.4 + github.com/charmbracelet/bubbles v0.19.0 + github.com/charmbracelet/bubbletea v0.27.0 + github.com/charmbracelet/lipgloss v0.13.0 github.com/charmbracelet/x/exp/teatest v0.0.0-20240408110044-525ba71bb562 github.com/dimchansky/utfbom v1.1.1 github.com/fatih/color v1.17.0 @@ -54,8 +54,8 @@ require ( github.com/novln/docker-parser v1.0.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 - github.com/projectcontour/contour v1.30.0 - github.com/prometheus/client_golang v1.19.1 + github.com/projectcontour/contour v1.29.0 + github.com/prometheus/client_golang v1.20.2 github.com/spaolacci/murmur3 v1.1.0 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 @@ -66,22 +66,22 @@ require ( github.com/wI2L/jsondiff v0.6.0 go.etcd.io/etcd/client/v3 v3.5.15 go.etcd.io/etcd/server/v3 v3.5.15 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 - go.opentelemetry.io/contrib/instrumentation/runtime v0.53.0 - go.opentelemetry.io/otel v1.28.0 - go.opentelemetry.io/otel/exporters/prometheus v0.50.0 - go.opentelemetry.io/otel/exporters/zipkin v1.28.0 - go.opentelemetry.io/otel/metric v1.28.0 - go.opentelemetry.io/otel/sdk v1.28.0 - go.opentelemetry.io/otel/sdk/metric v1.28.0 - go.opentelemetry.io/otel/trace v1.28.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 + go.opentelemetry.io/contrib/instrumentation/runtime v0.54.0 + go.opentelemetry.io/otel v1.29.0 + go.opentelemetry.io/otel/exporters/prometheus v0.51.0 + go.opentelemetry.io/otel/exporters/zipkin v1.29.0 + go.opentelemetry.io/otel/metric v1.29.0 + go.opentelemetry.io/otel/sdk v1.29.0 + go.opentelemetry.io/otel/sdk/metric v1.29.0 + go.opentelemetry.io/otel/trace v1.29.0 go.uber.org/atomic v1.11.0 go.uber.org/mock v0.4.0 go.uber.org/zap v1.27.0 golang.org/x/sync v0.8.0 - golang.org/x/text v0.16.0 + golang.org/x/text v0.17.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.15.3 + helm.sh/helm/v3 v3.15.4 k8s.io/api v0.30.3 k8s.io/apiextensions-apiserver v0.30.3 k8s.io/apimachinery v0.30.3 @@ -89,8 +89,8 @@ require ( k8s.io/client-go v0.30.3 k8s.io/kubectl v0.30.3 oras.land/oras-go/v2 v2.5.0 - sigs.k8s.io/controller-runtime v0.18.4 - sigs.k8s.io/secrets-store-csi-driver v1.4.4 + sigs.k8s.io/controller-runtime v0.18.2 + sigs.k8s.io/secrets-store-csi-driver v1.4.5 ) require ( @@ -105,11 +105,11 @@ require ( github.com/Microsoft/hcsshim v0.12.4 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/aws/aws-sdk-go v1.54.6 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/charmbracelet/x/ansi v0.1.4 // indirect - github.com/charmbracelet/x/exp/golden v0.0.0-20240408110044-525ba71bb562 // indirect + github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b // indirect github.com/charmbracelet/x/input v0.1.2 // indirect github.com/charmbracelet/x/term v0.1.1 // indirect github.com/charmbracelet/x/windows v0.1.2 // indirect @@ -170,13 +170,13 @@ require ( github.com/agext/levenshtein v1.2.3 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/atotto/clipboard v0.1.4 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymanbagabas/go-udiff v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -184,7 +184,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.3 // indirect github.com/cloudflare/circl v1.3.9 // indirect - github.com/containerd/containerd v1.7.20 + github.com/containerd/containerd v1.7.21 github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect @@ -228,7 +228,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-getter v1.7.5 + github.com/hashicorp/go-getter v1.7.6 github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect @@ -254,19 +254,18 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect - github.com/moby/spdystream v0.2.0 // indirect + github.com/moby/spdystream v0.4.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/ulid v1.3.1 // indirect @@ -302,8 +301,8 @@ require ( go.etcd.io/etcd/client/v2 v2.305.15 // indirect go.etcd.io/etcd/pkg/v3 v3.5.15 // indirect go.etcd.io/etcd/raft/v3 v3.5.15 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.starlark.net v0.0.0-20240520160348-046347dcd104 // indirect @@ -313,12 +312,12 @@ require ( golang.org/x/mod v0.19.0 // indirect golang.org/x/net v0.27.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.22.0 // indirect + golang.org/x/sys v0.24.0 // indirect golang.org/x/term v0.22.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -329,7 +328,7 @@ require ( k8s.io/component-base v0.30.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240620174524-b456828f718b // indirect - k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect oras.land/oras-go v1.2.5 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.17.2 // indirect diff --git a/go.sum b/go.sum index 891ba471d0..79e8a1c047 100644 --- a/go.sum +++ b/go.sum @@ -195,8 +195,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 h1:GJHeeA2N7xrG3q30L2UXDyuWRzDM900/65j70wcM4Ww= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= @@ -279,38 +279,38 @@ github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.54.6 h1:HEYUib3yTt8E6vxjMWM3yAq5b+qjj/6aKA62mkgux9g= github.com/aws/aws-sdk-go v1.54.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= -github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= -github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= -github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= -github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.20.3 h1:QdoWu2A7sOU7g38Uj1dH9rCvJcINiAV7B/exER1AOKo= -github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.20.3/go.mod h1:AOsjRDzfgBXF2xsVqwoirlk69ZzSzZIiZdxMyqTih6k= -github.com/aws/aws-sdk-go-v2/service/cloudformation v1.53.3 h1:mIpL+FXa+2U6oc85b/15JwJhNUU+c/LHwxM3hpQIxXQ= -github.com/aws/aws-sdk-go-v2/service/cloudformation v1.53.3/go.mod h1:lcQ7+K0Q9x0ozhjBwDfBkuY8qexSP/QXLgp0jj+/NZg= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.173.0 h1:ta62lid9JkIpKZtZZXSj6rP2AqY5x1qYGq53ffxqD9Q= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.173.0/go.mod h1:o6QDjdVKpP5EF0dp/VlvqckzuSDATr1rLdHt3A5m0YY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= -github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= -github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8= +github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= +github.com/aws/aws-sdk-go-v2/config v1.27.31 h1:kxBoRsjhT3pq0cKthgj6RU6bXTm/2SgdoUMyrVw0rAI= +github.com/aws/aws-sdk-go-v2/config v1.27.31/go.mod h1:z04nZdSWFPaDwK3DdJOG2r+scLQzMYuJeW0CujEm9FM= +github.com/aws/aws-sdk-go-v2/credentials v1.17.30 h1:aau/oYFtibVovr2rDt8FHlU17BTicFEMAi29V1U+L5Q= +github.com/aws/aws-sdk-go-v2/credentials v1.17.30/go.mod h1:BPJ/yXV92ZVq6G8uYvbU0gSl8q94UB63nMT5ctNO38g= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 h1:yjwoSyDZF8Jth+mUk5lSPJCkMC0lMy6FaCD51jm6ayE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12/go.mod h1:fuR57fAgMk7ot3WcNQfb6rSEn+SUffl7ri+aa8uKysI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.20.5 h1:Kl55Z27pxKA6XGjnnKwPK6Um+DRZK32ocIYm1g7IOUM= +github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.20.5/go.mod h1:r6W6g2+YsfTBfuvxRLvCf6xxlQRSoNTdRDGFX7noKu0= +github.com/aws/aws-sdk-go-v2/service/cloudformation v1.53.5 h1:YeTVIy7cJLeahs7K0jQGDGAd1YYND/to/z8N3kqZBhY= +github.com/aws/aws-sdk-go-v2/service/cloudformation v1.53.5/go.mod h1:y45SdA9v+dLlweaqwAQMoFeXqdRvgwevafa2X8iTqZQ= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.177.0 h1:LAdDRIj5BEZM9fLDTUWUyPzWvv5A++nCEps/RGmZNOo= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.177.0/go.mod h1:ISODge3zgdwOEa4Ou6WM9PKbxJWJ15DYKnr2bfmCAIA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 h1:tJ5RnkHCiSH0jyd6gROjlJtNwov0eGYNz8s8nFcR0jQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18/go.mod h1:++NHzT+nAF7ZPrHPsA+ENvsXkOO8wEu+C6RXltAG4/c= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 h1:zCsFCKvbj25i7p1u94imVoO447I/sFv8qq+lGJhRN0c= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.5/go.mod h1:ZeDX1SnKsVlejeuz41GiajjZpRSWR7/42q/EyA/QEiM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 h1:SKvPgvdvmiTWoi0GAJ7AsJfOz3ngVkD/ERbs5pUnHNI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5/go.mod h1:20sz31hv/WsPa3HhU3hfrIet2kxM4Pe0r20eBZ20Tac= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 h1:OMsEmCyz2i89XwRwPouAJvhj81wINh+4UK+k/0Yo/q8= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.5/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0= +github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= +github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= @@ -341,16 +341,16 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.3 h1:9liNh8t+u26xl5ddmWLmsOsdNLwkdRTg5AG+JnTiM80= github.com/chai2010/gettext-go v1.0.3/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= -github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= -github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s= -github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk= -github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs= -github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8= +github.com/charmbracelet/bubbles v0.19.0 h1:gKZkKXPP6GlDk6EcfujDK19PCQqRjaJZQ7QRERx1UF0= +github.com/charmbracelet/bubbles v0.19.0/go.mod h1:WILteEqZ+krG5c3ntGEMeG99nCupcuIk7V0/zOP0tOA= +github.com/charmbracelet/bubbletea v0.27.0 h1:Mznj+vvYuYagD9Pn2mY7fuelGvP0HAXtZYGgRBCbHvU= +github.com/charmbracelet/bubbletea v0.27.0/go.mod h1:5MdP9XH6MbQkgGhnlxUqCNmBXf9I74KRQ8HIidRxV1Y= +github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= +github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= -github.com/charmbracelet/x/exp/golden v0.0.0-20240408110044-525ba71bb562 h1:9GaTAb7z1kfZ/rqzRQDBujEAWBNxetEAb4/NNVQBuX8= -github.com/charmbracelet/x/exp/golden v0.0.0-20240408110044-525ba71bb562/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/teatest v0.0.0-20240408110044-525ba71bb562 h1:8ePTYvWHJvthAosOpbQ6LO3Oxn/eWl8QZ9s5OTF1jdU= github.com/charmbracelet/x/exp/teatest v0.0.0-20240408110044-525ba71bb562/go.mod h1:0SMV+i+eNGuC1HCylD8oWbDA27tTtIP3kllV0GJFHUA= github.com/charmbracelet/x/input v0.1.2 h1:QJAZr33eOhDowkkEQ24rsJy4Llxlm+fRDf/cQrmqJa0= @@ -380,8 +380,8 @@ github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSU github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0= github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE= -github.com/containerd/containerd v1.7.20 h1:Sl6jQYk3TRavaU83h66QMbI2Nqg9Jm6qzwX57Vsn1SQ= -github.com/containerd/containerd v1.7.20/go.mod h1:52GsS5CwquuqPuLncsXwG0t2CiUce+KsNHJZQJvAgR0= +github.com/containerd/containerd v1.7.21 h1:USGXRK1eOC/SX0L195YgxTHb0a00anxajOzgfN0qrCA= +github.com/containerd/containerd v1.7.21/go.mod h1:e3Jz1rYRUZ2Lt51YrH9Rz0zPyJBOlSvB3ghr2jbVD8g= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= @@ -687,8 +687,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= -github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-getter v1.7.6 h1:5jHuM+aH373XNtXl9TNTUH5Qd69Trve11tHIrB+6yj4= +github.com/hashicorp/go-getter v1.7.6/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -790,14 +790,13 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= +github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg= +github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -814,10 +813,12 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= +github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -833,8 +834,6 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -847,10 +846,10 @@ github.com/novln/docker-parser v1.0.0/go.mod h1:oCeM32fsoUwkwByB5wVjsrsVQySzPWkl github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0= -github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA= -github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os= -github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo= +github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU= +github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -877,13 +876,13 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= -github.com/projectcontour/contour v1.30.0 h1:VrZFHhCD3jd3+dLvLuqrK/T++uKbKvRKM5zjQelUaEM= -github.com/projectcontour/contour v1.30.0/go.mod h1:64BJdN6uIkGGt3jJ6b3OLLapysgfEUNX/6K8vsaTRIg= +github.com/projectcontour/contour v1.29.0 h1:gCrV4/Q8ZEpDRtNqMtAzq8t6K0wYCJkBEKSxxedDZ8g= +github.com/projectcontour/contour v1.29.0/go.mod h1:C5FDDAhjhDK4CufMdysPfYexIzntJBZEqCImwsGP1N0= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= +github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -898,7 +897,6 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -1046,30 +1044,30 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/contrib/instrumentation/runtime v0.53.0 h1:nOlJEAJyrcy8hexK65M+dsCHIx7CVVbybcFDNkcTcAc= -go.opentelemetry.io/contrib/instrumentation/runtime v0.53.0/go.mod h1:u79lGGIlkg3Ryw425RbMjEkGYNxSnXRyR286O840+u4= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/contrib/instrumentation/runtime v0.54.0 h1:KD+8SJvRaW9n0vE0UgkytT207J3CmV1hGf9GYYU73ns= +go.opentelemetry.io/contrib/instrumentation/runtime v0.54.0/go.mod h1:/CsTuLR28IN3Vn13YEc72HljfHiGOMXiCbl4xiCSDhA= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/exporters/prometheus v0.50.0 h1:2Ewsda6hejmbhGFyUvWZjUThC98Cf8Zy6g0zkIimOng= -go.opentelemetry.io/otel/exporters/prometheus v0.50.0/go.mod h1:pMm5PkUo5YwbLiuEf7t2xg4wbP0/eSJrMxIMxKosynY= -go.opentelemetry.io/otel/exporters/zipkin v1.28.0 h1:q86SrM4sgdc1eDABeA+307DUWy1qaT3fDCVbeKYGfY4= -go.opentelemetry.io/otel/exporters/zipkin v1.28.0/go.mod h1:mkxt8tmE/1YujUHsMIgTPvBN2HVE3kXlRZWeKsTsFgI= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= -go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/otel/exporters/prometheus v0.51.0 h1:G7uexXb/K3T+T9fNLCCKncweEtNEBMTO+46hKX5EdKw= +go.opentelemetry.io/otel/exporters/prometheus v0.51.0/go.mod h1:v0mFe5Kk7woIh938mrZBJBmENYquyA0IICrlYm4Y0t4= +go.opentelemetry.io/otel/exporters/zipkin v1.29.0 h1:rqaUJdM9ItWf6DGrelaShXnJpb8rd3HTbcZWptvcsWA= +go.opentelemetry.io/otel/exporters/zipkin v1.29.0/go.mod h1:wDIyU6DjrUYqUgnmzjWnh1HOQGZCJ6YXMIJCdMc+T9Y= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= +go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= +go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= @@ -1313,8 +1311,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1331,8 +1329,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1570,8 +1568,8 @@ google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d h1:PksQg4dV6Sem3/H google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:s7iA721uChleev562UJO2OYB0PPT9CMFjV+Ce7VJH5M= google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d h1:Aqf0fiIdUQEj0Gn9mKFFXoQfTTEaNopWpfVyYADxiSg= google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Od4k8V1LQSizPRUK4OzZ7TBE/20k+jPczUDAEyvn69Y= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1658,8 +1656,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -helm.sh/helm/v3 v3.15.3 h1:HcZDaVFe9uHa6hpsR54mJjYyRy4uz/pc6csg27nxFOc= -helm.sh/helm/v3 v3.15.3/go.mod h1:FzSIP8jDQaa6WAVg9F+OkKz7J0ZmAga4MABtTbsb9WQ= +helm.sh/helm/v3 v3.15.4 h1:UFHd6oZ1IN3FsUZ7XNhOQDyQ2QYknBNWRHH57e9cbHY= +helm.sh/helm/v3 v3.15.4/go.mod h1:phOwlxqGSgppCY/ysWBNRhG3MtnpsttOzxaTK+Mt40E= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1687,8 +1685,8 @@ k8s.io/kube-openapi v0.0.0-20240620174524-b456828f718b h1:Q9xmGWBvOGd8UJyccgpYlL k8s.io/kube-openapi v0.0.0-20240620174524-b456828f718b/go.mod h1:UxDHUPsUwTOOxSU+oXURfFBcAS6JwiRXTYqYwfuGowc= k8s.io/kubectl v0.30.3 h1:YIBBvMdTW0xcDpmrOBzcpUVsn+zOgjMYIu7kAq+yqiI= k8s.io/kubectl v0.30.3/go.mod h1:IcR0I9RN2+zzTRUa1BzZCm4oM0NLOawE6RzlDvd1Fpo= -k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak= -k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c= @@ -1696,16 +1694,16 @@ oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZH rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.18.4 h1:87+guW1zhvuPLh1PHybKdYFLU0YJp4FhJRmiHvm5BZw= -sigs.k8s.io/controller-runtime v0.18.4/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg= +sigs.k8s.io/controller-runtime v0.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q= +sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0= sigs.k8s.io/kustomize/kyaml v0.17.2 h1:+AzvoJUY0kq4QAhH/ydPHHMRLijtUKiyVyh7fOSshr0= sigs.k8s.io/kustomize/kyaml v0.17.2/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= -sigs.k8s.io/secrets-store-csi-driver v1.4.4 h1:q4tLZ5fKJgKV9YCYrUlLI4inxPLJTH9xN3K5JH/1Xuc= -sigs.k8s.io/secrets-store-csi-driver v1.4.4/go.mod h1:0/wMVOv8qLx7YNVMGU+Sh7S4D6TH6GhyEpouo28OTUU= +sigs.k8s.io/secrets-store-csi-driver v1.4.5 h1:ta4aiNbl2EAWKrn3hFnN4Nll8mC6cgBP9+O4XdzorHM= +sigs.k8s.io/secrets-store-csi-driver v1.4.5/go.mod h1:0/wMVOv8qLx7YNVMGU+Sh7S4D6TH6GhyEpouo28OTUU= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json index a8fad28162..7accdb2471 100644 --- a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json +++ b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json @@ -715,7 +715,7 @@ }, "tags": { "type": { - "$ref": "#/118" + "$ref": "#/121" }, "flags": 0, "description": "Resource tags." @@ -777,7 +777,7 @@ }, "connections": { "type": { - "$ref": "#/103" + "$ref": "#/106" }, "flags": 0, "description": "Specifies a connection to another resource." @@ -791,35 +791,35 @@ }, "extensions": { "type": { - "$ref": "#/104" + "$ref": "#/107" }, "flags": 0, "description": "Extensions spec of the resource" }, "resourceProvisioning": { "type": { - "$ref": "#/107" + "$ref": "#/110" }, "flags": 0, "description": "Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource." }, "resources": { "type": { - "$ref": "#/109" + "$ref": "#/112" }, "flags": 0, "description": "A collection of references to resources associated with the container" }, "restartPolicy": { "type": { - "$ref": "#/113" + "$ref": "#/116" }, "flags": 0, "description": "Restart policy for the container" }, "runtimes": { "type": { - "$ref": "#/114" + "$ref": "#/117" }, "flags": 0, "description": "The properties for runtime configuration" @@ -900,49 +900,49 @@ }, "env": { "type": { - "$ref": "#/71" + "$ref": "#/74" }, "flags": 0, "description": "environment" }, "ports": { "type": { - "$ref": "#/76" + "$ref": "#/79" }, "flags": 0, "description": "container ports" }, "readinessProbe": { "type": { - "$ref": "#/77" + "$ref": "#/80" }, "flags": 0, "description": "Properties for readiness/liveness probe" }, "livenessProbe": { "type": { - "$ref": "#/77" + "$ref": "#/80" }, "flags": 0, "description": "Properties for readiness/liveness probe" }, "volumes": { "type": { - "$ref": "#/96" + "$ref": "#/99" }, "flags": 0, "description": "container volumes" }, "command": { "type": { - "$ref": "#/97" + "$ref": "#/100" }, "flags": 0, "description": "Entrypoint array. Overrides the container image's ENTRYPOINT" }, "args": { "type": { - "$ref": "#/98" + "$ref": "#/101" }, "flags": 0, "description": "Arguments to the entrypoint. Overrides the container image's CMD" @@ -982,12 +982,65 @@ } ] }, + { + "$type": "ObjectType", + "name": "EnvironmentVariable", + "properties": { + "value": { + "type": { + "$ref": "#/0" + }, + "flags": 0, + "description": "The value of the environment variable" + }, + "valueFrom": { + "type": { + "$ref": "#/72" + }, + "flags": 0, + "description": "The reference to the variable" + } + } + }, + { + "$type": "ObjectType", + "name": "EnvironmentVariableReference", + "properties": { + "secretRef": { + "type": { + "$ref": "#/73" + }, + "flags": 1, + "description": "This secret is used within a recipe. Secrets are encrypted, often have fine-grained access control, auditing and are recommended to be used to hold sensitive data." + } + } + }, + { + "$type": "ObjectType", + "name": "SecretReference", + "properties": { + "source": { + "type": { + "$ref": "#/0" + }, + "flags": 1, + "description": "The ID of an Applications.Core/SecretStore resource containing sensitive data required for recipe execution." + }, + "key": { + "type": { + "$ref": "#/0" + }, + "flags": 1, + "description": "The key for the secret in the secret store." + } + } + }, { "$type": "ObjectType", "name": "ContainerEnv", "properties": {}, "additionalProperties": { - "$ref": "#/0" + "$ref": "#/71" } }, { @@ -1003,7 +1056,7 @@ }, "protocol": { "type": { - "$ref": "#/75" + "$ref": "#/78" }, "flags": 0, "description": "The protocol in use by the port" @@ -1036,10 +1089,10 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/73" + "$ref": "#/76" }, { - "$ref": "#/74" + "$ref": "#/77" } ] }, @@ -1048,7 +1101,7 @@ "name": "ContainerPorts", "properties": {}, "additionalProperties": { - "$ref": "#/72" + "$ref": "#/75" } }, { @@ -1087,13 +1140,13 @@ }, "elements": { "exec": { - "$ref": "#/78" + "$ref": "#/81" }, "httpGet": { - "$ref": "#/80" + "$ref": "#/83" }, "tcp": { - "$ref": "#/83" + "$ref": "#/86" } } }, @@ -1110,7 +1163,7 @@ }, "kind": { "type": { - "$ref": "#/79" + "$ref": "#/82" }, "flags": 1, "description": "Discriminator property for HealthProbeProperties." @@ -1141,14 +1194,14 @@ }, "headers": { "type": { - "$ref": "#/81" + "$ref": "#/84" }, "flags": 0, "description": "Custom HTTP headers to add to the get request" }, "kind": { "type": { - "$ref": "#/82" + "$ref": "#/85" }, "flags": 1, "description": "Discriminator property for HealthProbeProperties." @@ -1180,7 +1233,7 @@ }, "kind": { "type": { - "$ref": "#/84" + "$ref": "#/87" }, "flags": 1, "description": "Discriminator property for HealthProbeProperties." @@ -1206,10 +1259,10 @@ }, "elements": { "ephemeral": { - "$ref": "#/86" + "$ref": "#/89" }, "persistent": { - "$ref": "#/91" + "$ref": "#/94" } } }, @@ -1219,14 +1272,14 @@ "properties": { "managedStore": { "type": { - "$ref": "#/89" + "$ref": "#/92" }, "flags": 1, "description": "The managed store for the ephemeral volume" }, "kind": { "type": { - "$ref": "#/90" + "$ref": "#/93" }, "flags": 1, "description": "Discriminator property for Volume." @@ -1245,10 +1298,10 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/87" + "$ref": "#/90" }, { - "$ref": "#/88" + "$ref": "#/91" } ] }, @@ -1262,7 +1315,7 @@ "properties": { "permission": { "type": { - "$ref": "#/94" + "$ref": "#/97" }, "flags": 0, "description": "The persistent volume permission" @@ -1276,7 +1329,7 @@ }, "kind": { "type": { - "$ref": "#/95" + "$ref": "#/98" }, "flags": 1, "description": "Discriminator property for Volume." @@ -1295,10 +1348,10 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/92" + "$ref": "#/95" }, { - "$ref": "#/93" + "$ref": "#/96" } ] }, @@ -1311,7 +1364,7 @@ "name": "ContainerVolumes", "properties": {}, "additionalProperties": { - "$ref": "#/85" + "$ref": "#/88" } }, { @@ -1346,7 +1399,7 @@ }, "iam": { "type": { - "$ref": "#/100" + "$ref": "#/103" }, "flags": 0, "description": "IAM properties" @@ -1359,14 +1412,14 @@ "properties": { "kind": { "type": { - "$ref": "#/101" + "$ref": "#/104" }, "flags": 1, "description": "The kind of IAM provider to configure" }, "roles": { "type": { - "$ref": "#/102" + "$ref": "#/105" }, "flags": 0, "description": "RBAC permissions to be assigned on the source resource" @@ -1388,7 +1441,7 @@ "name": "ContainerPropertiesConnections", "properties": {}, "additionalProperties": { - "$ref": "#/99" + "$ref": "#/102" } }, { @@ -1409,10 +1462,10 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/105" + "$ref": "#/108" }, { - "$ref": "#/106" + "$ref": "#/109" } ] }, @@ -1432,7 +1485,7 @@ { "$type": "ArrayType", "itemType": { - "$ref": "#/108" + "$ref": "#/111" } }, { @@ -1451,13 +1504,13 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/110" + "$ref": "#/113" }, { - "$ref": "#/111" + "$ref": "#/114" }, { - "$ref": "#/112" + "$ref": "#/115" } ] }, @@ -1467,7 +1520,7 @@ "properties": { "kubernetes": { "type": { - "$ref": "#/115" + "$ref": "#/118" }, "flags": 0, "description": "The runtime configuration properties for Kubernetes" @@ -1487,7 +1540,7 @@ }, "pod": { "type": { - "$ref": "#/117" + "$ref": "#/120" }, "flags": 0, "description": "A strategic merge patch that will be applied to the PodSpec object when this container is being deployed." @@ -1502,7 +1555,7 @@ "name": "KubernetesPodSpec", "properties": {}, "additionalProperties": { - "$ref": "#/116" + "$ref": "#/119" } }, { @@ -1551,28 +1604,28 @@ }, "type": { "type": { - "$ref": "#/120" + "$ref": "#/123" }, "flags": 10, "description": "The resource type" }, "apiVersion": { "type": { - "$ref": "#/121" + "$ref": "#/124" }, "flags": 10, "description": "The resource api version" }, "properties": { "type": { - "$ref": "#/123" + "$ref": "#/126" }, "flags": 1, "description": "Environment properties" }, "tags": { "type": { - "$ref": "#/156" + "$ref": "#/158" }, "flags": 0, "description": "Resource tags." @@ -1599,7 +1652,7 @@ "properties": { "provisioningState": { "type": { - "$ref": "#/131" + "$ref": "#/134" }, "flags": 2, "description": "Provisioning state of the resource at the time the operation was called" @@ -1613,7 +1666,7 @@ }, "providers": { "type": { - "$ref": "#/132" + "$ref": "#/135" }, "flags": 0, "description": "The Cloud providers configuration." @@ -1627,21 +1680,21 @@ }, "recipes": { "type": { - "$ref": "#/141" + "$ref": "#/144" }, "flags": 0, "description": "Specifies Recipes linked to the Environment." }, "recipeConfig": { "type": { - "$ref": "#/142" + "$ref": "#/145" }, "flags": 0, "description": "Configuration for Recipes. Defines how each type of Recipe should be configured and run." }, "extensions": { "type": { - "$ref": "#/155" + "$ref": "#/157" }, "flags": 0, "description": "The environment extension." @@ -1680,25 +1733,25 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/124" + "$ref": "#/127" }, { - "$ref": "#/125" + "$ref": "#/128" }, { - "$ref": "#/126" + "$ref": "#/129" }, { - "$ref": "#/127" + "$ref": "#/130" }, { - "$ref": "#/128" + "$ref": "#/131" }, { - "$ref": "#/129" + "$ref": "#/132" }, { - "$ref": "#/130" + "$ref": "#/133" } ] }, @@ -1708,14 +1761,14 @@ "properties": { "azure": { "type": { - "$ref": "#/133" + "$ref": "#/136" }, "flags": 0, "description": "The Azure cloud provider definition." }, "aws": { "type": { - "$ref": "#/134" + "$ref": "#/137" }, "flags": 0, "description": "The AWS cloud provider definition." @@ -1762,7 +1815,7 @@ }, "parameters": { "type": { - "$ref": "#/116" + "$ref": "#/119" }, "flags": 0, "description": "Any object" @@ -1770,10 +1823,10 @@ }, "elements": { "bicep": { - "$ref": "#/136" + "$ref": "#/139" }, "terraform": { - "$ref": "#/138" + "$ref": "#/141" } } }, @@ -1790,7 +1843,7 @@ }, "templateKind": { "type": { - "$ref": "#/137" + "$ref": "#/140" }, "flags": 1, "description": "Discriminator property for RecipeProperties." @@ -1814,7 +1867,7 @@ }, "templateKind": { "type": { - "$ref": "#/139" + "$ref": "#/142" }, "flags": 1, "description": "Discriminator property for RecipeProperties." @@ -1830,7 +1883,7 @@ "name": "DictionaryOfRecipeProperties", "properties": {}, "additionalProperties": { - "$ref": "#/135" + "$ref": "#/138" } }, { @@ -1838,7 +1891,7 @@ "name": "EnvironmentPropertiesRecipes", "properties": {}, "additionalProperties": { - "$ref": "#/140" + "$ref": "#/143" } }, { @@ -1847,21 +1900,21 @@ "properties": { "terraform": { "type": { - "$ref": "#/143" + "$ref": "#/146" }, "flags": 0, "description": "Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment." }, "env": { "type": { - "$ref": "#/153" + "$ref": "#/155" }, "flags": 0, "description": "The environment variables injected during Terraform Recipe execution for the recipes in the environment." }, "envSecrets": { "type": { - "$ref": "#/154" + "$ref": "#/156" }, "flags": 0, "description": "Environment variables containing sensitive information can be stored as secrets. The secrets are stored in Applications.Core/SecretStores resource." @@ -1874,14 +1927,14 @@ "properties": { "authentication": { "type": { - "$ref": "#/144" + "$ref": "#/147" }, "flags": 0, "description": "Authentication information used to access private Terraform module sources. Supported module sources: Git." }, "providers": { "type": { - "$ref": "#/152" + "$ref": "#/154" }, "flags": 0, "description": "Configuration for Terraform Recipe Providers. Controls how Terraform interacts with cloud providers, SaaS providers, and other APIs. For more information, please see: https://developer.hashicorp.com/terraform/language/providers/configuration." @@ -1894,7 +1947,7 @@ "properties": { "git": { "type": { - "$ref": "#/145" + "$ref": "#/148" }, "flags": 0, "description": "Authentication information used to access private Terraform modules from Git repository sources." @@ -1907,7 +1960,7 @@ "properties": { "pat": { "type": { - "$ref": "#/147" + "$ref": "#/150" }, "flags": 0, "description": "Personal Access Token (PAT) configuration used to authenticate to Git platforms." @@ -1932,7 +1985,7 @@ "name": "GitAuthConfigPat", "properties": {}, "additionalProperties": { - "$ref": "#/146" + "$ref": "#/149" } }, { @@ -1941,34 +1994,14 @@ "properties": { "secrets": { "type": { - "$ref": "#/150" + "$ref": "#/152" }, "flags": 0, "description": "Sensitive data in provider configuration can be stored as secrets. The secrets are stored in Applications.Core/SecretStores resource." } }, "additionalProperties": { - "$ref": "#/116" - } - }, - { - "$type": "ObjectType", - "name": "SecretReference", - "properties": { - "source": { - "type": { - "$ref": "#/0" - }, - "flags": 1, - "description": "The ID of an Applications.Core/SecretStore resource containing sensitive data required for recipe execution." - }, - "key": { - "type": { - "$ref": "#/0" - }, - "flags": 1, - "description": "The key for the secret in the secret store." - } + "$ref": "#/119" } }, { @@ -1976,13 +2009,13 @@ "name": "ProviderConfigPropertiesSecrets", "properties": {}, "additionalProperties": { - "$ref": "#/149" + "$ref": "#/73" } }, { "$type": "ArrayType", "itemType": { - "$ref": "#/148" + "$ref": "#/151" } }, { @@ -1990,7 +2023,7 @@ "name": "TerraformConfigPropertiesProviders", "properties": {}, "additionalProperties": { - "$ref": "#/151" + "$ref": "#/153" } }, { @@ -2006,7 +2039,7 @@ "name": "RecipeConfigPropertiesEnvSecrets", "properties": {}, "additionalProperties": { - "$ref": "#/149" + "$ref": "#/73" } }, { @@ -2028,7 +2061,7 @@ "name": "Applications.Core/environments@2023-10-01-preview", "scopeType": 0, "body": { - "$ref": "#/122" + "$ref": "#/125" }, "flags": 0, "functions": {} @@ -2061,28 +2094,28 @@ }, "type": { "type": { - "$ref": "#/158" + "$ref": "#/160" }, "flags": 10, "description": "The resource type" }, "apiVersion": { "type": { - "$ref": "#/159" + "$ref": "#/161" }, "flags": 10, "description": "The resource api version" }, "properties": { "type": { - "$ref": "#/161" + "$ref": "#/163" }, "flags": 1, "description": "ExtenderResource portable resource properties" }, "tags": { "type": { - "$ref": "#/174" + "$ref": "#/176" }, "flags": 0, "description": "Resource tags." @@ -2123,7 +2156,7 @@ }, "provisioningState": { "type": { - "$ref": "#/169" + "$ref": "#/171" }, "flags": 2, "description": "Provisioning state of the resource at the time the operation was called" @@ -2137,28 +2170,28 @@ }, "secrets": { "type": { - "$ref": "#/116" + "$ref": "#/119" }, "flags": 0, "description": "Any object" }, "recipe": { "type": { - "$ref": "#/170" + "$ref": "#/172" }, "flags": 0, "description": "The recipe used to automatically deploy underlying infrastructure for a portable resource" }, "resourceProvisioning": { "type": { - "$ref": "#/173" + "$ref": "#/175" }, "flags": 0, "description": "Specifies how the underlying service/resource is provisioned and managed. Available values are 'recipe', where Radius manages the lifecycle of the resource through a Recipe, and 'manual', where a user manages the resource and provides the values." } }, "additionalProperties": { - "$ref": "#/116" + "$ref": "#/119" } }, { @@ -2192,12 +2225,6 @@ { "$type": "UnionType", "elements": [ - { - "$ref": "#/162" - }, - { - "$ref": "#/163" - }, { "$ref": "#/164" }, @@ -2212,6 +2239,12 @@ }, { "$ref": "#/168" + }, + { + "$ref": "#/169" + }, + { + "$ref": "#/170" } ] }, @@ -2228,7 +2261,7 @@ }, "parameters": { "type": { - "$ref": "#/116" + "$ref": "#/119" }, "flags": 0, "description": "Any object" @@ -2247,10 +2280,10 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/171" + "$ref": "#/173" }, { - "$ref": "#/172" + "$ref": "#/174" } ] }, @@ -2266,7 +2299,7 @@ "$type": "FunctionType", "parameters": [], "output": { - "$ref": "#/116" + "$ref": "#/119" } }, { @@ -2274,13 +2307,13 @@ "name": "Applications.Core/extenders@2023-10-01-preview", "scopeType": 0, "body": { - "$ref": "#/160" + "$ref": "#/162" }, "flags": 0, "functions": { "listSecrets": { "type": { - "$ref": "#/175" + "$ref": "#/177" }, "description": "listSecrets" } @@ -2314,28 +2347,28 @@ }, "type": { "type": { - "$ref": "#/177" + "$ref": "#/179" }, "flags": 10, "description": "The resource type" }, "apiVersion": { "type": { - "$ref": "#/178" + "$ref": "#/180" }, "flags": 10, "description": "The resource api version" }, "properties": { "type": { - "$ref": "#/180" + "$ref": "#/182" }, "flags": 1, "description": "Gateway properties" }, "tags": { "type": { - "$ref": "#/196" + "$ref": "#/198" }, "flags": 0, "description": "Resource tags." @@ -2376,7 +2409,7 @@ }, "provisioningState": { "type": { - "$ref": "#/188" + "$ref": "#/190" }, "flags": 2, "description": "Provisioning state of the resource at the time the operation was called" @@ -2397,21 +2430,21 @@ }, "hostname": { "type": { - "$ref": "#/189" + "$ref": "#/191" }, "flags": 0, "description": "Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io." }, "routes": { "type": { - "$ref": "#/191" + "$ref": "#/193" }, "flags": 1, "description": "Routes attached to this Gateway" }, "tls": { "type": { - "$ref": "#/192" + "$ref": "#/194" }, "flags": 0, "description": "TLS configuration definition for Gateway resource." @@ -2456,12 +2489,6 @@ { "$type": "UnionType", "elements": [ - { - "$ref": "#/181" - }, - { - "$ref": "#/182" - }, { "$ref": "#/183" }, @@ -2476,6 +2503,12 @@ }, { "$ref": "#/187" + }, + { + "$ref": "#/188" + }, + { + "$ref": "#/189" } ] }, @@ -2536,7 +2569,7 @@ { "$type": "ArrayType", "itemType": { - "$ref": "#/190" + "$ref": "#/192" } }, { @@ -2552,7 +2585,7 @@ }, "minimumProtocolVersion": { "type": { - "$ref": "#/195" + "$ref": "#/197" }, "flags": 0, "description": "Tls Minimum versions for Gateway resource." @@ -2578,10 +2611,10 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/193" + "$ref": "#/195" }, { - "$ref": "#/194" + "$ref": "#/196" } ] }, @@ -2598,7 +2631,7 @@ "name": "Applications.Core/gateways@2023-10-01-preview", "scopeType": 0, "body": { - "$ref": "#/179" + "$ref": "#/181" }, "flags": 0, "functions": {} @@ -2631,28 +2664,28 @@ }, "type": { "type": { - "$ref": "#/198" + "$ref": "#/200" }, "flags": 10, "description": "The resource type" }, "apiVersion": { "type": { - "$ref": "#/199" + "$ref": "#/201" }, "flags": 10, "description": "The resource api version" }, "properties": { "type": { - "$ref": "#/201" + "$ref": "#/203" }, "flags": 1, "description": "The properties of SecretStore" }, "tags": { "type": { - "$ref": "#/219" + "$ref": "#/224" }, "flags": 0, "description": "Resource tags." @@ -2693,7 +2726,7 @@ }, "provisioningState": { "type": { - "$ref": "#/209" + "$ref": "#/211" }, "flags": 2, "description": "Provisioning state of the resource at the time the operation was called" @@ -2707,14 +2740,14 @@ }, "type": { "type": { - "$ref": "#/212" + "$ref": "#/217" }, "flags": 0, "description": "The type of SecretStore data" }, "data": { "type": { - "$ref": "#/218" + "$ref": "#/223" }, "flags": 1, "description": "An object to represent key-value type secrets" @@ -2759,12 +2792,6 @@ { "$type": "UnionType", "elements": [ - { - "$ref": "#/202" - }, - { - "$ref": "#/203" - }, { "$ref": "#/204" }, @@ -2779,6 +2806,12 @@ }, { "$ref": "#/208" + }, + { + "$ref": "#/209" + }, + { + "$ref": "#/210" } ] }, @@ -2790,14 +2823,35 @@ "$type": "StringLiteralType", "value": "certificate" }, + { + "$type": "StringLiteralType", + "value": "basicAuthentication" + }, + { + "$type": "StringLiteralType", + "value": "azureWorkloadIdentity" + }, + { + "$type": "StringLiteralType", + "value": "awsIRSA" + }, { "$type": "UnionType", "elements": [ { - "$ref": "#/210" + "$ref": "#/212" + }, + { + "$ref": "#/213" + }, + { + "$ref": "#/214" + }, + { + "$ref": "#/215" }, { - "$ref": "#/211" + "$ref": "#/216" } ] }, @@ -2807,7 +2861,7 @@ "properties": { "encoding": { "type": { - "$ref": "#/216" + "$ref": "#/221" }, "flags": 0, "description": "The type of SecretValue Encoding" @@ -2821,7 +2875,7 @@ }, "valueFrom": { "type": { - "$ref": "#/217" + "$ref": "#/222" }, "flags": 0, "description": "The Secret value source properties" @@ -2840,10 +2894,10 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/214" + "$ref": "#/219" }, { - "$ref": "#/215" + "$ref": "#/220" } ] }, @@ -2872,7 +2926,7 @@ "name": "SecretStorePropertiesData", "properties": {}, "additionalProperties": { - "$ref": "#/213" + "$ref": "#/218" } }, { @@ -2889,14 +2943,14 @@ "properties": { "type": { "type": { - "$ref": "#/223" + "$ref": "#/231" }, "flags": 2, "description": "The type of SecretStore data" }, "data": { "type": { - "$ref": "#/224" + "$ref": "#/232" }, "flags": 2, "description": "An object to represent key-value type secrets" @@ -2911,14 +2965,35 @@ "$type": "StringLiteralType", "value": "certificate" }, + { + "$type": "StringLiteralType", + "value": "basicAuthentication" + }, + { + "$type": "StringLiteralType", + "value": "azureWorkloadIdentity" + }, + { + "$type": "StringLiteralType", + "value": "awsIRSA" + }, { "$type": "UnionType", "elements": [ { - "$ref": "#/221" + "$ref": "#/226" + }, + { + "$ref": "#/227" + }, + { + "$ref": "#/228" }, { - "$ref": "#/222" + "$ref": "#/229" + }, + { + "$ref": "#/230" } ] }, @@ -2927,14 +3002,14 @@ "name": "SecretStoreListSecretsResultData", "properties": {}, "additionalProperties": { - "$ref": "#/213" + "$ref": "#/218" } }, { "$type": "FunctionType", "parameters": [], "output": { - "$ref": "#/220" + "$ref": "#/225" } }, { @@ -2942,13 +3017,13 @@ "name": "Applications.Core/secretStores@2023-10-01-preview", "scopeType": 0, "body": { - "$ref": "#/200" + "$ref": "#/202" }, "flags": 0, "functions": { "listSecrets": { "type": { - "$ref": "#/225" + "$ref": "#/233" }, "description": "listSecrets" } @@ -2982,28 +3057,28 @@ }, "type": { "type": { - "$ref": "#/227" + "$ref": "#/235" }, "flags": 10, "description": "The resource type" }, "apiVersion": { "type": { - "$ref": "#/228" + "$ref": "#/236" }, "flags": 10, "description": "The resource api version" }, "properties": { "type": { - "$ref": "#/230" + "$ref": "#/238" }, "flags": 1, "description": "Volume properties" }, "tags": { "type": { - "$ref": "#/262" + "$ref": "#/270" }, "flags": 0, "description": "Resource tags." @@ -3045,7 +3120,7 @@ }, "provisioningState": { "type": { - "$ref": "#/238" + "$ref": "#/246" }, "flags": 2, "description": "Provisioning state of the resource at the time the operation was called" @@ -3060,7 +3135,7 @@ }, "elements": { "azure.com.keyvault": { - "$ref": "#/239" + "$ref": "#/247" } } }, @@ -3096,25 +3171,25 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/231" + "$ref": "#/239" }, { - "$ref": "#/232" + "$ref": "#/240" }, { - "$ref": "#/233" + "$ref": "#/241" }, { - "$ref": "#/234" + "$ref": "#/242" }, { - "$ref": "#/235" + "$ref": "#/243" }, { - "$ref": "#/236" + "$ref": "#/244" }, { - "$ref": "#/237" + "$ref": "#/245" } ] }, @@ -3124,14 +3199,14 @@ "properties": { "certificates": { "type": { - "$ref": "#/252" + "$ref": "#/260" }, "flags": 0, "description": "The KeyVault certificates that this volume exposes" }, "keys": { "type": { - "$ref": "#/254" + "$ref": "#/262" }, "flags": 0, "description": "The KeyVault keys that this volume exposes" @@ -3145,14 +3220,14 @@ }, "secrets": { "type": { - "$ref": "#/260" + "$ref": "#/268" }, "flags": 0, "description": "The KeyVault secrets that this volume exposes" }, "kind": { "type": { - "$ref": "#/261" + "$ref": "#/269" }, "flags": 1, "description": "Discriminator property for VolumeProperties." @@ -3172,14 +3247,14 @@ }, "encoding": { "type": { - "$ref": "#/244" + "$ref": "#/252" }, "flags": 0, "description": "Represents secret encodings" }, "format": { "type": { - "$ref": "#/247" + "$ref": "#/255" }, "flags": 0, "description": "Represents certificate formats" @@ -3193,7 +3268,7 @@ }, "certType": { "type": { - "$ref": "#/251" + "$ref": "#/259" }, "flags": 0, "description": "Represents certificate types" @@ -3223,13 +3298,13 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/241" + "$ref": "#/249" }, { - "$ref": "#/242" + "$ref": "#/250" }, { - "$ref": "#/243" + "$ref": "#/251" } ] }, @@ -3245,10 +3320,10 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/245" + "$ref": "#/253" }, { - "$ref": "#/246" + "$ref": "#/254" } ] }, @@ -3268,13 +3343,13 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/248" + "$ref": "#/256" }, { - "$ref": "#/249" + "$ref": "#/257" }, { - "$ref": "#/250" + "$ref": "#/258" } ] }, @@ -3283,7 +3358,7 @@ "name": "AzureKeyVaultVolumePropertiesCertificates", "properties": {}, "additionalProperties": { - "$ref": "#/240" + "$ref": "#/248" } }, { @@ -3318,7 +3393,7 @@ "name": "AzureKeyVaultVolumePropertiesKeys", "properties": {}, "additionalProperties": { - "$ref": "#/253" + "$ref": "#/261" } }, { @@ -3334,7 +3409,7 @@ }, "encoding": { "type": { - "$ref": "#/259" + "$ref": "#/267" }, "flags": 0, "description": "Represents secret encodings" @@ -3371,13 +3446,13 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/256" + "$ref": "#/264" }, { - "$ref": "#/257" + "$ref": "#/265" }, { - "$ref": "#/258" + "$ref": "#/266" } ] }, @@ -3386,7 +3461,7 @@ "name": "AzureKeyVaultVolumePropertiesSecrets", "properties": {}, "additionalProperties": { - "$ref": "#/255" + "$ref": "#/263" } }, { @@ -3406,7 +3481,7 @@ "name": "Applications.Core/volumes@2023-10-01-preview", "scopeType": 0, "body": { - "$ref": "#/229" + "$ref": "#/237" }, "flags": 0, "functions": {} diff --git a/hack/bicep-types-radius/generated/index.json b/hack/bicep-types-radius/generated/index.json index 9e9032ebcf..6657645e5d 100644 --- a/hack/bicep-types-radius/generated/index.json +++ b/hack/bicep-types-radius/generated/index.json @@ -4,22 +4,22 @@ "$ref": "applications/applications.core/2023-10-01-preview/types.json#/53" }, "Applications.Core/containers@2023-10-01-preview": { - "$ref": "applications/applications.core/2023-10-01-preview/types.json#/119" + "$ref": "applications/applications.core/2023-10-01-preview/types.json#/122" }, "Applications.Core/environments@2023-10-01-preview": { - "$ref": "applications/applications.core/2023-10-01-preview/types.json#/157" + "$ref": "applications/applications.core/2023-10-01-preview/types.json#/159" }, "Applications.Core/extenders@2023-10-01-preview": { - "$ref": "applications/applications.core/2023-10-01-preview/types.json#/176" + "$ref": "applications/applications.core/2023-10-01-preview/types.json#/178" }, "Applications.Core/gateways@2023-10-01-preview": { - "$ref": "applications/applications.core/2023-10-01-preview/types.json#/197" + "$ref": "applications/applications.core/2023-10-01-preview/types.json#/199" }, "Applications.Core/secretStores@2023-10-01-preview": { - "$ref": "applications/applications.core/2023-10-01-preview/types.json#/226" + "$ref": "applications/applications.core/2023-10-01-preview/types.json#/234" }, "Applications.Core/volumes@2023-10-01-preview": { - "$ref": "applications/applications.core/2023-10-01-preview/types.json#/263" + "$ref": "applications/applications.core/2023-10-01-preview/types.json#/271" }, "Applications.Dapr/pubSubBrokers@2023-10-01-preview": { "$ref": "applications/applications.dapr/2023-10-01-preview/types.json#/44" diff --git a/pkg/cli/cmd/install/kubernetes/kubernetes.go b/pkg/cli/cmd/install/kubernetes/kubernetes.go index 73989ef36f..9a03dbe782 100644 --- a/pkg/cli/cmd/install/kubernetes/kubernetes.go +++ b/pkg/cli/cmd/install/kubernetes/kubernetes.go @@ -41,9 +41,9 @@ func NewCommand(factory framework.Factory) (*cobra.Command, framework.Runner) { Long: `Install Radius in a Kubernetes cluster using the Radius Helm chart. By default 'rad install kubernetes' will install Radius with the version matching the rad CLI version. -Radius will be installed in the 'radius-system' namespace. For more information visit https://docs.radapp.io/concepts/architecture/ +Radius will be installed in the 'radius-system' namespace. For more information visit https://docs.radapp.io/concepts/technical/architecture/ -Overrides can be set by specifying Helm chart values with the '--set' flag. For more information visit https://docs.radapp.io/operations/platforms/kubernetes/install/. +Overrides can be set by specifying Helm chart values with the '--set' flag. For more information visit https://docs.radapp.io/guides/operations/kubernetes/install/. `, Example: `# Install Radius with default settings in current Kubernetes context rad install kubernetes diff --git a/pkg/corerp/api/v20231001preview/container_conversion.go b/pkg/corerp/api/v20231001preview/container_conversion.go index e92a0c2eff..be3c48c97c 100644 --- a/pkg/corerp/api/v20231001preview/container_conversion.go +++ b/pkg/corerp/api/v20231001preview/container_conversion.go @@ -18,6 +18,7 @@ package v20231001preview import ( "encoding/json" + "fmt" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/corerp/datamodel" @@ -101,6 +102,11 @@ func (src *ContainerResource) ConvertTo() (v1.DataModelInterface, error) { } } + convertedEnvironmentVariables, err := toEnvironmentVariableDataModel(src.Properties.Container.Env) + if err != nil { + return nil, err + } + converted := &datamodel.ContainerResource{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -123,7 +129,7 @@ func (src *ContainerResource) ConvertTo() (v1.DataModelInterface, error) { Container: datamodel.Container{ Image: to.String(src.Properties.Container.Image), ImagePullPolicy: toImagePullPolicyDataModel(src.Properties.Container.ImagePullPolicy), - Env: to.StringMap(src.Properties.Container.Env), + Env: convertedEnvironmentVariables, LivenessProbe: livenessProbe, Ports: ports, ReadinessProbe: readinessProbe, @@ -147,10 +153,71 @@ func (src *ContainerResource) ConvertTo() (v1.DataModelInterface, error) { Resource: to.String(src.Properties.Identity.Resource), } } - return converted, nil } +// toEnvironmentVariableDataModel: Converts from versioned datamodel to base datamodel +func toEnvironmentVariableDataModel(e map[string]*EnvironmentVariable) (map[string]datamodel.EnvironmentVariable, error) { + environmentVariableMap := map[string]datamodel.EnvironmentVariable{} + + for key, val := range e { + if val == nil { + return nil, v1.NewClientErrInvalidRequest(fmt.Sprintf("Environment variable %s is nil", key)) + } + // An environment variable can have either value(Value) or secret value(ValueFrom), but not both + if val.Value != nil && val.ValueFrom != nil { + return nil, v1.NewClientErrInvalidRequest(fmt.Sprintf("Environment variable %s has both value and secret value", key)) + } + + // An environment variable must have either value(Value) or secret value(ValueFrom) + if val.Value == nil && val.ValueFrom == nil { + return nil, v1.NewClientErrInvalidRequest(fmt.Sprintf("Environment variable %s has neither value nor secret value", key)) + } + + if val.Value != nil { + environmentVariableMap[key] = datamodel.EnvironmentVariable{ + Value: val.Value, + } + } else { + environmentVariableMap[key] = datamodel.EnvironmentVariable{ + ValueFrom: &datamodel.EnvironmentVariableReference{ + SecretRef: &datamodel.EnvironmentVariableSecretReference{ + Source: to.String(val.ValueFrom.SecretRef.Source), + Key: to.String(val.ValueFrom.SecretRef.Key), + }, + }, + } + + } + + } + return environmentVariableMap, nil +} + +// fromEnvironmentVariableDataModel: Converts from base datamodel to versioned datamodel +func fromEnvironmentVariableDataModel(e map[string]datamodel.EnvironmentVariable) map[string]*EnvironmentVariable { + environmentVariableMap := map[string]*EnvironmentVariable{} + + for key, val := range e { + if val.Value != nil { + environmentVariableMap[key] = &EnvironmentVariable{ + Value: val.Value, + } + } else if val.ValueFrom != nil { + environmentVariableMap[key] = &EnvironmentVariable{ + ValueFrom: &EnvironmentVariableReference{ + SecretRef: &SecretReference{ + Source: to.Ptr(val.ValueFrom.SecretRef.Source), + Key: to.Ptr(val.ValueFrom.SecretRef.Key), + }, + }, + } + } + } + + return environmentVariableMap +} + // ConvertFrom converts from version-agnostic datamodel to the versioned Container resource. func (dst *ContainerResource) ConvertFrom(src v1.DataModelInterface) error { c, ok := src.(*datamodel.ContainerResource) @@ -250,7 +317,7 @@ func (dst *ContainerResource) ConvertFrom(src v1.DataModelInterface) error { Container: &Container{ Image: to.Ptr(c.Properties.Container.Image), ImagePullPolicy: fromImagePullPolicyDataModel(c.Properties.Container.ImagePullPolicy), - Env: *to.StringMapPtr(c.Properties.Container.Env), + Env: fromEnvironmentVariableDataModel(c.Properties.Container.Env), LivenessProbe: livenessProbe, Ports: ports, ReadinessProbe: readinessProbe, diff --git a/pkg/corerp/api/v20231001preview/container_conversion_test.go b/pkg/corerp/api/v20231001preview/container_conversion_test.go index 8760ced3b6..b798adfd60 100644 --- a/pkg/corerp/api/v20231001preview/container_conversion_test.go +++ b/pkg/corerp/api/v20231001preview/container_conversion_test.go @@ -61,6 +61,11 @@ func TestContainerConvertVersionedToDataModel(t *testing.T) { err: nil, emptyExt: true, }, + { + filename: "containerresource-nil-env-variables.json", + err: v1.NewClientErrInvalidRequest("Environment variable DB_USER has neither value nor secret value"), + emptyExt: false, + }, } for _, tt := range conversionTests { @@ -91,6 +96,22 @@ func TestContainerConvertVersionedToDataModel(t *testing.T) { return } + if tt.filename == "containerresource.json" { + require.Equal(t, map[string]datamodel.EnvironmentVariable{ + "DB_USER": { + Value: to.Ptr("DB_USER"), + }, + "DB_PASSWORD": { + ValueFrom: &datamodel.EnvironmentVariableReference{ + SecretRef: &datamodel.EnvironmentVariableSecretReference{ + Source: "secret.id", + Key: "DB_PASSWORD", + }, + }, + }, + }, ct.Properties.Container.Env) + } + val, ok := ct.Properties.Connections["inventory"] require.True(t, ok) require.Equal(t, "inventory_route_id", val.Source) @@ -176,6 +197,22 @@ func TestContainerConvertDataModelToVersioned(t *testing.T) { return } + if tt.filename == "containerresourcedatamodel.json" { + require.Equal(t, map[string]datamodel.EnvironmentVariable{ + "DB_USER": { + Value: to.Ptr("DB_USER"), + }, + "DB_PASSWORD": { + ValueFrom: &datamodel.EnvironmentVariableReference{ + SecretRef: &datamodel.EnvironmentVariableSecretReference{ + Source: "secret.id", + Key: "DB_PASSWORD", + }, + }, + }, + }, r.Properties.Container.Env) + } + val, ok := r.Properties.Connections["inventory"] require.True(t, ok) require.Equal(t, "inventory_route_id", val.Source) diff --git a/pkg/corerp/api/v20231001preview/secretstore_conversion.go b/pkg/corerp/api/v20231001preview/secretstore_conversion.go index cf1049f25b..24a3bed0e0 100644 --- a/pkg/corerp/api/v20231001preview/secretstore_conversion.go +++ b/pkg/corerp/api/v20231001preview/secretstore_conversion.go @@ -106,6 +106,12 @@ func toSecretStoreDataTypeDataModel(src *SecretStoreDataType) datamodel.SecretTy return datamodel.SecretTypeGeneric case SecretStoreDataTypeCertificate: return datamodel.SecretTypeCert + case SecretStoreDataTypeBasicAuthentication: + return datamodel.SecretTypeBasicAuthentication + case SecretStoreDataTypeAzureWorkloadIdentity: + return datamodel.SecretTypeAzureWorkloadIdentity + case SecretStoreDataTypeAwsIRSA: + return datamodel.SecretTypeAWSIRSA } return datamodel.SecretTypeGeneric @@ -117,6 +123,12 @@ func fromSecretStoreDataTypeDataModel(src datamodel.SecretType) *SecretStoreData return to.Ptr(SecretStoreDataTypeGeneric) case datamodel.SecretTypeCert: return to.Ptr(SecretStoreDataTypeCertificate) + case datamodel.SecretTypeBasicAuthentication: + return to.Ptr(SecretStoreDataTypeBasicAuthentication) + case datamodel.SecretTypeAzureWorkloadIdentity: + return to.Ptr(SecretStoreDataTypeAzureWorkloadIdentity) + case datamodel.SecretTypeAWSIRSA: + return to.Ptr(SecretStoreDataTypeAwsIRSA) } return nil } diff --git a/pkg/corerp/api/v20231001preview/secretstore_conversion_test.go b/pkg/corerp/api/v20231001preview/secretstore_conversion_test.go index e1024ac086..60357c5948 100644 --- a/pkg/corerp/api/v20231001preview/secretstore_conversion_test.go +++ b/pkg/corerp/api/v20231001preview/secretstore_conversion_test.go @@ -159,3 +159,49 @@ func TestSecretStoreConvertFromValidation(t *testing.T) { require.ErrorAs(t, tc.err, &err) } } + +func TestSecretStorefromSecretStoreDataTypeDataModel(t *testing.T) { + tests := []struct { + name string + input datamodel.SecretType + expected *SecretStoreDataType + }{ + { + name: "Generic Secret Type", + input: datamodel.SecretTypeGeneric, + expected: to.Ptr(SecretStoreDataTypeGeneric), + }, + { + name: "Certificate Secret Type", + input: datamodel.SecretTypeCert, + expected: to.Ptr(SecretStoreDataTypeCertificate), + }, + { + name: "Basic Authentication Secret Type", + input: datamodel.SecretTypeBasicAuthentication, + expected: to.Ptr(SecretStoreDataTypeBasicAuthentication), + }, + { + name: "Azure Workload Identity Secret Type", + input: datamodel.SecretTypeAzureWorkloadIdentity, + expected: to.Ptr(SecretStoreDataTypeAzureWorkloadIdentity), + }, + { + name: "AWS IRSA Secret Type", + input: datamodel.SecretTypeAWSIRSA, + expected: to.Ptr(SecretStoreDataTypeAwsIRSA), + }, + { + name: "None Secret Type", + input: datamodel.SecretTypeNone, + expected: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := fromSecretStoreDataTypeDataModel(tt.input) + require.Equal(t, tt.expected, result) + }) + } +} diff --git a/pkg/corerp/api/v20231001preview/testdata/containerresource-nil-env-variables.json b/pkg/corerp/api/v20231001preview/testdata/containerresource-nil-env-variables.json new file mode 100644 index 0000000000..1716f56f5c --- /dev/null +++ b/pkg/corerp/api/v20231001preview/testdata/containerresource-nil-env-variables.json @@ -0,0 +1,80 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/containers/container0", + "name": "container0", + "type": "Applications.Core/containers", + "properties": { + "status": { + "outputResources": [ + { + "id": "/planes/test/local/providers/Test.Namespace/testResources/test-resource" + } + ] + }, + "provisioningState": "Succeeded", + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", + "connections": { + "inventory": { + "source": "inventory_route_id", + "disableDefaultEnvVars": true, + "iam": { + "kind": "azure", + "roles": [ + "read" + ] + } + } + }, + "restartPolicy": "Always", + "container": { + "image": "ghcr.io/radius-project/webapptutorial-todoapp", + "livenessProbe": { + "kind": "tcp", + "failureThreshold": 5, + "initialDelaySeconds": 5, + "periodSeconds": 5, + "timeoutSeconds": 5, + "containerPort": 8080 + }, + "env": { + "DB_USER": { } + }, + "command": [ + "/bin/sh" + ], + "args": [ + "-c", + "while true; do echo hello; sleep 10;done" + ], + "workingDir": "/app" + }, + "identity": { + "kind": "azure.com.workload", + "oidcIssuer": "https://oidcuri/id", + "resource": "resourceid" + }, + "extensions": [ + { + "kind": "manualScaling", + "replicas": 2 + }, + { + "kind": "daprSidecar", + "appId": "app-id", + "appPort": 80, + "config": "config", + "protocol": "http" + }, + { + "kind": "kubernetesMetadata", + "annotations": { + "prometheus.io/scrape": "true", + "prometheus.io/port": "80" + }, + "labels": { + "foo/bar/team": "credit", + "foo/bar/contact": "radiususer" + } + } + ] + } +} \ No newline at end of file diff --git a/pkg/corerp/api/v20231001preview/testdata/containerresource.json b/pkg/corerp/api/v20231001preview/testdata/containerresource.json index 9e244bfebb..0775bb9705 100644 --- a/pkg/corerp/api/v20231001preview/testdata/containerresource.json +++ b/pkg/corerp/api/v20231001preview/testdata/containerresource.json @@ -35,6 +35,17 @@ "timeoutSeconds": 5, "containerPort": 8080 }, + "env": { + "DB_USER": { "value": "DB_USER" }, + "DB_PASSWORD": { + "valueFrom": { + "secretRef": { + "source": "secret.id", + "key": "DB_PASSWORD" + } + } + } + }, "command": [ "/bin/sh" ], diff --git a/pkg/corerp/api/v20231001preview/testdata/containerresourcedatamodel.json b/pkg/corerp/api/v20231001preview/testdata/containerresourcedatamodel.json index 815b126465..9540ae59f0 100644 --- a/pkg/corerp/api/v20231001preview/testdata/containerresourcedatamodel.json +++ b/pkg/corerp/api/v20231001preview/testdata/containerresourcedatamodel.json @@ -52,6 +52,17 @@ "containerPort": 8080 } }, + "env": { + "DB_USER": { "value": "DB_USER" }, + "DB_PASSWORD": { + "valueFrom": { + "secretRef": { + "source": "secret.id", + "key": "DB_PASSWORD" + } + } + } + }, "command": [ "/bin/sh" ], diff --git a/pkg/corerp/api/v20231001preview/zz_generated_constants.go b/pkg/corerp/api/v20231001preview/zz_generated_constants.go index 5e3909b5a8..8e38920b85 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_constants.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_constants.go @@ -329,6 +329,15 @@ func PossibleRestartPolicyValues() []RestartPolicy { type SecretStoreDataType string const ( + // SecretStoreDataTypeAwsIRSA - awsIRSA type is used to represent registry authentication using AWS IRSA (IAM Roles for Service +// accounts) and the secretstore resource is expected to have the key 'roleARN'. + SecretStoreDataTypeAwsIRSA SecretStoreDataType = "awsIRSA" + // SecretStoreDataTypeAzureWorkloadIdentity - azureWorkloadIdentity type is used to represent registry authentication using +// azure federated identity and the secretstore resource is expected to have the keys 'clientId' and 'tenantId'. + SecretStoreDataTypeAzureWorkloadIdentity SecretStoreDataType = "azureWorkloadIdentity" + // SecretStoreDataTypeBasicAuthentication - basicAuthentication type is used to represent username and password based authentication +// and the secretstore resource is expected to have the keys 'username' and 'password'. + SecretStoreDataTypeBasicAuthentication SecretStoreDataType = "basicAuthentication" // SecretStoreDataTypeCertificate - Certificate secret data type SecretStoreDataTypeCertificate SecretStoreDataType = "certificate" // SecretStoreDataTypeGeneric - Generic secret data type @@ -338,6 +347,9 @@ const ( // PossibleSecretStoreDataTypeValues returns the possible values for the SecretStoreDataType const type. func PossibleSecretStoreDataTypeValues() []SecretStoreDataType { return []SecretStoreDataType{ + SecretStoreDataTypeAwsIRSA, + SecretStoreDataTypeAzureWorkloadIdentity, + SecretStoreDataTypeBasicAuthentication, SecretStoreDataTypeCertificate, SecretStoreDataTypeGeneric, } diff --git a/pkg/corerp/api/v20231001preview/zz_generated_models.go b/pkg/corerp/api/v20231001preview/zz_generated_models.go index 6b494e07bf..40dfe5d89d 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_models.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_models.go @@ -278,7 +278,7 @@ type Container struct { Command []*string // environment - Env map[string]*string + Env map[string]*EnvironmentVariable // The pull policy for the container image ImagePullPolicy *ImagePullPolicy @@ -454,7 +454,7 @@ type ContainerUpdate struct { Command []*string // environment - Env map[string]*string + Env map[string]*EnvironmentVariableUpdate // The registry and image to download and run in your container Image *string @@ -620,6 +620,36 @@ type EnvironmentResourceUpdateProperties struct { Simulated *bool } +// EnvironmentVariable - Environment variables type +type EnvironmentVariable struct { + // The value of the environment variable + Value *string + + // The reference to the variable + ValueFrom *EnvironmentVariableReference +} + +// EnvironmentVariableReference - The reference to the variable +type EnvironmentVariableReference struct { + // REQUIRED; The secret reference + SecretRef *SecretReference +} + +// EnvironmentVariableReferenceUpdate - The reference to the variable +type EnvironmentVariableReferenceUpdate struct { + // The secret reference + SecretRef *SecretReferenceUpdate +} + +// EnvironmentVariableUpdate - Environment variables type +type EnvironmentVariableUpdate struct { + // The value of the environment variable + Value *string + + // The reference to the variable + ValueFrom *EnvironmentVariableReferenceUpdate +} + // EphemeralVolume - Specifies an ephemeral volume for a container type EphemeralVolume struct { // REQUIRED; Discriminator property for Volume. @@ -1312,7 +1342,8 @@ type Recipe struct { // RecipeConfigProperties - Configuration for Recipes. Defines how each type of Recipe should be configured and run. type RecipeConfigProperties struct { - // Environment variables injected during Terraform Recipe execution for the recipes in the environment. + // Environment variables injected during recipe execution for the recipes in the environment, currently supported for Terraform +// recipes. Env map[string]*string // Environment variables containing sensitive information can be stored as secrets. The secrets are stored in Applications.Core/SecretStores @@ -1475,6 +1506,16 @@ type SecretReference struct { Source *string } +// SecretReferenceUpdate - This secret is used within a recipe. Secrets are encrypted, often have fine-grained access control, +// auditing and are recommended to be used to hold sensitive data. +type SecretReferenceUpdate struct { + // The key for the secret in the secret store. + Key *string + + // The ID of an Applications.Core/SecretStore resource containing sensitive data required for recipe execution. + Source *string +} + // SecretStoreListSecretsResult - The list of secrets type SecretStoreListSecretsResult struct { // REQUIRED; An object to represent key-value type secrets diff --git a/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go b/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go index 4b9af82111..18f150d197 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go @@ -1415,6 +1415,122 @@ func (e *EnvironmentResourceUpdateProperties) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type EnvironmentVariable. +func (e EnvironmentVariable) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "value", e.Value) + populate(objectMap, "valueFrom", e.ValueFrom) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type EnvironmentVariable. +func (e *EnvironmentVariable) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", e, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "value": + err = unpopulate(val, "Value", &e.Value) + delete(rawMsg, key) + case "valueFrom": + err = unpopulate(val, "ValueFrom", &e.ValueFrom) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", e, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type EnvironmentVariableReference. +func (e EnvironmentVariableReference) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "secretRef", e.SecretRef) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type EnvironmentVariableReference. +func (e *EnvironmentVariableReference) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", e, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "secretRef": + err = unpopulate(val, "SecretRef", &e.SecretRef) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", e, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type EnvironmentVariableReferenceUpdate. +func (e EnvironmentVariableReferenceUpdate) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "secretRef", e.SecretRef) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type EnvironmentVariableReferenceUpdate. +func (e *EnvironmentVariableReferenceUpdate) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", e, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "secretRef": + err = unpopulate(val, "SecretRef", &e.SecretRef) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", e, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type EnvironmentVariableUpdate. +func (e EnvironmentVariableUpdate) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "value", e.Value) + populate(objectMap, "valueFrom", e.ValueFrom) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type EnvironmentVariableUpdate. +func (e *EnvironmentVariableUpdate) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", e, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "value": + err = unpopulate(val, "Value", &e.Value) + delete(rawMsg, key) + case "valueFrom": + err = unpopulate(val, "ValueFrom", &e.ValueFrom) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", e, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type EphemeralVolume. func (e EphemeralVolume) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -3569,6 +3685,37 @@ func (s *SecretReference) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type SecretReferenceUpdate. +func (s SecretReferenceUpdate) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "key", s.Key) + populate(objectMap, "source", s.Source) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type SecretReferenceUpdate. +func (s *SecretReferenceUpdate) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "key": + err = unpopulate(val, "Key", &s.Key) + delete(rawMsg, key) + case "source": + err = unpopulate(val, "Source", &s.Source) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type SecretStoreListSecretsResult. func (s SecretStoreListSecretsResult) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) diff --git a/pkg/corerp/datamodel/container.go b/pkg/corerp/datamodel/container.go index c1b6f0eeb9..dd67ff83f4 100644 --- a/pkg/corerp/datamodel/container.go +++ b/pkg/corerp/datamodel/container.go @@ -116,16 +116,37 @@ type ConnectionProperties struct { // Container - Definition of a container. type Container struct { - Image string `json:"image,omitempty"` - ImagePullPolicy string `json:"imagePullPolicy,omitempty"` - Env map[string]string `json:"env,omitempty"` - LivenessProbe HealthProbeProperties `json:"livenessProbe,omitempty"` - Ports map[string]ContainerPort `json:"ports,omitempty"` - ReadinessProbe HealthProbeProperties `json:"readinessProbe,omitempty"` - Volumes map[string]VolumeProperties `json:"volumes,omitempty"` - Command []string `json:"command,omitempty"` - Args []string `json:"args,omitempty"` - WorkingDir string `json:"workingDir,omitempty"` + Image string `json:"image,omitempty"` + ImagePullPolicy string `json:"imagePullPolicy,omitempty"` + Env map[string]EnvironmentVariable `json:"env,omitempty"` + LivenessProbe HealthProbeProperties `json:"livenessProbe,omitempty"` + Ports map[string]ContainerPort `json:"ports,omitempty"` + ReadinessProbe HealthProbeProperties `json:"readinessProbe,omitempty"` + Volumes map[string]VolumeProperties `json:"volumes,omitempty"` + Command []string `json:"command,omitempty"` + Args []string `json:"args,omitempty"` + WorkingDir string `json:"workingDir,omitempty"` +} + +// EnvironmentVariable - Environment variable for the container +type EnvironmentVariable struct { + // Value is the property for the environment variable specified by the user. Such as "key": "value" + Value *string `json:"value,omitempty"` + // ValueFrom is the property for the environment variable specified by a reference to a secret. + ValueFrom *EnvironmentVariableReference `json:"valueFrom,omitempty"` +} + +// EnvironmentVariableReference - Environment variable reference for the container +type EnvironmentVariableReference struct { + // SecretRef is the property for the environment variable specified by a reference to a secret. + SecretRef *EnvironmentVariableSecretReference `json:"secretRef"` +} + +// EnvironmentVariableSecretReference - Environment variable secret reference for the container +type EnvironmentVariableSecretReference struct { + // Source is either the resource id of a radius Applications.Core/secretStore resource or a kubernetes secret reference. + Source string `json:"source"` + Key string `json:"key"` } // ContainerPort - Specifies a listening port for the container diff --git a/pkg/corerp/datamodel/secretstore.go b/pkg/corerp/datamodel/secretstore.go index 5ec0993538..86f9bdc6df 100644 --- a/pkg/corerp/datamodel/secretstore.go +++ b/pkg/corerp/datamodel/secretstore.go @@ -45,6 +45,12 @@ const ( SecretTypeGeneric SecretType = "generic" // SecretTypeCert is the certificate secret type. SecretTypeCert SecretType = "certificate" + // SecretTypeBasicAuthentication is the basicAuthentication secret type. + SecretTypeBasicAuthentication SecretType = "basicAuthentication" + // SecretTypeAzureWorkloadIdentity is the azureWorkloadIdentity secret type. + SecretTypeAzureWorkloadIdentity SecretType = "azureWorkloadIdentity" + // SecretTypeAWSIRSA is the awsIRSA secret type. + SecretTypeAWSIRSA SecretType = "awsIRSA" ) // SecretStore represents secret store resource. diff --git a/pkg/corerp/frontend/controller/secretstores/kubernetes.go b/pkg/corerp/frontend/controller/secretstores/kubernetes.go index 3825a03a21..62296b5ec6 100644 --- a/pkg/corerp/frontend/controller/secretstores/kubernetes.go +++ b/pkg/corerp/frontend/controller/secretstores/kubernetes.go @@ -46,6 +46,9 @@ func getOrDefaultType(t datamodel.SecretType) (datamodel.SecretType, error) { t = datamodel.SecretTypeGeneric case datamodel.SecretTypeCert: case datamodel.SecretTypeGeneric: + case datamodel.SecretTypeBasicAuthentication: + case datamodel.SecretTypeAzureWorkloadIdentity: + case datamodel.SecretTypeAWSIRSA: default: err = fmt.Errorf("'%s' is invalid secret type", t) } @@ -76,9 +79,16 @@ func getOrDefaultEncoding(t datamodel.SecretType, e datamodel.SecretValueEncodin } // ValidateAndMutateRequest checks the type and encoding of the secret store, and ensures that the secret store data is -// valid. If any of these checks fail, a BadRequestResponse is returned. +// valid and required keys are present for the secret type. If any of these checks fail, a BadRequestResponse is returned. func ValidateAndMutateRequest(ctx context.Context, newResource *datamodel.SecretStore, oldResource *datamodel.SecretStore, options *controller.Options) (rest.Response, error) { + // Define a map of required keys for each SecretType + var requiredKeys = map[datamodel.SecretType][]string{ + datamodel.SecretTypeBasicAuthentication: {UsernameKey, PasswordKey}, + datamodel.SecretTypeAzureWorkloadIdentity: {ClientIdKey, TenantIdKey}, + datamodel.SecretTypeAWSIRSA: {RoleARNKey}, + } var err error + newResource.Properties.Type, err = getOrDefaultType(newResource.Properties.Type) if err != nil { return rest.NewBadRequestResponse(err.Error()), nil @@ -116,6 +126,15 @@ func ValidateAndMutateRequest(ctx context.Context, newResource *datamodel.Secret } } + // Validate that required keys for the secret type are present in the secret data + if keys, ok := requiredKeys[newResource.Properties.Type]; ok { + for _, key := range keys { + if _, ok := newResource.Properties.Data[key]; !ok { + return rest.NewBadRequestResponse(fmt.Sprintf("$.properties.data must contain '%s' key for %s type.", key, newResource.Properties.Type)), nil + } + } + } + return nil, nil } diff --git a/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go b/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go index 2eda9c94ec..676107a91b 100644 --- a/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go +++ b/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go @@ -51,6 +51,11 @@ const ( testFileGenericValueGlobalScope = "secretstores_datamodel_global_scope.json" testFileGenericValueInvalidResource = "secretstores_datamodel_global_scope_invalid_resource.json" testFileGenericValueEmptyResource = "secretstores_datamodel_global_scope_empty_resource.json" + + testFileBasicAuthentication = "secretstores_datamodel_basicauth.json" + testFileBasicAuthenticationInvalid = "secretstores_datamodel_basicauth_invalid.json" + testFileAWSIRSA = "secretstores_datamodel_awsirsa.json" + testFileAzureWorkloadIdentity = "secretstores_datamodel_azwi.json" ) func TestGetNamespace(t *testing.T) { @@ -247,53 +252,109 @@ func TestGetOrDefaultEncoding(t *testing.T) { } func TestValidateAndMutateRequest(t *testing.T) { - t.Run("default type is generic", func(t *testing.T) { - newResource := testutil.MustGetTestData[datamodel.SecretStore](testFileCertValueFrom) - newResource.Properties.Type = "" - - resp, err := ValidateAndMutateRequest(context.TODO(), newResource, nil, nil) - require.NoError(t, err) - require.Nil(t, resp) - - // assert - require.Equal(t, datamodel.SecretTypeGeneric, newResource.Properties.Type) - }) - - t.Run("new resource, but referencing valueFrom", func(t *testing.T) { - newResource := testutil.MustGetTestData[datamodel.SecretStore](testFileCertValueFrom) - newResource.Properties.Resource = "" - resp, err := ValidateAndMutateRequest(context.TODO(), newResource, nil, nil) - require.NoError(t, err) - - // assert - r := resp.(*rest.BadRequestResponse) - require.True(t, r.Body.Error.Message == "$.properties.data[tls.crt].Value must be given to create the secret." || - r.Body.Error.Message == "$.properties.data[tls.key].Value must be given to create the secret.") - }) - - t.Run("update the existing resource - type not matched", func(t *testing.T) { - oldResource := testutil.MustGetTestData[datamodel.SecretStore](testFileCertValueFrom) - oldResource.Properties.Type = datamodel.SecretTypeGeneric - newResource := testutil.MustGetTestData[datamodel.SecretStore](testFileCertValueFrom) - resp, err := ValidateAndMutateRequest(context.TODO(), newResource, oldResource, nil) - require.NoError(t, err) - - // assert - r := resp.(*rest.BadRequestResponse) - require.Equal(t, "$.properties.type cannot change from 'generic' to 'certificate'.", r.Body.Error.Message) - }) - - t.Run("inherit resource id from existing resource", func(t *testing.T) { - oldResource := testutil.MustGetTestData[datamodel.SecretStore](testFileCertValueFrom) - newResource := testutil.MustGetTestData[datamodel.SecretStore](testFileCertValueFrom) - newResource.Properties.Resource = "" - resp, err := ValidateAndMutateRequest(context.TODO(), newResource, oldResource, nil) + tests := []struct { + name string + testFile string + oldResource *datamodel.SecretStore + modifyResource func(*datamodel.SecretStore, *datamodel.SecretStore) + assertions func(*testing.T, rest.Response, error, *datamodel.SecretStore, *datamodel.SecretStore) + }{ + { + name: "default type is generic", + testFile: testFileCertValueFrom, + modifyResource: func(newResource, oldResource *datamodel.SecretStore) { + newResource.Properties.Type = "" + }, + assertions: func(t *testing.T, resp rest.Response, err error, newResource, oldResource *datamodel.SecretStore) { + require.NoError(t, err) + require.Nil(t, resp) + require.Equal(t, datamodel.SecretTypeGeneric, newResource.Properties.Type) + }, + }, + { + name: "new resource, but referencing valueFrom", + testFile: testFileCertValueFrom, + modifyResource: func(newResource, oldResource *datamodel.SecretStore) { + newResource.Properties.Resource = "" + }, + assertions: func(t *testing.T, resp rest.Response, err error, newResource, oldResource *datamodel.SecretStore) { + require.NoError(t, err) + r := resp.(*rest.BadRequestResponse) + require.True(t, r.Body.Error.Message == "$.properties.data[tls.crt].Value must be given to create the secret." || + r.Body.Error.Message == "$.properties.data[tls.key].Value must be given to create the secret.") + }, + }, + { + name: "update the existing resource - type not matched", + testFile: testFileCertValueFrom, + oldResource: testutil.MustGetTestData[datamodel.SecretStore](testFileCertValueFrom), + modifyResource: func(newResource, oldResource *datamodel.SecretStore) { + oldResource.Properties.Type = datamodel.SecretTypeGeneric + }, + assertions: func(t *testing.T, resp rest.Response, err error, newResource, oldResource *datamodel.SecretStore) { + require.NoError(t, err) + r := resp.(*rest.BadRequestResponse) + require.Equal(t, "$.properties.type cannot change from 'generic' to 'certificate'.", r.Body.Error.Message) + }, + }, + { + name: "inherit resource id from existing resource", + testFile: testFileCertValueFrom, + oldResource: testutil.MustGetTestData[datamodel.SecretStore](testFileCertValueFrom), + modifyResource: func(newResource, oldResource *datamodel.SecretStore) { + newResource.Properties.Resource = "" + }, + assertions: func(t *testing.T, resp rest.Response, err error, newResource, oldResource *datamodel.SecretStore) { + require.NoError(t, err) + require.Nil(t, resp) + require.Equal(t, oldResource.Properties.Resource, newResource.Properties.Resource) + }, + }, + { + name: "new basicAuthentication resource", + testFile: testFileBasicAuthentication, + assertions: func(t *testing.T, resp rest.Response, err error, newResource, oldResource *datamodel.SecretStore) { + require.NoError(t, err) + require.Nil(t, resp) + }, + }, + { + name: "new awsIRSA resource", + testFile: testFileAWSIRSA, + assertions: func(t *testing.T, resp rest.Response, err error, newResource, oldResource *datamodel.SecretStore) { + require.NoError(t, err) + require.Nil(t, resp) + }, + }, + { + name: "new azureWorkloadIdentity resource", + testFile: testFileAzureWorkloadIdentity, + assertions: func(t *testing.T, resp rest.Response, err error, newResource, oldResource *datamodel.SecretStore) { + require.NoError(t, err) + require.Nil(t, resp) + }, + }, + { + name: "invalid basicAuthentication resource", + testFile: testFileBasicAuthenticationInvalid, + assertions: func(t *testing.T, resp rest.Response, err error, newResource, oldResource *datamodel.SecretStore) { + require.NoError(t, err) + r := resp.(*rest.BadRequestResponse) + require.True(t, r.Body.Error.Message == "$.properties.data must contain 'password' key for basicAuthentication type.") + }, + }, + } - // assert - require.NoError(t, err) - require.Nil(t, resp) - require.Equal(t, oldResource.Properties.Resource, newResource.Properties.Resource) - }) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + newResource := testutil.MustGetTestData[datamodel.SecretStore](tt.testFile) + if tt.modifyResource != nil { + tt.modifyResource(newResource, tt.oldResource) + } + resp, err := ValidateAndMutateRequest(context.TODO(), newResource, tt.oldResource, nil) + tt.assertions(t, resp, err, newResource, tt.oldResource) + }) + } } func TestUpsertSecret(t *testing.T) { diff --git a/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_awsirsa.json b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_awsirsa.json new file mode 100644 index 0000000000..eec09c3e9f --- /dev/null +++ b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_awsirsa.json @@ -0,0 +1,29 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/secretStores/secret0", + "name": "secret0", + "type": "applications.core/secretstores", + "location": "global", + "systemData": { + "createdAt": "2022-03-22T18:54:52.6857175Z", + "createdBy": "fake@hotmail.com", + "createdByType": "User", + "lastModifiedAt": "2022-03-22T18:57:52.6857175Z", + "lastModifiedBy": "fake@hotmail.com", + "lastModifiedByType": "User" + }, + "provisioningState": "Succeeded", + "properties": { + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", + "type": "awsIRSA", + "data": { + "roleARN": { + "value": "test-role-arn" + } + } + }, + "tenantId": "00000000-0000-0000-0000-000000000000", + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "resourceGroup": "testGroup", + "createdApiVersion": "2023-10-01-preview", + "updatedApiVersion": "2023-10-01-preview" +} \ No newline at end of file diff --git a/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_azwi.json b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_azwi.json new file mode 100644 index 0000000000..a05d157bfc --- /dev/null +++ b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_azwi.json @@ -0,0 +1,32 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/secretStores/secret0", + "name": "secret0", + "type": "applications.core/secretstores", + "location": "global", + "systemData": { + "createdAt": "2022-03-22T18:54:52.6857175Z", + "createdBy": "fake@hotmail.com", + "createdByType": "User", + "lastModifiedAt": "2022-03-22T18:57:52.6857175Z", + "lastModifiedBy": "fake@hotmail.com", + "lastModifiedByType": "User" + }, + "provisioningState": "Succeeded", + "properties": { + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", + "type": "azureWorkloadIdentity", + "data": { + "clientId": { + "value": "test-client-Id" + }, + "tenantId": { + "value": "test-tenant-Id" + } + } + }, + "tenantId": "00000000-0000-0000-0000-000000000000", + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "resourceGroup": "testGroup", + "createdApiVersion": "2023-10-01-preview", + "updatedApiVersion": "2023-10-01-preview" +} \ No newline at end of file diff --git a/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_basicauth.json b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_basicauth.json new file mode 100644 index 0000000000..4b4e3f1259 --- /dev/null +++ b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_basicauth.json @@ -0,0 +1,32 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/secretStores/secret0", + "name": "secret0", + "type": "applications.core/secretstores", + "location": "global", + "systemData": { + "createdAt": "2022-03-22T18:54:52.6857175Z", + "createdBy": "fake@hotmail.com", + "createdByType": "User", + "lastModifiedAt": "2022-03-22T18:57:52.6857175Z", + "lastModifiedBy": "fake@hotmail.com", + "lastModifiedByType": "User" + }, + "provisioningState": "Succeeded", + "properties": { + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", + "type": "basicAuthentication", + "data": { + "username": { + "value": "uname123" + }, + "password": { + "value": "testpwd-dGxzLmNlcnQK" + } + } + }, + "tenantId": "00000000-0000-0000-0000-000000000000", + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "resourceGroup": "testGroup", + "createdApiVersion": "2023-10-01-preview", + "updatedApiVersion": "2023-10-01-preview" +} \ No newline at end of file diff --git a/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_basicauth_invalid.json b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_basicauth_invalid.json new file mode 100644 index 0000000000..d0e7585a17 --- /dev/null +++ b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_basicauth_invalid.json @@ -0,0 +1,29 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/secretStores/secret0", + "name": "secret0", + "type": "applications.core/secretstores", + "location": "global", + "systemData": { + "createdAt": "2022-03-22T18:54:52.6857175Z", + "createdBy": "fake@hotmail.com", + "createdByType": "User", + "lastModifiedAt": "2022-03-22T18:57:52.6857175Z", + "lastModifiedBy": "fake@hotmail.com", + "lastModifiedByType": "User" + }, + "provisioningState": "Succeeded", + "properties": { + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", + "type": "basicAuthentication", + "data": { + "username": { + "value": "uname123" + } + } + }, + "tenantId": "00000000-0000-0000-0000-000000000000", + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "resourceGroup": "testGroup", + "createdApiVersion": "2023-10-01-preview", + "updatedApiVersion": "2023-10-01-preview" +} \ No newline at end of file diff --git a/pkg/corerp/frontend/controller/secretstores/types.go b/pkg/corerp/frontend/controller/secretstores/types.go index 0e53242532..34e61ffad8 100644 --- a/pkg/corerp/frontend/controller/secretstores/types.go +++ b/pkg/corerp/frontend/controller/secretstores/types.go @@ -19,4 +19,19 @@ package secretstores const ( // ResourceTypeName is the resource type name for secret stores. ResourceTypeName = "Applications.Core/secretStores" + + // UsernameKey is a required key in a secret store when SecretType is Basic Authentication. + UsernameKey = "username" + + // PasswordKey is a required key in a secret store when SecretType is Basic Authentication. + PasswordKey = "password" + + // ClientIdKey is a required key in a secret store when SecretType is Azure Workload Identity. + ClientIdKey = "clientId" + + // TenantIdKey is a required key in a secret store when SecretType is Azure workload Identity. + TenantIdKey = "tenantId" + + // RoleARNKey is a required key in a secret store when SecretType is AWS IRSA. + RoleARNKey = "roleARN" ) diff --git a/pkg/corerp/renderers/container/render.go b/pkg/corerp/renderers/container/render.go index 58670f15a3..e041d1454b 100644 --- a/pkg/corerp/renderers/container/render.go +++ b/pkg/corerp/renderers/container/render.go @@ -73,7 +73,7 @@ type Renderer struct { RoleAssignmentMap map[datamodel.IAMKind]RoleAssignmentData } -// GetDependencyIDs parses the connections, ports and volumes of a container resource to return the Radius and Azure +// GetDependencyIDs parses the connections, ports, environment variables, and volumes of a container resource to return the Radius and Azure // resource IDs. func (r Renderer) GetDependencyIDs(ctx context.Context, dm v1.DataModelInterface) (radiusResourceIDs []resources.ID, azureResourceIDs []resources.ID, err error) { resource, ok := dm.(*datamodel.ContainerResource) @@ -109,6 +109,23 @@ func (r Renderer) GetDependencyIDs(ctx context.Context, dm v1.DataModelInterface } } + // Environment variables can be sourced from secrets, which are resources. We need to iterate over the environment variables to handle any possible instances. + for _, envVars := range properties.Container.Env { + if envVars.ValueFrom != nil && envVars.ValueFrom.SecretRef != nil { + // If the string begins with a '/', it is a radius resourceID. + if strings.HasPrefix(envVars.ValueFrom.SecretRef.Source, "/") { + resourceID, err := resources.ParseResource(envVars.ValueFrom.SecretRef.Source) + if err != nil { + return nil, nil, v1.NewClientErrInvalidRequest(fmt.Sprintf("invalid source: %s. Must be either a kubernetes secret name or a valid resourceID", envVars.ValueFrom.SecretRef.Source)) + } + + if resources_radius.IsRadiusResource(resourceID) { + radiusResourceIDs = append(radiusResourceIDs, resourceID) + } + } + } + } + for _, volume := range properties.Container.Volumes { switch volume.Kind { case datamodel.Persistent: @@ -362,7 +379,10 @@ func (r Renderer) makeDeployment( } for k, v := range properties.Container.Env { - env[k] = corev1.EnvVar{Name: k, Value: v} + env[k], err = convertEnvVar(k, v, options) + if err != nil { + return []rpv1.OutputResource{}, nil, fmt.Errorf("failed to convert environment variable: %w", err) + } } // Append in sorted order @@ -616,6 +636,67 @@ func (r Renderer) makeDeployment( return outputResources, secretData, nil } +// convertEnvVar function to convert from map[string]EnvironmentVariable to map[string]corev1.EnvVar +func convertEnvVar(key string, env datamodel.EnvironmentVariable, options renderers.RenderOptions) (corev1.EnvVar, error) { + if env.Value != nil { + return corev1.EnvVar{Name: key, Value: *env.Value}, nil + } else if env.ValueFrom != nil { + // There are two cases to handle here: + // 1. The value comes from a kubernetes secret + // 2. The value comes from a Applications.Core/SecretStore resource id. + + // If the value comes from a kubernetes secret, we'll reference it. + if strings.HasPrefix(env.ValueFrom.SecretRef.Source, "/") { + secretStore, ok := options.Dependencies[env.ValueFrom.SecretRef.Source].Resource.(*datamodel.SecretStore) + if !ok { + return corev1.EnvVar{}, fmt.Errorf("failed to find source in dependencies: %s", env.ValueFrom.SecretRef.Source) + } + + // The format may be / or , as an example "default/my-secret" or "my-secret". We split the string on '/' + // and take the second part if the secret is namespace qualified. + var name string + if strings.Contains(secretStore.Properties.Resource, "/") { + parts := strings.Split(secretStore.Properties.Resource, "/") + if len(parts) == 2 { + name = parts[1] + } else { + name = secretStore.Properties.Resource + } + } else { + name = env.ValueFrom.SecretRef.Source + } + + return corev1.EnvVar{ + Name: key, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: name, + }, + Key: env.ValueFrom.SecretRef.Key, + }, + }, + }, nil + + } else { + return corev1.EnvVar{ + Name: key, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: env.ValueFrom.SecretRef.Source, + }, + Key: env.ValueFrom.SecretRef.Key, + }, + }, + }, nil + } + + } else { + return corev1.EnvVar{}, fmt.Errorf("failed to convert environment variable: %s, both value and valueFrom cannot be nil", key) + } +} + func getEnvVarsAndSecretData(resource *datamodel.ContainerResource, dependencies map[string]renderers.RendererDependency) (map[string]corev1.EnvVar, map[string][]byte, error) { env := map[string]corev1.EnvVar{} secretData := map[string][]byte{} diff --git a/pkg/corerp/renderers/container/render_test.go b/pkg/corerp/renderers/container/render_test.go index 3c1743fa8f..bdb9db0860 100644 --- a/pkg/corerp/renderers/container/render_test.go +++ b/pkg/corerp/renderers/container/render_test.go @@ -53,6 +53,13 @@ const ( envVarValue1 = "TEST_VALUE_1" envVarName2 = "TEST_VAR_2" envVarValue2 = "81" + envVarSource2 = "TEST_SOURCE_2" + envVarName3 = "TEST_VAR_3" + envVarSource3 = "/planes/radius/local/resourceGroups/test-group/providers/Applications.Core/secretStores/test-secret" + envVarValue3 = "/planes/are/cool" + envVarName4 = "TEST_VAR_4" + envVarSource4 = "test_Namespace/TEST_SOURCE_4" + envVarValue4 = "TEST_VALUE_4" secretName = "test-container" tempVolName = "TempVolume" @@ -173,6 +180,35 @@ func Test_GetDependencyIDs_Success(t *testing.T) { ContainerPort: 5000, }, }, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + ValueFrom: &datamodel.EnvironmentVariableReference{ + SecretRef: &datamodel.EnvironmentVariableSecretReference{ + Source: envVarSource2, + Key: envVarValue2, + }, + }, + }, + envVarName3: { + ValueFrom: &datamodel.EnvironmentVariableReference{ + SecretRef: &datamodel.EnvironmentVariableSecretReference{ + Source: envVarSource3, + Key: envVarValue3, + }, + }, + }, + envVarName4: { + ValueFrom: &datamodel.EnvironmentVariableReference{ + SecretRef: &datamodel.EnvironmentVariableSecretReference{ + Source: envVarSource4, + Key: envVarValue4, + }, + }, + }, + }, Volumes: map[string]datamodel.VolumeProperties{ "vol1": { Kind: datamodel.Persistent, @@ -193,12 +229,13 @@ func Test_GetDependencyIDs_Success(t *testing.T) { renderer := Renderer{} radiusResourceIDs, azureResourceIDs, err := renderer.GetDependencyIDs(ctx, resource) require.NoError(t, err) - require.Len(t, radiusResourceIDs, 2) + require.Len(t, radiusResourceIDs, 3) require.Len(t, azureResourceIDs, 1) expectedRadiusResourceIDs := []resources.ID{ makeRadiusResourceID(t, "Applications.Datastores/redisCaches", "A"), makeRadiusResourceID(t, "Applications.Datastores/redisCaches", "B"), + resources.MustParse(envVarSource3), } require.ElementsMatch(t, expectedRadiusResourceIDs, radiusResourceIDs) @@ -272,14 +309,57 @@ func Test_Render_Basic(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + ValueFrom: &datamodel.EnvironmentVariableReference{ + SecretRef: &datamodel.EnvironmentVariableSecretReference{ + Source: envVarSource2, + Key: envVarValue2, + }, + }, + }, + envVarName3: { + ValueFrom: &datamodel.EnvironmentVariableReference{ + SecretRef: &datamodel.EnvironmentVariableSecretReference{ + Source: envVarSource3, + Key: envVarValue3, + }, + }, + }, + envVarName4: { + ValueFrom: &datamodel.EnvironmentVariableReference{ + SecretRef: &datamodel.EnvironmentVariableSecretReference{ + Source: envVarSource4, + Key: envVarValue4, + }, + }, + }, }, }, } + resource := makeResource(properties) - dependencies := map[string]renderers.RendererDependency{} + dependencies := map[string]renderers.RendererDependency{ + + envVarSource3: { + ResourceID: resources.MustParse(envVarSource3), + Resource: &datamodel.SecretStore{ + BaseResource: apiv1.BaseResource{ + TrackedResource: apiv1.TrackedResource{ + ID: envVarSource3, + }, + }, + Properties: &datamodel.SecretStoreProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Application: applicationResourceID, + }, + }, + }, + }, + } ctx := testcontext.New(t) renderer := Renderer{} @@ -325,7 +405,30 @@ func Test_Render_Basic(t *testing.T) { expectedEnv := []corev1.EnvVar{ {Name: envVarName1, Value: envVarValue1}, - {Name: envVarName2, Value: envVarValue2}, + {Name: envVarName2, ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: envVarSource2, + }, + Key: envVarValue2, + }, + }}, + {Name: envVarName3, ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: envVarSource3, + }, + Key: envVarValue3, + }, + }}, + {Name: envVarName4, ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: envVarSource4, + }, + Key: envVarValue4, + }, + }}, } require.Equal(t, expectedEnv, container.Env) @@ -333,6 +436,28 @@ func Test_Render_Basic(t *testing.T) { require.Len(t, output.Resources, 4) } +func Test_Render_WithInvalidEnvironmentVariables(t *testing.T) { + properties := datamodel.ContainerProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Application: applicationResourceID, + }, + Container: datamodel.Container{ + Image: "someimage:latest", + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: {}, + }, + }, + } + resource := makeResource(properties) + dependencies := map[string]renderers.RendererDependency{} + + ctx := testcontext.New(t) + renderer := Renderer{} + _, err := renderer.Render(ctx, resource, renderers.RenderOptions{Dependencies: dependencies}) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to convert environment variable: failed to convert environment variable: TEST_VAR_1, both value and valueFrom cannot be nil") +} + func Test_Render_WithCommandArgsWorkingDir(t *testing.T) { properties := datamodel.ContainerProperties{ BasicResourceProperties: rpv1.BasicResourceProperties{ @@ -340,9 +465,13 @@ func Test_Render_WithCommandArgsWorkingDir(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, Command: []string{"command1", "command2"}, Args: []string{"arg1", "arg2"}, @@ -497,9 +626,13 @@ func Test_Render_Connections(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, }, } @@ -659,9 +792,13 @@ func Test_Render_Connections_SecretsGetHashed(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, }, } @@ -954,9 +1091,13 @@ func Test_Render_EphemeralVolumes(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, Volumes: map[string]datamodel.VolumeProperties{ tempVolName: { @@ -1244,9 +1385,13 @@ func Test_Render_ReadinessProbeHttpGet(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, ReadinessProbe: datamodel.HealthProbeProperties{ Kind: datamodel.HTTPGetHealthProbe, @@ -1323,9 +1468,13 @@ func Test_Render_ReadinessProbeTcp(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, ReadinessProbe: datamodel.HealthProbeProperties{ Kind: datamodel.TCPHealthProbe, @@ -1393,9 +1542,13 @@ func Test_Render_LivenessProbeExec(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, LivenessProbe: datamodel.HealthProbeProperties{ Kind: datamodel.ExecHealthProbe, @@ -1608,9 +1761,13 @@ func Test_Render_ImagePullPolicySpecified(t *testing.T) { Container: datamodel.Container{ Image: "someimage:latest", ImagePullPolicy: "Never", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, }, } @@ -1665,9 +1822,13 @@ func Test_Render_StrategicPatchMerge(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, }, Runtimes: &datamodel.RuntimeProperties{ @@ -1727,9 +1888,13 @@ func Test_Render_BaseManifest(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, Volumes: map[string]datamodel.VolumeProperties{ "ephemeralVolume": { @@ -1755,9 +1920,13 @@ func Test_Render_BaseManifest(t *testing.T) { }, Container: datamodel.Container{ Image: "someimage:latest", - Env: map[string]string{ - envVarName1: envVarValue1, - envVarName2: envVarValue2, + Env: map[string]datamodel.EnvironmentVariable{ + envVarName1: { + Value: to.Ptr(envVarValue1), + }, + envVarName2: { + Value: to.Ptr(envVarValue2), + }, }, }, }, diff --git a/pkg/kubeutil/client.go b/pkg/kubeutil/client.go index 593a9269c5..63cf3aad79 100644 --- a/pkg/kubeutil/client.go +++ b/pkg/kubeutil/client.go @@ -28,6 +28,9 @@ import ( "k8s.io/client-go/rest" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" csidriver "sigs.k8s.io/secrets-store-csi-driver/apis/v1alpha1" + + // Import kubernetes auth plugins + _ "k8s.io/client-go/plugin/pkg/client/auth" ) // Clients is a collection of Kubernetes clients. diff --git a/pkg/recipes/configloader/mock_secret_loader.go b/pkg/recipes/configloader/mock_secret_loader.go index 0c57f9808f..f48ad4cdc9 100644 --- a/pkg/recipes/configloader/mock_secret_loader.go +++ b/pkg/recipes/configloader/mock_secret_loader.go @@ -13,6 +13,7 @@ import ( context "context" reflect "reflect" + recipes "github.com/radius-project/radius/pkg/recipes" gomock "go.uber.org/mock/gomock" ) @@ -40,10 +41,10 @@ func (m *MockSecretsLoader) EXPECT() *MockSecretsLoaderMockRecorder { } // LoadSecrets mocks base method. -func (m *MockSecretsLoader) LoadSecrets(arg0 context.Context, arg1 map[string][]string) (map[string]map[string]string, error) { +func (m *MockSecretsLoader) LoadSecrets(arg0 context.Context, arg1 map[string][]string) (map[string]recipes.SecretData, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LoadSecrets", arg0, arg1) - ret0, _ := ret[0].(map[string]map[string]string) + ret0, _ := ret[0].(map[string]recipes.SecretData) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -61,19 +62,19 @@ type MockSecretsLoaderLoadSecretsCall struct { } // Return rewrite *gomock.Call.Return -func (c *MockSecretsLoaderLoadSecretsCall) Return(arg0 map[string]map[string]string, arg1 error) *MockSecretsLoaderLoadSecretsCall { +func (c *MockSecretsLoaderLoadSecretsCall) Return(arg0 map[string]recipes.SecretData, arg1 error) *MockSecretsLoaderLoadSecretsCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do -func (c *MockSecretsLoaderLoadSecretsCall) Do(f func(context.Context, map[string][]string) (map[string]map[string]string, error)) *MockSecretsLoaderLoadSecretsCall { +func (c *MockSecretsLoaderLoadSecretsCall) Do(f func(context.Context, map[string][]string) (map[string]recipes.SecretData, error)) *MockSecretsLoaderLoadSecretsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockSecretsLoaderLoadSecretsCall) DoAndReturn(f func(context.Context, map[string][]string) (map[string]map[string]string, error)) *MockSecretsLoaderLoadSecretsCall { +func (c *MockSecretsLoaderLoadSecretsCall) DoAndReturn(f func(context.Context, map[string][]string) (map[string]recipes.SecretData, error)) *MockSecretsLoaderLoadSecretsCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/pkg/recipes/configloader/secrets.go b/pkg/recipes/configloader/secrets.go index f6e811bb79..821f58c879 100644 --- a/pkg/recipes/configloader/secrets.go +++ b/pkg/recipes/configloader/secrets.go @@ -20,6 +20,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" + "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/ucp/resources" ) @@ -35,10 +36,18 @@ type secretsLoader struct { ArmClientOptions *arm.ClientOptions } -// LoadSecrets loads secrets from secret stores based on input map of provided secret store IDs and secret keys. -// It returns a map of secret data, where the keys are the secret store IDs and the values are maps of secret keys and their corresponding values. -func (e *secretsLoader) LoadSecrets(ctx context.Context, secretStoreIDResourceKeys map[string][]string) (secretData map[string]map[string]string, err error) { - for secretStoreID, secretKeys := range secretStoreIDResourceKeys { +// LoadSecrets loads secrets from secret stores based on input map of provided secret store IDs and secret keys filter. +// It returns a map with keys as secret store IDs and corresponding SecretData{}. +// If the input secret keys filter for a secret store ID is nil or empty, it retrieves secret data for all keys for that secret store ID. +// Eg: secretStoreKeysFilter = {"SecretStoreID1": nil} or secretKeysFilter = {"SecretStoreID1": []} will retrieve data for all secrets from "SecretStoreID1". +// --- +// When the secret keys filter is populated for a secret store ID, it retrieves secret data for the specified keys for the associated secret store ID. +// The function returns a map of secret data, where the keys are the secret store IDs and the values are maps of secret keys and their corresponding values. +// Eg; secretStoreKeysFilter = {"SecretStoreID1": ["secretkey1", "secretkey2"]} will retrieve data for only "secretkey1" and "secretkey2" keys from "SecretStoreID1". +func (e *secretsLoader) LoadSecrets(ctx context.Context, secretStoreKeysFilter map[string][]string) (secretData map[string]recipes.SecretData, err error) { + secretData = make(map[string]recipes.SecretData) + + for secretStoreID, secretKeysFilter := range secretStoreKeysFilter { secretStoreResourceID, err := resources.ParseResource(secretStoreID) if err != nil { return nil, err @@ -55,33 +64,55 @@ func (e *secretsLoader) LoadSecrets(ctx context.Context, secretStoreIDResourceKe return nil, err } - // Populate the secretData map. - secretData, err = populateSecretData(secretStoreID, secretKeys, &secrets) + // Populate secretStoreData with secret type and map of secret keys and values + secretStoreData, err := populateSecretData(secretStoreID, secretKeysFilter, &secrets) if err != nil { return nil, err } + + secretData[secretStoreID] = secretStoreData } return secretData, nil } // populateSecretData is a helper function to populate secret data from a secret store. -func populateSecretData(secretStoreID string, secretKeys []string, secrets *v20231001preview.SecretStoresClientListSecretsResponse) (map[string]map[string]string, error) { - secretData := make(map[string]map[string]string) - +// It takes a secret store ID, a filter for secret keys, and a response containing the secret data. +// It returns SecretData{} populated with secret Type and a map with secret keys and their corresponding values. +// --- +// If the secret keys filter is nil or empty, it retrieves data for all keys in the secret store. +// Eg: secretKeysFilter = nil or secretKeysFilter = [] will retrieve data for all secrets for secretStoreID. +// --- +// If the secret keys filter is populated, it retrieves data for the specified keys. +// Eg: secretKeysFilter = [secretkey1] will retrieve data for only 'secretkey1' key. +func populateSecretData(secretStoreID string, secretKeysFilter []string, secrets *v20231001preview.SecretStoresClientListSecretsResponse) (recipes.SecretData, error) { if secrets == nil { - return nil, fmt.Errorf("secrets not found for secret store ID '%s'", secretStoreID) + return recipes.SecretData{}, fmt.Errorf("secrets not found for secret store ID '%s'", secretStoreID) + } + + if secrets.Type == nil { + return recipes.SecretData{}, fmt.Errorf("secret store type is not set for secret store ID '%s'", secretStoreID) + } + + secretData := recipes.SecretData{ + Type: string(*secrets.Type), + Data: make(map[string]string), + } + + // If secretKeysFilter is nil or empty, populate secretKeysFilter with all keys + if len(secretKeysFilter) == 0 { + secretKeysFilter = make([]string, 0, len(secrets.Data)) + for secretKey := range secrets.Data { + secretKeysFilter = append(secretKeysFilter, secretKey) + } } - for _, secretKey := range secretKeys { - if secretDataValue, ok := secrets.Data[secretKey]; ok { - if secretData[secretStoreID] == nil { - secretData[secretStoreID] = make(map[string]string) - } - secretData[secretStoreID][secretKey] = *secretDataValue.Value - } else { - return nil, fmt.Errorf("a secret key was not found in secret store '%s'", secretStoreID) + for _, secretKey := range secretKeysFilter { + secretDataValue, ok := secrets.Data[secretKey] + if !ok { + return recipes.SecretData{}, fmt.Errorf("a secret key was not found in secret store '%s'", secretStoreID) } + secretData.Data[secretKey] = *secretDataValue.Value } return secretData, nil diff --git a/pkg/recipes/configloader/secrets_test.go b/pkg/recipes/configloader/secrets_test.go index a0e2f18010..5587ff25d4 100644 --- a/pkg/recipes/configloader/secrets_test.go +++ b/pkg/recipes/configloader/secrets_test.go @@ -4,25 +4,28 @@ import ( "testing" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" + "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/to" "github.com/stretchr/testify/require" ) func Test_populateSecretData(t *testing.T) { + secretStoreDataTypeGeneric := v20231001preview.SecretStoreDataTypeGeneric tests := []struct { name string secretKeys []string secrets *v20231001preview.SecretStoresClientListSecretsResponse secretStoreID string - expectedSecrets map[string]map[string]string + expectedSecrets recipes.SecretData expectError bool expectedErrMsg string }{ { - name: "success", - secretKeys: []string{"secretKey1", "secretKey2"}, + name: "success - data for input secretKey1 returned", + secretKeys: []string{"secretKey1"}, secrets: &v20231001preview.SecretStoresClientListSecretsResponse{ SecretStoreListSecretsResult: v20231001preview.SecretStoreListSecretsResult{ + Type: &secretStoreDataTypeGeneric, Data: map[string]*v20231001preview.SecretValueProperties{ "secretKey1": { Value: to.Ptr("secretValue1"), @@ -33,8 +36,31 @@ func Test_populateSecretData(t *testing.T) { }}, }, secretStoreID: "testSecretStore", - expectedSecrets: map[string]map[string]string{ - "testSecretStore": { + expectedSecrets: recipes.SecretData{ + Type: "generic", + Data: map[string]string{"secretKey1": "secretValue1"}, + }, + expectError: false, + }, + { + name: "success - data for all keys returned with nil secretKeys input", + secretKeys: nil, + secrets: &v20231001preview.SecretStoresClientListSecretsResponse{ + SecretStoreListSecretsResult: v20231001preview.SecretStoreListSecretsResult{ + Type: &secretStoreDataTypeGeneric, + Data: map[string]*v20231001preview.SecretValueProperties{ + "secretKey1": { + Value: to.Ptr("secretValue1"), + }, + "secretKey2": { + Value: to.Ptr("secretValue2"), + }, + }}, + }, + secretStoreID: "testSecretStore", + expectedSecrets: recipes.SecretData{ + Type: "generic", + Data: map[string]string{ "secretKey1": "secretValue1", "secretKey2": "secretValue2", }, @@ -42,25 +68,61 @@ func Test_populateSecretData(t *testing.T) { expectError: false, }, { - name: "fail with nil secrets input", + name: "success - returned with nil secretKeys input when no secret data exist", + secretKeys: nil, + secrets: &v20231001preview.SecretStoresClientListSecretsResponse{ + SecretStoreListSecretsResult: v20231001preview.SecretStoreListSecretsResult{ + Type: &secretStoreDataTypeGeneric, + Data: nil}, + }, + secretStoreID: "testSecretStore", + expectedSecrets: recipes.SecretData{ + Type: "generic", + Data: map[string]string{}, + }, + expectError: false, + }, + { + name: "fail - nil secrets input", secretKeys: []string{"secretKey1"}, secrets: nil, secretStoreID: "testSecretStore", - expectedSecrets: nil, + expectedSecrets: recipes.SecretData{}, expectError: true, expectedErrMsg: "secrets not found for secret store ID 'testSecretStore'", }, { - name: "missing secret key", + name: "fail - missing secret key", secretKeys: []string{"missingKey"}, secrets: &v20231001preview.SecretStoresClientListSecretsResponse{ - SecretStoreListSecretsResult: v20231001preview.SecretStoreListSecretsResult{}, + SecretStoreListSecretsResult: v20231001preview.SecretStoreListSecretsResult{ + Type: &secretStoreDataTypeGeneric, + }, }, secretStoreID: "testSecretStore", - expectedSecrets: nil, + expectedSecrets: recipes.SecretData{}, expectError: true, expectedErrMsg: "a secret key was not found in secret store 'testSecretStore'", }, + { + name: "fail - missing secret type", + secretKeys: []string{"secretKey1"}, + secrets: &v20231001preview.SecretStoresClientListSecretsResponse{ + SecretStoreListSecretsResult: v20231001preview.SecretStoreListSecretsResult{ + Data: map[string]*v20231001preview.SecretValueProperties{ + "secretKey1": { + Value: to.Ptr("secretValue1"), + }, + "secretKey2": { + Value: to.Ptr("secretValue2"), + }, + }}, + }, + secretStoreID: "testSecretStore", + expectedSecrets: recipes.SecretData{}, + expectError: true, + expectedErrMsg: "secret store type is not set for secret store ID 'testSecretStore'", + }, } for _, tt := range tests { diff --git a/pkg/recipes/configloader/types.go b/pkg/recipes/configloader/types.go index 65dff77a73..a6c2a3c825 100644 --- a/pkg/recipes/configloader/types.go +++ b/pkg/recipes/configloader/types.go @@ -31,5 +31,5 @@ type ConfigurationLoader interface { //go:generate mockgen -typed -destination=./mock_secret_loader.go -package=configloader -self_package github.com/radius-project/radius/pkg/recipes/configloader github.com/radius-project/radius/pkg/recipes/configloader SecretsLoader type SecretsLoader interface { - LoadSecrets(ctx context.Context, secretStoreIDs map[string][]string) (secretData map[string]map[string]string, err error) + LoadSecrets(ctx context.Context, secretStoreIDs map[string][]string) (secretData map[string]recipes.SecretData, err error) } diff --git a/pkg/recipes/driver/gitconfig.go b/pkg/recipes/driver/gitconfig.go index 1996246755..c23524a3df 100644 --- a/pkg/recipes/driver/gitconfig.go +++ b/pkg/recipes/driver/gitconfig.go @@ -24,6 +24,7 @@ import ( "strings" git "github.com/go-git/go-git/v5" + "github.com/radius-project/radius/pkg/recipes" ) // getGitURLWithSecrets returns the git URL with secrets information added. @@ -147,7 +148,7 @@ func GetGitURL(templatePath string) (*url.URL, error) { // addSecretsToGitConfigIfApplicable adds secrets to the Git configuration file if applicable. // It is a wrapper function to addSecretsToGitConfig() -func addSecretsToGitConfigIfApplicable(secretStoreID string, secretData map[string]map[string]string, requestDirPath string, templatePath string) error { +func addSecretsToGitConfigIfApplicable(secretStoreID string, secretData map[string]recipes.SecretData, requestDirPath string, templatePath string) error { if secretStoreID == "" || secretData == nil { return nil } @@ -157,7 +158,7 @@ func addSecretsToGitConfigIfApplicable(secretStoreID string, secretData map[stri return fmt.Errorf("secrets not found for secret store ID %q", secretStoreID) } - err := addSecretsToGitConfig(requestDirPath, secrets, templatePath) + err := addSecretsToGitConfig(requestDirPath, secrets.Data, templatePath) if err != nil { return err } @@ -167,7 +168,7 @@ func addSecretsToGitConfigIfApplicable(secretStoreID string, secretData map[stri // unsetGitConfigForDir removes a conditional include directive from the global Git configuration if applicable. // It is a wrapper function to unsetGitConfigForDir() -func unsetGitConfigForDirIfApplicable(secretStoreID string, secretData map[string]map[string]string, requestDirPath string, templatePath string) error { +func unsetGitConfigForDirIfApplicable(secretStoreID string, secretData map[string]recipes.SecretData, requestDirPath string, templatePath string) error { if secretStoreID == "" || secretData == nil { return nil } @@ -177,7 +178,7 @@ func unsetGitConfigForDirIfApplicable(secretStoreID string, secretData map[strin return fmt.Errorf("secrets not found for secret store ID %q", secretStoreID) } - err := unsetGitConfigForDir(requestDirPath, secrets, templatePath) + err := unsetGitConfigForDir(requestDirPath, secrets.Data, templatePath) if err != nil { return err } diff --git a/pkg/recipes/driver/gitconfig_test.go b/pkg/recipes/driver/gitconfig_test.go index 870e577bc9..84d24eb02f 100644 --- a/pkg/recipes/driver/gitconfig_test.go +++ b/pkg/recipes/driver/gitconfig_test.go @@ -22,6 +22,7 @@ import ( "path/filepath" "testing" + "github.com/radius-project/radius/pkg/recipes" "github.com/stretchr/testify/require" ) @@ -192,24 +193,31 @@ func Test_GetGitURL(t *testing.T) { // Additional tests exist for inner function call addSecretsToGitConfig() in TestAddConfig(). func Test_addSecretsToGitConfigIfApplicable(t *testing.T) { templatePath := "git::dev.azure.com/project/module" + secretDetails := map[string]recipes.SecretData{ + "existingID": { + Type: "generic", + Data: map[string]string{"key": "value"}, + }, + } + tests := []struct { name string secretStoreID string - secretData map[string]map[string]string + secretData map[string]recipes.SecretData expectError bool expectErrMsg string }{ { name: "Secrets not found for secret store ID", secretStoreID: "missingID", - secretData: map[string]map[string]string{"existingID": {"key": "value"}}, + secretData: secretDetails, expectError: true, expectErrMsg: "secrets not found for secret store ID \"missingID\"", }, { name: "Successful secrets addition", secretStoreID: "existingID", - secretData: map[string]map[string]string{"existingID": {"key": "value"}}, + secretData: secretDetails, expectError: false, }, { @@ -247,7 +255,7 @@ func Test_unsetGitConfigForDirIfApplicable(t *testing.T) { tests := []struct { name string secretStoreID string - secretData map[string]map[string]string + secretData map[string]recipes.SecretData expectError bool expectErrMsg string expectCallToFunc bool @@ -255,14 +263,24 @@ func Test_unsetGitConfigForDirIfApplicable(t *testing.T) { { name: "Secrets not found for secret store ID", secretStoreID: "missingID", - secretData: map[string]map[string]string{"existingID": {"key": "value"}}, - expectError: true, - expectErrMsg: "secrets not found for secret store ID \"missingID\"", + secretData: map[string]recipes.SecretData{ + "existingID": { + Type: "generic", + Data: map[string]string{"key": "value"}, + }, + }, + expectError: true, + expectErrMsg: "secrets not found for secret store ID \"missingID\"", }, { - name: "Successful call to unsetGitConfigForDir", - secretStoreID: "existingID", - secretData: map[string]map[string]string{"existingID": {"username": "test-user"}, "pat": {"pat": "ghp_token"}}, + name: "Successful call to unsetGitConfigForDir", + secretStoreID: "existingID", + secretData: map[string]recipes.SecretData{ + "existingID": { + Type: "generic", + Data: map[string]string{"username": "test-user", "pat": "ghp_token"}, + }, + }, expectError: false, expectCallToFunc: true, }, diff --git a/pkg/recipes/driver/types.go b/pkg/recipes/driver/types.go index 7adcc0e635..f61d46571a 100644 --- a/pkg/recipes/driver/types.go +++ b/pkg/recipes/driver/types.go @@ -82,7 +82,7 @@ type BaseOptions struct { // "secretKey": "secretKeyXYZ", // }, // } - Secrets map[string]map[string]string + Secrets map[string]recipes.SecretData } // ExecuteOptions is the options for the Execute method. diff --git a/pkg/recipes/engine/engine.go b/pkg/recipes/engine/engine.go index 7fd76b4ba9..5814c7bc5b 100644 --- a/pkg/recipes/engine/engine.go +++ b/pkg/recipes/engine/engine.go @@ -225,7 +225,7 @@ func (e *engine) getDriver(ctx context.Context, recipeMetadata recipes.ResourceM return definition, driver, nil } -func (e *engine) getRecipeConfigSecrets(ctx context.Context, driver recipedriver.Driver, configuration *recipes.Configuration, definition *recipes.EnvironmentDefinition) (secretData map[string]map[string]string, err error) { +func (e *engine) getRecipeConfigSecrets(ctx context.Context, driver recipedriver.Driver, configuration *recipes.Configuration, definition *recipes.EnvironmentDefinition) (secretData map[string]recipes.SecretData, err error) { driverWithSecrets, ok := driver.(recipedriver.DriverWithSecrets) if !ok { return nil, nil diff --git a/pkg/recipes/terraform/config/config.go b/pkg/recipes/terraform/config/config.go index 99839645b8..c9cae26868 100644 --- a/pkg/recipes/terraform/config/config.go +++ b/pkg/recipes/terraform/config/config.go @@ -111,7 +111,7 @@ func (cfg *TerraformConfig) Save(ctx context.Context, workingDir string) error { // It also updates module provider block if aliases exist and required_provider configuration to the file. // Save() must be called to save the generated providers config. requiredProviders contains a list of provider names // that are required for the module. -func (cfg *TerraformConfig) AddProviders(ctx context.Context, requiredProviders map[string]*RequiredProviderInfo, ucpConfiguredProviders map[string]providers.Provider, envConfig *recipes.Configuration, secrets map[string]map[string]string) error { +func (cfg *TerraformConfig) AddProviders(ctx context.Context, requiredProviders map[string]*RequiredProviderInfo, ucpConfiguredProviders map[string]providers.Provider, envConfig *recipes.Configuration, secrets map[string]recipes.SecretData) error { logger := ucplog.FromContextOrDiscard(ctx) providerConfigs, err := getProviderConfigs(ctx, requiredProviders, ucpConfiguredProviders, envConfig, secrets) if err != nil { @@ -230,7 +230,7 @@ func newModuleConfig(moduleSource string, moduleVersion string, params ...Recipe // The function returns a map where the keys are provider names and the values are slices of maps. // Each map in the slice represents a specific configuration for the corresponding provider. // This structure allows for multiple configurations per provider. -func getProviderConfigs(ctx context.Context, requiredProviders map[string]*RequiredProviderInfo, ucpConfiguredProviders map[string]providers.Provider, envConfig *recipes.Configuration, secrets map[string]map[string]string) (map[string][]map[string]any, error) { +func getProviderConfigs(ctx context.Context, requiredProviders map[string]*RequiredProviderInfo, ucpConfiguredProviders map[string]providers.Provider, envConfig *recipes.Configuration, secrets map[string]recipes.SecretData) (map[string][]map[string]any, error) { // Get recipe provider configurations from the environment configuration providerConfigs, err := providers.GetRecipeProviderConfigs(ctx, envConfig, secrets) if err != nil { diff --git a/pkg/recipes/terraform/config/providers/types.go b/pkg/recipes/terraform/config/providers/types.go index 5f55112c35..dace465f80 100644 --- a/pkg/recipes/terraform/config/providers/types.go +++ b/pkg/recipes/terraform/config/providers/types.go @@ -50,7 +50,7 @@ func GetUCPConfiguredTerraformProviders(ucpConn sdk.Connection, secretProvider * // GetRecipeProviderConfigs returns the Terraform provider configurations for Terraform providers // specified under the RecipeConfig/Terraform/Providers section under environment configuration. // The function also extracts secrets from the secrets data input and updates the provider configurations with secrets as applicable. -func GetRecipeProviderConfigs(ctx context.Context, envConfig *recipes.Configuration, secrets map[string]map[string]string) (map[string][]map[string]any, error) { +func GetRecipeProviderConfigs(ctx context.Context, envConfig *recipes.Configuration, secrets map[string]recipes.SecretData) (map[string][]map[string]any, error) { providerConfigs := make(map[string][]map[string]any) // If the provider is not configured, or has empty configuration, skip this iteration @@ -96,14 +96,14 @@ func GetRecipeProviderConfigs(ctx context.Context, envConfig *recipes.Configurat } // extractSecretsFromRecipeConfig extracts secrets for env recipe configuration from the secrets data input and updates the currentConfig map. -func extractSecretsFromRecipeConfig(recipeConfigSecrets map[string]datamodel.SecretReference, secrets map[string]map[string]string) (map[string]any, error) { +func extractSecretsFromRecipeConfig(recipeConfigSecrets map[string]datamodel.SecretReference, secrets map[string]recipes.SecretData) (map[string]any, error) { secretsConfig := make(map[string]any) // Extract secrets from configDetails if they are present for secretName, secretReference := range recipeConfigSecrets { // Extract secret value from the secrets data input if secretIDs, ok := secrets[secretReference.Source]; ok { - if secretValue, ok := secretIDs[secretReference.Key]; ok { + if secretValue, ok := secretIDs.Data[secretReference.Key]; ok { secretsConfig[secretName] = secretValue } else { return nil, fmt.Errorf("missing secret key in secret store id: %s", secretReference.Source) diff --git a/pkg/recipes/terraform/config/providers/types_test.go b/pkg/recipes/terraform/config/providers/types_test.go index f52d2bef87..9d577ef7aa 100644 --- a/pkg/recipes/terraform/config/providers/types_test.go +++ b/pkg/recipes/terraform/config/providers/types_test.go @@ -13,7 +13,7 @@ func Test_GetRecipeProviderConfigs(t *testing.T) { testCases := []struct { desc string envConfig *recipes.Configuration - secrets map[string]map[string]string + secrets map[string]recipes.SecretData expected map[string][]map[string]any }{ { @@ -132,9 +132,15 @@ func Test_GetRecipeProviderConfigs(t *testing.T) { }, }, }, - secrets: map[string]map[string]string{ - "secretstoreid1": {"secretkey1": "secretvalue1"}, - "secretstoreid2": {"secretkey2": "secretvalue2"}, + secrets: map[string]recipes.SecretData{ + "secretstoreid1": { + Type: "generic", + Data: map[string]string{"secretkey1": "secretvalue1"}, + }, + "secretstoreid2": { + Type: "generic", + Data: map[string]string{"secretkey2": "secretvalue2"}, + }, }, expected: map[string][]map[string]any{ "azurerm": { @@ -172,9 +178,15 @@ func Test_GetRecipeProviderConfigs(t *testing.T) { }, }, }, - secrets: map[string]map[string]string{ - "secretstoreid1": {"secretkey1": "secretvalue1"}, - "secretstoreid2": {"secretkey2": "secretvalue2"}, + secrets: map[string]recipes.SecretData{ + "secretstoreid1": { + Type: "generic", + Data: map[string]string{"secretkey1": "secretvalue1"}, + }, + "secretstoreid2": { + Type: "generic", + Data: map[string]string{"secretkey2": "secretvalue2"}, + }, }, expected: map[string][]map[string]any{ "azurerm": { @@ -218,10 +230,16 @@ func Test_GetRecipeProviderConfigs(t *testing.T) { }, }, }, - secrets: map[string]map[string]string{ - "secretstoreid1": {"secretkey1": "secretvalue1", - "secret-usedid-env": "secretvalue-usedid-env"}, - "secretstore-env": {"secretkey-env": "secretvalue-env"}, + secrets: map[string]recipes.SecretData{ + "secretstoreid1": { + Type: "generic", + Data: map[string]string{"secretkey1": "secretvalue1", + "secret-usedid-env": "secretvalue-usedid-env"}, + }, + "secretstore-env": { + Type: "generic", + Data: map[string]string{"secretkey-env": "secretvalue-env"}, + }, }, expected: map[string][]map[string]any{ "azurerm": { @@ -258,8 +276,11 @@ func Test_GetRecipeProviderConfigs(t *testing.T) { }, }, }, - secrets: map[string]map[string]string{ - "secretstoreid1": {"secretkey1": "secretvalue-clientid"}, + secrets: map[string]recipes.SecretData{ + "secretstoreid1": { + Type: "generic", + Data: map[string]string{"secretkey1": "secretvalue-clientid"}, + }, }, expected: map[string][]map[string]any{ "azurerm": { @@ -287,7 +308,7 @@ func Test_extractSecretsFromRecipeConfig(t *testing.T) { name string currentConfig map[string]any recipeConfigSecrets map[string]datamodel.SecretReference - secrets map[string]map[string]string + secrets map[string]recipes.SecretData expectedConfig map[string]any expectError bool expectedErrorMessage string @@ -297,8 +318,11 @@ func Test_extractSecretsFromRecipeConfig(t *testing.T) { recipeConfigSecrets: map[string]datamodel.SecretReference{ "password": {Source: "dbSecrets", Key: "dbPass"}, }, - secrets: map[string]map[string]string{ - "dbSecrets": {"dbPass": "secretPassword"}, + secrets: map[string]recipes.SecretData{ + "dbSecrets": { + Type: "generic", + Data: map[string]string{"dbPass": "secretPassword"}, + }, }, expectedConfig: map[string]any{ "password": "secretPassword", @@ -310,8 +334,11 @@ func Test_extractSecretsFromRecipeConfig(t *testing.T) { recipeConfigSecrets: map[string]datamodel.SecretReference{ "password": {Source: "missingSource", Key: "dbPass"}, }, - secrets: map[string]map[string]string{ - "dbSecrets": {"dbPass": "secretPassword"}, + secrets: map[string]recipes.SecretData{ + "dbSecrets": { + Type: "generic", + Data: map[string]string{"dbPass": "secretPassword"}, + }, }, expectError: true, expectedErrorMessage: "missing secret store id: missingSource", @@ -321,8 +348,11 @@ func Test_extractSecretsFromRecipeConfig(t *testing.T) { recipeConfigSecrets: map[string]datamodel.SecretReference{ "password": {Source: "dbSecrets", Key: "missingKey"}, }, - secrets: map[string]map[string]string{ - "dbSecrets": {"dbPass": "secretPassword"}, + secrets: map[string]recipes.SecretData{ + "dbSecrets": { + Type: "generic", + Data: map[string]string{"dbPass": "secretPassword"}, + }, }, expectError: true, expectedErrorMessage: "missing secret key in secret store id: dbSecrets", @@ -339,12 +369,29 @@ func Test_extractSecretsFromRecipeConfig(t *testing.T) { { name: "missing recipeConfigSecrets", recipeConfigSecrets: nil, - secrets: map[string]map[string]string{ - "dbSecrets": {"dbPass": "secretPassword"}, + secrets: map[string]recipes.SecretData{ + "dbSecrets": { + Type: "generic", + Data: map[string]string{"dbPass": "secretPassword"}, + }, }, expectedConfig: map[string]any{}, expectError: false, }, + { + name: "missing secrets data", + recipeConfigSecrets: map[string]datamodel.SecretReference{ + "password": {Source: "dbSecrets", Key: "missingKey"}, + }, + secrets: map[string]recipes.SecretData{ + "dbSecrets": { + Type: "generic", + }, + }, + expectedConfig: map[string]any{}, + expectError: true, + expectedErrorMessage: "missing secret key in secret store id: dbSecrets", + }, } for _, tt := range tests { diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index 1c36a022ff..4fd6709e2f 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -224,8 +224,8 @@ func (e executor) setEnvironmentVariables(tf *tfexec.Terraform, options Options) if recipeConfig != nil && recipeConfig.EnvSecrets != nil && len(recipeConfig.EnvSecrets) > 0 { for secretName, secretReference := range recipeConfig.EnvSecrets { // Extract secret value from the secrets input - if secretIDs, ok := options.Secrets[secretReference.Source]; ok { - if secretValue, ok := secretIDs[secretReference.Key]; ok { + if secretData, ok := options.Secrets[secretReference.Source]; ok { + if secretValue, ok := secretData.Data[secretReference.Key]; ok { envVarUpdate = true envVars[secretName] = secretValue } else { diff --git a/pkg/recipes/terraform/execute_test.go b/pkg/recipes/terraform/execute_test.go index 80db042c82..1a79932995 100644 --- a/pkg/recipes/terraform/execute_test.go +++ b/pkg/recipes/terraform/execute_test.go @@ -157,13 +157,16 @@ func TestSetEnvironmentVariables(t *testing.T) { }, }, }, - Secrets: map[string]map[string]string{ - "secretstoreid1": {"secretkey1": "secretvalue1"}, + Secrets: map[string]recipes.SecretData{ + "secretstoreid1": { + Type: "generic", + Data: map[string]string{"secretkey1": "secretvalue1"}, + }, }, }, }, { - name: "missing secret data", + name: "missing secret keys", opts: Options{ EnvConfig: &recipes.Configuration{ RecipeConfig: dm.RecipeConfigProperties{ @@ -181,8 +184,32 @@ func TestSetEnvironmentVariables(t *testing.T) { }, }, }, - Secrets: map[string]map[string]string{ - "secretstoreid2": {"secretkey2": "secretvalue2"}, + Secrets: map[string]recipes.SecretData{ + "secretstoreid2": { + Type: "generic", + Data: map[string]string{"secretkey2": "secretvalue2"}, + }, + }, + }, + wantErr: true, + }, + { + name: "missing secret data", + opts: Options{ + EnvConfig: &recipes.Configuration{ + RecipeConfig: dm.RecipeConfigProperties{ + EnvSecrets: map[string]dm.SecretReference{ + "TEST_ENV_VAR3": { + Source: "secretstoreid1", + Key: "secretkey1", + }, + }, + }, + }, + Secrets: map[string]recipes.SecretData{ + "secretstoreid2": { + Type: "generic", + }, }, }, wantErr: true, diff --git a/pkg/recipes/terraform/types.go b/pkg/recipes/terraform/types.go index 652b97a570..4d1959cb60 100644 --- a/pkg/recipes/terraform/types.go +++ b/pkg/recipes/terraform/types.go @@ -64,9 +64,8 @@ type Options struct { ResourceRecipe *recipes.ResourceMetadata // Secrets represents a map of secrets required for recipe execution. - // The outer map's key represents the secretStoreIDs while - // while the inner map's key-value pairs represent the [secretKey]secretValue. - Secrets map[string]map[string]string + // The map's key represents the secretStoreIDs while the value represents the secret data. + Secrets map[string]recipes.SecretData } // NewTerraform creates a working directory for Terraform execution and new Terraform executor with Terraform logs enabled. diff --git a/pkg/recipes/types.go b/pkg/recipes/types.go index 50da74df49..f61f202966 100644 --- a/pkg/recipes/types.go +++ b/pkg/recipes/types.go @@ -108,6 +108,12 @@ type RecipeOutput struct { Status *rpv1.RecipeStatus } +// SecretData represents secrets data and includes secret type and a map of secret keys to their values. +type SecretData struct { + Type string `json:"type"` + Data map[string]string `json:"data"` +} + // PrepareRecipeOutput populates the recipe output from the recipe deployment output stored in the "result" object. // outputs map is the value of "result" output from the recipe deployment response. func (ro *RecipeOutput) PrepareRecipeResponse(resultValue map[string]any) error { diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json index 62141141c6..28bfa030d6 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json @@ -2676,7 +2676,7 @@ "type": "object", "description": "environment", "additionalProperties": { - "type": "string" + "$ref": "#/definitions/EnvironmentVariable" } }, "ports": { @@ -3002,7 +3002,7 @@ "type": "object", "description": "environment", "additionalProperties": { - "type": "string" + "$ref": "#/definitions/EnvironmentVariableUpdate" } }, "ports": { @@ -3320,6 +3320,57 @@ } } }, + "EnvironmentVariable": { + "type": "object", + "description": "Environment variables type", + "properties": { + "value": { + "type": "string", + "description": "The value of the environment variable" + }, + "valueFrom": { + "$ref": "#/definitions/EnvironmentVariableReference", + "description": "The reference to the variable" + } + } + }, + "EnvironmentVariableReference": { + "type": "object", + "description": "The reference to the variable", + "properties": { + "secretRef": { + "$ref": "#/definitions/SecretReference", + "description": "The secret reference" + } + }, + "required": [ + "secretRef" + ] + }, + "EnvironmentVariableReferenceUpdate": { + "type": "object", + "description": "The reference to the variable", + "properties": { + "secretRef": { + "$ref": "#/definitions/SecretReferenceUpdate", + "description": "The secret reference" + } + } + }, + "EnvironmentVariableUpdate": { + "type": "object", + "description": "Environment variables type", + "properties": { + "value": { + "type": "string", + "description": "The value of the environment variable" + }, + "valueFrom": { + "$ref": "#/definitions/EnvironmentVariableReferenceUpdate", + "description": "The reference to the variable" + } + } + }, "EnvironmentVariables": { "type": "object", "description": "The environment variables injected during Terraform Recipe execution for the recipes in the environment.", @@ -4356,7 +4407,7 @@ }, "env": { "$ref": "#/definitions/EnvironmentVariables", - "description": "Environment variables injected during Terraform Recipe execution for the recipes in the environment." + "description": "Environment variables injected during recipe execution for the recipes in the environment, currently supported for Terraform recipes." }, "envSecrets": { "type": "object", @@ -4655,12 +4706,29 @@ "key" ] }, + "SecretReferenceUpdate": { + "type": "object", + "description": "This secret is used within a recipe. Secrets are encrypted, often have fine-grained access control, auditing and are recommended to be used to hold sensitive data.", + "properties": { + "source": { + "type": "string", + "description": "The ID of an Applications.Core/SecretStore resource containing sensitive data required for recipe execution." + }, + "key": { + "type": "string", + "description": "The key for the secret in the secret store." + } + } + }, "SecretStoreDataType": { "type": "string", "description": "The type of SecretStore data", "enum": [ "generic", - "certificate" + "certificate", + "basicAuthentication", + "azureWorkloadIdentity", + "awsIRSA" ], "x-ms-enum": { "name": "SecretStoreDataType", @@ -4675,6 +4743,21 @@ "name": "certificate", "value": "certificate", "description": "Certificate secret data type" + }, + { + "name": "basicAuthentication", + "value": "basicAuthentication", + "description": "basicAuthentication type is used to represent username and password based authentication and the secretstore resource is expected to have the keys 'username' and 'password'." + }, + { + "name": "azureWorkloadIdentity", + "value": "azureWorkloadIdentity", + "description": "azureWorkloadIdentity type is used to represent registry authentication using azure federated identity and the secretstore resource is expected to have the keys 'clientId' and 'tenantId'." + }, + { + "name": "awsIRSA", + "value": "awsIRSA", + "description": "awsIRSA type is used to represent registry authentication using AWS IRSA (IAM Roles for Service accounts) and the secretstore resource is expected to have the key 'roleARN'." } ] } diff --git a/test/functional-portable/cli/noncloud/cli_test.go b/test/functional-portable/cli/noncloud/cli_test.go index 4a79c3dc0d..48b67e09bf 100644 --- a/test/functional-portable/cli/noncloud/cli_test.go +++ b/test/functional-portable/cli/noncloud/cli_test.go @@ -69,7 +69,8 @@ func verifyRecipeCLI(ctx context.Context, t *testing.T, test rp.RPTest) { resourceType := "Applications.Datastores/redisCaches" file := "../../../testrecipes/test-bicep-recipes/corerp-redis-recipe.bicep" - target := fmt.Sprintf("br:%s/dev/test-bicep-recipes/redis-recipe:%s", registry, generateUniqueTag()) + target := fmt.Sprintf("br:%s/dev/test-bicep-recipes/redis-recipe:%s", + strings.TrimPrefix(registry, "registry="), generateUniqueTag()) recipeName := "recipeName" recipeTemplate := fmt.Sprintf("%s/recipes/local-dev/rediscaches:%s", registry, version) diff --git a/test/functional-portable/corerp/cloud/resources/persistent_volume_test.go b/test/functional-portable/corerp/cloud/resources/persistent_volume_test.go index 9722ffa6b3..5c97741fe6 100644 --- a/test/functional-portable/corerp/cloud/resources/persistent_volume_test.go +++ b/test/functional-portable/corerp/cloud/resources/persistent_volume_test.go @@ -26,6 +26,7 @@ import ( ) func Test_PersistentVolume(t *testing.T) { + t.Skip("https://github.com/radius-project/radius/issues/7853") template := "testdata/corerp-resources-volume-azure-keyvault.bicep" name := "corerp-resources-volume-azure-keyvault" appNamespace := "corerp-resources-volume-azure-keyvault-app" diff --git a/test/functional-portable/corerp/cloud/resources/storage_test.go b/test/functional-portable/corerp/cloud/resources/storage_test.go index fa1cf3b4ee..b8f945288e 100644 --- a/test/functional-portable/corerp/cloud/resources/storage_test.go +++ b/test/functional-portable/corerp/cloud/resources/storage_test.go @@ -27,6 +27,7 @@ import ( // Test_Storage tests if a container on an Azure Storage Account can be created and then deleted by the magpiego with the workload identity. func Test_Storage(t *testing.T) { + t.Skip("https://github.com/radius-project/radius/issues/7853") template := "testdata/corerp-resources-container-workload.bicep" name := "corerp-resources-container-workload" appNamespace := "azstorage-workload-app" diff --git a/test/functional-portable/corerp/cloud/resources/testdata/corerp-resources-container-workload.bicep b/test/functional-portable/corerp/cloud/resources/testdata/corerp-resources-container-workload.bicep index 13ade6ed20..2c0e66fdb8 100644 --- a/test/functional-portable/corerp/cloud/resources/testdata/corerp-resources-container-workload.bicep +++ b/test/functional-portable/corerp/cloud/resources/testdata/corerp-resources-container-workload.bicep @@ -53,7 +53,9 @@ resource container 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieimage env: { - CONNECTION_STORAGE_ACCOUNTNAME: storageAccount.name + CONNECTION_STORAGE_ACCOUNTNAME: { + value: storageAccount.name + } } readinessProbe:{ kind:'httpGet' diff --git a/test/functional-portable/corerp/noncloud/mechanics/testdata/corerp-mechanics-redeploy-withupdatedresource.step2.bicep b/test/functional-portable/corerp/noncloud/mechanics/testdata/corerp-mechanics-redeploy-withupdatedresource.step2.bicep index d882dd9381..556cca4782 100644 --- a/test/functional-portable/corerp/noncloud/mechanics/testdata/corerp-mechanics-redeploy-withupdatedresource.step2.bicep +++ b/test/functional-portable/corerp/noncloud/mechanics/testdata/corerp-mechanics-redeploy-withupdatedresource.step2.bicep @@ -25,7 +25,9 @@ resource mechanicsd 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieimage env: { - TEST: 'updated' + TEST: { + value: 'updated' + } } } } diff --git a/test/functional-portable/corerp/noncloud/resources/container_test.go b/test/functional-portable/corerp/noncloud/resources/container_test.go index 4dc8e47e53..572d20bc00 100644 --- a/test/functional-portable/corerp/noncloud/resources/container_test.go +++ b/test/functional-portable/corerp/noncloud/resources/container_test.go @@ -354,3 +354,29 @@ func Test_Container_FailDueToBadHealthProbe(t *testing.T) { test.Test(t) } + +func Test_Container_Secrets(t *testing.T) { + template := "testdata/corerp-resources-container-secrets.bicep" + name := "corerp-resources-container-secrets" + appNamespace := "corerp-resources-container-secrets" + + test := rp.NewRPTest(t, name, []rp.TestStep{ + { + Executor: step.NewDeployExecutor(template, testutil.GetMagpieImage()), + SkipKubernetesOutputResourceValidation: true, + SkipObjectValidation: true, + RPResources: &validation.RPResourceSet{ + Resources: []validation.RPResource{}, + }, + K8sObjects: &validation.K8sObjectSet{ + Namespaces: map[string][]validation.K8sObject{ + appNamespace: { + validation.NewK8sPodForResource(name, "cntr-cntr-secrets"), + }, + }, + }, + }, + }) + + test.Test(t) +} diff --git a/test/functional-portable/corerp/noncloud/resources/recipe_terraform_test.go b/test/functional-portable/corerp/noncloud/resources/recipe_terraform_test.go index d61145fa5e..20014e8e14 100644 --- a/test/functional-portable/corerp/noncloud/resources/recipe_terraform_test.go +++ b/test/functional-portable/corerp/noncloud/resources/recipe_terraform_test.go @@ -144,7 +144,7 @@ func Test_TerraformRecipe_KubernetesPostgres(t *testing.T) { appName := "corerp-resources-terraform-pg-app" envName := "corerp-resources-terraform-pg-env" extenderName := "pgs-resources-terraform-pgsapp" - secretName := "pgs-hostsecret" + secretName := "pgs-secretstore" secretResourceName := appName + "/" + secretName userName := "postgres" password := "abc-123-hgd-@#$'" diff --git a/test/functional-portable/corerp/noncloud/resources/testdata/containers/corerp-resources-friendly-container-version-1.bicep b/test/functional-portable/corerp/noncloud/resources/testdata/containers/corerp-resources-friendly-container-version-1.bicep index 865cbcb717..458f29c5e3 100644 --- a/test/functional-portable/corerp/noncloud/resources/testdata/containers/corerp-resources-friendly-container-version-1.bicep +++ b/test/functional-portable/corerp/noncloud/resources/testdata/containers/corerp-resources-friendly-container-version-1.bicep @@ -19,7 +19,9 @@ resource webapp 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieimage env: { - DBCONNECTION: redis.listSecrets().connectionString + DBCONNECTION: { + value: redis.listSecrets().connectionString + } } readinessProbe: { kind: 'httpGet' diff --git a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-container-secrets.bicep b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-container-secrets.bicep new file mode 100644 index 0000000000..ab32b5c1d3 --- /dev/null +++ b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-container-secrets.bicep @@ -0,0 +1,59 @@ + +extension radius + +@description('Specifies the image of the container resource.') +param magpieimage string + +@description('Specifies the environment for resources.') +param environment string + +resource app 'Applications.Core/applications@2023-10-01-preview' = { + name: 'corerp-resources-container-secrets' + properties: { + environment: environment + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'corerp-resources-container-secrets' + } + ] + } +} + +resource container 'Applications.Core/containers@2023-10-01-preview' = { + name: 'cntr-cntr-secrets' + properties: { + application: app.id + container: { + image: magpieimage + env: { + DB_USER: { value: 'DB_USER' } + DB_PASSWORD: { + valueFrom: { + secretRef: { + source: saltysecret.id + key: 'DB_PASSWORD' + } + } + } + } + ports: { + web: { + containerPort: 5000 + } + } + } + } + } + + resource saltysecret 'Applications.Core/secretStores@2023-10-01-preview' = { + name: 'saltysecret' + properties: { + application: app.id + data: { + DB_PASSWORD: { + value: 'password' + } + } + } + } diff --git a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-extender.bicep b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-extender.bicep index 70143fdcc5..1f6b23f3c2 100644 --- a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-extender.bicep +++ b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-extender.bicep @@ -33,9 +33,15 @@ resource container 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieimage env: { - TWILIO_NUMBER: twilio.properties.fromNumber - TWILIO_SID: twilio.listSecrets().accountSid - TWILIO_ACCOUNT: twilio.listSecrets().authToken + TWILIO_NUMBER: { + value: twilio.properties.fromNumber + } + TWILIO_SID: { + value: twilio.listSecrets().accountSid + } + TWILIO_ACCOUNT: { + value: twilio.listSecrets().authToken + } } } connections: {} diff --git a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-dns.bicep b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-dns.bicep index 298aad6acf..650ad24f02 100644 --- a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-dns.bicep +++ b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-dns.bicep @@ -79,7 +79,9 @@ resource backendcontainerdns 'Applications.Core/containers@2023-10-01-preview' = container: { image: magpieimage env: { - gatewayUrl: gateway.properties.url + gatewayUrl: { + value: gateway.properties.url + } } ports: { web: { diff --git a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-kubernetesmetadata.bicep b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-kubernetesmetadata.bicep index 4417712d88..f9f9975718 100644 --- a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-kubernetesmetadata.bicep +++ b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-kubernetesmetadata.bicep @@ -93,7 +93,9 @@ resource backendContainer 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieimage env: { - gatewayUrl: gateway.properties.url + gatewayUrl: { + value: gateway.properties.url + } } ports: { web: { diff --git a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-sslpassthrough.bicep b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-sslpassthrough.bicep index 544e594f48..85467f5b03 100644 --- a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-sslpassthrough.bicep +++ b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-gateway-sslpassthrough.bicep @@ -54,8 +54,12 @@ resource frontendContainer 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieimage env: { - TLS_KEY: tlskey - TLS_CERT: tlscrt + TLS_KEY: { + value: tlskey + } + TLS_CERT: { + value: tlscrt + } } ports: { web: { diff --git a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-simulatedenv.bicep b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-simulatedenv.bicep index 6c923bee3d..d200878ee8 100644 --- a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-simulatedenv.bicep +++ b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-simulatedenv.bicep @@ -91,7 +91,9 @@ resource backendContainer 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieimage env: { - gatewayUrl: gateway.properties.url + gatewayUrl: { + value: gateway.properties.url + } } ports: { web: { diff --git a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-terraform-postgres.bicep b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-terraform-postgres.bicep index f102bdf6db..39336803a5 100644 --- a/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-terraform-postgres.bicep +++ b/test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-terraform-postgres.bicep @@ -24,13 +24,15 @@ resource env 'Applications.Core/environments@2023-10-01-preview' = { providers: { postgresql: [ { alias: 'pgdb-test' - username: userName - password: password sslmode: 'disable' - secrets: { - host: { - source: pgshostsecret.id - key: 'host' + secrets: { + username: { + source: pgsecretstore.id + key: 'username' + } + password: { + source: pgsecretstore.id + key: 'password' } } } ] @@ -39,6 +41,12 @@ resource env 'Applications.Core/environments@2023-10-01-preview' = { env: { PGPORT: '5432' } + envSecrets: { + PGHOST: { + source: pgsecretstore.id + key: 'host' + } + } } recipes: { 'Applications.Core/extenders': { @@ -79,12 +87,18 @@ resource pgsapp 'Applications.Core/extenders@2023-10-01-preview' = { } } -resource pgshostsecret 'Applications.Core/secretStores@2023-10-01-preview' = { - name: 'pgs-hostsecret' +resource pgsecretstore 'Applications.Core/secretStores@2023-10-01-preview' = { + name: 'pgs-secretstore' properties: { - resource: 'corerp-resources-terraform-pg-app/pgs-hostsecret' + resource: 'corerp-resources-terraform-pg-app/pgs-secretstore' type: 'generic' data: { + username: { + value: userName + } + password: { + value: password + } host: { value: 'postgres.corerp-resources-terraform-pg-app.svc.cluster.local' } diff --git a/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-serviceinvocation.bicep b/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-serviceinvocation.bicep index 316b57d407..869a7a5251 100644 --- a/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-serviceinvocation.bicep +++ b/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-serviceinvocation.bicep @@ -21,7 +21,9 @@ resource frontend 'Applications.Core/containers@2023-10-01-preview' = { image: magpieimage env: { // Used by magpie to communicate with the backend. - CONNECTION_DAPRHTTP_APPID: 'backend' + CONNECTION_DAPRHTTP_APPID: { + value: 'backend' + } } readinessProbe:{ kind:'httpGet' diff --git a/test/functional-portable/datastoresrp/cloud/resources/testdata/datastoresrp-resources-microsoft-sql.bicep b/test/functional-portable/datastoresrp/cloud/resources/testdata/datastoresrp-resources-microsoft-sql.bicep index 66d13ab024..7cc28e8233 100644 --- a/test/functional-portable/datastoresrp/cloud/resources/testdata/datastoresrp-resources-microsoft-sql.bicep +++ b/test/functional-portable/datastoresrp/cloud/resources/testdata/datastoresrp-resources-microsoft-sql.bicep @@ -46,7 +46,9 @@ resource sqlapp 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieImage env: { - CONNECTION_SQL_CONNECTIONSTRING: db.listSecrets().connectionString + CONNECTION_SQL_CONNECTIONSTRING: { + value: db.listSecrets().connectionString + } } readinessProbe: { kind: 'httpGet' diff --git a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-mongodb-recipe.bicep b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-mongodb-recipe.bicep index 1f80e4083c..7286d82f2b 100644 --- a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-mongodb-recipe.bicep +++ b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-mongodb-recipe.bicep @@ -53,7 +53,9 @@ resource webapp 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieimage env: { - DBCONNECTION: recipedb.listSecrets().connectionString + DBCONNECTION: { + value: recipedb.listSecrets().connectionString + } } readinessProbe: { kind: 'httpGet' diff --git a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-redis-manual.bicep b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-redis-manual.bicep index f453e2550c..31197271b4 100644 --- a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-redis-manual.bicep +++ b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-redis-manual.bicep @@ -1,5 +1,4 @@ extension radius - param magpieimage string param environment string @@ -19,7 +18,9 @@ resource webapp 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieimage env: { - DBCONNECTION: redis.listSecrets().connectionString + DBCONNECTION: { + value: redis.listSecrets().connectionString + } } readinessProbe: { kind: 'httpGet' diff --git a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-simulatedenv-recipe.bicep b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-simulatedenv-recipe.bicep index f1585f6364..61c2989922 100644 --- a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-simulatedenv-recipe.bicep +++ b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-simulatedenv-recipe.bicep @@ -54,7 +54,9 @@ resource webapp 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieimage env: { - DBCONNECTION: recipedb.listSecrets().connectionString + DBCONNECTION: { + value: recipedb.listSecrets().connectionString + } } readinessProbe: { kind: 'httpGet' diff --git a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep index c30dd40801..1439c50243 100644 --- a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep +++ b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep @@ -46,7 +46,9 @@ resource webapp 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieImage env: { - CONNECTION_SQL_CONNECTIONSTRING: db.listSecrets().connectionString + CONNECTION_SQL_CONNECTIONSTRING: { + value: db.listSecrets().connectionString + } } readinessProbe: { kind: 'httpGet' @@ -82,9 +84,15 @@ resource sqlContainer 'Applications.Core/containers@2023-10-01-preview' = { container: { image: sqlImage env: { - ACCEPT_EULA: 'Y' - MSSQL_PID: 'Developer' - MSSQL_SA_PASSWORD: password + ACCEPT_EULA: { + value: 'Y' + } + MSSQL_PID: { + value: 'Developer' + } + MSSQL_SA_PASSWORD: { + value: password + } } ports: { sql: { diff --git a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep index 9f980e0186..dbd75d2e1b 100644 --- a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep +++ b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep @@ -71,7 +71,9 @@ resource webapp 'Applications.Core/containers@2023-10-01-preview' = { container: { image: magpieImage env: { - CONNECTION_SQL_CONNECTIONSTRING: db.listSecrets().connectionString + CONNECTION_SQL_CONNECTIONSTRING: { + value: db.listSecrets().connectionString + } } readinessProbe: { kind: 'httpGet' diff --git a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-rs-mongodb-manual.bicep b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-rs-mongodb-manual.bicep index 930382836b..e7f514e072 100644 --- a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-rs-mongodb-manual.bicep +++ b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-rs-mongodb-manual.bicep @@ -43,9 +43,15 @@ resource mongoContainer 'Applications.Core/containers@2023-10-01-preview' = { container: { image: 'ghcr.io/radius-project/mirror/mongo:4.2' env: { - DBCONNECTION: mongo.listSecrets().connectionString - MONGO_INITDB_ROOT_USERNAME: username - MONGO_INITDB_ROOT_PASSWORD: password + DBCONNECTION: { + value: mongo.listSecrets().connectionString + } + MONGO_INITDB_ROOT_USERNAME: { + value: username + } + MONGO_INITDB_ROOT_PASSWORD: { + value: password + } } ports: { mongo: { diff --git a/typespec/Applications.Core/common.tsp b/typespec/Applications.Core/common.tsp index 13aac0cadd..df8c7ae04a 100644 --- a/typespec/Applications.Core/common.tsp +++ b/typespec/Applications.Core/common.tsp @@ -17,3 +17,12 @@ limitations under the License. import "@typespec/openapi"; using OpenAPI; + +@doc("This specifies a reference to a secret. Secrets are encrypted, often have fine-grained access control, auditing and are recommended to be used to hold sensitive data.") +model SecretReference { + @doc("The ID of an Applications.Core/SecretStore resource containing sensitive data required for recipe execution.") + source: string; + + @doc("The key for the secret in the secret store.") + key: string; +} diff --git a/typespec/Applications.Core/containers.tsp b/typespec/Applications.Core/containers.tsp index aa5963bf80..c9b02cf90d 100644 --- a/typespec/Applications.Core/containers.tsp +++ b/typespec/Applications.Core/containers.tsp @@ -240,7 +240,7 @@ model Container { imagePullPolicy?: ImagePullPolicy; @doc("environment") - env?: Record; + env?: Record; @doc("container ports") ports?: Record; @@ -264,6 +264,21 @@ model Container { workingDir?: string; } +@doc("Environment variables type") +model EnvironmentVariable { + @doc("The value of the environment variable") + value?: string; + + @doc("The reference to the variable") + valueFrom?: EnvironmentVariableReference; +} + +@doc("The reference to the variable") +model EnvironmentVariableReference { + @doc("The secret reference") + secretRef: SecretReference; +} + @doc("The image pull policy for the container") enum ImagePullPolicy { @doc("Always") diff --git a/typespec/Applications.Core/environments.tsp b/typespec/Applications.Core/environments.tsp index e1eea5e636..476042b5da 100644 --- a/typespec/Applications.Core/environments.tsp +++ b/typespec/Applications.Core/environments.tsp @@ -79,7 +79,7 @@ model RecipeConfigProperties { @doc("Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment.") terraform?: TerraformConfigProperties; - @doc("Environment variables injected during Terraform Recipe execution for the recipes in the environment.") + @doc("Environment variables injected during recipe execution for the recipes in the environment, currently supported for Terraform recipes.") env?: EnvironmentVariables; @doc("Environment variables containing sensitive information can be stored as secrets. The secrets are stored in Applications.Core/SecretStores resource.") diff --git a/typespec/Applications.Core/secretstores.tsp b/typespec/Applications.Core/secretstores.tsp index 41f35a2cab..d3837bc9ce 100644 --- a/typespec/Applications.Core/secretstores.tsp +++ b/typespec/Applications.Core/secretstores.tsp @@ -68,6 +68,15 @@ enum SecretStoreDataType { @doc("Certificate secret data type") certificate, + + @doc("basicAuthentication type is used to represent username and password based authentication and the secretstore resource is expected to have the keys 'username' and 'password'.") + basicAuthentication, + + @doc("azureWorkloadIdentity type is used to represent registry authentication using azure federated identity and the secretstore resource is expected to have the keys 'clientId' and 'tenantId'.") + azureWorkloadIdentity, + + @doc("awsIRSA type is used to represent registry authentication using AWS IRSA (IAM Roles for Service accounts) and the secretstore resource is expected to have the key 'roleARN'.") + awsIRSA, } @doc("The type of SecretValue Encoding")