diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml index a9ba95b0..a5165c1a 100644 --- a/.github/workflows/e2e-test.yaml +++ b/.github/workflows/e2e-test.yaml @@ -8,9 +8,24 @@ on: - ready_for_review - labeled workflow_call: + inputs: + github_ref: + required: false + type: string + gcr-registry: + required: false + type: string + intents-operator-tag: + required: false + type: string + credentials-operator-tag: + required: false + type: string secrets: AZURE_CREDENTIALS: required: true + B64_GCLOUD_SERVICE_ACCOUNT_JSON: + required: false jobs: test-chart-deployment: @@ -19,6 +34,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # explicitly checkout helm-charts repository since this is a reusable workflow that's called from other repositories + repository: 'otterize/helm-charts' + ref: ${{ inputs.github_ref }} - name: Set up Helm uses: azure/setup-helm@v4.2.0 @@ -34,22 +53,55 @@ jobs: kubectl wait pods -n kube-system -l k8s-app=calico-node --for condition=Ready --timeout=90s kubectl wait pods -n kube-system -l k8s-app=calico-kube-controllers --for condition=Ready --timeout=90s + - name: Login to GCR + if: "${{ inputs.gcr-registry != '' }}" + uses: docker/login-action@v2 + with: + registry: ${{ inputs.gcr-registry }} + username: _json_key_base64 + password: ${{ secrets.B64_GCLOUD_SERVICE_ACCOUNT_JSON}} + + - name: Load intents-operator docker image from GCR + if: "${{ inputs.gcr-registry != '' && inputs.intents-operator-tag != ''}}" + run: |- + docker pull ${{ inputs.gcr-registry }}/intents-operator:${{ inputs.intents-operator-tag }} + minikube image load ${{ inputs.gcr-registry }}/intents-operator:${{ inputs.intents-operator-tag }} + + - name: Load credentials-operator docker image from GCR + if: "${{ inputs.gcr-registry != '' && inputs.credentials-operator-tag != ''}}" + run: |- + docker pull ${{ inputs.gcr-registry }}/credentials-operator:${{ inputs.credentials-operator-tag }} + minikube image load ${{ inputs.gcr-registry }}/credentials-operator:${{ inputs.credentials-operator-tag }} + - name: Deploy Otterize run: |- helm dep up ./otterize-kubernetes # schema validation using kubectl dry run + OPERATOR_FLAGS="" + if [ -n "${{ inputs.intents-operator-tag }}" ]; then + OPERATOR_FLAGS="$OPERATOR_FLAGS --set-string intentsOperator.operator.repository=${{ inputs.gcr-registry }} --set-string intentsOperator.operator.image=intents-operator --set-string intentsOperator.operator.tag=${{ inputs.intents-operator-tag }} --set-string intentsOperator.operator.pullPolicy=Never" + fi + if [ -n "${{ inputs.credentials-operator-tag }}" ]; then + OPERATOR_FLAGS="$OPERATOR_FLAGS --set-string credentialsOperator.operator.repository=${{ inputs.gcr-registry }} --set-string credentialsOperator.operator.image=credentials-operator --set-string credentialsOperator.operator.tag=${{ inputs.credentials-operator-tag }} --set-string credentialsOperator.operator.pullPolicy=Never" + fi + TELEMETRY_FLAG="--set global.telemetry.enabled=false" + kubectl create namespace otterize-system # required for dry-run - helm template otterize ./otterize-kubernetes -n otterize-system --set global.telemetry.enabled=false | kubectl apply --dry-run=server -f - + helm template otterize ./otterize-kubernetes -n otterize-system $OPERATOR_FLAGS $TELEMETRY_FLAG | kubectl apply --dry-run=server -f - kubectl delete namespace otterize-system # clean up # installation - helm install otterize ./otterize-kubernetes -n otterize-system --wait --create-namespace --set global.telemetry.enabled=false + helm install otterize ./otterize-kubernetes -n otterize-system --wait --create-namespace $OPERATOR_FLAGS $TELEMETRY_FLAG test-database-integrations: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 + with: + # explicitly checkout helm-charts repository since this is a reusable workflow that's called from other repositories + repository: 'otterize/helm-charts' + ref: ${{ inputs.github_ref }} - name: Start minikube uses: medyagh/setup-minikube@master @@ -57,6 +109,12 @@ jobs: - name: Set up Helm uses: azure/setup-helm@v4.2.0 + - name: Setup go + uses: actions/setup-go@v5 + with: + go-version: 1.22.1 + cache-dependency-path: tests/go.sum + - name: Set up gotestfmt uses: GoTestTools/gotestfmt-action@v2 with: @@ -66,21 +124,61 @@ jobs: - name: Helm dependency update run: helm dep up ./otterize-kubernetes + - name: Login to GCR + if: "${{ inputs.gcr-registry != '' }}" + uses: docker/login-action@v2 + with: + registry: ${{ inputs.gcr-registry }} + username: _json_key_base64 + password: ${{ secrets.B64_GCLOUD_SERVICE_ACCOUNT_JSON}} + + - name: Load intents-operator docker image from GCR + if: "${{ inputs.gcr-registry != '' && inputs.intents-operator-tag != ''}}" + run: |- + docker pull ${{ inputs.gcr-registry }}/intents-operator:${{ inputs.intents-operator-tag }} + minikube image load ${{ inputs.gcr-registry }}/intents-operator:${{ inputs.intents-operator-tag }} + + - name: Load credentials-operator docker image from GCR + if: "${{ inputs.gcr-registry != '' && inputs.credentials-operator-tag != ''}}" + run: |- + docker pull ${{ inputs.gcr-registry }}/credentials-operator:${{ inputs.credentials-operator-tag }} + minikube image load ${{ inputs.gcr-registry }}/credentials-operator:${{ inputs.credentials-operator-tag }} + - name: Run E2E tests - database integrations run: | cd tests + if [ -n "${{ inputs.intents-operator-tag }}" ]; then + export INTENTS_OPERATOR_REPOSITORY=${{ inputs.gcr-registry }} + export INTENTS_OPERATOR_TAG=${{ inputs.intents-operator-tag }} + export INTENTS_OPERATOR_IMAGE=intents-operator + fi + if [ -n "${{ inputs.credentials-operator-tag }}" ]; then + export CREDENTIALS_OPERATOR_REPOSITORY=${{ inputs.gcr-registry }} + export CREDENTIALS_OPERATOR_TAG=${{ inputs.credentials-operator-tag }} + export CREDENTIALS_OPERATOR_IMAGE=credentials-operator + fi go test -v -json ./databases/... | tee gotest.log | gotestfmt test-azure-integration: - if: contains(github.event.pull_request.labels.*.name, 'run-azure-e2e-tests') || (github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/'))) + if: contains(github.event.pull_request.labels.*.name, 'run-azure-e2e-tests') || (github.event_name == 'push' && github.repository == 'otterize/helm-charts' && startsWith(github.ref, 'refs/tags/')) timeout-minutes: 5 runs-on: ubuntu-latest concurrency: group: azure-e2e-tests # do not allow concurrent runs of this job cancel-in-progress: false steps: + - name: Fail on custom registry + if: "${{ inputs.gcr-registry != '' }}" + run: | + echo "This job does not support custom docker registry" + exit 1 + - name: Checkout repository uses: actions/checkout@v4 + with: + # explicitly checkout helm-charts repository since this is a reusable workflow that's called from other repositories + repository: 'otterize/helm-charts' + ref: ${{ inputs.github_ref }} - name: Log in with Azure uses: azure/login@v2 @@ -128,4 +226,13 @@ jobs: - name: Run E2E tests - azure integrations run: | cd tests - go test -v -json ./azureiam/... | tee gotest.log | gotestfmt \ No newline at end of file + go test -v -json ./azureiam/... | tee gotest.log | gotestfmt + + e2e-test: + needs: + - test-chart-deployment + - test-database-integrations + runs-on: ubuntu-latest + steps: + - run: |- + echo Success! This step is only here to depend on the tests. \ No newline at end of file diff --git a/intents-operator/templates/intents-operator-manager-clusterrole.yaml b/intents-operator/templates/intents-operator-manager-clusterrole.yaml index efaf4eb0..0136570b 100644 --- a/intents-operator/templates/intents-operator-manager-clusterrole.yaml +++ b/intents-operator/templates/intents-operator-manager-clusterrole.yaml @@ -113,6 +113,12 @@ rules: - get - list - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - get - apiGroups: - iam.cnrm.cloud.google.com resources: @@ -269,4 +275,4 @@ rules: - get - list - watch -{{ end }} \ No newline at end of file +{{ end }} diff --git a/tests/azureiam/azureiam_test.go b/tests/azureiam/azureiam_test.go index 90cd5015..1e96ab93 100644 --- a/tests/azureiam/azureiam_test.go +++ b/tests/azureiam/azureiam_test.go @@ -129,22 +129,16 @@ func (s *AzureIAMTestSuite) initAzureAgent() { } func (s *AzureIAMTestSuite) installOtterizeForAzureIAM() { - values := map[string]any{ - "global": map[string]any{ - "azure": map[string]any{ - "enabled": true, - "subscriptionID": s.conf.SubscriptionID, - "resourceGroup": s.conf.ResourceGroup, - "aksClusterName": s.conf.AKSClusterName, - "userAssignedIdentityID": s.conf.OtterizeOperatorUserAssignedIdentityClientID, - }, - "deployment": map[string]any{ - "networkMapper": false, - }, - "telemetry": map[string]interface{}{ - "enabled": false, - }, - }, + values := s.GetDefaultHelmChartValues() + if _, ok := values["global"]; !ok { + values["global"] = map[string]any{} + } + values["global"].(map[string]any)["azure"] = map[string]any{ + "enabled": true, + "subscriptionID": s.conf.SubscriptionID, + "resourceGroup": s.conf.ResourceGroup, + "aksClusterName": s.conf.AKSClusterName, + "userAssignedIdentityID": s.conf.OtterizeOperatorUserAssignedIdentityClientID, } s.InstallOtterizeHelmChart(values) diff --git a/tests/base_suite.go b/tests/base_suite.go index ec861b9a..8c6048ec 100644 --- a/tests/base_suite.go +++ b/tests/base_suite.go @@ -89,6 +89,59 @@ func (s *BaseSuite) SetupSuite() { }) } +func (s *BaseSuite) GetDefaultHelmChartValues() map[string]any { + defaultValues := map[string]any{ + "global": map[string]any{ + "deployment": map[string]any{ + "networkMapper": false, + }, + "telemetry": map[string]any{ + "enabled": false, + }, + }, + "intentsOperator": map[string]any{ + "debug": true, + }, + "credentialsOperator": map[string]any{ + "debug": true, + }, + } + + intentsOperatorRepository := os.Getenv("INTENTS_OPERATOR_REPOSITORY") + intentsOperatorImage := os.Getenv("INTENTS_OPERATOR_IMAGE") + intentsOperatorTag := os.Getenv("INTENTS_OPERATOR_TAG") + + if intentsOperatorTag != "" { + if _, ok := defaultValues["intentsOperator"]; !ok { + defaultValues["intentsOperator"] = map[string]any{} + } + defaultValues["intentsOperator"].(map[string]any)["operator"] = map[string]any{ + "tag": intentsOperatorTag, + "image": intentsOperatorImage, + "repository": intentsOperatorRepository, + "pullPolicy": "Never", + } + } + + credentialsOperatorRepository := os.Getenv("CREDENTIALS_OPERATOR_REPOSITORY") + credentialsOperatorImage := os.Getenv("CREDENTIALS_OPERATOR_IMAGE") + credentialsOperatorTag := os.Getenv("CREDENTIALS_OPERATOR_TAG") + + if credentialsOperatorTag != "" { + if _, ok := defaultValues["credentialsOperator"]; !ok { + defaultValues["credentialsOperator"] = map[string]any{} + } + defaultValues["credentialsOperator"].(map[string]any)["operator"] = map[string]any{ + "tag": credentialsOperatorTag, + "image": credentialsOperatorImage, + "repository": credentialsOperatorRepository, + "pullPolicy": "Never", + } + } + + return defaultValues +} + func (s *BaseSuite) InstallOtterizeHelmChart(values map[string]any) { // Load Chart.yaml chart, err := loader.Load(OtterizeKubernetesChartPath) diff --git a/tests/databases/postgres/postgres_test.go b/tests/databases/postgres/postgres_test.go index 8d0b3647..6f5617aa 100644 --- a/tests/databases/postgres/postgres_test.go +++ b/tests/databases/postgres/postgres_test.go @@ -39,7 +39,7 @@ type PostgresTestSuite struct { func (s *PostgresTestSuite) SetupSuite() { s.BaseSuite.SetupSuite() - s.installOtterizeNetworkMapperDisabled() + s.InstallOtterizeHelmChart(s.GetDefaultHelmChartValues()) s.PGServerConfClient = s.DynamicClient.Resource(schema.GroupVersionResource{ Group: "k8s.otterize.com", @@ -54,20 +54,6 @@ func (s *PostgresTestSuite) TearDownSuite() { s.UninstallOtterizeHelmChart(ctx) } -func (s *PostgresTestSuite) installOtterizeNetworkMapperDisabled() { - values := map[string]interface{}{ - "global": map[string]interface{}{ - "deployment": map[string]interface{}{ - "networkMapper": false, - }, - "telemetry": map[string]interface{}{ - "enabled": false, - }, - }, - } - s.InstallOtterizeHelmChart(values) -} - func (s *PostgresTestSuite) SetupTest() { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Minute)) defer cancel() @@ -87,8 +73,6 @@ func (s *PostgresTestSuite) SetupTest() { // Get client pod name s.clientPod = s.FindPodByLabel(ctx, s.testNamespaceName, "app=psql-client") - - s.applyPGServerConf(ctx) } func (s *PostgresTestSuite) TearDownTest() { @@ -274,7 +258,7 @@ func (s *PostgresTestSuite) deployDatabaseClient(ctx context.Context) { s.WaitForDeploymentAvailability(ctx, clientDeployment.Namespace, clientDeployment.Name) } -func (s *PostgresTestSuite) applyPGServerConf(ctx context.Context) { +func (s *PostgresTestSuite) applyPGServerConfWithInlinePassword(ctx context.Context) { pgServerConf := v1alpha3.PostgreSQLServerConfig{ TypeMeta: metav1.TypeMeta{ Kind: "PostgreSQLServerConfig", @@ -329,6 +313,8 @@ func (s *PostgresTestSuite) TestWorkloadFailsToAccessDatabase() { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) defer cancel() + s.applyPGServerConfWithInlinePassword(ctx) + logrus.Info("Validating client pod fails to access the database") s.ReadPodLogsUntilSubstring(ctx, s.clientPod, "password authentication failed") } @@ -337,6 +323,8 @@ func (s *PostgresTestSuite) TestAddSelectAndInsertPermissionsForDB() { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) defer cancel() + s.applyPGServerConfWithInlinePassword(ctx) + s.ReadPodLogsUntilSubstring(ctx, s.clientPod, "password authentication failed") s.applyIntents(ctx, []v1alpha3.DatabaseOperation{v1alpha3.DatabaseOperationInsert, v1alpha3.DatabaseOperationSelect}) @@ -350,6 +338,8 @@ func (s *PostgresTestSuite) TestInsertPermissionWithoutSelect() { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) defer cancel() + s.applyPGServerConfWithInlinePassword(ctx) + s.ReadPodLogsUntilSubstring(ctx, s.clientPod, "password authentication failed") s.applyIntents(ctx, []v1alpha3.DatabaseOperation{v1alpha3.DatabaseOperationInsert}) @@ -363,6 +353,8 @@ func (s *PostgresTestSuite) TestSelectPermissionWithoutInsert() { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) defer cancel() + s.applyPGServerConfWithInlinePassword(ctx) + s.ReadPodLogsUntilSubstring(ctx, s.clientPod, "password authentication failed") s.applyIntents(ctx, []v1alpha3.DatabaseOperation{v1alpha3.DatabaseOperationSelect}) diff --git a/tests/go.mod b/tests/go.mod index 40c8a52b..a4ae4958 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -16,7 +16,7 @@ require ( github.com/otterize/intents-operator/src v0.0.0-20240602085502-4104fcfa98d5 github.com/samber/lo v1.39.0 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/viper v1.18.2 + github.com/spf13/viper v1.16.0 github.com/stretchr/testify v1.9.0 helm.sh/helm/v3 v3.15.0-rc.1 k8s.io/api v0.30.0 @@ -131,13 +131,11 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/rubenv/sql-migrate v1.5.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/suessflorian/gqlfetch v0.6.0 // indirect @@ -152,9 +150,8 @@ require ( go.opentelemetry.io/otel/metric v1.19.0 // indirect go.opentelemetry.io/otel/trace v1.19.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.21.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/exp v0.0.0-20230124195608-d38c7dcee874 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sync v0.6.0 // indirect diff --git a/tests/go.sum b/tests/go.sum index 9a26835d..c13efad8 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -426,10 +426,6 @@ github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWx github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -442,8 +438,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -451,10 +445,12 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -530,8 +526,8 @@ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4 golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20230124195608-d38c7dcee874 h1:kWC3b7j6Fu09SnEBr7P4PuQyM0R6sqyH9R+EjIvT1nQ= +golang.org/x/exp v0.0.0-20230124195608-d38c7dcee874/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=