From 6e329f3a37db276d26a473dbabfa4d1d71e5ecce Mon Sep 17 00:00:00 2001 From: Levi Blackstone Date: Fri, 23 Jun 2023 09:36:28 -0600 Subject: [PATCH] Overhaul logic for resource diffing (#2445) This commit incorporates several significant changes to the resource diff logic. These changes are summarized below, followed by detailed explanations of each: 1. Drop usage of the "kubectl.kubernetes.io/last-applied-configuration" annotation. 2. Compute preview diffs using resource inputs rather than making a dry-run API call. 3. Automatically update `.metadata.managedFields` to work with resources that were managed with client-side apply, and later upgraded to use server-side apply. 4. Fix a bug with the diff calculation so that resource drift is detected accurately after a refresh. --- 1. Previous versions of the provider used the "kubectl.kubernetes.io/last-applied-configuration" annotation to store a copy of the configuration as part of the Kubernetes resource. This annotation was used for input diff computation in client-side apply mode. Specifically, input diffs were computed between the new inputs and the last-applied-configuration value from the previous live state. Using this annotation resulted in a large number of reported issues (30+) for the provider, and it will no longer be used. 14916d3 added logic to prune a map based on a target map. This logic is used to prune live state to match the previous inputs before performing an input diff computation. This avoids the need to store the last-applied-configuration. 2. Server-side apply mode in 3.x versions of the provider performed several network-bound API calls as part of the resource diff computation. This led to poor performance and diverged from the way that other Pulumi providers compute resource diffs. This behavior was brought in line with other providers, and now previews resource changes using the client-side inputs. Accepted changes are still updated using server-side apply. Note that this now requires the stack to be refreshed to detect resource drift in both client-side apply and server-side apply modes. 3. In server-side apply (SSA) mode, Kubernetes uses information in the `.metadata.managedFields` property to determine ownership of each field in a resource. Kubernetes v1.18+ sets the managedFields property in both client-side and server-side apply modes. Operations performed using client-side mode set a field manager with an operation type of "Update", while server-side operations use "Apply". Kubernetes treats these as separate field managers, which can lead to problems during server-side updates. To avoid this class of problems, the provider automatically takes ownership of fields managed by known client-side field managers during the first SSA-enabled update. This change should be transparent to users. See https://github.com/kubernetes/kubernetes/issues/99003 for additional details. 4. Previous versions of the provider contained a bug where previous inputs rather than live state were used to compute a diff. This prevented the provider from detecting resource drift caused by other controllers. This bug was fixed so that drift will be corrected on update after a refresh is performed. --------- Co-authored-by: Ramon Quitales --- CHANGELOG.md | 14 +- provider/go.mod | 40 +- provider/go.sum | 88 +-- provider/pkg/await/await.go | 162 ++--- provider/pkg/clients/unstructured.go | 21 + provider/pkg/provider/provider.go | 586 +++++------------- provider/pkg/provider/util.go | 8 +- provider/pkg/provider/util_test.go | 36 ++ tests/go.mod | 42 +- tests/go.sum | 88 +-- tests/sdk/dotnet/dotnet_test.go | 29 +- tests/sdk/go/go_test.go | 41 +- .../drift-correct/configmap-csa/Pulumi.yaml | 3 + .../drift-correct/configmap-csa/index.ts | 39 ++ .../drift-correct/configmap-csa/package.json | 11 + .../drift-correct/configmap-csa/tsconfig.json | 22 + .../drift-correct/configmap-ssa/Pulumi.yaml | 3 + .../drift-correct/configmap-ssa/index.ts | 39 ++ .../drift-correct/configmap-ssa/package.json | 11 + .../drift-correct/configmap-ssa/tsconfig.json | 22 + tests/sdk/nodejs/examples/examples_test.go | 27 +- tests/sdk/nodejs/nodejs_test.go | 206 +++++- .../server-side-apply-upgrade/step1/index.ts | 3 +- .../server-side-apply-upgrade/step2/index.ts | 3 +- .../server-side-apply-upgrade/step3/index.ts | 6 +- tests/sdk/python/guestbook-old/__main__.py | 12 +- tests/sdk/python/guestbook/__main__.py | 12 +- tests/sdk/python/provider-old/__main__.py | 2 +- tests/sdk/python/provider/__main__.py | 2 +- tests/sdk/python/python_test.go | 17 +- tests/sdk/python/smoke-test-old/__main__.py | 2 +- tests/sdk/python/smoke-test/__main__.py | 2 +- tests/sdk/python/yaml-test/manifest.yaml | 5 + 33 files changed, 905 insertions(+), 699 deletions(-) create mode 100644 tests/sdk/nodejs/drift-correct/configmap-csa/Pulumi.yaml create mode 100644 tests/sdk/nodejs/drift-correct/configmap-csa/index.ts create mode 100644 tests/sdk/nodejs/drift-correct/configmap-csa/package.json create mode 100644 tests/sdk/nodejs/drift-correct/configmap-csa/tsconfig.json create mode 100644 tests/sdk/nodejs/drift-correct/configmap-ssa/Pulumi.yaml create mode 100644 tests/sdk/nodejs/drift-correct/configmap-ssa/index.ts create mode 100644 tests/sdk/nodejs/drift-correct/configmap-ssa/package.json create mode 100644 tests/sdk/nodejs/drift-correct/configmap-ssa/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 709f24f703..b2cc47f33d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,22 +4,22 @@ Breaking changes: -- Enable Server-side Apply by default (https://github.com/pulumi/pulumi-kubernetes/pull/2398) - Remove deprecated enableDryRun provider flag (https://github.com/pulumi/pulumi-kubernetes/pull/2400) - Remove deprecated helm/v2 SDK (https://github.com/pulumi/pulumi-kubernetes/pull/2396) - Remove deprecated enableReplaceCRD provider flag (https://github.com/pulumi/pulumi-kubernetes/pull/2402) - Drop support for Kubernetes clusters older than v1.13 (https://github.com/pulumi/pulumi-kubernetes/pull/2414) - Make all resource output properties required (https://github.com/pulumi/pulumi-kubernetes/pull/2422) -- Drop support for legacy pulumi.com/initialApiVersion annotation (https://github.com/pulumi/pulumi-kubernetes/pull/2443) Other changes: -- Automatically fall back to client-side preview if server-side preview fails (https://github.com/pulumi/pulumi-kubernetes/pull/2419) - Enable Server-side Apply by default (https://github.com/pulumi/pulumi-kubernetes/pull/2398) -- Remove deprecated enableDryRun provider flag (https://github.com/pulumi/pulumi-kubernetes/pull/2400) -- Remove deprecated helm/v2 SDK (https://github.com/pulumi/pulumi-kubernetes/pull/2396) -- Remove deprecated enableReplaceCRD provider flag (https://github.com/pulumi/pulumi-kubernetes/pull/2402) -- Drop support for Kubernetes clusters older than v1.13 (https://github.com/pulumi/pulumi-kubernetes/pull/2414) +- Automatically fall back to client-side preview if server-side preview fails (https://github.com/pulumi/pulumi-kubernetes/pull/2419) +- Drop support for legacy pulumi.com/initialApiVersion annotation (https://github.com/pulumi/pulumi-kubernetes/pull/2443) +- Overhaul logic for resource diffing (https://github.com/pulumi/pulumi-kubernetes/pull/2445) + - Drop usage of the "kubectl.kubernetes.io/last-applied-configuration" annotation. + - Compute preview diffs using resource inputs rather than making a dry-run API call. + - Automatically update .metadata.managedFields to work with resources that were managed with client-side apply, and later upgraded to use server-side apply. + - Fix a bug with the diff calculation so that resource drift is detected accurately after a refresh. ## 3.30.2 (July 11, 2023) diff --git a/provider/go.mod b/provider/go.mod index 7343fec990..5a753e9399 100644 --- a/provider/go.mod +++ b/provider/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( github.com/ahmetb/go-linq v3.0.0+incompatible github.com/evanphx/json-patch v5.6.0+incompatible + github.com/fluxcd/pkg/ssa v0.28.1 github.com/golang/protobuf v1.5.3 github.com/google/gnostic v0.5.7-v3refs github.com/imdario/mergo v0.3.14 @@ -18,11 +19,11 @@ require ( google.golang.org/grpc v1.55.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.12.0 - k8s.io/api v0.27.1 - k8s.io/apimachinery v0.27.1 + k8s.io/api v0.27.2 + k8s.io/apimachinery v0.27.2 k8s.io/cli-runtime v0.27.1 - k8s.io/client-go v0.27.1 - k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a + k8s.io/client-go v0.27.2 + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f k8s.io/kubectl v0.27.1 sigs.k8s.io/kustomize/api v0.13.2 sigs.k8s.io/kustomize/kyaml v0.14.1 @@ -92,7 +93,7 @@ require ( github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-gorp/gorp/v3 v3.0.5 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect github.com/go-openapi/swag v0.22.3 // indirect @@ -168,10 +169,10 @@ require ( github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pkg/term v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/prometheus/client_golang v1.15.1 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rubenv/sql-migrate v1.3.1 // indirect @@ -201,13 +202,13 @@ require ( go.uber.org/atomic v1.9.0 // indirect gocloud.dev v0.27.0 // indirect gocloud.dev/secrets/hashivault v0.27.0 // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect - golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect + golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.110.0 // indirect google.golang.org/appengine v1.6.7 // indirect @@ -217,9 +218,9 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.27.1 // indirect - k8s.io/apiserver v0.27.1 // indirect - k8s.io/component-base v0.27.1 // indirect + k8s.io/apiextensions-apiserver v0.27.2 // indirect + k8s.io/apiserver v0.27.2 // indirect + k8s.io/component-base v0.27.2 // indirect k8s.io/klog/v2 v2.90.1 // indirect k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect oras.land/oras-go v1.2.2 // indirect @@ -257,6 +258,7 @@ require ( github.com/docker/cli v20.10.21+incompatible // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/emicklei/go-restful/v3 v3.10.1 // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.4.0 // indirect github.com/go-git/go-git/v5 v5.6.0 // indirect @@ -287,6 +289,8 @@ require ( go.opentelemetry.io/otel v1.14.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect golang.org/x/mod v0.10.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.1 // indirect lukechampine.com/frand v1.4.2 // indirect + sigs.k8s.io/cli-utils v0.34.0 // indirect + sigs.k8s.io/controller-runtime v0.15.0 // indirect ) diff --git a/provider/go.sum b/provider/go.sum index d75ea397a7..433746ad05 100644 --- a/provider/go.sum +++ b/provider/go.sum @@ -637,6 +637,8 @@ github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -650,6 +652,8 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fluxcd/pkg/ssa v0.28.1 h1:h5r5irAgDjgkmIqagOLOa/U7/Rx2fT2NKIb+vDTYOMg= +github.com/fluxcd/pkg/ssa v0.28.1/go.mod h1:o55eBzWz7P/tqnCn5c622RZvjTP/GqvitqZUbsMIRwk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= @@ -665,6 +669,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 h1:Uc+IZ7gYqAf/rSGFplbWBSHaGolEQlNLgMgSE3ccnIQ= @@ -713,11 +718,13 @@ github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= @@ -763,8 +770,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= @@ -1448,7 +1455,7 @@ github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1ls github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1459,7 +1466,7 @@ github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1561,8 +1568,8 @@ github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -1570,8 +1577,8 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1586,8 +1593,9 @@ github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+ github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/common/assets v0.1.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI= github.com/prometheus/common/assets v0.2.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= @@ -1605,8 +1613,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY= github.com/prometheus/prometheus v0.37.0/go.mod h1:egARUgz+K93zwqsVIAneFlLZefyGOON44WyAp4Xqbbk= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= @@ -1915,6 +1923,7 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -1922,6 +1931,7 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= gocloud.dev v0.27.0 h1:j0WTUsnKTxCsWO7y8T+YCiBZUmLl9w/WIowqAY3yo0g= gocloud.dev v0.27.0/go.mod h1:YlYKhYsY5/1JdHGWQDkAuqkezVKowu7qbe9aIeUF6p0= gocloud.dev/secrets/hashivault v0.27.0 h1:AAeGJXr0tiHHJgg5tL8atOGktB4eK9EJAqkZbPKAcOo= @@ -2097,8 +2107,8 @@ golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2141,8 +2151,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2295,8 +2305,8 @@ 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.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2306,8 +2316,8 @@ golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2332,8 +2342,9 @@ golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2421,8 +2432,8 @@ golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2434,6 +2445,7 @@ golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNq golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -2730,10 +2742,10 @@ k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= -k8s.io/api v0.27.1 h1:Z6zUGQ1Vd10tJ+gHcNNNgkV5emCyW+v2XTmn+CLjSd0= -k8s.io/api v0.27.1/go.mod h1:z5g/BpAiD+f6AArpqNjkY+cji8ueZDU/WV1jcj5Jk4E= -k8s.io/apiextensions-apiserver v0.27.1 h1:Hp7B3KxKHBZ/FxmVFVpaDiXI6CCSr49P1OJjxKO6o4g= -k8s.io/apiextensions-apiserver v0.27.1/go.mod h1:8jEvRDtKjVtWmdkhOqE84EcNWJt/uwF8PC4627UZghY= +k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo= +k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4= +k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo= +k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= @@ -2741,14 +2753,14 @@ k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apimachinery v0.27.1 h1:EGuZiLI95UQQcClhanryclaQE6xjg1Bts6/L3cD7zyc= -k8s.io/apimachinery v0.27.1/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM= +k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg= +k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/apiserver v0.27.1 h1:phY+BtXjjzd+ta3a4kYbomC81azQSLa1K8jo9RBw7Lg= -k8s.io/apiserver v0.27.1/go.mod h1:UGrOjLY2KsieA9Fw6lLiTObxTb8Z1xEba4uqSuMY0WU= +k8s.io/apiserver v0.27.2 h1:p+tjwrcQEZDrEorCZV2/qE8osGTINPuS5ZNqWAvKm5E= +k8s.io/apiserver v0.27.2/go.mod h1:EsOf39d75rMivgvvwjJ3OW/u9n1/BmUMK5otEOJrb1Y= k8s.io/cli-runtime v0.27.1 h1:MMzp5Q/Xmr5L1Lrowuc+Y/r95XINC6c6/fE3aN7JDRM= k8s.io/cli-runtime v0.27.1/go.mod h1:tEbTB1XP/nTH3wujsi52bw91gWpErtWiS15R6CwYsAI= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= @@ -2757,15 +2769,15 @@ k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30= -k8s.io/client-go v0.27.1 h1:oXsfhW/qncM1wDmWBIuDzRHNS2tLhK3BZv512Nc59W8= -k8s.io/client-go v0.27.1/go.mod h1:f8LHMUkVb3b9N8bWturc+EDtVVVwZ7ueTVquFAJb2vA= +k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE= +k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ= k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= -k8s.io/component-base v0.27.1 h1:kEB8p8lzi4gCs5f2SPU242vOumHJ6EOsOnDM3tTuDTM= -k8s.io/component-base v0.27.1/go.mod h1:UGEd8+gxE4YWoigz5/lb3af3Q24w98pDseXcXZjw+E0= +k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo= +k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= @@ -2792,8 +2804,8 @@ k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2R k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a h1:gmovKNur38vgoWfGtP5QOGNOA7ki4n6qNYoFAgMlNvg= -k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/kubectl v0.27.1 h1:9T5c5KdpburYiW8XKQSH0Uly1kMNE90aGSnbYUZNdcA= k8s.io/kubectl v0.27.1/go.mod h1:QsAkSmrRsKTPlAFzF8kODGDl4p35BIwQnc9XFhkcsy8= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= @@ -2818,6 +2830,10 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= +sigs.k8s.io/cli-utils v0.34.0/go.mod h1:EXyMwPMu9OL+LRnj0JEMsGG/fRvbgFadcVlSnE8RhFs= +sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= +sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/provider/pkg/await/await.go b/provider/pkg/await/await.go index 0d1d61a486..8717958cf9 100644 --- a/provider/pkg/await/await.go +++ b/provider/pkg/await/await.go @@ -16,10 +16,11 @@ package await import ( "context" + "encoding/json" "fmt" "os" - "regexp" + fluxssa "github.com/fluxcd/pkg/ssa" "github.com/pulumi/pulumi-kubernetes/provider/v3/pkg/clients" "github.com/pulumi/pulumi-kubernetes/provider/v3/pkg/cluster" "github.com/pulumi/pulumi-kubernetes/provider/v3/pkg/kinds" @@ -142,11 +143,6 @@ func skipRetry(gvk schema.GroupVersionKind, k8sVersion *cluster.ServerVersion, e const ssaConflictDocLink = "https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/managing-resources-with-server-side-apply/#handle-field-conflicts-on-existing-resources" -// Resources created with Client-side Apply (CSA) will assign a field manager with an operation of type "Update" that -// conflicts with Server-side Apply (SSA) field managers that use type "Apply". This regex is used to check for known -// CSA field manager names to determine if the apply operation should be retried. -var csaConflictRegex = regexp.MustCompile(`conflict with "(pulumi-resource-kubernetes|pulumi-resource-kubernetes.exe|pulumi-kubernetes)"`) - // Creation (as the usage, `await.Creation`, implies) will block until one of the following is true: // (1) the Kubernetes resource is reported to be initialized; (2) the initialization timeout has // occurred; or (3) an error has occurred while the resource was being initialized. @@ -188,8 +184,7 @@ func Creation(c CreateConfig) (*unstructured.Unstructured, error) { } if c.ServerSideApply { - // Always force on preview to avoid erroneous conflict errors for resource replacements - force := c.Preview || patchForce(c.Inputs) + force := patchForce(c.Inputs, nil, c.Preview) options := metav1.PatchOptions{ FieldManager: c.FieldManager, Force: &force, @@ -342,14 +337,9 @@ func Read(c ReadConfig) (*unstructured.Unstructured, error) { return live, nil } -// Update takes `lastSubmitted` (the last version of a Kubernetes API object submitted to the API -// server) and `currentSubmitted` (the version of the Kubernetes API object being submitted for an -// update currently) and blocks until one of the following is true: (1) the Kubernetes resource is -// reported to be updated; (2) the update timeout has occurred; or (3) an error has occurred while -// the resource was being updated. -// -// Update updates an existing resource with new values. Currently, this client supports the -// Kubernetes-standard three-way JSON patch, and the newer Server-side Apply patch. See references [1], [2], [3]. +// Update updates an existing resource with new values. This client uses a Server-side Apply (SSA) patch by default, but +// also supports the older three-way JSON patch and the strategic merge patch as fallback options. +// See references [1], [2], [3]. // // nolint // [1]: @@ -359,48 +349,6 @@ func Read(c ReadConfig) (*unstructured.Unstructured, error) { // [3]: // https://kubernetes.io/docs/reference/using-api/server-side-apply func Update(c UpdateConfig) (*unstructured.Unstructured, error) { - // - // TREAD CAREFULLY. The semantics of a Kubernetes update are subtle, and you should proceed to - // change them only if you understand them deeply. - // - // Briefly: when a user updates an existing resource definition (e.g., by modifying YAML), the API - // server must decide how to apply the changes inside it, to the version of the resource that it - // has stored in etcd. In Kubernetes this decision is turns out to be quite complex. `kubectl` - // currently uses the three-way "strategic merge" and falls back to the three-way JSON merge. We - // currently support the second, but eventually we'll have to support the first, too. - // - // (NOTE: This comment is scoped to the question of how to patch an existing resource, rather than - // how to recognize when a resource needs to be re-created from scratch.) - // - // There are several reasons for this complexity: - // - // * It's important not to clobber fields set or default-set by the server (e.g., NodePort, - // namespace, service type, etc.), or by out-of-band tooling like admission controllers - // (which, e.g., might do something like add a sidecar to a container list). - // * For example, consider a scenario where a user renames a container. It is a reasonable - // expectation the old version of the container gets destroyed when the update is applied. And - // if the update strategy is set to three-way JSON merge patching, it is. - // * But, consider if their administrator has set up (say) the Istio admission controller, which - // embeds a sidecar container in pods submitted to the API. This container would not be present - // in the YAML file representing that pod, but when an update is applied by the user, they - // not want it to get destroyed. And, so, when the strategy is set to three-way strategic - // merge, the container is not destroyed. (With this strategy, fields can have "merge keys" as - // part of their schema, which tells the API server how to merge each particular field.) - // - // What's worse is, currently nearly all of this logic exists on the client rather than the - // server, though there is work moving forward to move this to the server. - // - // So the roadmap is: - // - // - [x] Implement `Update` using the three-way JSON merge strategy. - // - [x] Cause `Update` to default to the three-way JSON merge patch strategy. (This will require - // plumbing, because it expects nominal types representing the API schema, but the - // discovery client is completely dynamic.) - // - [x] Support server-side apply. - // - // In the next major release, we will default to using Server-side Apply, which will simplify this logic. - // - client, err := c.ClientSet.ResourceClientForObject(c.Inputs) if err != nil { return nil, err @@ -427,11 +375,18 @@ func Update(c UpdateConfig) (*unstructured.Unstructured, error) { } } else { if c.ServerSideApply { + if !c.Preview { + err = fixCSAFieldManagers(c, liveOldObj, client) + if err != nil { + return nil, err + } + } + objYAML, err := yaml.Marshal(c.Inputs.Object) if err != nil { return nil, err } - force := patchForce(c.Inputs) + force := patchForce(c.Inputs, liveOldObj, c.Preview) options := metav1.PatchOptions{ FieldManager: c.FieldManager, Force: &force, @@ -443,23 +398,10 @@ func Update(c UpdateConfig) (*unstructured.Unstructured, error) { // Issue patch request. // NOTE: We can use the same client because if the `kind` changes, this will cause // a replace (i.e., destroy and create). - maybeRetry := true for { currentOutputs, err = client.Patch(c.Context, c.Inputs.GetName(), types.ApplyPatchType, objYAML, options) if err != nil { if errors.IsConflict(err) { - if maybeRetry { - // If the patch failed, use heuristics to determine if the resource was created using - // Client-side Apply. If so, retry once with the force patch option. - if csaConflictRegex.MatchString(err.Error()) && - metadata.GetLabel(c.Inputs, metadata.LabelManagedBy) == "pulumi" { - force = true - options.Force = &force - maybeRetry = false // Only retry once - continue - } - } - err = fmt.Errorf("Server-Side Apply field conflict detected. see %s for troubleshooting help\n: %w", ssaConflictDocLink, err) } @@ -547,6 +489,60 @@ func Update(c UpdateConfig) (*unstructured.Unstructured, error) { return live, nil } +// fixCSAFieldManagers patches the field managers for an existing resource that was managed using client-side apply. +// The new server-side apply field manager takes ownership of all these fields to avoid conflicts. +func fixCSAFieldManagers(c UpdateConfig, live *unstructured.Unstructured, client dynamic.ResourceInterface) error { + if managedFields := live.GetManagedFields(); len(managedFields) > 0 { + patches, err := fluxssa.PatchReplaceFieldsManagers(live, []fluxssa.FieldManager{ + { + // take ownership of changes made with 'kubectl apply --server-side --force-conflicts' + Name: "kubectl", + OperationType: metav1.ManagedFieldsOperationApply, + }, + { + // take ownership of changes made with 'kubectl apply' + Name: "kubectl", + OperationType: metav1.ManagedFieldsOperationUpdate, + }, + { + // take ownership of changes made with 'kubectl apply' + Name: "before-first-apply", + OperationType: metav1.ManagedFieldsOperationUpdate, + }, + // The following are possible field manager values for resources that were created using this provider under + // CSA mode. Note the "Update" operation type, which Kubernetes treats as a separate field manager even if + // the name is identical. See https://github.com/kubernetes/kubernetes/issues/99003 + { + // take ownership of changes made with pulumi-kubernetes CSA + Name: "pulumi-kubernetes", + OperationType: metav1.ManagedFieldsOperationUpdate, + }, + { + // take ownership of changes made with pulumi-kubernetes CSA + Name: "pulumi-kubernetes.exe", + OperationType: metav1.ManagedFieldsOperationUpdate, + }, + { + // take ownership of changes made with pulumi-kubernetes CSA + Name: "pulumi-resource-kubernetes", + OperationType: metav1.ManagedFieldsOperationUpdate, + }, + }, c.FieldManager) + if err != nil { + return err + } + patch, err := json.Marshal(patches) + if err != nil { + return err + } + live, err = client.Patch(c.Context, live.GetName(), types.JSONPatchType, patch, metav1.PatchOptions{}) + if err != nil { + return err + } + } + return nil +} + // Deletion (as the usage, `await.Deletion`, implies) will block until one of the following is true: // (1) the Kubernetes resource is reported to be deleted; (2) the initialization timeout has // occurred; or (3) an error has occurred while the resource was being deleted. @@ -712,12 +708,30 @@ func clearStatus(context context.Context, host *pulumiprovider.HostClient, urn r } // patchForce decides whether to overwrite patch conflicts. -func patchForce(obj *unstructured.Unstructured) bool { - if metadata.IsAnnotationTrue(obj, metadata.AnnotationPatchForce) { +func patchForce(inputs, live *unstructured.Unstructured, preview bool) bool { + if metadata.IsAnnotationTrue(inputs, metadata.AnnotationPatchForce) { return true } if enabled, exists := os.LookupEnv("PULUMI_K8S_ENABLE_PATCH_FORCE"); exists { return enabled == "true" } + if preview { + // Always force on preview if no previous state to avoid erroneous conflict errors for resource replacements. + if live == nil { + return true + } + // If the resource includes a CSA field manager for this provider, then force the update. Field managers will be + // adjusted before this on real updates, so only force on preview. + for _, managedField := range live.GetManagedFields() { + if managedField.Operation != metav1.ManagedFieldsOperationUpdate { + continue + } + switch managedField.Manager { + case "pulumi-resource-kubernetes", "pulumi-kubernetes", "pulumi-kubernetes.exe": + return true + } + } + } + return false } diff --git a/provider/pkg/clients/unstructured.go b/provider/pkg/clients/unstructured.go index e13ea3ca0f..97c336c59d 100644 --- a/provider/pkg/clients/unstructured.go +++ b/provider/pkg/clients/unstructured.go @@ -50,6 +50,27 @@ func FromUnstructured(uns *unstructured.Unstructured) (metav1.Object, error) { return metaObj, nil } +// ToUnstructured converts a typed Kubernetes resource into the Unstructured equivalent. +func ToUnstructured(object metav1.Object) (*unstructured.Unstructured, error) { + result, err := runtime.DefaultUnstructuredConverter.ToUnstructured(object) + if err != nil { + return nil, err + } + return &unstructured.Unstructured{ + Object: result, + }, nil +} + +// Normalize converts an Unstructured Kubernetes resource into the typed equivalent and then back to Unstructured. +// This process normalizes semantically-equivalent resources into an identical output, which is important for diffing. +func Normalize(uns *unstructured.Unstructured) (*unstructured.Unstructured, error) { + obj, err := FromUnstructured(uns) + if err != nil { + return nil, err + } + return ToUnstructured(obj) +} + func PodFromUnstructured(uns *unstructured.Unstructured) (*corev1.Pod, error) { const expectedAPIVersion = "v1" diff --git a/provider/pkg/provider/provider.go b/provider/pkg/provider/provider.go index 6f35d57c72..362458093a 100644 --- a/provider/pkg/provider/provider.go +++ b/provider/pkg/provider/provider.go @@ -35,7 +35,6 @@ import ( "github.com/golang/protobuf/ptypes/empty" pbempty "github.com/golang/protobuf/ptypes/empty" structpb "github.com/golang/protobuf/ptypes/struct" - "github.com/imdario/mergo" pkgerrors "github.com/pkg/errors" checkjob "github.com/pulumi/cloud-ready-checks/pkg/kubernetes/job" "github.com/pulumi/pulumi-kubernetes/provider/v3/pkg/await" @@ -65,7 +64,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientapi "k8s.io/client-go/tools/clientcmd/api" @@ -1291,6 +1289,11 @@ func (k *kubeProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) ( oldInputs := propMapToUnstructured(olds) newInputs := propMapToUnstructured(news) + newInputs, err = normalize(newInputs) + if err != nil { + return nil, err + } + if k.serverSideApplyMode && isPatchURN(urn) { if len(newInputs.GetName()) == 0 { return nil, fmt.Errorf("patch resources require the resource `.metadata.name` to be set") @@ -1463,17 +1466,24 @@ func (k *kubeProvider) helmHookWarning(ctx context.Context, newInputs *unstructu // Diff checks what impacts a hypothetical update will have on the resource's properties. func (k *kubeProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { // - // Behavior as of v0.12.x: We take 2 inputs: + // Behavior as of v4.0: We take 2 inputs: // // 1. req.News, the new resource inputs, i.e., the property bag coming from a custom resource like // k8s.core.v1.Service - // 2. req.Olds, the old _state_ returned by a `Create` or an `Update`. The old state has the form - // {inputs: {...}, live: {...}}, and is a struct that contains the old inputs as well as the - // last computed value obtained from the Kubernetes API server. + // 2. req.Olds, the old _state_ returned by a `Create` or an `Update`. // - // The list of properties that would cause replacement is then computed between the old and new - // _inputs_, as in Kubernetes this captures changes the user made that result in replacement - // (which is not true of the old computed values). + // Kubernetes sets many additional fields that are not present in the resource inputs. We want to compare new inputs + // to the most recent "live" values for the resource, but also don't want to show diffs for fields that are managed + // by the cluster. We accomplish this by pruning the live state to match the shape of the old inputs, and then + // comparing between the "pruned live" inputs and the new inputs. + // + // Note that comparing the old inputs to the new inputs will miss resource drift caused by other controllers + // modifying the resources because the Pulumi inputs have not changed. Prior versions (pre-4.0) of the provider + // used the "kubectl.kubernetes.io/last-applied-configuration" annotation to partially work around this problem. + // This annotation was updated by the provider and some `kubectl` commands to capture the most recent set of inputs + // used to produce the current resource state. When the annotation was present, this value was used instead of the + // old inputs for the diff computation. This approach led to many problems, so the v4.0 release of the provider + // removed the use of this annotation in favor of the "pruned live" input approach. // urn := resource.URN(req.GetUrn()) @@ -1481,8 +1491,7 @@ func (k *kubeProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*p label := fmt.Sprintf("%s.Diff(%s)", k.label(), urn) logger.V(9).Infof("%s executing", label) - // Get old state. This is an object of the form {inputs: {...}, live: {...}} where `inputs` is the - // previous resource inputs supplied by the user, and `live` is the computed state of that inputs + // Get old state. This is an object that includes `inputs` previously supplied by the user, and the live state // we received back from the API server. oldState, err := plugin.UnmarshalProperties(req.GetOlds(), plugin.MarshalOptions{ Label: fmt.Sprintf("%s.olds", label), KeepUnknowns: true, SkipNulls: true, KeepSecrets: true, @@ -1503,7 +1512,18 @@ func (k *kubeProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*p } newInputs := propMapToUnstructured(newResInputs) - oldInputs, _ := parseCheckpointObject(oldState) + + oldInputs, oldLive := parseCheckpointObject(oldState) + + oldInputs, err = normalize(oldInputs) + if err != nil { + return nil, err + } + newInputs, err = normalize(newInputs) + if err != nil { + return nil, err + } + oldLivePruned := pruneLiveState(oldLive, oldInputs) gvk := k.gvkFromUnstructured(newInputs) @@ -1526,33 +1546,32 @@ func (k *kubeProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*p if namespacedKind { // Explicitly set the "default" namespace if unset so that the diff ignores it. - oldInputs.SetNamespace(canonicalNamespace(oldInputs.GetNamespace())) + oldLivePruned.SetNamespace(canonicalNamespace(oldLivePruned.GetNamespace())) newInputs.SetNamespace(canonicalNamespace(newInputs.GetNamespace())) } else { // Clear the namespace if it was set erroneously. - oldInputs.SetNamespace("") + oldLivePruned.SetNamespace("") newInputs.SetNamespace("") } - if oldInputs.GroupVersionKind().Empty() { - oldInputs.SetGroupVersionKind(gvk) + if oldLivePruned.GroupVersionKind().Empty() { + oldLivePruned.SetGroupVersionKind(gvk) } // If a resource was created without SSA enabled, and then the related provider was changed to enable SSA, a // resourceVersion may have been set on the old resource state. This produces erroneous diffs, so remove the - // value from the oldInputs prior to computing the diff. - if k.serverSideApplyMode && len(oldInputs.GetResourceVersion()) > 0 { - oldInputs.SetResourceVersion("") + // value from the oldLivePruned prior to computing the diff. + if k.serverSideApplyMode && len(oldLivePruned.GetResourceVersion()) > 0 { + oldLivePruned.SetResourceVersion("") } var patch []byte - var patchBase map[string]any + patchBase := oldLivePruned.Object - // Always compute a client-side patch. - patch, err = k.inputPatch(oldInputs, newInputs) + // Compute a diff between the pruned live state and the new inputs. + patch, err = k.inputPatch(oldLivePruned, newInputs) if err != nil { return nil, pkgerrors.Wrapf( err, "Failed to check for changes in resource %s/%s", newInputs.GetNamespace(), newInputs.GetName()) } - patchBase = oldInputs.Object patchObj := map[string]any{} if err = json.Unmarshal(patch, &patchObj); err != nil { @@ -1562,41 +1581,6 @@ func (k *kubeProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*p newInputs.GetNamespace(), newInputs.GetName()) } - fieldManager := k.fieldManagerName(nil, newResInputs, newInputs) - - // Try to compute a server-side patch. - ssPatch, ssPatchBase, ssPatchOk, err := k.tryServerSidePatch(oldInputs, newInputs, gvk, fieldManager) - if err != nil { - return nil, err - } - - // If the server-side patch succeeded, then merge that patch into the client-side patch and override any conflicts - // with the server-side values. - if ssPatchOk { - logger.V(1).Infof("calculated diffs for %s/%s using dry-run and inputs", newInputs.GetNamespace(), newInputs.GetName()) - // Merge the server-side patch into the client-side patch, and ensure that any fields that are set to null in the - // server-side patch are set to null in the client-side patch. - err = mergo.Merge(&patchBase, ssPatchBase, mergo.WithOverwriteWithEmptyValue) - if err != nil { - return nil, err - } - - ssPatchObj := map[string]any{} - if err = json.Unmarshal(ssPatch, &ssPatchObj); err != nil { - return nil, pkgerrors.Wrapf( - err, "Failed to check for changes in resource %s/%s because of an error serializing "+ - "the JSON patch describing resource changes", - newInputs.GetNamespace(), newInputs.GetName()) - } - err = mergo.Merge(&patchObj, ssPatchObj, mergo.WithOverride) - if err != nil { - return nil, err - } - } else { - logger.V(1).Infof("calculated diffs for %s/%s using inputs only", newInputs.GetNamespace(), newInputs.GetName()) - } - - // Pack up PB, ship response back. hasChanges := pulumirpc.DiffResponse_DIFF_NONE var replaces []string @@ -1605,9 +1589,9 @@ func (k *kubeProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*p // Changing the identity of the resource always causes a replacement. forceNewFields := []string{".metadata.name", ".metadata.namespace"} if !isPatchURN(urn) { // Patch resources can be updated in place for all other properties. - forceNewFields = k.forceNewProperties(oldInputs) + forceNewFields = k.forceNewProperties(newInputs) } - if detailedDiff, err = convertPatchToDiff(patchObj, patchBase, newInputs.Object, oldInputs.Object, forceNewFields...); err != nil { + if detailedDiff, err = convertPatchToDiff(patchObj, patchBase, newInputs.Object, oldLivePruned.Object, forceNewFields...); err != nil { return nil, pkgerrors.Wrapf( err, "Failed to check for changes in resource %s/%s because of an error "+ "converting JSON patch describing resource changes to a diff", @@ -1685,10 +1669,10 @@ func (k *kubeProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*p // auto-generate the name). !metadata.IsAutonamed(newInputs) && // 3. The new, user-specified name is the same as the old name. - newInputs.GetName() == oldInputs.GetName() && + newInputs.GetName() == oldLivePruned.GetName() && // 4. The resource is being deployed to the same namespace (i.e., we aren't creating the // object in a new namespace and then deleting the old one). - newInputs.GetNamespace() == oldInputs.GetNamespace() + newInputs.GetNamespace() == oldLivePruned.GetNamespace() return &pulumirpc.DiffResponse{ Changes: hasChanges, @@ -1700,23 +1684,22 @@ func (k *kubeProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*p }, nil } -// Create allocates a new instance of the provided resource and returns its unique ID afterwards. +// Create allocates a new instance of the provided resource and returns its unique ID. // (The input ID must be blank.) If this call fails, the resource must not have been created (i.e., // it is "transactional"). func (k *kubeProvider) Create( ctx context.Context, req *pulumirpc.CreateRequest, ) (*pulumirpc.CreateResponse, error) { // - // Behavior as of v0.12.x: We take 1 input: + // Behavior as of v4.0: We take 1 input: // // 1. `req.Properties`, the new resource inputs submitted by the user, after having been returned // by `Check`. // // This is used to create a new resource, and the computed values are returned. Importantly: // - // * The return is formatted as a "checkpoint object", i.e., an object of the form - // {inputs: {...}, live: {...}}. This is important both for `Diff` and for `Update`. See - // comments in those methods for details. + // * The return is formatted as a "checkpoint object", which includes both inputs and the live state of the + // resource. This is important both for `Diff` and for `Update`. See comments in those methods for details. // urn := resource.URN(req.GetUrn()) @@ -1758,14 +1741,6 @@ func (k *kubeProvider) Create( return &pulumirpc.CreateResponse{Id: "", Properties: req.GetProperties()}, nil } - annotatedInputs, err := k.withLastAppliedConfig(newInputs) - if err != nil { - return nil, pkgerrors.Wrapf( - err, "Failed to create resource %s/%s because of an error generating the %s value in "+ - "`.metadata.annotations`", - newInputs.GetNamespace(), newInputs.GetName(), lastAppliedConfigKey) - } - initialAPIVersion := newInputs.GetAPIVersion() fieldManager := k.fieldManagerName(nil, newResInputs, newInputs) @@ -1773,14 +1748,14 @@ func (k *kubeProvider) Create( if newResInputs.ContainsSecrets() { _ = k.host.Log(ctx, diag.Warning, urn, fmt.Sprintf( "rendered file %s contains a secret value in plaintext", - renderPathForResource(annotatedInputs, k.yamlDirectory))) + renderPathForResource(newInputs, k.yamlDirectory))) } - err := renderYaml(annotatedInputs, k.yamlDirectory) + err := renderYaml(newInputs, k.yamlDirectory) if err != nil { return nil, err } - obj := checkpointObject(newInputs, annotatedInputs, newResInputs, initialAPIVersion, fieldManager) + obj := checkpointObject(newInputs, newInputs, newResInputs, initialAPIVersion, fieldManager) inputsAndComputed, err := plugin.MarshalProperties( obj, plugin.MarshalOptions{ Label: fmt.Sprintf("%s.inputsAndComputed", label), @@ -1793,10 +1768,10 @@ func (k *kubeProvider) Create( } _ = k.host.LogStatus(ctx, diag.Info, urn, fmt.Sprintf( - "rendered %s", renderPathForResource(annotatedInputs, k.yamlDirectory))) + "rendered %s", renderPathForResource(newInputs, k.yamlDirectory))) return &pulumirpc.CreateResponse{ - Id: fqObjName(annotatedInputs), Properties: inputsAndComputed, + Id: fqObjName(newInputs), Properties: inputsAndComputed, }, nil } @@ -1817,7 +1792,7 @@ func (k *kubeProvider) Create( Resources: resources, ServerSideApply: k.serverSideApplyMode, }, - Inputs: annotatedInputs, + Inputs: newInputs, Timeout: req.Timeout, Preview: req.GetPreview(), } @@ -1905,17 +1880,17 @@ func (k *kubeProvider) Create( // include some properties. func (k *kubeProvider) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { // - // Behavior as of v0.12.x: We take 1 input: + // Behavior as of v4.0: We take 2 inputs: // - // 1. `req.Properties`, the new resource inputs submitted by the user, after having been persisted + // 1. `req.Properties`, the previous state of the resource. + // 2. `req.Inputs`, the old resource inputs submitted by the user, after having been persisted // (e.g., by `Create` or `Update`). // // We use this information to read the live version of a Kubernetes resource. This is sometimes // then checkpointed (e.g., in the case of `refresh`). Specifically: // - // * The return is formatted as a "checkpoint object", i.e., an object of the form - // {inputs: {...}, live: {...}}. This is important both for `Diff` and for `Update`. See - // comments in those methods for details. + // * The return is formatted as a "checkpoint object", which includes both inputs and the live state of the + // resource. This is important both for `Diff` and for `Update`. See comments in those methods for details. // urn := resource.URN(req.GetUrn()) @@ -1966,7 +1941,14 @@ func (k *kubeProvider) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*p } readFromCluster := false - oldInputs, oldLive := parseCheckpointObject(oldState) + oldInputs := propMapToUnstructured(oldInputsPM) + _, oldLive := parseCheckpointObject(oldState) + + oldInputs, err = normalize(oldInputs) + if err != nil { + return nil, err + } + if oldInputs.GroupVersionKind().Empty() { if oldLive.GroupVersionKind().Empty() { gvk, err := k.gvkFromURN(urn) @@ -2065,8 +2047,8 @@ func (k *kubeProvider) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*p // initialize. } - // Attempt to parse the inputs for this object. If parsing was unsuccessful, retain the old inputs. - liveInputs := parseLiveInputs(liveObj, oldInputs) + // Prune the live inputs to remove properties that are not present in the program inputs. + liveInputs := pruneLiveState(liveObj, oldInputs) if readFromCluster { // If no previous inputs were known, populate the inputs from the live cluster state for the resource. @@ -2089,6 +2071,7 @@ func (k *kubeProvider) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*p unstructured.RemoveNestedField(liveInputs.Object, "metadata", "managedFields") unstructured.RemoveNestedField(liveInputs.Object, "metadata", "resourceVersion") unstructured.RemoveNestedField(liveInputs.Object, "metadata", "uid") + unstructured.RemoveNestedField(liveInputs.Object, "metadata", "annotations", "deployment.kubernetes.io/revision") unstructured.RemoveNestedField(liveInputs.Object, "metadata", "annotations", lastAppliedConfigKey) } @@ -2130,8 +2113,9 @@ func (k *kubeProvider) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*p return &pulumirpc.ReadResponse{Id: id, Properties: state, Inputs: inputs}, nil } -// Update updates an existing resource with new values. Currently, this client supports the -// Kubernetes-standard three-way JSON patch, and the newer Server-side Apply patch. See references [1], [2], [3]. +// Update updates an existing resource with new values. This client uses a Server-side Apply (SSA) patch by default, but +// also supports the older three-way JSON patch and the strategic merge patch as fallback options. +// See references [1], [2], [3]. // // nolint // [1]: @@ -2144,61 +2128,25 @@ func (k *kubeProvider) Update( ctx context.Context, req *pulumirpc.UpdateRequest, ) (*pulumirpc.UpdateResponse, error) { // - // Behavior as of v3.20.0: We take 2 inputs: + // Behavior as of v4.0: We take 2 inputs: // // 1. req.News, the new resource inputs, i.e., the property bag coming from a custom resource like // k8s.core.v1.Service - // 2. req.Olds, the old _state_ returned by a `Create` or an `Update`. The old state has the form - // {inputs: {...}, live: {...}}, and is a struct that contains the old inputs as well as the - // last computed value obtained from the Kubernetes API server. + // 2. req.Olds, the old _state_ returned by a `Create` or an `Update`. The old state is a struct that + // contains the old inputs as well as the last computed value obtained from the Kubernetes API server. + // + // The provider uses Server-side Apply (SSA) patches by default, which allows the provider to send the desired + // state of the resource to Kubernetes, and let the server decide how to merge in the changes. Previews are + // computed using the dry-run option for the API call, which computes the result on the server without persisting + // the changes. // - // Unlike other providers, the update is computed as a three-way merge between: (1) the new - // inputs, (2) the computed state returned by the API server, and (3) the old inputs. This is the + // Previous versions of the provider used Client-side Apply (CSA) instead, which required the provider to compute + // the merge patch, and then send the patch to the server. This patch is computed as a three-way merge between: + // (1) the new inputs, (2) the computed state returned by the API server, and (3) the old inputs. This is the // main reason why the old state is an object with both the old inputs and the live version of the - // object. + // object. CSA is provided as a fallback option, but is generally less reliable than using SSA. // - // - // TREAD CAREFULLY. The semantics of a Kubernetes update are subtle, and you should proceed to - // change them only if you understand them deeply. - // - // Briefly: when a user updates an existing resource definition (e.g., by modifying YAML), the API - // server must decide how to apply the changes inside it, to the version of the resource that it - // has stored in etcd. In Kubernetes this decision is turns out to be quite complex. `kubectl` - // currently uses the three-way "strategic merge" and falls back to the three-way JSON merge. We - // currently support the second, but eventually we'll have to support the first, too. - // - // (NOTE: This comment is scoped to the question of how to patch an existing resource, rather than - // how to recognize when a resource needs to be re-created from scratch.) - // - // There are several reasons for this complexity: - // - // * It's important not to clobber fields set or default-set by the server (e.g., NodePort, - // namespace, service type, etc.), or by out-of-band tooling like admission controllers - // (which, e.g., might do something like add a sidecar to a container list). - // * For example, consider a scenario where a user renames a container. It is a reasonable - // expectation the old version of the container gets destroyed when the update is applied. And - // if the update strategy is set to three-way JSON merge patching, it is. - // * But, consider if their administrator has set up (say) the Istio admission controller, which - // embeds a sidecar container in pods submitted to the API. This container would not be present - // in the YAML file representing that pod, but when an update is applied by the user, they - // not want it to get destroyed. And, so, when the strategy is set to three-way strategic - // merge, the container is not destroyed. (With this strategy, fields can have "merge keys" as - // part of their schema, which tells the API server how to merge each particular field.) - // - // What's worse is, currently nearly all of this logic exists on the client rather than the - // server, though there is work moving forward to move this to the server. - // - // So the roadmap is: - // - // - [x] Implement `Update` using the three-way JSON merge strategy. - // - [x] Cause `Update` to default to the three-way JSON merge patch strategy. (This will require - // plumbing, because it expects nominal types representing the API schema, but the - // discovery client is completely dynamic.) - // - [x] Support server-side apply. - // - // In the next major release, we will default to using Server-side Apply, which will simplify this logic. - // urn := resource.URN(req.GetUrn()) label := fmt.Sprintf("%s.Update(%s)", k.label(), urn) logger.V(9).Infof("%s executing", label) @@ -2228,6 +2176,10 @@ func (k *kubeProvider) Update( return nil, pkgerrors.Wrapf(err, "update failed because malformed resource inputs") } newInputs := propMapToUnstructured(newResInputs) + newInputs, err = normalize(newInputs) + if err != nil { + return nil, err + } // If this is a preview and the input values contain unknowns, or an unregistered GVK, return them as-is. This is // compatible with prior behavior implemented by the Pulumi engine. @@ -2240,15 +2192,21 @@ func (k *kubeProvider) Update( return k.helmReleaseProvider.Update(ctx, req) } // Ignore old state; we'll get it from Kubernetes later. - oldInputs, _ := parseCheckpointObject(oldState) + oldInputs, oldLive := parseCheckpointObject(oldState) + + // Pre-4.0 versions of the provider used the last-applied-configuration annotation for client-side diffing. This + // annotation was automatically added to all resources by Pulumi. This annotation is no longer used, and needs to + // be removed from resources where it is present. To avoid causing a large update after upgrading to a 4.x release, + // only perform this operation during Update for resources that include other changes. This removal will not show + // up in the preview, which is symmetric with the previous creation behavior, which also does not show this + // annotation during preview. + removeLastAppliedConfigurationAnnotation(oldLive, oldInputs) - annotatedInputs, err := k.withLastAppliedConfig(newInputs) + oldInputs, err = normalize(oldInputs) if err != nil { - return nil, pkgerrors.Wrapf( - err, "Failed to update resource %s/%s because of an error generating the %s value in "+ - "`.metadata.annotations`", - newInputs.GetNamespace(), newInputs.GetName(), lastAppliedConfigKey) + return nil, err } + oldLivePruned := pruneLiveState(oldLive, oldInputs) initialAPIVersion := initialAPIVersion(oldState, oldInputs) fieldManagerOld := k.fieldManagerName(nil, oldState, oldInputs) @@ -2258,14 +2216,14 @@ func (k *kubeProvider) Update( if newResInputs.ContainsSecrets() { _ = k.host.LogStatus(ctx, diag.Warning, urn, fmt.Sprintf( "rendered file %s contains a secret value in plaintext", - renderPathForResource(annotatedInputs, k.yamlDirectory))) + renderPathForResource(newInputs, k.yamlDirectory))) } - err := renderYaml(annotatedInputs, k.yamlDirectory) + err := renderYaml(newInputs, k.yamlDirectory) if err != nil { return nil, err } - obj := checkpointObject(newInputs, annotatedInputs, newResInputs, initialAPIVersion, fieldManager) + obj := checkpointObject(newInputs, oldLive, newResInputs, initialAPIVersion, fieldManager) inputsAndComputed, err := plugin.MarshalProperties( obj, plugin.MarshalOptions{ Label: fmt.Sprintf("%s.inputsAndComputed", label), @@ -2278,7 +2236,7 @@ func (k *kubeProvider) Update( } _ = k.host.LogStatus(ctx, diag.Info, urn, fmt.Sprintf( - "rendered %s", renderPathForResource(annotatedInputs, k.yamlDirectory))) + "rendered %s", renderPathForResource(newInputs, k.yamlDirectory))) return &pulumirpc.UpdateResponse{Properties: inputsAndComputed}, nil } @@ -2299,8 +2257,8 @@ func (k *kubeProvider) Update( Resources: resources, ServerSideApply: k.serverSideApplyMode, }, - Previous: oldInputs, - Inputs: annotatedInputs, + Previous: oldLivePruned, + Inputs: newInputs, Timeout: req.Timeout, Preview: req.GetPreview(), } @@ -2590,121 +2548,6 @@ func (k *kubeProvider) readLiveObject(obj *unstructured.Unstructured) (*unstruct return rc.Get(k.canceler.context, obj.GetName(), metav1.GetOptions{}) } -func (k *kubeProvider) serverSidePatch(oldInputs, newInputs *unstructured.Unstructured, fieldManager string, -) ([]byte, map[string]any, error) { - - client, err := k.clientSet.ResourceClient(oldInputs.GroupVersionKind(), oldInputs.GetNamespace()) - if err != nil { - return nil, nil, err - } - - liveObject, err := client.Get(k.canceler.context, oldInputs.GetName(), metav1.GetOptions{}) - if err != nil { - return nil, nil, err - } - - // If the new resource does not exist, we need to dry-run a Create rather than a Patch. - var newObject *unstructured.Unstructured - _, err = client.Get(k.canceler.context, newInputs.GetName(), metav1.GetOptions{}) - switch { - case apierrors.IsNotFound(err): - newObject, err = client.Create(k.canceler.context, newInputs, metav1.CreateOptions{ - DryRun: []string{metav1.DryRunAll}, - }) - case newInputs.GetNamespace() != oldInputs.GetNamespace(): - client, err := k.clientSet.ResourceClient(newInputs.GroupVersionKind(), newInputs.GetNamespace()) - if err != nil { - return nil, nil, err - } - newObject, err = client.Create(k.canceler.context, newInputs, metav1.CreateOptions{ - DryRun: []string{metav1.DryRunAll}, - }) - if err != nil { - return nil, nil, err - } - case err == nil: - if k.serverSideApplyMode { - objYAML, err := yaml.Marshal(newInputs.Object) - if err != nil { - return nil, nil, err - } - - // Always force on diff to avoid erroneous conflict errors for resource replacements - force := true - if v := metadata.GetAnnotationValue(newInputs, metadata.AnnotationPatchFieldManager); len(v) > 0 { - fieldManager = v - } - - newObject, err = client.Patch( - k.canceler.context, newInputs.GetName(), types.ApplyPatchType, objYAML, metav1.PatchOptions{ - DryRun: []string{metav1.DryRunAll}, - FieldManager: fieldManager, - FieldValidation: metav1.FieldValidationWarn, - Force: &force, - }) - if err != nil { - return nil, nil, err - } - } else { - liveInputs := parseLiveInputs(liveObject, oldInputs) - - resources, err := k.getResources() - if err != nil { - return nil, nil, err - } - - patch, patchType, _, err := openapi.PatchForResourceUpdate(resources, liveInputs, newInputs, liveObject) - if err != nil { - return nil, nil, err - } - - newObject, err = client.Patch(k.canceler.context, newInputs.GetName(), patchType, patch, metav1.PatchOptions{ - DryRun: []string{metav1.DryRunAll}, - }) - if err != nil { - return nil, nil, err - } - } - - default: - return nil, nil, err - } - if err != nil { - return nil, nil, err - } - - if k.serverSideApplyMode { - objMetadata := newObject.Object["metadata"].(map[string]any) - if len(objMetadata) == 1 { - delete(newObject.Object, "metadata") - } else { - delete(newObject.Object["metadata"].(map[string]any), "managedFields") - } - objMetadata = liveObject.Object["metadata"].(map[string]any) - if len(objMetadata) == 1 { - delete(liveObject.Object, "metadata") - } else { - delete(liveObject.Object["metadata"].(map[string]any), "managedFields") - } - } - - liveJSON, err := liveObject.MarshalJSON() - if err != nil { - return nil, nil, err - } - newJSON, err := newObject.MarshalJSON() - if err != nil { - return nil, nil, err - } - - patch, err := jsonpatch.CreateMergePatch(liveJSON, newJSON) - if err != nil { - return nil, nil, err - } - - return patch, liveObject.Object, nil -} - // inputPatch calculates a patch on the client-side by comparing old inputs to the current inputs. func (k *kubeProvider) inputPatch( oldInputs, newInputs *unstructured.Unstructured, @@ -2733,101 +2576,6 @@ func (k *kubeProvider) isDryRunDisabledError(err error) bool { strings.Contains(se.Status().Message, "does not support dry run")) } -// tryServerSidePatch attempts to compute a server-side patch. Returns true iff the operation succeeded. -// The following are expected ways in which the server-side apply can not work; -// we return no error, but a `false` value indicating that there's no result either. -// The caller will fall back to using the client-side diff. In the particular case of an -// update to an immutable field, it is already known from the schema when -// replacement will be necessary. -func (k *kubeProvider) tryServerSidePatch( - oldInputs, newInputs *unstructured.Unstructured, - gvk schema.GroupVersionKind, - fieldManager string, -) (ssPatch []byte, ssPatchBase map[string]interface{}, ok bool, err error) { - // If the cluster is unreachable, compute patch using inputs. - if k.clusterUnreachable { - return nil, nil, false, nil - } - - // If the resource's GVK changed, so compute patch using inputs. - if oldInputs.GroupVersionKind().String() != gvk.String() { - return nil, nil, false, nil - } - // If the GVK is unregistered, compute the patch using inputs. - if !k.gvkExists(newInputs) { - return nil, nil, false, nil - } - // TODO: Skipping server-side diff for resources with computed values is a hack. We will want to address this - // more granularly so that previews are as accurate as possible, but this is an easy workaround for a critical - // bug. - if hasComputedValue(newInputs) || hasComputedValue(oldInputs) { - return nil, nil, false, nil - } - - ssPatch, ssPatchBase, err = k.serverSidePatch(oldInputs, newInputs, fieldManager) - if err == nil { - // The server-side patch succeeded. - return ssPatch, ssPatchBase, true, nil - } - - if apierrors.IsForbidden(err) { - // If the user doesn't have permission to run a Server-side preview, compute the patch using inputs - logger.V(1).Infof("unable to compute Server-side patch: %s", err) - return nil, nil, false, nil - } - if k.isDryRunDisabledError(err) { - return nil, nil, false, err - } - if se, isStatusError := err.(*apierrors.StatusError); isStatusError { - if se.Status().Code == http.StatusUnprocessableEntity && - strings.Contains(se.ErrStatus.Message, "field is immutable") { - // This error occurs if the resource field is immutable. - // Ignore this error since this case is handled by the replacement logic. - return nil, nil, false, nil - } - } - if err.Error() == "name is required" { - // If the name was removed in this update, then the resource will be replaced with an auto-named resource. - // Ignore this error since this case is handled by the replacement logic. - return nil, nil, false, nil - } - - return nil, nil, false, err -} - -func (k *kubeProvider) withLastAppliedConfig(config *unstructured.Unstructured) (*unstructured.Unstructured, error) { - if k.serverSideApplyMode { - // Skip last-applied-config annotation if the resource supports server-side apply. - return config, nil - } - - // CRDs are updated using a separate mechanism, so skip the last-applied-configuration annotation, and delete it - // if it was present from a previous update. - if clients.IsCRD(config) { - // Deep copy the config before returning. - config = config.DeepCopy() - - annotations := getAnnotations(config) - delete(annotations, lastAppliedConfigKey) - config.SetAnnotations(annotations) - return config, nil - } - - // Serialize the inputs and add the last-applied-configuration annotation. - marshaled, err := config.MarshalJSON() - if err != nil { - return nil, err - } - - // Deep copy the config before returning. - config = config.DeepCopy() - - annotations := getAnnotations(config) - annotations[lastAppliedConfigKey] = string(marshaled) - config.SetAnnotations(annotations) - return config, nil -} - // fieldManagerName returns the name to use for the Server-Side Apply fieldManager. The values are looked up with the // following precedence: // 1. Resource annotation (this will likely change to a typed option field in the next major release) @@ -2899,6 +2647,50 @@ func (k *kubeProvider) loadPulumiConfig() (map[string]any, bool) { return pConfig, true } +// removeLastAppliedConfigurationAnnotation is used by the Update method to remove an existing +// last-applied-configuration annotation from a resource. This annotation was set automatically by the provider, so it +// does not show up in the resource inputs. If the value is present in the live state, copy that value into the old +// inputs so that a negative diff will be generated for it. +func removeLastAppliedConfigurationAnnotation(oldLive, oldInputs *unstructured.Unstructured) { + oldLiveValue, existsInOldLive, _ := unstructured.NestedString(oldLive.Object, + "metadata", "annotations", lastAppliedConfigKey) + _, existsInOldInputs, _ := unstructured.NestedString(oldInputs.Object, + "metadata", "annotations", lastAppliedConfigKey) + + if existsInOldLive && !existsInOldInputs { + contract.IgnoreError(unstructured.SetNestedField( + oldInputs.Object, oldLiveValue, "metadata", "annotations", lastAppliedConfigKey)) + } +} + +// pruneLiveState prunes a live resource object to match the shape of the input object that created the resource. +func pruneLiveState(live, oldInputs *unstructured.Unstructured) *unstructured.Unstructured { + oldLivePruned := &unstructured.Unstructured{ + Object: pruneMap(live.Object, oldInputs.Object), + } + + return oldLivePruned +} + +// shouldNormalize returns false for CRDs and CustomResources, and true otherwise. +func shouldNormalize(uns *unstructured.Unstructured) bool { + return !clients.IsCRD(uns) && kinds.KnownGroupVersions.Has(uns.GetAPIVersion()) +} + +// normalize converts an Unstructured resource into a normalized form so that semantically equivalent representations +// are set to the same output shape. This is important to avoid generating diffs for inputs that will produce the same +// result on the cluster. +func normalize(uns *unstructured.Unstructured) (*unstructured.Unstructured, error) { + if shouldNormalize(uns) { + normalized, err := clients.Normalize(uns) + if err != nil { + return nil, err + } + uns = pruneLiveState(normalized, uns) + } + return uns, nil +} + func mapReplStripSecrets(v resource.PropertyValue) (any, bool) { if v.IsSecret() { return v.SecretValue().Element.MapRepl(nil, mapReplStripSecrets), true @@ -2934,15 +2726,6 @@ func propMapToUnstructured(pm resource.PropertyMap) *unstructured.Unstructured { return &unstructured.Unstructured{Object: pm.MapRepl(mapReplUnderscoreToDash, mapReplStripSecrets)} } -func getAnnotations(config *unstructured.Unstructured) map[string]string { - annotations := config.GetAnnotations() - if annotations == nil { - annotations = make(map[string]string) - } - - return annotations -} - // initialAPIVersion retrieves the initialAPIVersion property from the checkpoint file and falls back to using // the version from the resource metadata if that property is not present. func initialAPIVersion(state resource.PropertyMap, oldInputs *unstructured.Unstructured) string { @@ -2982,21 +2765,6 @@ func checkpointObject(inputs, live *unstructured.Unstructured, fromInputs resour } } - // Ensure that the annotation we add for lastAppliedConfig is treated as a secret if any of the inputs were secret - // (the value of this annotation is a string-ified JSON so marking the entire thing as a secret is really the best - // that we can do). - if fromInputs.ContainsSecrets() || isSecretKind { - if _, has := object["metadata"]; has && object["metadata"].IsObject() { - metadata := object["metadata"].ObjectValue() - if _, has := metadata["annotations"]; has && metadata["annotations"].IsObject() { - annotations := metadata["annotations"].ObjectValue() - if lastAppliedConfig, has := annotations[lastAppliedConfigKey]; has && !lastAppliedConfig.IsSecret() { - annotations[lastAppliedConfigKey] = resource.MakeSecret(lastAppliedConfig) - } - } - } - } - object["__inputs"] = resource.NewObjectProperty(inputsPM) object[initialAPIVersionKey] = resource.NewStringProperty(initialAPIVersion) object[fieldManagerKey] = resource.NewStringProperty(fieldManager) @@ -3064,54 +2832,6 @@ func canonicalNamespace(ns string) string { // deleteResponse causes the resource to be deleted from the state. var deleteResponse = &pulumirpc.ReadResponse{Id: "", Properties: nil} -// parseLastAppliedConfig attempts to find and parse an annotation that records the last applied configuration for the -// given live object state. -func parseLastAppliedConfig(live *unstructured.Unstructured) *unstructured.Unstructured { - // If `kubectl.kubernetes.io/last-applied-configuration` metadata annotation is present, parse it into a real object - // and use it as the current set of live inputs. Otherwise, return nil. - if live == nil { - return nil - } - - annotations := live.GetAnnotations() - if annotations == nil { - return nil - } - lastAppliedConfig, ok := annotations[lastAppliedConfigKey] - if !ok { - return nil - } - - liveInputs := &unstructured.Unstructured{} - if err := liveInputs.UnmarshalJSON([]byte(lastAppliedConfig)); err != nil { - return nil - } - return liveInputs -} - -// parseLiveInputs attempts to parse the provider inputs that produced the given live object out of the object's state. -// This is used by Read. -func parseLiveInputs(live, oldInputs *unstructured.Unstructured) *unstructured.Unstructured { - // First try to find and parse a `kubectl.kubernetes.io/last-applied-configuration` metadata anotation. If that - // succeeds, we are done. - if inputs := parseLastAppliedConfig(live); inputs != nil { - return inputs - } - - // If no such annotation was present--or if parsing failed--either retain the old inputs if they exist, or - // attempt to propagate the live object's GVK, any Pulumi-generated autoname and its annotation, and return - // the result. - if oldInputs != nil && len(oldInputs.Object) > 0 { - return oldInputs - } - - inputs := &unstructured.Unstructured{Object: map[string]any{}} - inputs.SetGroupVersionKind(live.GroupVersionKind()) - metadata.AdoptOldAutonameIfUnnamed(inputs, live) - - return inputs -} - // convertPatchToDiff converts the given JSON merge patch to a Pulumi detailed diff. func convertPatchToDiff( patch, oldLiveState, newInputs, oldInputs map[string]any, forceNewFields ...string, diff --git a/provider/pkg/provider/util.go b/provider/pkg/provider/util.go index b3986382e7..d50bafa81b 100644 --- a/provider/pkg/provider/util.go +++ b/provider/pkg/provider/util.go @@ -155,14 +155,10 @@ func pruneMap(source, target map[string]any) map[string]any { switch valueT.Kind() { case reflect.Map: nestedResult := pruneMap(value.(map[string]any), targetValue.(map[string]any)) - if len(nestedResult) > 0 { - result[key] = nestedResult - } + result[key] = nestedResult case reflect.Slice: nestedResult := pruneSlice(value.([]any), targetValue.([]any)) - if len(nestedResult) > 0 { - result[key] = nestedResult - } + result[key] = nestedResult default: result[key] = value } diff --git a/provider/pkg/provider/util_test.go b/provider/pkg/provider/util_test.go index d7802fa06a..c033341881 100644 --- a/provider/pkg/provider/util_test.go +++ b/provider/pkg/provider/util_test.go @@ -846,6 +846,24 @@ func TestPruneMap(t *testing.T) { }, }, }, + { + name: "nested empty map", + description: "a map with an empty nested map where target matches", + args: args{ + source: map[string]any{ + "a": "a", + "b": map[string]any{}, + }, + target: map[string]any{ + "a": "a", + "b": map[string]any{}, + }, + }, + want: map[string]any{ + "a": "a", + "b": map[string]any{}, + }, + }, { name: "nested value slice", description: "a map with a nested slice of simple values where target matches", @@ -896,6 +914,24 @@ func TestPruneMap(t *testing.T) { "b": []any{"c", "d"}, }, }, + { + name: "nested empty slice", + description: "a map with an empty nested slice of simple values where target matches", + args: args{ + source: map[string]any{ + "a": "a", + "b": []any{}, + }, + target: map[string]any{ + "a": "a", + "b": []any{}, + }, + }, + want: map[string]any{ + "a": "a", + "b": []any{}, + }, + }, { name: "nested map slice", description: "a map with a nested slice of map values where target matches", diff --git a/tests/go.mod b/tests/go.mod index 5983c5ebba..e0300b66b3 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -13,9 +13,9 @@ require ( github.com/pulumi/pulumi/pkg/v3 v3.74.0 github.com/pulumi/pulumi/sdk/v3 v3.74.0 github.com/stretchr/testify v1.8.3 - helm.sh/helm/v3 v3.11.3 - k8s.io/apimachinery v0.27.1 - k8s.io/client-go v0.27.1 + helm.sh/helm/v3 v3.12.0 + k8s.io/apimachinery v0.27.2 + k8s.io/client-go v0.27.2 ) require ( @@ -106,14 +106,16 @@ require ( github.com/emicklei/go-restful/v3 v3.10.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/color v1.13.0 // indirect + github.com/fluxcd/pkg/ssa v0.28.1 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.4.0 // indirect github.com/go-git/go-git/v5 v5.6.0 // indirect github.com/go-gorp/gorp/v3 v3.0.5 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect @@ -214,10 +216,10 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pkg/term v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/prometheus/client_golang v1.15.1 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect github.com/pulumi/cloud-ready-checks v1.0.1-0.20230201174945-00fe9c1b68fd // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect @@ -254,14 +256,14 @@ require ( gocloud.dev/secrets/hashivault v0.27.0 // indirect golang.org/x/crypto v0.5.0 // indirect golang.org/x/mod v0.10.0 // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect - golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.9.1 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.110.0 // indirect google.golang.org/appengine v1.6.7 // indirect @@ -273,17 +275,19 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.27.1 // indirect - k8s.io/apiextensions-apiserver v0.27.1 // indirect - k8s.io/apiserver v0.27.1 // indirect + k8s.io/api v0.27.2 // indirect + k8s.io/apiextensions-apiserver v0.27.2 // indirect + k8s.io/apiserver v0.27.2 // indirect k8s.io/cli-runtime v0.27.1 // indirect - k8s.io/component-base v0.27.1 // indirect + k8s.io/component-base v0.27.2 // indirect k8s.io/klog/v2 v2.90.1 // indirect - k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a // indirect + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/kubectl v0.27.1 // indirect k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect lukechampine.com/frand v1.4.2 // indirect oras.land/oras-go v1.2.2 // indirect + sigs.k8s.io/cli-utils v0.34.0 // indirect + sigs.k8s.io/controller-runtime v0.15.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.13.2 // indirect sigs.k8s.io/kustomize/kyaml v0.14.1 // indirect diff --git a/tests/go.sum b/tests/go.sum index 51c3470df7..ca93e07557 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -634,6 +634,8 @@ github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -647,6 +649,8 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fluxcd/pkg/ssa v0.28.1 h1:h5r5irAgDjgkmIqagOLOa/U7/Rx2fT2NKIb+vDTYOMg= +github.com/fluxcd/pkg/ssa v0.28.1/go.mod h1:o55eBzWz7P/tqnCn5c622RZvjTP/GqvitqZUbsMIRwk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= @@ -662,6 +666,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= @@ -708,11 +713,13 @@ github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= @@ -758,8 +765,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= @@ -1440,7 +1447,7 @@ github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1ls github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1451,7 +1458,7 @@ github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1552,8 +1559,8 @@ github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -1561,8 +1568,8 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1577,8 +1584,9 @@ github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+ github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/common/assets v0.1.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI= github.com/prometheus/common/assets v0.2.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= @@ -1596,8 +1604,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY= github.com/prometheus/prometheus v0.37.0/go.mod h1:egARUgz+K93zwqsVIAneFlLZefyGOON44WyAp4Xqbbk= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= @@ -1904,6 +1912,7 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -1911,6 +1920,7 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= gocloud.dev v0.27.0 h1:j0WTUsnKTxCsWO7y8T+YCiBZUmLl9w/WIowqAY3yo0g= gocloud.dev v0.27.0/go.mod h1:YlYKhYsY5/1JdHGWQDkAuqkezVKowu7qbe9aIeUF6p0= gocloud.dev/secrets/hashivault v0.27.0 h1:AAeGJXr0tiHHJgg5tL8atOGktB4eK9EJAqkZbPKAcOo= @@ -2085,8 +2095,8 @@ golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2129,8 +2139,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2282,8 +2292,8 @@ 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.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2293,8 +2303,8 @@ golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2319,8 +2329,9 @@ golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2407,8 +2418,8 @@ golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2420,6 +2431,7 @@ golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNq golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -2714,10 +2726,10 @@ k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= -k8s.io/api v0.27.1 h1:Z6zUGQ1Vd10tJ+gHcNNNgkV5emCyW+v2XTmn+CLjSd0= -k8s.io/api v0.27.1/go.mod h1:z5g/BpAiD+f6AArpqNjkY+cji8ueZDU/WV1jcj5Jk4E= -k8s.io/apiextensions-apiserver v0.27.1 h1:Hp7B3KxKHBZ/FxmVFVpaDiXI6CCSr49P1OJjxKO6o4g= -k8s.io/apiextensions-apiserver v0.27.1/go.mod h1:8jEvRDtKjVtWmdkhOqE84EcNWJt/uwF8PC4627UZghY= +k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo= +k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4= +k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo= +k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= @@ -2725,14 +2737,14 @@ k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apimachinery v0.27.1 h1:EGuZiLI95UQQcClhanryclaQE6xjg1Bts6/L3cD7zyc= -k8s.io/apimachinery v0.27.1/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM= +k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg= +k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/apiserver v0.27.1 h1:phY+BtXjjzd+ta3a4kYbomC81azQSLa1K8jo9RBw7Lg= -k8s.io/apiserver v0.27.1/go.mod h1:UGrOjLY2KsieA9Fw6lLiTObxTb8Z1xEba4uqSuMY0WU= +k8s.io/apiserver v0.27.2 h1:p+tjwrcQEZDrEorCZV2/qE8osGTINPuS5ZNqWAvKm5E= +k8s.io/apiserver v0.27.2/go.mod h1:EsOf39d75rMivgvvwjJ3OW/u9n1/BmUMK5otEOJrb1Y= k8s.io/cli-runtime v0.27.1 h1:MMzp5Q/Xmr5L1Lrowuc+Y/r95XINC6c6/fE3aN7JDRM= k8s.io/cli-runtime v0.27.1/go.mod h1:tEbTB1XP/nTH3wujsi52bw91gWpErtWiS15R6CwYsAI= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= @@ -2741,15 +2753,15 @@ k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30= -k8s.io/client-go v0.27.1 h1:oXsfhW/qncM1wDmWBIuDzRHNS2tLhK3BZv512Nc59W8= -k8s.io/client-go v0.27.1/go.mod h1:f8LHMUkVb3b9N8bWturc+EDtVVVwZ7ueTVquFAJb2vA= +k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE= +k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ= k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= -k8s.io/component-base v0.27.1 h1:kEB8p8lzi4gCs5f2SPU242vOumHJ6EOsOnDM3tTuDTM= -k8s.io/component-base v0.27.1/go.mod h1:UGEd8+gxE4YWoigz5/lb3af3Q24w98pDseXcXZjw+E0= +k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo= +k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= @@ -2776,8 +2788,8 @@ k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2R k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a h1:gmovKNur38vgoWfGtP5QOGNOA7ki4n6qNYoFAgMlNvg= -k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/kubectl v0.27.1 h1:9T5c5KdpburYiW8XKQSH0Uly1kMNE90aGSnbYUZNdcA= k8s.io/kubectl v0.27.1/go.mod h1:QsAkSmrRsKTPlAFzF8kODGDl4p35BIwQnc9XFhkcsy8= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= @@ -2802,6 +2814,10 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= +sigs.k8s.io/cli-utils v0.34.0/go.mod h1:EXyMwPMu9OL+LRnj0JEMsGG/fRvbgFadcVlSnE8RhFs= +sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= +sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/tests/sdk/dotnet/dotnet_test.go b/tests/sdk/dotnet/dotnet_test.go index b4be8a16cf..871dc04750 100644 --- a/tests/sdk/dotnet/dotnet_test.go +++ b/tests/sdk/dotnet/dotnet_test.go @@ -96,8 +96,9 @@ func TestDotnet_YamlLocal(t *testing.T) { func TestDotnet_Helm(t *testing.T) { test := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join("helm", "step1"), - Quick: true, + Dir: filepath.Join("helm", "step1"), + Quick: true, + ExpectRefreshChanges: true, ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { // Ensure that all `Services` have `status` marked as a `Secret` for _, res := range stackInfo.Deployment.Resources { @@ -118,8 +119,9 @@ func TestDotnet_Helm(t *testing.T) { func TestDotnet_HelmLocal(t *testing.T) { test := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join("helm-local", "step1"), - Quick: true, + Dir: filepath.Join("helm-local", "step1"), + Quick: true, + ExpectRefreshChanges: true, ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { assert.NotNil(t, stackInfo.Deployment) assert.Equal(t, 12, len(stackInfo.Deployment.Resources)) @@ -151,21 +153,15 @@ func TestDotnet_HelmLocal(t *testing.T) { } } }, - EditDirs: []integration.EditDir{ - { - Dir: filepath.Join("helm-local", "step2"), - Additive: true, - ExpectNoChanges: true, - }, - }, }) integration.ProgramTest(t, &test) } func TestDotnet_HelmApiVersions(t *testing.T) { test := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join("helm-api-versions", "step1"), - Quick: true, + Dir: filepath.Join("helm-api-versions", "step1"), + Quick: true, + ExpectRefreshChanges: true, OrderedConfig: []integration.ConfigValue{ { Key: "pulumi:disable-default-providers[0]", @@ -183,9 +179,10 @@ func TestDotnet_HelmApiVersions(t *testing.T) { func TestDotnet_HelmAllowCRDRendering(t *testing.T) { test := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join("helm-skip-crd-rendering", "step1"), - Quick: true, - SkipRefresh: true, + Dir: filepath.Join("helm-skip-crd-rendering", "step1"), + Quick: true, + SkipRefresh: true, + ExpectRefreshChanges: true, ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { assert.NotNil(t, stackInfo.Deployment) assert.Equal(t, 8, len(stackInfo.Deployment.Resources)) diff --git a/tests/sdk/go/go_test.go b/tests/sdk/go/go_test.go index fa27caab3e..62d9dc894f 100644 --- a/tests/sdk/go/go_test.go +++ b/tests/sdk/go/go_test.go @@ -76,8 +76,9 @@ func TestGo(t *testing.T) { t.Run("Helm Local", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-local", "step1"), - Quick: true, + Dir: filepath.Join(cwd, "helm-local", "step1"), + Quick: true, + ExpectRefreshChanges: true, ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { // Verify resource creation order using the Event stream. The Chart resources must be created // first, followed by the dependent ConfigMap. (The ConfigMap doesn't actually need the Chart, but @@ -122,6 +123,7 @@ func TestGo(t *testing.T) { Config: map[string]string{ "namespace": namespace, }, + ExpectRefreshChanges: true, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assert.NotEmpty(t, stack.Outputs["svc_ip"]) }, @@ -142,32 +144,36 @@ func TestGo(t *testing.T) { Config: map[string]string{ "namespace": namespace, }, - NoParallel: true, - Verbose: true, + ExpectRefreshChanges: true, + NoParallel: true, + Verbose: true, }) integration.ProgramTest(t, &options) }) t.Run("Helm Remote", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm", "step1"), - Quick: true, + Dir: filepath.Join(cwd, "helm", "step1"), + Quick: true, + ExpectRefreshChanges: true, }) integration.ProgramTest(t, &options) }) t.Run("Helm Release", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release", "step1"), - Quick: true, + Dir: filepath.Join(cwd, "helm-release", "step1"), + Quick: true, + ExpectRefreshChanges: true, }) integration.ProgramTest(t, &options) }) t.Run("Helm Release With Empty Local Folder", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release", "step1"), - Quick: true, + Dir: filepath.Join(cwd, "helm-release", "step1"), + Quick: true, + ExpectRefreshChanges: true, PrePulumiCommand: func(verb string) (func(err error) error, error) { // Create an empty folder to test that the Helm provider doesn't fail when the folder is empty, and we should // be fetching from remote. @@ -183,16 +189,18 @@ func TestGo(t *testing.T) { t.Run("Helm Release Local", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release-local", "step1"), - Quick: true, + Dir: filepath.Join(cwd, "helm-release-local", "step1"), + Quick: true, + ExpectRefreshChanges: true, }) integration.ProgramTest(t, &options) }) t.Run("Helm Release Local Compressed", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release-local-tar", "step1"), - Quick: true, + Dir: filepath.Join(cwd, "helm-release-local-tar", "step1"), + Quick: true, + ExpectRefreshChanges: true, }) integration.ProgramTest(t, &options) }) @@ -236,8 +244,9 @@ func TestGo(t *testing.T) { t.Run("Helm API Versions", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-api-versions", "step1"), - Quick: true, + Dir: filepath.Join(cwd, "helm-api-versions", "step1"), + Quick: true, + ExpectRefreshChanges: true, }) integration.ProgramTest(t, &options) }) diff --git a/tests/sdk/nodejs/drift-correct/configmap-csa/Pulumi.yaml b/tests/sdk/nodejs/drift-correct/configmap-csa/Pulumi.yaml new file mode 100644 index 0000000000..8ce897523d --- /dev/null +++ b/tests/sdk/nodejs/drift-correct/configmap-csa/Pulumi.yaml @@ -0,0 +1,3 @@ +name: drift-correct-tests +description: Tests handling of drift correction with refresh an reapply +runtime: nodejs diff --git a/tests/sdk/nodejs/drift-correct/configmap-csa/index.ts b/tests/sdk/nodejs/drift-correct/configmap-csa/index.ts new file mode 100644 index 0000000000..903b2d24ae --- /dev/null +++ b/tests/sdk/nodejs/drift-correct/configmap-csa/index.ts @@ -0,0 +1,39 @@ +// Copyright 2016-2022, Pulumi Corporation. +// +// 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. + +import * as k8s from "@pulumi/kubernetes"; + +// This test creates a Provider with `enableServerSideApply` disabled. The following scenarios are tested: +// 1. Create a Namespace and ConfigMap with pulumi. +// 2. Externally delete labels in the ConfigMap using kubectl. +// 3. Rerun the pulumi program and verify that the labels are restored. + +// Create provider with CSA enabled. +const provider = new k8s.Provider("k8s", { + enableServerSideApply: false, + enableConfigMapMutable: true, // Reuse the same ConfigMap rather than replacing on change. +}); + +// Create a randomly-named Namespace. +const ns = new k8s.core.v1.Namespace("test", undefined, {provider}); + +export const cm = new k8s.core.v1.ConfigMap("test", { + metadata: { + namespace: ns.metadata.name, + annotations: { + "pulumi.com/patchForce": "true", + }, + }, + data: {foo: "bar"}, +}, {provider}); diff --git a/tests/sdk/nodejs/drift-correct/configmap-csa/package.json b/tests/sdk/nodejs/drift-correct/configmap-csa/package.json new file mode 100644 index 0000000000..8cd8d8e38e --- /dev/null +++ b/tests/sdk/nodejs/drift-correct/configmap-csa/package.json @@ -0,0 +1,11 @@ +{ + "name": "drift-correct", + "version": "0.1.0", + "dependencies": { + "@pulumi/pulumi": "latest", + "@pulumi/random": "latest" + }, + "peerDependencies": { + "@pulumi/kubernetes": "latest" + } +} diff --git a/tests/sdk/nodejs/drift-correct/configmap-csa/tsconfig.json b/tests/sdk/nodejs/drift-correct/configmap-csa/tsconfig.json new file mode 100644 index 0000000000..5dacccbd42 --- /dev/null +++ b/tests/sdk/nodejs/drift-correct/configmap-csa/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "outDir": "bin", + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "sourceMap": true, + "stripInternal": true, + "experimentalDecorators": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true, + "strictNullChecks": true + }, + "files": [ + "index.ts" + ] +} + diff --git a/tests/sdk/nodejs/drift-correct/configmap-ssa/Pulumi.yaml b/tests/sdk/nodejs/drift-correct/configmap-ssa/Pulumi.yaml new file mode 100644 index 0000000000..8ce897523d --- /dev/null +++ b/tests/sdk/nodejs/drift-correct/configmap-ssa/Pulumi.yaml @@ -0,0 +1,3 @@ +name: drift-correct-tests +description: Tests handling of drift correction with refresh an reapply +runtime: nodejs diff --git a/tests/sdk/nodejs/drift-correct/configmap-ssa/index.ts b/tests/sdk/nodejs/drift-correct/configmap-ssa/index.ts new file mode 100644 index 0000000000..8fb7d2934a --- /dev/null +++ b/tests/sdk/nodejs/drift-correct/configmap-ssa/index.ts @@ -0,0 +1,39 @@ +// Copyright 2016-2022, Pulumi Corporation. +// +// 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. + +import * as k8s from "@pulumi/kubernetes"; + +// This test creates a Provider with `enableServerSideApply` enabled. The following scenarios are tested: +// 1. Create a Namespace and ConfigMap with pulumi. +// 2. Externally delete labels in the ConfigMap using kubectl. +// 3. Rerun the pulumi program and verify that the labels are restored. + +// Create provider with SSA enabled. +const provider = new k8s.Provider("k8s", { + enableServerSideApply: false, + enableConfigMapMutable: true, // Reuse the same ConfigMap rather than replacing on change. +}); + +// Create a randomly-named Namespace. +const ns = new k8s.core.v1.Namespace("test", undefined, {provider}); + +export const cm = new k8s.core.v1.ConfigMap("test", { + metadata: { + namespace: ns.metadata.name, + annotations: { + "pulumi.com/patchForce": "true", + }, + }, + data: {foo: "bar"}, +}, {provider}); diff --git a/tests/sdk/nodejs/drift-correct/configmap-ssa/package.json b/tests/sdk/nodejs/drift-correct/configmap-ssa/package.json new file mode 100644 index 0000000000..8cd8d8e38e --- /dev/null +++ b/tests/sdk/nodejs/drift-correct/configmap-ssa/package.json @@ -0,0 +1,11 @@ +{ + "name": "drift-correct", + "version": "0.1.0", + "dependencies": { + "@pulumi/pulumi": "latest", + "@pulumi/random": "latest" + }, + "peerDependencies": { + "@pulumi/kubernetes": "latest" + } +} diff --git a/tests/sdk/nodejs/drift-correct/configmap-ssa/tsconfig.json b/tests/sdk/nodejs/drift-correct/configmap-ssa/tsconfig.json new file mode 100644 index 0000000000..5dacccbd42 --- /dev/null +++ b/tests/sdk/nodejs/drift-correct/configmap-ssa/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "outDir": "bin", + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "sourceMap": true, + "stripInternal": true, + "experimentalDecorators": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true, + "strictNullChecks": true + }, + "files": [ + "index.ts" + ] +} + diff --git a/tests/sdk/nodejs/examples/examples_test.go b/tests/sdk/nodejs/examples/examples_test.go index 7cd541bf96..fca339b533 100644 --- a/tests/sdk/nodejs/examples/examples_test.go +++ b/tests/sdk/nodejs/examples/examples_test.go @@ -45,7 +45,8 @@ func TestAccGuestbook(t *testing.T) { skipIfShort(t) test := getBaseOptions(t). With(integration.ProgramTestOptions{ - Dir: filepath.Join(getCwd(t), "guestbook"), + Dir: filepath.Join(getCwd(t), "guestbook"), + ExpectRefreshChanges: true, ExtraRuntimeValidation: func( t *testing.T, stackInfo integration.RuntimeValidationStackInfo, ) { @@ -386,6 +387,7 @@ func TestHelmRelease(t *testing.T) { SkipRefresh: false, Verbose: true, ExtraRuntimeValidation: validationFunc, + ExpectRefreshChanges: true, EditDirs: []integration.EditDir{ { Dir: filepath.Join(getCwd(t), "helm-release", "step2"), @@ -404,9 +406,10 @@ func TestHelmReleaseCRD(t *testing.T) { skipIfShort(t) test := getBaseOptions(t). With(integration.ProgramTestOptions{ - Dir: filepath.Join(getCwd(t), "helm-release-crd", "step1"), - SkipRefresh: false, - Verbose: true, + Dir: filepath.Join(getCwd(t), "helm-release-crd", "step1"), + SkipRefresh: false, + ExpectRefreshChanges: true, + Verbose: true, EditDirs: []integration.EditDir{ { Dir: filepath.Join(getCwd(t), "helm-release-crd", "step2"), @@ -423,9 +426,10 @@ func TestHelmReleaseNamespace(t *testing.T) { skipIfShort(t) test := getBaseOptions(t). With(integration.ProgramTestOptions{ - Dir: filepath.Join(getCwd(t), "helm-release-namespace"), - SkipRefresh: false, - Verbose: true, + Dir: filepath.Join(getCwd(t), "helm-release-namespace"), + SkipRefresh: false, + Verbose: true, + ExpectRefreshChanges: true, // Ensure that the rule was found in the release's namespace. ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { assert.NotEmpty(t, stackInfo.Outputs["alertManagerNamespace"].(string)) @@ -466,10 +470,11 @@ func TestHelmReleaseRedis(t *testing.T) { skipIfShort(t) test := getBaseOptions(t). With(integration.ProgramTestOptions{ - Dir: filepath.Join(getCwd(t), "helm-release-redis", "step1"), - SkipRefresh: false, - Verbose: true, - Quick: true, + Dir: filepath.Join(getCwd(t), "helm-release-redis", "step1"), + SkipRefresh: false, + ExpectRefreshChanges: true, + Verbose: true, + Quick: true, EditDirs: []integration.EditDir{ { Dir: filepath.Join(getCwd(t), "helm-release-redis", "step2"), diff --git a/tests/sdk/nodejs/nodejs_test.go b/tests/sdk/nodejs/nodejs_test.go index c112068029..1b00887fc7 100644 --- a/tests/sdk/nodejs/nodejs_test.go +++ b/tests/sdk/nodejs/nodejs_test.go @@ -1175,6 +1175,7 @@ func TestServerSideApplyEmptyMaps(t *testing.T) { ExpectRefreshChanges: true, // Enable destroy-on-cleanup so we can shell out to kubectl to make external changes to the resource and reuse the same stack. DestroyOnCleanup: true, + Quick: true, OrderedConfig: []integration.ConfigValue{ { Key: "pulumi:disable-default-providers[0]", @@ -1221,7 +1222,13 @@ func TestServerSideApplyEmptyMaps(t *testing.T) { assert.NoError(t, err) assert.NotContains(t, string(out), "bar") // ConfigMap should no longer have label foo=bar. - // Re-run `pulumi up --refresh` to update the ConfigMap and re-add the label. + // Run `pulumi up` + `pulumi refresh` to refresh the state and detect the missing label. + // (The program tester runs these as separate steps, so the `pulumi up` doesn't detect a change until after the + // subsequent refresh is performed.) + err = pt.TestPreviewUpdateAndEdits() + assert.NoError(t, err) + + // Re-run `pulumi up` to update the ConfigMap and re-add the label. err = pt.TestPreviewUpdateAndEdits() assert.NoError(t, err) @@ -1338,6 +1345,19 @@ func TestServerSideApplyUpgrade(t *testing.T) { assert.True(t, ok) assert.NoError(t, err) assert.Equalf(t, "false", enableSSA, "SSA should be disabled") + for _, res := range stackInfo.Deployment.Resources { + if res.Type == "kubernetes:apps/v1:Deployment" { + live := &unstructured.Unstructured{Object: res.Outputs} + foundExpectedManager := false + for _, managedField := range live.GetManagedFields() { + if managedField.Manager == "pulumi-kubernetes" && managedField.Operation == "Update" { + foundExpectedManager = true + break + } + } + assert.Truef(t, foundExpectedManager, "missing expected pulumi-kubernetes field manager with operation type Update") + } + } }, EditDirs: []integration.EditDir{ { @@ -1350,6 +1370,19 @@ func TestServerSideApplyUpgrade(t *testing.T) { assert.True(t, ok) assert.NoError(t, err) assert.Equalf(t, "true", enableSSA, "SSA should be enabled") + for _, res := range stackInfo.Deployment.Resources { + if res.Type == "kubernetes:apps/v1:Deployment" { + live := &unstructured.Unstructured{Object: res.Outputs} + foundExpectedManager := false + for _, managedField := range live.GetManagedFields() { + if managedField.Manager == "pulumi-kubernetes" && managedField.Operation == "Update" { + foundExpectedManager = true + break + } + } + assert.Truef(t, foundExpectedManager, "missing expected pulumi-kubernetes field manager with operation type Update") + } + } }, }, { @@ -1358,11 +1391,26 @@ func TestServerSideApplyUpgrade(t *testing.T) { ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { for _, res := range stackInfo.Deployment.Resources { if res.Type == "kubernetes:apps/v1:Deployment" { + live := &unstructured.Unstructured{Object: res.Outputs} + foundExpectedManager := false + for _, managedField := range live.GetManagedFields() { + // Operation type should change to Apply on first update to the Deployment. + if managedField.Manager == "pulumi-kubernetes" && managedField.Operation == "Apply" { + foundExpectedManager = true + break + } + } + assert.Truef(t, foundExpectedManager, "missing expected pulumi-kubernetes field manager with operation type Apply") containers, ok := openapi.Pluck(res.Outputs, "spec", "template", "spec", "containers") - assert.True(t, ok) + assert.Truef(t, ok, "failed to get containers") containerStatus := containers.([]any)[0].(map[string]any) image := containerStatus["image"] assert.Equalf(t, image.(string), "nginx:1.17", "image should be updated") + portsRaw := containerStatus["ports"] + portsArray := portsRaw.([]any) + assert.Equalf(t, len(portsArray), 1, "only one port should be set") + portMap := portsArray[0].(map[string]any) + assert.Equalf(t, portMap["containerPort"], float64(81), "port should be updated to 81") } } }, @@ -1520,3 +1568,157 @@ func TestStrictMode(t *testing.T) { }) integration.ProgramTest(t, &test) } + +// TestClientSideDriftCorrectCSA tests that we can successfully reapply a resource that has been +// modified outside of Pulumi using the client-side apply strategy. +func TestClientSideDriftCorrectCSA(t *testing.T) { + var ns, cmName string + + applyStep := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join("drift-correct", "configmap-csa"), + ExpectRefreshChanges: true, + // Enable destroy-on-cleanup so we can shell out to kubectl to make external changes to the resource and reuse the same stack. + DestroyOnCleanup: true, + OrderedConfig: []integration.ConfigValue{ + { + Key: "pulumi:disable-default-providers[0]", + Value: "kubernetes", + Path: true, + }, + }, + ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { + cm, ok := stackInfo.Outputs["cm"].(map[string]interface{}) + assert.True(t, ok) + + // Save the name and namespace for later use with kubectl. We check that the vars are empty, + // in case pulumi up creates a new ConfigMap/Namespace instead of updating the existing one on + // subsequent runs. + if ns == "" && cmName == "" { + ns = cm["metadata"].(map[string]interface{})["namespace"].(string) + cmName = cm["metadata"].(map[string]interface{})["name"].(string) + } + + // Validate we applied ConfigMap with data. + fooV, ok, err := unstructured.NestedString(cm, "data", "foo") + assert.True(t, ok) + assert.NoError(t, err) + assert.Equal(t, "bar", fooV) + }, + }) + + // Use manual lifecycle management since we need to run external commands in between pulumi up steps, while referencing + // the same stack. + pt := integration.ProgramTestManualLifeCycle(t, &applyStep) + err := pt.TestLifeCycleInitAndDestroy() + assert.NoError(t, err) + + // Sanity check with kubectl to verify that the ConfigMap was created with the wanted label. + out, err := exec.Command("kubectl", "get", "configmap", "-o", "yaml", "-n", ns, cmName).CombinedOutput() + assert.NoError(t, err) + assert.Contains(t, string(out), "bar") // ConfigMap should have been created with data foo: bar. + + // Update the ConfigMap and change the data foo: bar to foo: baz. + out, err = exec.Command("kubectl", "patch", "configmap", "-n", ns, cmName, "-p", `{"data":{"foo":"baz"}}`).CombinedOutput() + assert.NoError(t, err) + assert.Contains(t, string(out), "configmap/"+cmName+" patched") // Ensure CM was patched. + + // Use kubectl to verify that the ConfigMap was updated and now has data foo: baz. + out, err = exec.Command("kubectl", "get", "configmap", "-o", "yaml", "-n", ns, cmName).CombinedOutput() + assert.NoError(t, err) + assert.NotContains(t, string(out), "foo: bar") // ConfigMap should no longer have data foo: bar. + assert.Contains(t, string(out), "foo: baz") // ConfigMap should have data foo: baz. + + // Run `pulumi up` + `pulumi refresh` to refresh the state and detect the missing label. + // (The program tester runs these as separate steps, so the `pulumi up` doesn't detect a change until after the + // subsequent refresh is performed.) + err = pt.TestPreviewUpdateAndEdits() + assert.NoError(t, err) + + // Re-run `pulumi up` to update the ConfigMap and re-add the label. + err = pt.TestPreviewUpdateAndEdits() + assert.NoError(t, err) + + // Use kubectl to verify that the ConfigMap was updated and has the label again. + out, err = exec.Command("kubectl", "get", "configmap", "-o", "yaml", "-n", ns, cmName).CombinedOutput() + assert.NoError(t, err) + + assert.Contains(t, string(out), "foo: bar") // ConfigMap should have been updated with data foo: bar. + assert.NotContains(t, string(out), "foo: baz") // ConfigMap should no longer have data foo: baz. +} + +// TestClientSideDriftCorrectSSA tests that we can successfully reapply a resource that has been +// modified outside of Pulumi, with SSA enabled. +func TestClientSideDriftCorrectSSA(t *testing.T) { + var ns, cmName string + + applyStep := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join("drift-correct", "configmap-ssa"), + ExpectRefreshChanges: true, + // Enable destroy-on-cleanup so we can shell out to kubectl to make external changes to the resource and reuse the same stack. + DestroyOnCleanup: true, + OrderedConfig: []integration.ConfigValue{ + { + Key: "pulumi:disable-default-providers[0]", + Value: "kubernetes", + Path: true, + }, + }, + ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { + cm, ok := stackInfo.Outputs["cm"].(map[string]interface{}) + assert.True(t, ok) + + // Save the name and namespace for later use with kubectl. We check that the vars are empty, + // in case pulumi up creates a new ConfigMap/Namespace instead of updating the existing one on + // subsequent runs. + if ns == "" && cmName == "" { + ns = cm["metadata"].(map[string]interface{})["namespace"].(string) + cmName = cm["metadata"].(map[string]interface{})["name"].(string) + } + + // Validate we applied ConfigMap with data. + fooV, ok, err := unstructured.NestedString(cm, "data", "foo") + assert.True(t, ok) + assert.NoError(t, err) + assert.Equal(t, "bar", fooV) + }, + }) + + // Use manual lifecycle management since we need to run external commands in between pulumi up steps, while referencing + // the same stack. + pt := integration.ProgramTestManualLifeCycle(t, &applyStep) + err := pt.TestLifeCycleInitAndDestroy() + assert.NoError(t, err) + + // Sanity check with kubectl to verify that the ConfigMap was created with the wanted label. + out, err := exec.Command("kubectl", "get", "configmap", "-o", "yaml", "-n", ns, cmName).CombinedOutput() + assert.NoError(t, err) + assert.Contains(t, string(out), "bar") // ConfigMap should have been created with data foo: bar. + + // Update the ConfigMap and change the data foo: bar to foo: baz. + out, err = exec.Command("kubectl", "patch", "configmap", "-n", ns, cmName, "-p", `{"data":{"foo":"baz"}}`).CombinedOutput() + assert.NoError(t, err) + assert.Contains(t, string(out), "configmap/"+cmName+" patched") // Ensure CM was patched. + + // Use kubectl to verify that the ConfigMap was updated and now has data foo: baz. + out, err = exec.Command("kubectl", "get", "configmap", "-o", "yaml", "-n", ns, cmName).CombinedOutput() + assert.NoError(t, err) + assert.NotContains(t, string(out), "foo: bar") // ConfigMap should no longer have data foo: bar. + assert.Contains(t, string(out), "foo: baz") // ConfigMap should have data foo: baz. + + // Run `pulumi up` + `pulumi refresh` to refresh the state and detect the missing label. + // (The program tester runs these as separate steps, so the `pulumi up` doesn't detect a change until after the + // subsequent refresh is performed.) + err = pt.TestPreviewUpdateAndEdits() + assert.NoError(t, err) + + // Re-run `pulumi up` to update the ConfigMap and re-add the label. + err = pt.TestPreviewUpdateAndEdits() + assert.NoError(t, err) + + // Use kubectl to verify that the ConfigMap was updated and has the label again. + out, err = exec.Command("kubectl", "get", "configmap", "-o", "yaml", "-n", ns, cmName).CombinedOutput() + assert.NoError(t, err) + + assert.Contains(t, string(out), "foo: bar") // ConfigMap should have been updated with data foo: bar. + assert.NotContains(t, string(out), "foo: baz") // ConfigMap should no longer have data foo: baz. +} diff --git a/tests/sdk/nodejs/server-side-apply-upgrade/step1/index.ts b/tests/sdk/nodejs/server-side-apply-upgrade/step1/index.ts index de35c43c00..13f8655af0 100644 --- a/tests/sdk/nodejs/server-side-apply-upgrade/step1/index.ts +++ b/tests/sdk/nodejs/server-side-apply-upgrade/step1/index.ts @@ -19,7 +19,7 @@ import * as k8s from "@pulumi/kubernetes"; // Server-side Apply (SSA). This test validates the following scenario does not fail with a field manager conflict: // 1. Create a Deployment with Client-side Apply. // 2. Update the Provider to use Server-side Apply. -// 3. Change a field in the Deployment. +// 3. Change fields in the Deployment. // Create provider with SSA disabled. export const provider = new k8s.Provider("k8s", {enableServerSideApply: false}); @@ -40,6 +40,7 @@ const deployment = new k8s.apps.v1.Deployment("nginx", { containers: [{ name: "nginx", image: "nginx:1.16", + ports: [{containerPort: 80}], }], } } diff --git a/tests/sdk/nodejs/server-side-apply-upgrade/step2/index.ts b/tests/sdk/nodejs/server-side-apply-upgrade/step2/index.ts index 4a309b4d7c..784ff917fa 100644 --- a/tests/sdk/nodejs/server-side-apply-upgrade/step2/index.ts +++ b/tests/sdk/nodejs/server-side-apply-upgrade/step2/index.ts @@ -19,7 +19,7 @@ import * as k8s from "@pulumi/kubernetes"; // Server-side Apply (SSA). This test validates the following scenario does not fail with a field manager conflict: // 1. Create a Deployment with Client-side Apply. // 2. Update the Provider to use Server-side Apply. -// 3. Change a field in the Deployment. +// 3. Change fields in the Deployment. // Create provider with SSA disabled. export const provider = new k8s.Provider("k8s", {enableServerSideApply: true}); // Enable SSA @@ -40,6 +40,7 @@ const deployment = new k8s.apps.v1.Deployment("nginx", { containers: [{ name: "nginx", image: "nginx:1.16", + ports: [{containerPort: 80}], }], } } diff --git a/tests/sdk/nodejs/server-side-apply-upgrade/step3/index.ts b/tests/sdk/nodejs/server-side-apply-upgrade/step3/index.ts index 5cfa114ff1..e69b66fa34 100644 --- a/tests/sdk/nodejs/server-side-apply-upgrade/step3/index.ts +++ b/tests/sdk/nodejs/server-side-apply-upgrade/step3/index.ts @@ -19,7 +19,7 @@ import * as k8s from "@pulumi/kubernetes"; // Server-side Apply (SSA). This test validates the following scenario does not fail with a field manager conflict: // 1. Create a Deployment with Client-side Apply. // 2. Update the Provider to use Server-side Apply. -// 3. Change a field in the Deployment. +// 3. Change fields in the Deployment. // Create provider with SSA disabled. export const provider = new k8s.Provider("k8s", {enableServerSideApply: true}); @@ -40,6 +40,10 @@ const deployment = new k8s.apps.v1.Deployment("nginx", { containers: [{ name: "nginx", image: "nginx:1.17", // Update a field to ensure that field manager conflicts don't cause an error + ports: [ + // Change the port to ensure that the existing value is updated rather than adding a second port to the array + {containerPort: 81}, + ], }], } } diff --git a/tests/sdk/python/guestbook-old/__main__.py b/tests/sdk/python/guestbook-old/__main__.py index 952f955ddf..840507cebf 100644 --- a/tests/sdk/python/guestbook-old/__main__.py +++ b/tests/sdk/python/guestbook-old/__main__.py @@ -25,7 +25,7 @@ redis_leader_deployment = Deployment( "redis-leader", metadata={ - "namespace": namespace + "namespace": namespace.metadata.apply(lambda x: x.name), }, spec={ "selector": { @@ -57,7 +57,7 @@ redis_leader_service = Service( "redis-leader", metadata={ - "namespace": namespace, + "namespace": namespace.metadata.apply(lambda x: x.name), "labels": redis_leader_labels }, spec={ @@ -77,7 +77,7 @@ redis_follower_deployment = Deployment( "redis-follower", metadata={ - "namespace": namespace + "namespace": namespace.metadata.apply(lambda x: x.name), }, spec={ "selector": { @@ -117,7 +117,7 @@ redis_follower_service = Service( "redis-follower", metadata={ - "namespace": namespace, + "namespace": namespace.metadata.apply(lambda x: x.name), "labels": redis_follower_labels }, spec={ @@ -136,7 +136,7 @@ frontend_service = Service( "frontend", metadata={ - "namespace": namespace, + "namespace": namespace.metadata.apply(lambda x: x.name), "labels": frontend_labels }, spec={ @@ -152,7 +152,7 @@ frontend_deployment = Deployment( "frontend", metadata={ - "namespace": namespace, + "namespace": namespace.metadata.apply(lambda x: x.name), }, spec={ "selector": { diff --git a/tests/sdk/python/guestbook/__main__.py b/tests/sdk/python/guestbook/__main__.py index 5bfe677dea..89422f5c38 100644 --- a/tests/sdk/python/guestbook/__main__.py +++ b/tests/sdk/python/guestbook/__main__.py @@ -37,7 +37,7 @@ redis_leader_deployment = Deployment( "redis-leader", metadata=ObjectMetaArgs( - namespace=namespace + namespace=namespace.metadata.apply(lambda x: x.name), ), spec=DeploymentSpecArgs( selector=LabelSelectorArgs( @@ -69,7 +69,7 @@ redis_leader_service = Service( "redis-leader", metadata=ObjectMetaArgs( - namespace=namespace, + namespace=namespace.metadata.apply(lambda x: x.name), labels=redis_leader_labels ), spec=ServiceSpecArgs( @@ -89,7 +89,7 @@ redis_follower_deployment = Deployment( "redis-follower", metadata=ObjectMetaArgs( - namespace=namespace + namespace=namespace.metadata.apply(lambda x: x.name), ), spec=DeploymentSpecArgs( selector=LabelSelectorArgs( @@ -129,7 +129,7 @@ redis_follower_service = Service( "redis-follower", metadata=ObjectMetaArgs( - namespace=namespace, + namespace=namespace.metadata.apply(lambda x: x.name), labels=redis_follower_labels ), spec=ServiceSpecArgs( @@ -148,7 +148,7 @@ frontend_service = Service( "frontend", metadata=ObjectMetaArgs( - namespace=namespace, + namespace=namespace.metadata.apply(lambda x: x.name), labels=frontend_labels ), spec=ServiceSpecArgs( @@ -164,7 +164,7 @@ frontend_deployment = Deployment( "frontend", metadata=ObjectMetaArgs( - namespace=namespace, + namespace=namespace.metadata.apply(lambda x: x.name), ), spec=DeploymentSpecArgs( selector=LabelSelectorArgs( diff --git a/tests/sdk/python/provider-old/__main__.py b/tests/sdk/python/provider-old/__main__.py index 354180f517..919601cfad 100644 --- a/tests/sdk/python/provider-old/__main__.py +++ b/tests/sdk/python/provider-old/__main__.py @@ -23,7 +23,7 @@ nginx = Pod( "nginx", metadata={ - "namespace": namespace, + "namespace": namespace.metadata.apply(lambda x: x.name), }, spec={ "containers": [{ diff --git a/tests/sdk/python/provider/__main__.py b/tests/sdk/python/provider/__main__.py index f842bbd1e7..9781aa372b 100644 --- a/tests/sdk/python/provider/__main__.py +++ b/tests/sdk/python/provider/__main__.py @@ -24,7 +24,7 @@ nginx = Pod( "nginx", metadata=ObjectMetaArgs( - namespace=namespace, + namespace=namespace.metadata.apply(lambda x: x.name), ), spec=PodSpecArgs( containers=[ContainerArgs( diff --git a/tests/sdk/python/python_test.go b/tests/sdk/python/python_test.go index d633c84121..7e091eda07 100644 --- a/tests/sdk/python/python_test.go +++ b/tests/sdk/python/python_test.go @@ -239,8 +239,9 @@ func TestGuestbook(t *testing.T) { for _, dir := range []string{"guestbook", "guestbook-old"} { t.Run(dir, func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, dir), - NoParallel: true, + Dir: filepath.Join(cwd, dir), + NoParallel: true, + ExpectRefreshChanges: true, ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { assert.NotNil(t, stackInfo.Deployment) assert.Equal(t, 9, len(stackInfo.Deployment.Resources)) @@ -351,7 +352,8 @@ func TestHelm(t *testing.T) { t.FailNow() } options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm", "step1"), + Dir: filepath.Join(cwd, "helm", "step1"), + ExpectRefreshChanges: true, }) integration.ProgramTest(t, &options) } @@ -362,7 +364,8 @@ func TestHelmRelease(t *testing.T) { t.FailNow() } options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release", "step1"), + Dir: filepath.Join(cwd, "helm-release", "step1"), + ExpectRefreshChanges: true, EditDirs: []integration.EditDir{ { Dir: filepath.Join(cwd, "helm-release", "step2"), @@ -380,7 +383,8 @@ func TestHelmLocal(t *testing.T) { t.FailNow() } options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-local", "step1"), + Dir: filepath.Join(cwd, "helm-local", "step1"), + ExpectRefreshChanges: true, ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { // Verify resource creation order using the Event stream. The Chart resources must be created // first, followed by the dependent ConfigMap. (The ConfigMap doesn't actually need the Chart, but @@ -419,7 +423,8 @@ func TestHelmApiVersions(t *testing.T) { t.FailNow() } options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-api-versions", "step1"), + Dir: filepath.Join(cwd, "helm-api-versions", "step1"), + ExpectRefreshChanges: true, }) integration.ProgramTest(t, &options) } diff --git a/tests/sdk/python/smoke-test-old/__main__.py b/tests/sdk/python/smoke-test-old/__main__.py index 5ab8d9eff2..16cc958232 100644 --- a/tests/sdk/python/smoke-test-old/__main__.py +++ b/tests/sdk/python/smoke-test-old/__main__.py @@ -19,7 +19,7 @@ pod = Pod( "smoke-test", metadata={ - "namespace": namespace, + "namespace": namespace.metadata.apply(lambda x: x.name), }, spec={ "containers": [ diff --git a/tests/sdk/python/smoke-test/__main__.py b/tests/sdk/python/smoke-test/__main__.py index 1551b0c1cf..c9486ecbc2 100644 --- a/tests/sdk/python/smoke-test/__main__.py +++ b/tests/sdk/python/smoke-test/__main__.py @@ -20,7 +20,7 @@ pod = Pod( "smoke-test", metadata=ObjectMetaArgs( - namespace=namespace, + namespace=namespace.metadata.apply(lambda x: x.name), ), spec=PodSpecArgs( containers=[ diff --git a/tests/sdk/python/yaml-test/manifest.yaml b/tests/sdk/python/yaml-test/manifest.yaml index 908721f96f..008f43ccc9 100644 --- a/tests/sdk/python/yaml-test/manifest.yaml +++ b/tests/sdk/python/yaml-test/manifest.yaml @@ -46,6 +46,11 @@ spec: type: object spec: type: object + properties: + foo: + type: string + bar: + type: string type: object served: true storage: true