Compare commits

...

47 Commits
v1.8.1 ... main

Author SHA1 Message Date
Sergen Yalçın 4c6bfc216d
Merge pull request #519 from erhancagirici/xp-v2-module-update
bump upjet go module to v2
2025-08-04 14:49:37 +03:00
Erhan Cagirici 8ff62171a7
Merge pull request #518 from crossplane/xp-v2
Generate crossplane v2 providers
2025-08-01 17:31:26 +03:00
Erhan Cagirici d1183ac46f bump upjet go module to v2
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-08-01 01:50:50 +03:00
Erhan Cagirici b4eb48bcba go mod: bump crossplane-runtime to v2
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 21:05:12 +03:00
Erhan Cagirici cf5fef8c8a fix: use astutils for import manipulation in resolver
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 21:05:12 +03:00
Erhan Cagirici 4b39d500a1 rename crossplane-runtime import paths for v2
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 21:05:12 +03:00
Erhan Cagirici 2c719f0b96 examples: generate namespaced examples
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici 3157e7da7e regenerate unittest mocks
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici b3b8ef0938 remove connection publisher options from controller template
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici 88e0308220 generate gated controller setup methods
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici 72f730ba32 update unit tests for namespaced MRs
update sensitive tests
update types builder tests
add tests for FileProducer connection secret ref resolution
add namespaced tests for api callbacks

Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici 7e1dd50d8f remove migration framework
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici c4427804fd make MR metric recording keys namespaced
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici 0636518853 add MR namespace to various logs
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici a30bbb511d generate local cross-resource references for namespaced crds
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici b64c7f1799 remove ESS configuration
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici b20c833a70 handle local secret references for sensitive observations
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici 96746e6b15 runtime resolution of local secret refs in sensitive parameters of namespaced MRs
- inject namespace to the local secret ref if MR is namespaced

- cross-namespace secret refs are effectively not allowed for namespaced MRs

Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici 4b64e6ecb8 generate local secret refs for sensitive fields in namespaced MRs
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Erhan Cagirici 6ea3bc96f2 generate namespace-friendly Go structs for namespaced MR types
- namespaced MR Go structs now inline v2-style ManagedResourceSpec in the type template
as a result:
- writeConnectionSecretToRef becomes a local secret ref in namespaced MRs
- providerConfigRef becomes a typed reference with kind included
- deletionPolicy gets removed in namespaced MRs
- publishConnectionDetailsTo gets removed from all MRs

Signed-off-by: Erhan Cagirici <erhan@upbound.io>
2025-07-31 14:27:49 +03:00
Jared Watts 3d1ab3b9fb refactor: pipeline Run should return early after handling cluster only scenario
Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-07-31 14:27:49 +03:00
Jared Watts 9d7ed5871c test: update unit tests for namespaced usage
Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-07-31 14:27:49 +03:00
Jared Watts a0e93d3827 make external client and friends namespace aware
Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-07-31 14:27:49 +03:00
Jared Watts a590156eb9 enabled namespaced conversions to be registered too
Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-07-31 14:27:49 +03:00
Jared Watts 76770f82b2 Pipeline Run accepts both cluster scoped provider config and optional namespaced config
Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-07-31 14:27:49 +03:00
Jared Watts 8e9fcfd9c3 feat: make namespaced resource generation opt-in
Also take into account if there is a main.go template file when
generating controller setup and provider main.go files. There
will be no template in the monolith case, meaning we shoud generate
a consolidated controller setup and shouldn't generate main.go files
for each group.

Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-07-31 14:27:49 +03:00
Nic Cope 387fb83493 Generate namespaced Go types and controllers
This is mostly a case of finding hard coded paths and assumptions and making
them configurable.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2025-07-31 14:27:49 +03:00
Nic Cope 40bae8497f POC: Make api and controller paths configurable
This PR is a no-op when I run it on provider-upjet-aws. It generates
exactly the same code as before this PR.

My goal is to allow invoking the main apis/controllers loop twice.
Once for cluster scoped and once for namespaced resources. I'll do
that in a follow-up PR.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2025-07-31 14:27:49 +03:00
Sergen Yalçın 96241b0ae5
Merge pull request #512 from sergenyalcin/ignore-identity-in-diff
Sanitize Identity field in InstanceDiff
2025-07-31 13:17:36 +03:00
Sergen Yalçın ffb68a034a
Fix unit tests
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2025-07-31 12:32:12 +03:00
Sergen Yalçın 99c75ab7cb
Sanitize Identity field in Diff
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2025-07-31 12:32:09 +03:00
Sergen Yalçın 766236448e
Merge pull request #515 from sergenyalcin/custom-state-check
Custom state check configuration for TF plugin framework resources
2025-07-28 15:18:18 +03:00
Sergen Yalçın 9505d31da7
Change function name to TerraformPluginFrameworkIsStateEmptyFn
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2025-07-28 15:11:59 +03:00
Sergen Yalçın 5ac5cb0b35
Custom nil check for state
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2025-07-25 17:18:40 +03:00
Erhan Cagirici 0af42ca259
Merge pull request #507 from erhancagirici/remove-id-check-in-externalname-fw
remove id validation from setExternalName for resources without id field
2025-06-19 13:09:11 +03:00
Fatih Türken f794e5eddf remove id validation from setExternalName for resources without id field
Signed-off-by: Fatih Türken <turkenf@gmail.com>
Co-authored-by: Erhan Cagirici <erhan@upbound.io>
2025-06-19 11:28:35 +03:00
Sergen Yalçın c4332e6ed1
Merge pull request #506 from sergenyalcin/fix-sensitive-parameter-generation
Fix incorrectly generated connection string map
2025-06-18 16:55:31 +03:00
Sergen Yalçın dd08349e54
Fix linter
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2025-06-18 16:43:38 +03:00
Sergen Yalçın c42638efc0
Fix incorrectly generated connection string map
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2025-06-18 15:56:56 +03:00
Sergen Yalçın 9098842035
Merge pull request #504 from sergenyalcin/fix-wildcard-expand-during-conversion
Fix wildcard expand behavior when if the field path is not found during conversion
2025-06-17 18:23:52 +03:00
Sergen Yalçın c275d5ec5c
Fix wildcard expand behavior when if the field path is not found during conversion
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2025-06-17 18:16:04 +03:00
Erhan Cagirici f6111127e7
Merge pull request #500 from nikimanoledaki/nm/debug-provider
Validate that `ts.FrameworkProvider` is not nil to avoid panic
2025-06-04 12:31:12 +03:00
nikimanoledaki 60517ef9af
Validate that ts.FrameworkProvdier is not nil to avoid panic
Fetching the ts.FrameworkProvider.Schema field panics if the FrameworkProvider
struct is not set / nil. Validate that the FrameworkProvider is not nil before
continuing. Return an error message if it is nil.

Signed-off-by: nikimanoledaki <niki.manoledaki@grafana.com>
2025-06-03 18:01:46 +02:00
Sergen Yalçın 55edf18c68
Merge pull request #493 from sergenyalcin/bump-crossplane-runtime
Bump crossplane-runtime dependency
2025-05-09 15:48:45 +03:00
Sergen Yalçın fe88167010
Bump crossplane-runtime dependency
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2025-05-09 15:39:59 +03:00
Jean du Plessis 6fdbab083c
Merge pull request #491 from jbw976/changelogs 2025-05-09 10:43:48 +02:00
Jared Watts b8639959bc
feat(changelogs): add support for change logs in controller templates
Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-04-30 16:28:57 +01:00
174 changed files with 2814 additions and 8687 deletions

View File

@ -8,6 +8,7 @@
PROJECT_NAME := upjet
PROJECT_REPO := github.com/crossplane/$(PROJECT_NAME)
GO_PROJECT := github.com/crossplane/$(PROJECT_NAME)/v2
# GOLANGCILINT_VERSION is inherited from build submodule by default.
# Uncomment below if you need to override the version.

View File

@ -9,11 +9,11 @@ import (
"path/filepath"
"github.com/alecthomas/kingpin/v2"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
"github.com/spf13/afero"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"github.com/crossplane/upjet/pkg/transformers"
"github.com/crossplane/upjet/v2/pkg/transformers"
)
func main() {

View File

@ -10,7 +10,7 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/crossplane/upjet/pkg/registry"
"github.com/crossplane/upjet/v2/pkg/registry"
)
func main() {

View File

@ -116,7 +116,7 @@ conditions:
```go
import (
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/v2/pkg/config"
...
)
@ -143,7 +143,7 @@ also omit `bucket` and `bucket_prefix` arguments from the spec with
```go
import (
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/v2/pkg/config"
...
)
@ -175,7 +175,7 @@ Here, we can just use [IdentifierFromProvider] configuration:
```go
import (
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/v2/pkg/config"
...
)
@ -206,7 +206,7 @@ this id back (`GetIDFn`).
```go
import (
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/v2/pkg/config"
...
)

View File

@ -141,7 +141,7 @@ variables in the `Makefile`:
cat <<EOF > config/repository/config.go
package repository
import "github.com/crossplane/upjet/pkg/config"
import "github.com/crossplane/upjet/v2/pkg/config"
// Configure configures individual resources by adding custom ResourceConfigurators.
func Configure(p *config.Provider) {
@ -163,7 +163,7 @@ variables in the `Makefile`:
cat <<EOF > config/branch/config.go
package branch
import "github.com/crossplane/upjet/pkg/config"
import "github.com/crossplane/upjet/v2/pkg/config"
func Configure(p *config.Provider) {
p.AddResourceConfigurator("github_branch", func(r *config.Resource) {

107
go.mod
View File

@ -2,52 +2,50 @@
//
// SPDX-License-Identifier: CC0-1.0
module github.com/crossplane/upjet
module github.com/crossplane/upjet/v2
go 1.23.6
go 1.24.0
toolchain go1.24.5
require (
dario.cat/mergo v1.0.1
dario.cat/mergo v1.0.2
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/antchfx/htmlquery v1.2.4
github.com/crossplane/crossplane v1.19.1
github.com/crossplane/crossplane-runtime v1.19.0
github.com/crossplane/crossplane-runtime/v2 v2.0.0-20250730220209-c306b1c8b181
github.com/fatih/camelcase v1.0.0
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.7.0
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
github.com/hashicorp/hcl/v2 v2.19.1
github.com/hashicorp/terraform-json v0.17.1
github.com/hashicorp/terraform-plugin-framework v1.4.1
github.com/hashicorp/terraform-plugin-go v0.19.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.30.0
github.com/hashicorp/go-cty v1.5.0
github.com/hashicorp/hcl/v2 v2.23.0
github.com/hashicorp/terraform-json v0.25.0
github.com/hashicorp/terraform-plugin-framework v1.15.0
github.com/hashicorp/terraform-plugin-go v0.28.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0
github.com/iancoleman/strcase v0.2.0
github.com/json-iterator/go v1.1.12
github.com/mitchellh/go-ps v1.0.0
github.com/muvaf/typewriter v0.0.0-20210910160850-80e49fe1eb32
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.20.2
github.com/spf13/afero v1.11.0
github.com/prometheus/client_golang v1.22.0
github.com/spf13/afero v1.12.0
github.com/tmccombs/hcl2json v0.3.3
github.com/yuin/goldmark v1.4.13
github.com/zclconf/go-cty v1.14.1
github.com/zclconf/go-cty v1.16.2
github.com/zclconf/go-cty-yaml v1.0.3
golang.org/x/net v0.39.0
golang.org/x/tools v0.32.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.32.3
k8s.io/apimachinery v0.32.3
k8s.io/cli-runtime v0.29.1
k8s.io/client-go v0.32.3
k8s.io/api v0.33.0
k8s.io/apimachinery v0.33.0
k8s.io/client-go v0.33.0
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e
sigs.k8s.io/controller-runtime v0.19.0
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
sigs.k8s.io/yaml v1.4.0
)
require (
github.com/Masterminds/semver v1.5.0 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
github.com/antchfx/xpath v1.2.0 // indirect
@ -57,12 +55,11 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
@ -72,25 +69,19 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-plugin v1.5.1 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-plugin v1.6.3 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
github.com/hashicorp/terraform-registry-address v0.2.2 // indirect
github.com/hashicorp/terraform-registry-address v0.2.5 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
@ -101,47 +92,45 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.opentelemetry.io/otel v1.33.0 // indirect
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/oauth2 v0.28.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/oauth2 v0.29.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.31.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/time v0.11.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/grpc v1.68.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/grpc v1.72.1 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/apiextensions-apiserver v0.32.3 // indirect
k8s.io/component-base v0.32.3 // indirect
k8s.io/apiextensions-apiserver v0.33.0 // indirect
k8s.io/code-generator v0.33.0 // indirect
k8s.io/component-base v0.33.0 // indirect
k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
sigs.k8s.io/controller-tools v0.16.5 // indirect
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
sigs.k8s.io/controller-tools v0.18.0 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
)

289
go.sum
View File

@ -1,11 +1,5 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
@ -30,29 +24,20 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/crossplane/crossplane v1.19.1 h1:gSLvOeJ7IaOPAsX0hakgl4vYNJYkDlBDPMw4XvUPVZk=
github.com/crossplane/crossplane v1.19.1/go.mod h1:ysqAAphb2yM1MLZbF6gOJOveK0fhM8ulAb5Ui+cLnio=
github.com/crossplane/crossplane-runtime v1.19.0 h1:yZmF8cYy8/PYiA+aF3ONjVinfuV06Vi3hHQwjYR2Qak=
github.com/crossplane/crossplane-runtime v1.19.0/go.mod h1:z8CV6dXxO5yxzfJGa4bgnWuyAnjZ3hY19cqGDcr5Bg0=
github.com/crossplane/crossplane-runtime/v2 v2.0.0-20250730220209-c306b1c8b181 h1:yU9+BtCiiMmymYt499egG5FZ1IQ7WddSi1V6H0h0DTk=
github.com/crossplane/crossplane-runtime/v2 v2.0.0-20250730220209-c306b1c8b181/go.mod h1:pkd5UzmE8esaZAApevMutR832GjJ1Qgc5Ngr78ByxrI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
@ -60,14 +45,14 @@ github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
@ -85,39 +70,22 @@ github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4
github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/addlicense v0.0.0-20210428195630-6d92264d7170/go.mod h1:EMjYTRimagHs1FwlIqKyX3wAM0u3rA+McvlIIWmSamA=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@ -128,47 +96,41 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI=
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-plugin v1.5.1 h1:oGm7cWBaYIp3lJpx1RUEfLWophprE2EV/KUeqBYo+6k=
github.com/hashicorp/go-plugin v1.5.1/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4=
github.com/hashicorp/go-cty v1.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0=
github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg=
github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl/v2 v2.9.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI=
github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/terraform-json v0.17.1 h1:eMfvh/uWggKmY7Pmb3T85u86E2EQg6EQHgyRwf3RkyA=
github.com/hashicorp/terraform-json v0.17.1/go.mod h1:Huy6zt6euxaY9knPAFKjUITn8QxUFIe9VuSzb4zn/0o=
github.com/hashicorp/terraform-plugin-framework v1.4.1 h1:ZC29MoB3Nbov6axHdgPbMz7799pT5H8kIrM8YAsaVrs=
github.com/hashicorp/terraform-plugin-framework v1.4.1/go.mod h1:XC0hPcQbBvlbxwmjxuV/8sn8SbZRg4XwGMs22f+kqV0=
github.com/hashicorp/terraform-plugin-go v0.19.0 h1:BuZx/6Cp+lkmiG0cOBk6Zps0Cb2tmqQpDM3iAtnhDQU=
github.com/hashicorp/terraform-plugin-go v0.19.0/go.mod h1:EhRSkEPNoylLQntYsk5KrDHTZJh9HQoumZXbOGOXmec=
github.com/hashicorp/terraform-json v0.25.0 h1:rmNqc/CIfcWawGiwXmRuiXJKEiJu1ntGoxseG1hLhoQ=
github.com/hashicorp/terraform-json v0.25.0/go.mod h1:sMKS8fiRDX4rVlR6EJUMudg1WcanxCMoWwTLkgZP/vc=
github.com/hashicorp/terraform-plugin-framework v1.15.0 h1:LQ2rsOfmDLxcn5EeIwdXFtr03FVsNktbbBci8cOKdb4=
github.com/hashicorp/terraform-plugin-framework v1.15.0/go.mod h1:hxrNI/GY32KPISpWqlCoTLM9JZsGH3CyYlir09bD/fI=
github.com/hashicorp/terraform-plugin-go v0.28.0 h1:zJmu2UDwhVN0J+J20RE5huiF3XXlTYVIleaevHZgKPA=
github.com/hashicorp/terraform-plugin-go v0.28.0/go.mod h1:FDa2Bb3uumkTGSkTFpWSOwWJDwA7bf3vdP3ltLDTH6o=
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.30.0 h1:X7vB6vn5tON2b49ILa4W7mFAsndeqJ7bZFOGbVO+0Cc=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.30.0/go.mod h1:ydFcxbdj6klCqYEPkPvdvFKiNGKZLUs+896ODUXCyao=
github.com/hashicorp/terraform-registry-address v0.2.2 h1:lPQBg403El8PPicg/qONZJDC6YlgCVbWDtNmmZKtBno=
github.com/hashicorp/terraform-registry-address v0.2.2/go.mod h1:LtwNbCihUoUZ3RYriyS2wF/lGPB6gF9ICLRtuDk7hSo=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0 h1:NFPMacTrY/IdcIcnUB+7hsore1ZaRWU9cnB6jFoBnIM=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0/go.mod h1:QYmYnLfsosrxjCnGY1p9c7Zj6n9thnEE+7RObeYs3fA=
github.com/hashicorp/terraform-registry-address v0.2.5 h1:2GTftHqmUhVOeuu9CW3kwDkRe4pcBDq0uuK5VJngU1M=
github.com/hashicorp/terraform-registry-address v0.2.5/go.mod h1:PpzXWINwB5kuVS5CA7m1+eO2f1jKb5ZDIxrOPfpnGkg=
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
@ -179,8 +141,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -221,8 +183,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/muvaf/typewriter v0.0.0-20210910160850-80e49fe1eb32 h1:yBQlHXLeUJL3TWVmzup5uT3wG5FLxhiTAiTsmNVocys=
@ -235,38 +195,32 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -274,8 +228,6 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@ -290,8 +242,8 @@ github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6Ac
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
@ -299,8 +251,6 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@ -309,17 +259,25 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.8.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA=
github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.0.3 h1:og/eOQ7lvA/WWhHGFETVWNduJM7Rjsv2RRpx1sdFMLc=
github.com/zclconf/go-cty-yaml v1.0.3/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs=
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY=
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@ -331,23 +289,15 @@ golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -360,19 +310,16 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -389,15 +336,13 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -406,15 +351,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@ -430,32 +371,17 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@ -471,38 +397,35 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY=
k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/cli-runtime v0.29.1 h1:By3WVOlEWYfyxhGko0f/IuAOLQcbBSMzwSaDren2JUs=
k8s.io/cli-runtime v0.29.1/go.mod h1:vjEY9slFp8j8UoMhV5AlO8uulX9xk6ogfIesHobyBDU=
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k=
k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI=
k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs=
k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc=
k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg=
k8s.io/code-generator v0.33.0 h1:B212FVl6EFqNmlgdOZYWNi77yBv+ed3QgQsMR8YQCw4=
k8s.io/code-generator v0.33.0/go.mod h1:KnJRokGxjvbBQkSJkbVuBbu6z4B0rC7ynkpY5Aw6m9o=
k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk=
k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU=
k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7 h1:2OX19X59HxDprNCVrWi6jb7LW1PoqTlYqEq5H2oetog=
k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q=
sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
sigs.k8s.io/controller-tools v0.16.5 h1:5k9FNRqziBPwqr17AMEPPV/En39ZBplLAdOwwQHruP4=
sigs.k8s.io/controller-tools v0.16.5/go.mod h1:8vztuRVzs8IuuJqKqbXCSlXcw+lkAv/M2sTpg55qjMY=
sigs.k8s.io/controller-tools v0.18.0 h1:rGxGZCZTV2wJreeRgqVoWab/mfcumTMmSwKzoM9xrsE=
sigs.k8s.io/controller-tools v0.18.0/go.mod h1:gLKoiGBriyNh+x1rWtUQnakUYEujErjXs9pf+x/8n1U=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0=
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY=
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U=
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016 h1:kXv6kKdoEtedwuqMmkqhbkgvYKeycVbC8+iPCP9j5kQ=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=

View File

@ -7,7 +7,7 @@ package config
import (
"github.com/pkg/errors"
"github.com/crossplane/upjet/pkg/resource/json"
"github.com/crossplane/upjet/v2/pkg/resource/json"
)
const (

View File

@ -10,9 +10,9 @@ import (
fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/crossplane/upjet/pkg/config/conversion"
"github.com/crossplane/upjet/pkg/registry"
tjname "github.com/crossplane/upjet/pkg/types/name"
"github.com/crossplane/upjet/v2/pkg/config/conversion"
"github.com/crossplane/upjet/v2/pkg/registry"
tjname "github.com/crossplane/upjet/v2/pkg/types/name"
)
const (
@ -33,17 +33,17 @@ var (
DefaultBasePackages = BasePackages{
APIVersion: []string{
// Default package for ProviderConfig APIs
"apis/v1alpha1",
"apis/v1beta1",
"v1alpha1",
"v1beta1",
},
Controller: []string{
// Default package for ProviderConfig controllers
"internal/controller/providerconfig",
"providerconfig",
},
ControllerMap: map[string]string{
// Default package for ProviderConfig controllers
"internal/controller/providerconfig": PackageNameConfig,
"providerconfig": PackageNameConfig,
},
}

View File

@ -13,8 +13,8 @@ import (
fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/crossplane/upjet/pkg/config/conversion"
"github.com/crossplane/upjet/pkg/registry"
"github.com/crossplane/upjet/v2/pkg/config/conversion"
"github.com/crossplane/upjet/v2/pkg/registry"
)
func TestDefaultResource(t *testing.T) {

View File

@ -8,8 +8,8 @@ import (
"fmt"
"slices"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"

View File

@ -9,10 +9,10 @@ import (
"slices"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
jsoniter "github.com/json-iterator/go"
"k8s.io/apimachinery/pkg/runtime"

View File

@ -10,7 +10,7 @@ import (
"sort"
"strings"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath"
"github.com/pkg/errors"
)
@ -103,7 +103,7 @@ func Convert(params map[string]any, paths []string, mode ListConversionMode, opt
pv := fieldpath.Pave(params)
for _, fp := range paths {
exp, err := pv.ExpandWildcards(fp)
if err != nil {
if err != nil && !fieldpath.IsNotFound(err) {
return nil, errors.Wrapf(err, "cannot expand wildcards for the field path expression %s", fp)
}
for _, e := range exp {

View File

@ -8,7 +8,7 @@ import (
"reflect"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"

View File

@ -12,8 +12,8 @@ import (
"text/template"
"text/template/parse"
"github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/v2/pkg/errors"
"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath"
)
const (

View File

@ -8,8 +8,8 @@ import (
"context"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/crossplane/crossplane-runtime/v2/pkg/errors"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
)

View File

@ -15,9 +15,9 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/pkg/errors"
"github.com/crossplane/upjet/pkg/registry"
"github.com/crossplane/upjet/pkg/schema/traverser"
conversiontfjson "github.com/crossplane/upjet/pkg/types/conversion/tfjson"
"github.com/crossplane/upjet/v2/pkg/registry"
"github.com/crossplane/upjet/v2/pkg/schema/traverser"
conversiontfjson "github.com/crossplane/upjet/v2/pkg/types/conversion/tfjson"
)
// ResourceConfiguratorFn is a function that implements the ResourceConfigurator

View File

@ -10,11 +10,13 @@ import (
"strings"
"time"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1"
"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/pkg/errors"
@ -23,8 +25,8 @@ import (
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/upjet/pkg/config/conversion"
"github.com/crossplane/upjet/pkg/registry"
"github.com/crossplane/upjet/v2/pkg/config/conversion"
"github.com/crossplane/upjet/v2/pkg/registry"
)
// A ListType is a type of list.
@ -517,6 +519,12 @@ type Resource struct {
// Terraform InstanceDiff is computed during reconciliation.
TerraformCustomDiff CustomDiff
// TerraformPluginFrameworkIsStateEmptyFn allows customizing the logic
// for determining whether a Terraform Plugin Framework state value should
// be considered empty/nil for resource existence checks. If not set, the
// default behavior uses tfStateValue.IsNull().
TerraformPluginFrameworkIsStateEmptyFn TerraformPluginFrameworkIsStateEmptyFn
// ServerSideApplyMergeStrategies configures the server-side apply merge
// strategy for the fields at the given map keys. The map key is
// a Terraform configuration argument path such as a.b.c, without any
@ -642,6 +650,13 @@ type CustomDiff func(diff *terraform.InstanceDiff, state *terraform.InstanceStat
// the JSON tags and tfMap is obtained by using the TF tags.
type ConfigurationInjector func(jsonMap map[string]any, tfMap map[string]any) error
// TerraformPluginFrameworkIsStateEmptyFn is a function that determines whether
// a Terraform Plugin Framework state value should be considered empty/nil for the
// purpose of determining resource existence. This allows providers to implement
// custom logic to handle cases where the standard IsNull() check is insufficient,
// such as when provider interceptors add fields like region to all state values.
type TerraformPluginFrameworkIsStateEmptyFn func(ctx context.Context, tfStateValue tftypes.Value, resourceSchema rschema.Schema) (bool, error)
// SchemaElementOptions represents schema element options for the
// schema elements of a Resource.
type SchemaElementOptions map[string]*SchemaElementOption

View File

@ -9,11 +9,11 @@ import (
"fmt"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/crossplane/crossplane-runtime/v2/pkg/errors"
"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
"sigs.k8s.io/controller-runtime/pkg/client"
)

View File

@ -8,7 +8,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/pkg/errors"
"github.com/crossplane/upjet/pkg/schema/traverser"
"github.com/crossplane/upjet/v2/pkg/schema/traverser"
)
var _ ResourceSetter = &SingletonListEmbedder{}

View File

@ -7,7 +7,7 @@ package config
import (
"testing"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

View File

@ -7,7 +7,7 @@ package config
import (
"github.com/pkg/errors"
"github.com/crossplane/upjet/pkg/config/conversion"
"github.com/crossplane/upjet/v2/pkg/config/conversion"
)
// Mode denotes the mode of the runtime Terraform conversion, e.g.,

View File

@ -7,8 +7,8 @@ package controller
import (
"context"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -16,9 +16,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
ctrl "sigs.k8s.io/controller-runtime/pkg/manager"
"github.com/crossplane/upjet/pkg/controller/handler"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/pkg/terraform"
"github.com/crossplane/upjet/v2/pkg/controller/handler"
"github.com/crossplane/upjet/v2/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/terraform"
)
const (
@ -113,12 +113,11 @@ type APICallbacks struct {
enableStatusUpdates bool
}
func (ac *APICallbacks) callbackFn(name, op string) terraform.CallbackFn {
func (ac *APICallbacks) callbackFn(nn types.NamespacedName, op string) terraform.CallbackFn {
return func(err error, ctx context.Context) error {
nn := types.NamespacedName{Name: name}
tr := ac.newTerraformed()
if kErr := ac.kube.Get(ctx, nn, tr); kErr != nil {
return errors.Wrapf(kErr, errGetFmt, tr.GetObjectKind().GroupVersionKind().String(), name, op)
return errors.Wrapf(kErr, errGetFmt, tr.GetObjectKind().GroupVersionKind().String(), nn, op)
}
// For the no-fork architecture, we will need to be able to report
// reconciliation errors. The proper place is the `Synced`
@ -143,19 +142,19 @@ func (ac *APICallbacks) callbackFn(name, op string) terraform.CallbackFn {
if ac.enableStatusUpdates {
tr.SetConditions(resource.AsyncOperationFinishedCondition())
}
uErr := errors.Wrapf(ac.kube.Status().Update(ctx, tr), errUpdateStatusFmt, tr.GetObjectKind().GroupVersionKind().String(), name, op)
uErr := errors.Wrapf(ac.kube.Status().Update(ctx, tr), errUpdateStatusFmt, tr.GetObjectKind().GroupVersionKind().String(), nn, op)
if ac.eventHandler != nil {
rateLimiter := handler.NoRateLimiter
switch {
case err != nil:
rateLimiter = rateLimiterCallback
default:
ac.eventHandler.Forget(rateLimiterCallback, name)
ac.eventHandler.Forget(rateLimiterCallback, nn)
}
// TODO: use the errors.Join from
// github.com/crossplane/crossplane-runtime.
if ok := ac.eventHandler.RequestReconcile(rateLimiter, name, nil); !ok {
return errors.Errorf(errReconcileRequestFmt, tr.GetObjectKind().GroupVersionKind().String(), name, op)
if ok := ac.eventHandler.RequestReconcile(rateLimiter, nn, nil); !ok {
return errors.Errorf(errReconcileRequestFmt, tr.GetObjectKind().GroupVersionKind().String(), nn, op)
}
}
return uErr
@ -163,7 +162,7 @@ func (ac *APICallbacks) callbackFn(name, op string) terraform.CallbackFn {
}
// Create makes sure the error is saved in async operation condition.
func (ac *APICallbacks) Create(name string) terraform.CallbackFn {
func (ac *APICallbacks) Create(name types.NamespacedName) terraform.CallbackFn {
// request will be requeued although the managed reconciler already
// requeues with exponential back-off during the creation phase
// because the upjet external client returns ResourceExists &
@ -175,12 +174,12 @@ func (ac *APICallbacks) Create(name string) terraform.CallbackFn {
}
// Update makes sure the error is saved in async operation condition.
func (ac *APICallbacks) Update(name string) terraform.CallbackFn {
func (ac *APICallbacks) Update(name types.NamespacedName) terraform.CallbackFn {
return ac.callbackFn(name, "update")
}
// Destroy makes sure the error is saved in async operation condition.
func (ac *APICallbacks) Destroy(name string) terraform.CallbackFn {
func (ac *APICallbacks) Destroy(name types.NamespacedName) terraform.CallbackFn {
// request will be requeued although the managed reconciler requeues
// with exponential back-off during the deletion phase because
// during the async deletion operation, external client's

View File

@ -8,17 +8,18 @@ import (
"context"
"testing"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
xpfake "github.com/crossplane/crossplane-runtime/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/pkg/test"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
xpfake "github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
ctrl "sigs.k8s.io/controller-runtime/pkg/manager"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/pkg/resource/fake"
tjerrors "github.com/crossplane/upjet/pkg/terraform/errors"
"github.com/crossplane/upjet/v2/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/resource/fake"
tjerrors "github.com/crossplane/upjet/v2/pkg/terraform/errors"
)
func TestAPICallbacksCreate(t *testing.T) {
@ -88,14 +89,14 @@ func TestAPICallbacksCreate(t *testing.T) {
},
},
want: want{
err: errors.Wrapf(errBoom, errGetFmt, "", ", Kind=/name", "create"),
err: errors.Wrapf(errBoom, errGetFmt, "", ", Kind=//name", "create"),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
e := NewAPICallbacks(tc.args.mgr, tc.args.mg)
err := e.Create("name")(tc.args.err, context.TODO())
err := e.Create(types.NamespacedName{Name: "name"})(tc.args.err, context.TODO())
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nCreate(...): -want error, +got error:\n%s", tc.reason, diff)
}
@ -170,14 +171,14 @@ func TestAPICallbacksUpdate(t *testing.T) {
},
},
want: want{
err: errors.Wrapf(errBoom, errGetFmt, "", ", Kind=/name", "update"),
err: errors.Wrapf(errBoom, errGetFmt, "", ", Kind=//name", "update"),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
e := NewAPICallbacks(tc.args.mgr, tc.args.mg)
err := e.Update("name")(tc.args.err, context.TODO())
err := e.Update(types.NamespacedName{Name: "name"})(tc.args.err, context.TODO())
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nUpdate(...): -want error, +got error:\n%s", tc.reason, diff)
}
@ -252,14 +253,290 @@ func TestAPICallbacks_Destroy(t *testing.T) {
},
},
want: want{
err: errors.Wrapf(errBoom, errGetFmt, "", ", Kind=/name", "destroy"),
err: errors.Wrapf(errBoom, errGetFmt, "", ", Kind=//name", "destroy"),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
e := NewAPICallbacks(tc.args.mgr, tc.args.mg)
err := e.Destroy("name")(tc.args.err, context.TODO())
err := e.Destroy(types.NamespacedName{Name: "name"})(tc.args.err, context.TODO())
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nDestroy(...): -want error, +got error:\n%s", tc.reason, diff)
}
})
}
}
func TestAPICallbacksCreate_namespaced(t *testing.T) {
type args struct {
mgr ctrl.Manager
mg xpresource.ManagedKind
err error
}
type want struct {
err error
}
cases := map[string]struct {
reason string
args
want
}{
"CreateOperationFailed": {
reason: "It should update the condition with error if async apply failed",
args: args{
mg: xpresource.ManagedKind(xpfake.GVK(&fake.ModernTerraformed{})),
mgr: &xpfake.Manager{
Client: &test.MockClient{
MockGet: func(_ context.Context, gotKey client.ObjectKey, _ client.Object) error {
if diff := cmp.Diff(client.ObjectKey{Name: "name", Namespace: "foo-ns"}, gotKey); diff != "" {
t.Errorf("\nGet(...): -want object key, +got object key:\n%s", diff)
}
return nil
},
MockStatusUpdate: func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
got := obj.(resource.Terraformed).GetCondition(resource.TypeLastAsyncOperation)
if diff := cmp.Diff(resource.LastAsyncOperationCondition(tjerrors.NewApplyFailed(nil)), got); diff != "" {
t.Errorf("\nCreate(...): -want error, +got error:\n%s", diff)
}
return nil
},
},
Scheme: xpfake.SchemeWith(&fake.ModernTerraformed{}),
},
err: tjerrors.NewApplyFailed(nil),
},
},
"CreateOperationSucceeded": {
reason: "It should update the condition with success if the apply operation does not report error",
args: args{
mg: xpresource.ManagedKind(xpfake.GVK(&fake.ModernTerraformed{})),
mgr: &xpfake.Manager{
Client: &test.MockClient{
MockGet: func(_ context.Context, gotKey client.ObjectKey, _ client.Object) error {
if diff := cmp.Diff(client.ObjectKey{Name: "name", Namespace: "foo-ns"}, gotKey); diff != "" {
t.Errorf("\nGet(...): -want object key, +got object key:\n%s", diff)
}
return nil
},
MockStatusUpdate: func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
got := obj.(resource.Terraformed).GetCondition(resource.TypeLastAsyncOperation)
if diff := cmp.Diff(resource.LastAsyncOperationCondition(nil), got); diff != "" {
t.Errorf("\nCreate(...): -want error, +got error:\n%s", diff)
}
return nil
},
},
Scheme: xpfake.SchemeWith(&fake.ModernTerraformed{}),
},
},
},
"CannotGet": {
reason: "It should return error if it cannot get the resource to update",
args: args{
mg: xpresource.ManagedKind(xpfake.GVK(&fake.ModernTerraformed{})),
mgr: &xpfake.Manager{
Client: &test.MockClient{
MockGet: func(_ context.Context, _ client.ObjectKey, _ client.Object) error {
return errBoom
},
},
Scheme: xpfake.SchemeWith(&fake.ModernTerraformed{}),
},
},
want: want{
err: errors.Wrapf(errBoom, errGetFmt, "", ", Kind=/foo-ns/name", "create"),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
e := NewAPICallbacks(tc.args.mgr, tc.args.mg)
err := e.Create(types.NamespacedName{Name: "name", Namespace: "foo-ns"})(tc.args.err, context.TODO())
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nCreate(...): -want error, +got error:\n%s", tc.reason, diff)
}
})
}
}
func TestAPICallbacksUpdate_namespaced(t *testing.T) {
type args struct {
mgr ctrl.Manager
mg xpresource.ManagedKind
err error
}
type want struct {
err error
}
cases := map[string]struct {
reason string
args
want
}{
"UpdateOperationFailed": {
reason: "It should update the condition with error if async apply failed",
args: args{
mg: xpresource.ManagedKind(xpfake.GVK(&fake.ModernTerraformed{})),
mgr: &xpfake.Manager{
Client: &test.MockClient{
MockGet: func(_ context.Context, gotKey client.ObjectKey, _ client.Object) error {
if diff := cmp.Diff(client.ObjectKey{Name: "name", Namespace: "foo-ns"}, gotKey); diff != "" {
t.Errorf("\nGet(...): -want object key, +got object key:\n%s", diff)
}
return nil
},
MockStatusUpdate: func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
got := obj.(resource.Terraformed).GetCondition(resource.TypeLastAsyncOperation)
if diff := cmp.Diff(resource.LastAsyncOperationCondition(tjerrors.NewApplyFailed(nil)), got); diff != "" {
t.Errorf("\nUpdate(...): -want error, +got error:\n%s", diff)
}
return nil
},
},
Scheme: xpfake.SchemeWith(&fake.ModernTerraformed{}),
},
err: tjerrors.NewApplyFailed(nil),
},
},
"ApplyOperationSucceeded": {
reason: "It should update the condition with success if the apply operation does not report error",
args: args{
mg: xpresource.ManagedKind(xpfake.GVK(&fake.ModernTerraformed{})),
mgr: &xpfake.Manager{
Client: &test.MockClient{
MockGet: func(_ context.Context, gotKey client.ObjectKey, _ client.Object) error {
if diff := cmp.Diff(client.ObjectKey{Name: "name", Namespace: "foo-ns"}, gotKey); diff != "" {
t.Errorf("\nGet(...): -want object key, +got object key:\n%s", diff)
}
return nil
},
MockStatusUpdate: func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
got := obj.(resource.Terraformed).GetCondition(resource.TypeLastAsyncOperation)
if diff := cmp.Diff(resource.LastAsyncOperationCondition(nil), got); diff != "" {
t.Errorf("\nUpdate(...): -want error, +got error:\n%s", diff)
}
return nil
},
},
Scheme: xpfake.SchemeWith(&fake.ModernTerraformed{}),
},
},
},
"CannotGet": {
reason: "It should return error if it cannot get the resource to update",
args: args{
mg: xpresource.ManagedKind(xpfake.GVK(&fake.ModernTerraformed{})),
mgr: &xpfake.Manager{
Client: &test.MockClient{
MockGet: func(_ context.Context, _ client.ObjectKey, _ client.Object) error {
return errBoom
},
},
Scheme: xpfake.SchemeWith(&fake.ModernTerraformed{}),
},
},
want: want{
err: errors.Wrapf(errBoom, errGetFmt, "", ", Kind=/foo-ns/name", "update"),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
e := NewAPICallbacks(tc.args.mgr, tc.args.mg)
err := e.Update(types.NamespacedName{Name: "name", Namespace: "foo-ns"})(tc.args.err, context.TODO())
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nUpdate(...): -want error, +got error:\n%s", tc.reason, diff)
}
})
}
}
func TestAPICallbacks_Destroy_namespaced(t *testing.T) {
type args struct {
mgr ctrl.Manager
mg xpresource.ManagedKind
err error
}
type want struct {
err error
}
cases := map[string]struct {
reason string
args
want
}{
"DestroyOperationFailed": {
reason: "It should update the condition with error if async destroy failed",
args: args{
mg: xpresource.ManagedKind(xpfake.GVK(&fake.ModernTerraformed{})),
mgr: &xpfake.Manager{
Client: &test.MockClient{
MockGet: func(_ context.Context, gotKey client.ObjectKey, _ client.Object) error {
if diff := cmp.Diff(client.ObjectKey{Name: "name", Namespace: "foo-ns"}, gotKey); diff != "" {
t.Errorf("\nGet(...): -want object key, +got object key:\n%s", diff)
}
return nil
},
MockStatusUpdate: func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
got := obj.(resource.Terraformed).GetCondition(resource.TypeLastAsyncOperation)
if diff := cmp.Diff(resource.LastAsyncOperationCondition(tjerrors.NewDestroyFailed(nil)), got); diff != "" {
t.Errorf("\nDestroy(...): -want error, +got error:\n%s", diff)
}
return nil
},
},
Scheme: xpfake.SchemeWith(&fake.ModernTerraformed{}),
},
err: tjerrors.NewDestroyFailed(nil),
},
},
"DestroyOperationSucceeded": {
reason: "It should update the condition with success if the destroy operation does not report error",
args: args{
mg: xpresource.ManagedKind(xpfake.GVK(&fake.ModernTerraformed{})),
mgr: &xpfake.Manager{
Client: &test.MockClient{
MockGet: func(_ context.Context, gotKey client.ObjectKey, _ client.Object) error {
if diff := cmp.Diff(client.ObjectKey{Name: "name", Namespace: "foo-ns"}, gotKey); diff != "" {
t.Errorf("\nGet(...): -want object key, +got object key:\n%s", diff)
}
return nil
},
MockStatusUpdate: func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
got := obj.(resource.Terraformed).GetCondition(resource.TypeLastAsyncOperation)
if diff := cmp.Diff(resource.LastAsyncOperationCondition(nil), got); diff != "" {
t.Errorf("\nDestroy(...): -want error, +got error:\n%s", diff)
}
return nil
},
},
Scheme: xpfake.SchemeWith(&fake.ModernTerraformed{}),
},
},
},
"CannotGet": {
reason: "It should return error if it cannot get the resource to update",
args: args{
mg: xpresource.ManagedKind(xpfake.GVK(&fake.ModernTerraformed{})),
mgr: &xpfake.Manager{
Client: &test.MockClient{
MockGet: func(_ context.Context, _ client.ObjectKey, _ client.Object) error {
return errBoom
},
},
Scheme: xpfake.SchemeWith(&fake.ModernTerraformed{}),
},
},
want: want{
err: errors.Wrapf(errBoom, errGetFmt, "", ", Kind=/foo-ns/name", "destroy"),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
e := NewAPICallbacks(tc.args.mgr, tc.args.mg)
err := e.Destroy(types.NamespacedName{Name: "name", Namespace: "foo-ns"})(tc.args.err, context.TODO())
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nDestroy(...): -want error, +got error:\n%s", tc.reason, diff)
}

View File

@ -5,13 +5,13 @@
package conversion
import (
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"github.com/crossplane/upjet/pkg/config/conversion"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/config/conversion"
"github.com/crossplane/upjet/v2/pkg/resource"
)
const (

View File

@ -8,18 +8,18 @@ import (
"fmt"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/config/conversion"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/pkg/resource/fake"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/config/conversion"
"github.com/crossplane/upjet/v2/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/resource/fake"
)
const (
@ -170,7 +170,7 @@ func TestRoundTrip(t *testing.T) {
r := &registry{
scheme: s,
}
if err := r.RegisterConversions(p); err != nil {
if err := r.RegisterConversions(p, nil); err != nil {
t.Fatalf("\n%s\nRegisterConversions(p): Failed to register the conversions with the registry.\n", tc.reason)
}
err := r.RoundTrip(tc.args.dst, tc.args.src)

View File

@ -8,9 +8,9 @@ import (
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/runtime"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/config/conversion"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/config/conversion"
"github.com/crossplane/upjet/v2/pkg/resource"
)
const (
@ -21,17 +21,19 @@ var instance *registry
// registry represents the conversion hook registry for a provider.
type registry struct {
provider *config.Provider
providerCluster *config.Provider
providerNamespaced *config.Provider
scheme *runtime.Scheme
}
// RegisterConversions registers the API version conversions from the specified
// provider configuration with this registry.
func (r *registry) RegisterConversions(provider *config.Provider) error {
if r.provider != nil {
func (r *registry) RegisterConversions(providerCluster, providerNamespaced *config.Provider) error {
if r.providerCluster != nil || r.providerNamespaced != nil {
return errors.New(errAlreadyRegistered)
}
r.provider = provider
r.providerCluster = providerCluster
r.providerNamespaced = providerNamespaced
return nil
}
@ -39,10 +41,17 @@ func (r *registry) RegisterConversions(provider *config.Provider) error {
// registry for the specified Terraformed resource.
func (r *registry) GetConversions(tr resource.Terraformed) []conversion.Conversion {
t := tr.GetTerraformResourceType()
if r == nil || r.provider == nil || r.provider.Resources[t] == nil {
p := r.providerCluster
if tr.GetNamespace() != "" {
p = r.providerNamespaced
}
if p == nil || p.Resources[t] == nil {
return nil
}
return r.provider.Resources[t].Conversions
return p.Resources[t].Conversions
}
// GetConversions returns the conversion.Conversions registered for the
@ -56,12 +65,12 @@ func GetConversions(tr resource.Terraformed) []conversion.Conversion {
// for the types whose versions are to be converted. If a registration for a
// Go schema is not found in the specified registry, RoundTrip does not error
// but only wildcard conversions must be used with the registry.
func RegisterConversions(provider *config.Provider, scheme *runtime.Scheme) error {
func RegisterConversions(providerCluster, providerNamespaced *config.Provider, scheme *runtime.Scheme) error {
if instance != nil {
return errors.New(errAlreadyRegistered)
}
instance = &registry{
scheme: scheme,
}
return instance.RegisterConversions(provider)
return instance.RegisterConversions(providerCluster, providerNamespaced)
}

View File

@ -8,21 +8,22 @@ import (
"context"
"time"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/controller/handler"
"github.com/crossplane/upjet/pkg/metrics"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/pkg/resource/json"
"github.com/crossplane/upjet/pkg/terraform"
tferrors "github.com/crossplane/upjet/pkg/terraform/errors"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/controller/handler"
"github.com/crossplane/upjet/v2/pkg/metrics"
"github.com/crossplane/upjet/v2/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/resource/json"
"github.com/crossplane/upjet/v2/pkg/terraform"
tferrors "github.com/crossplane/upjet/v2/pkg/terraform/errors"
)
const (
@ -125,7 +126,7 @@ func (c *Connector) Connect(ctx context.Context, mg xpresource.Managed) (managed
providerHandle: ws.ProviderHandle,
eventHandler: c.eventHandler,
kube: c.kube,
logger: c.logger.WithValues("uid", mg.GetUID(), "name", mg.GetName(), "gvk", mg.GetObjectKind().GroupVersionKind().String()),
logger: c.logger.WithValues("uid", mg.GetUID(), "namespace", mg.GetNamespace(), "name", mg.GetName(), "gvk", mg.GetObjectKind().GroupVersionKind().String()),
}, nil
}
@ -140,7 +141,7 @@ type external struct {
logger logging.Logger
}
func (e *external) scheduleProvider(name string) (bool, error) {
func (e *external) scheduleProvider(name types.NamespacedName) (bool, error) {
if e.providerScheduler == nil || e.workspace == nil {
return false, nil
}
@ -176,7 +177,11 @@ func (e *external) Observe(ctx context.Context, mg xpresource.Managed) (managed.
// and serial.
// TODO(muvaf): Look for ways to reduce the cyclomatic complexity without
// increasing the difficulty of understanding the flow.
requeued, err := e.scheduleProvider(mg.GetName())
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
requeued, err := e.scheduleProvider(name)
if err != nil {
return managed.ExternalObservation{}, errors.Wrapf(err, "cannot schedule a native provider during observe: %s", mg.GetUID())
}
@ -308,7 +313,11 @@ func (e *external) Observe(ctx context.Context, mg xpresource.Managed) (managed.
tr.SetConditions(xpv1.Available())
e.logger.Debug("Resource is marked as available.")
if e.eventHandler != nil {
e.eventHandler.RequestReconcile(rateLimiterStatus, mg.GetName(), nil)
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
e.eventHandler.RequestReconcile(rateLimiterStatus, name, nil)
}
return managed.ExternalObservation{
ResourceExists: true,
@ -328,7 +337,11 @@ func (e *external) Observe(ctx context.Context, mg xpresource.Managed) (managed.
// now we do a Workspace.Refresh
default:
if e.eventHandler != nil {
e.eventHandler.Forget(rateLimiterStatus, mg.GetName())
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
e.eventHandler.Forget(rateLimiterStatus, name)
}
// TODO(cem): Consider skipping diff calculation (terraform plan) to
@ -356,7 +369,11 @@ func addTTR(mg xpresource.Managed) {
}
func (e *external) Create(ctx context.Context, mg xpresource.Managed) (managed.ExternalCreation, error) {
requeued, err := e.scheduleProvider(mg.GetName())
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
requeued, err := e.scheduleProvider(name)
if err != nil {
return managed.ExternalCreation{}, errors.Wrapf(err, "cannot schedule a native provider during create: %s", mg.GetUID())
}
@ -365,7 +382,7 @@ func (e *external) Create(ctx context.Context, mg xpresource.Managed) (managed.E
}
defer e.stopProvider()
if e.config.UseAsync {
return managed.ExternalCreation{}, errors.Wrap(e.workspace.ApplyAsync(e.callback.Create(mg.GetName())), errStartAsyncApply)
return managed.ExternalCreation{}, errors.Wrap(e.workspace.ApplyAsync(e.callback.Create(name)), errStartAsyncApply)
}
tr, ok := mg.(resource.Terraformed)
if !ok {
@ -391,7 +408,11 @@ func (e *external) Create(ctx context.Context, mg xpresource.Managed) (managed.E
}
func (e *external) Update(ctx context.Context, mg xpresource.Managed) (managed.ExternalUpdate, error) {
requeued, err := e.scheduleProvider(mg.GetName())
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
requeued, err := e.scheduleProvider(name)
if err != nil {
return managed.ExternalUpdate{}, errors.Wrapf(err, "cannot schedule a native provider during update: %s", mg.GetUID())
}
@ -400,7 +421,7 @@ func (e *external) Update(ctx context.Context, mg xpresource.Managed) (managed.E
}
defer e.stopProvider()
if e.config.UseAsync {
return managed.ExternalUpdate{}, errors.Wrap(e.workspace.ApplyAsync(e.callback.Update(mg.GetName())), errStartAsyncApply)
return managed.ExternalUpdate{}, errors.Wrap(e.workspace.ApplyAsync(e.callback.Update(name)), errStartAsyncApply)
}
tr, ok := mg.(resource.Terraformed)
if !ok {
@ -418,7 +439,11 @@ func (e *external) Update(ctx context.Context, mg xpresource.Managed) (managed.E
}
func (e *external) Delete(ctx context.Context, mg xpresource.Managed) (managed.ExternalDelete, error) {
requeued, err := e.scheduleProvider(mg.GetName())
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
requeued, err := e.scheduleProvider(name)
if err != nil {
return managed.ExternalDelete{}, errors.Wrapf(err, "cannot schedule a native provider during delete: %s", mg.GetUID())
}
@ -427,7 +452,7 @@ func (e *external) Delete(ctx context.Context, mg xpresource.Managed) (managed.E
}
defer e.stopProvider()
if e.config.UseAsync {
return managed.ExternalDelete{}, errors.Wrap(e.workspace.DestroyAsync(e.callback.Destroy(mg.GetName())), errStartAsyncDestroy)
return managed.ExternalDelete{}, errors.Wrap(e.workspace.DestroyAsync(e.callback.Destroy(name)), errStartAsyncDestroy)
}
return managed.ExternalDelete{}, errors.Wrap(e.workspace.Destroy(ctx), errDestroy)
}

View File

@ -8,21 +8,22 @@ import (
"context"
"fmt"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
"github.com/crossplane/crossplane-runtime/v2/pkg/meta"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/controller/handler"
"github.com/crossplane/upjet/pkg/metrics"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/pkg/terraform"
tferrors "github.com/crossplane/upjet/pkg/terraform/errors"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/controller/handler"
"github.com/crossplane/upjet/v2/pkg/metrics"
"github.com/crossplane/upjet/v2/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/terraform"
tferrors "github.com/crossplane/upjet/v2/pkg/terraform/errors"
)
// TerraformPluginFrameworkAsyncConnector is a managed reconciler Connecter
@ -42,7 +43,8 @@ func NewTerraformPluginFrameworkAsyncConnector(kube client.Client,
ots *OperationTrackerStore,
sf terraform.SetupFn,
cfg *config.Resource,
opts ...TerraformPluginFrameworkAsyncOption) *TerraformPluginFrameworkAsyncConnector {
opts ...TerraformPluginFrameworkAsyncOption,
) *TerraformPluginFrameworkAsyncConnector {
nfac := &TerraformPluginFrameworkAsyncConnector{
TerraformPluginFrameworkConnector: NewTerraformPluginFrameworkConnector(kube, sf, cfg, ots),
}
@ -178,7 +180,11 @@ func (n *terraformPluginFrameworkAsyncExternalClient) Create(_ context.Context,
n.opTracker.logger.Debug("Async create ended.", "error", err)
n.opTracker.LastOperation.MarkEnd()
if cErr := n.callback.Create(mg.GetName())(err, ctx); cErr != nil {
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
if cErr := n.callback.Create(name)(err, ctx); cErr != nil {
n.opTracker.logger.Info("Async create callback failed", "error", cErr.Error())
}
}()
@ -211,7 +217,11 @@ func (n *terraformPluginFrameworkAsyncExternalClient) Update(_ context.Context,
n.opTracker.logger.Debug("Async update ended.", "error", err)
n.opTracker.LastOperation.MarkEnd()
if cErr := n.callback.Update(mg.GetName())(err, ctx); cErr != nil {
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
if cErr := n.callback.Update(name)(err, ctx); cErr != nil {
n.opTracker.logger.Info("Async update callback failed", "error", cErr.Error())
}
}()
@ -248,7 +258,11 @@ func (n *terraformPluginFrameworkAsyncExternalClient) Delete(_ context.Context,
n.opTracker.logger.Debug("Async delete ended.", "error", err)
n.opTracker.LastOperation.MarkEnd()
if cErr := n.callback.Destroy(mg.GetName())(err, ctx); cErr != nil {
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
if cErr := n.callback.Destroy(name)(err, ctx); cErr != nil {
n.opTracker.logger.Info("Async delete callback failed", "error", cErr.Error())
}
}()

View File

@ -8,20 +8,21 @@ import (
"context"
"time"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
"github.com/crossplane/crossplane-runtime/v2/pkg/meta"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/controller/handler"
"github.com/crossplane/upjet/pkg/metrics"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/pkg/terraform"
tferrors "github.com/crossplane/upjet/pkg/terraform/errors"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/controller/handler"
"github.com/crossplane/upjet/v2/pkg/metrics"
"github.com/crossplane/upjet/v2/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/terraform"
tferrors "github.com/crossplane/upjet/v2/pkg/terraform/errors"
)
var defaultAsyncTimeout = 1 * time.Hour
@ -156,7 +157,11 @@ func (n *terraformPluginSDKAsyncExternal) Create(_ context.Context, mg xpresourc
n.opTracker.logger.Debug("Async create ended.", "error", err, "tfID", n.opTracker.GetTfID())
n.opTracker.LastOperation.MarkEnd()
if cErr := n.callback.Create(mg.GetName())(err, ctx); cErr != nil {
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
if cErr := n.callback.Create(name)(err, ctx); cErr != nil {
n.opTracker.logger.Info("Async create callback failed", "error", cErr.Error())
}
}()
@ -189,7 +194,11 @@ func (n *terraformPluginSDKAsyncExternal) Update(_ context.Context, mg xpresourc
n.opTracker.logger.Debug("Async update ended.", "error", err, "tfID", n.opTracker.GetTfID())
n.opTracker.LastOperation.MarkEnd()
if cErr := n.callback.Update(mg.GetName())(err, ctx); cErr != nil {
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
if cErr := n.callback.Update(name)(err, ctx); cErr != nil {
n.opTracker.logger.Info("Async update callback failed", "error", cErr.Error())
}
}()
@ -226,7 +235,11 @@ func (n *terraformPluginSDKAsyncExternal) Delete(_ context.Context, mg xpresourc
n.opTracker.logger.Debug("Async delete ended.", "error", err, "tfID", n.opTracker.GetTfID())
n.opTracker.LastOperation.MarkEnd()
if cErr := n.callback.Destroy(mg.GetName())(err, ctx); cErr != nil {
name := types.NamespacedName{
Namespace: mg.GetNamespace(),
Name: mg.GetName(),
}
if cErr := n.callback.Destroy(name)(err, ctx); cErr != nil {
n.opTracker.logger.Info("Async delete callback failed", "error", cErr.Error())
}
}()

View File

@ -8,18 +8,19 @@ import (
"context"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
tf "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/resource/fake"
"github.com/crossplane/upjet/pkg/terraform"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/resource/fake"
"github.com/crossplane/upjet/v2/pkg/terraform"
)
var (
@ -227,7 +228,7 @@ func TestAsyncTerraformPluginSDKCreate(t *testing.T) {
cfg: cfgAsync,
obj: objAsync,
fns: CallbackFns{
CreateFn: func(s string) terraform.CallbackFn {
CreateFn: func(nn types.NamespacedName) terraform.CallbackFn {
return func(err error, ctx context.Context) error {
return nil
}
@ -271,7 +272,7 @@ func TestAsyncTerraformPluginSDKUpdate(t *testing.T) {
cfg: cfgAsync,
obj: objAsync,
fns: CallbackFns{
UpdateFn: func(s string) terraform.CallbackFn {
UpdateFn: func(nn types.NamespacedName) terraform.CallbackFn {
return func(err error, ctx context.Context) error {
return nil
}
@ -315,7 +316,7 @@ func TestAsyncTerraformPluginSDKDelete(t *testing.T) {
cfg: cfgAsync,
obj: objAsync,
fns: CallbackFns{
DestroyFn: func(s string) terraform.CallbackFn {
DestroyFn: func(nn types.NamespacedName) terraform.CallbackFn {
return func(err error, ctx context.Context) error {
return nil
}

View File

@ -8,24 +8,25 @@ import (
"context"
"testing"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/logging"
xpmeta "github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
xpfake "github.com/crossplane/crossplane-runtime/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/pkg/test"
xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
xpmeta "github.com/crossplane/crossplane-runtime/v2/pkg/meta"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
xpfake "github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/pkg/resource/fake"
"github.com/crossplane/upjet/pkg/resource/json"
"github.com/crossplane/upjet/pkg/terraform"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/resource/fake"
"github.com/crossplane/upjet/v2/pkg/resource/json"
"github.com/crossplane/upjet/v2/pkg/terraform"
)
const (
@ -98,20 +99,20 @@ func (s StoreFns) Workspace(ctx context.Context, c resource.SecretClient, tr res
}
type CallbackFns struct {
CreateFn func(string) terraform.CallbackFn
UpdateFn func(string) terraform.CallbackFn
DestroyFn func(string) terraform.CallbackFn
CreateFn func(types.NamespacedName) terraform.CallbackFn
UpdateFn func(types.NamespacedName) terraform.CallbackFn
DestroyFn func(types.NamespacedName) terraform.CallbackFn
}
func (c CallbackFns) Create(name string) terraform.CallbackFn {
func (c CallbackFns) Create(name types.NamespacedName) terraform.CallbackFn {
return c.CreateFn(name)
}
func (c CallbackFns) Update(name string) terraform.CallbackFn {
func (c CallbackFns) Update(name types.NamespacedName) terraform.CallbackFn {
return c.UpdateFn(name)
}
func (c CallbackFns) Destroy(name string) terraform.CallbackFn {
func (c CallbackFns) Destroy(name types.NamespacedName) terraform.CallbackFn {
return c.DestroyFn(name)
}
@ -645,7 +646,7 @@ func TestCreate(t *testing.T) {
UseAsync: true,
},
c: CallbackFns{
CreateFn: func(s string) terraform.CallbackFn {
CreateFn: func(nn types.NamespacedName) terraform.CallbackFn {
return nil
},
},
@ -718,7 +719,7 @@ func TestUpdate(t *testing.T) {
UseAsync: true,
},
c: CallbackFns{
UpdateFn: func(s string) terraform.CallbackFn {
UpdateFn: func(nn types.NamespacedName) terraform.CallbackFn {
return nil
},
},
@ -782,7 +783,7 @@ func TestDelete(t *testing.T) {
UseAsync: true,
},
c: CallbackFns{
DestroyFn: func(_ string) terraform.CallbackFn {
DestroyFn: func(_ types.NamespacedName) terraform.CallbackFn {
return nil
},
},

View File

@ -13,11 +13,11 @@ import (
"strings"
"time"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
"github.com/crossplane/crossplane-runtime/v2/pkg/meta"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
fwdiag "github.com/hashicorp/terraform-plugin-framework/diag"
fwprovider "github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
@ -30,11 +30,11 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/metrics"
"github.com/crossplane/upjet/pkg/resource"
upjson "github.com/crossplane/upjet/pkg/resource/json"
"github.com/crossplane/upjet/pkg/terraform"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/metrics"
"github.com/crossplane/upjet/v2/pkg/resource"
upjson "github.com/crossplane/upjet/v2/pkg/resource/json"
"github.com/crossplane/upjet/v2/pkg/terraform"
)
// TerraformPluginFrameworkConnector is an external client, with credentials and
@ -109,8 +109,8 @@ type terraformPluginFrameworkExternalClient struct {
// Connect makes sure the underlying client is ready to issue requests to the
// provider API.
func (c *TerraformPluginFrameworkConnector) Connect(ctx context.Context, mg xpresource.Managed) (managed.ExternalClient, error) { //nolint:gocyclo
c.metricRecorder.ObserveReconcileDelay(mg.GetObjectKind().GroupVersionKind(), mg.GetName())
logger := c.logger.WithValues("uid", mg.GetUID(), "name", mg.GetName(), "gvk", mg.GetObjectKind().GroupVersionKind().String())
c.metricRecorder.ObserveReconcileDelay(mg.GetObjectKind().GroupVersionKind(), metrics.NameForManaged(mg))
logger := c.logger.WithValues("uid", mg.GetUID(), "name", mg.GetName(), "namespace", mg.GetNamespace(), "gvk", mg.GetObjectKind().GroupVersionKind().String())
logger.Debug("Connecting to the service provider")
start := time.Now()
ts, err := c.getTerraformSetup(ctx, c.kube, mg)
@ -124,7 +124,7 @@ func (c *TerraformPluginFrameworkConnector) Connect(ctx context.Context, mg xpre
externalName := meta.GetExternalName(tr)
params, err := getExtendedParameters(ctx, tr, externalName, c.config, ts, c.isManagementPoliciesEnabled, c.kube)
if err != nil {
return nil, errors.Wrapf(err, "failed to get the extended parameters for resource %q", mg.GetName())
return nil, errors.Wrapf(err, "failed to get the extended parameters for resource %q", client.ObjectKeyFromObject(mg))
}
resourceSchema, err := c.getResourceSchema(ctx)
@ -202,6 +202,10 @@ func (c *TerraformPluginFrameworkConnector) getResourceSchema(ctx context.Contex
// at the terraform setup layer with the relevant provider meta if needed
// by the provider implementation.
func (c *TerraformPluginFrameworkConnector) configureProvider(ctx context.Context, ts terraform.Setup) (tfprotov5.ProviderServer, error) {
if ts.FrameworkProvider == nil {
return nil, fmt.Errorf("cannot retrieve framework provider")
}
var schemaResp fwprovider.SchemaResponse
ts.FrameworkProvider.Schema(ctx, fwprovider.SchemaRequest{}, &schemaResp)
if schemaResp.Diagnostics.HasError() {
@ -319,7 +323,31 @@ func (n *terraformPluginFrameworkExternalClient) Observe(ctx context.Context, mg
}
n.opTracker.SetFrameworkTFState(readResponse.NewState)
resourceExists := !tfStateValue.IsNull()
// Determine if the resource exists based on Terraform state
var resourceExists bool
if !tfStateValue.IsNull() {
// Resource state is not null, assume it exists
resourceExists = true
// If a custom empty state check function is configured, use it to verify existence
if n.config.TerraformPluginFrameworkIsStateEmptyFn != nil {
isEmpty, err := n.config.TerraformPluginFrameworkIsStateEmptyFn(ctx, tfStateValue, n.resourceSchema)
if err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, "cannot check if TF State is empty")
}
// Override existence based on custom check result
resourceExists = !isEmpty
// If custom check determines resource doesn't exist, reset state to nil
if !resourceExists {
nilTfValue := tftypes.NewValue(n.resourceValueTerraformType, nil)
nildynamicValue, err := tfprotov5.NewDynamicValue(n.resourceValueTerraformType, nilTfValue)
if err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, "cannot create nil dynamic value")
}
n.opTracker.SetFrameworkTFState(&nildynamicValue)
}
}
}
var stateValueMap map[string]any
if resourceExists {
@ -376,7 +404,7 @@ func (n *terraformPluginFrameworkExternalClient) Observe(ctx context.Context, mg
return managed.ExternalObservation{}, errors.Wrap(err, "cannot get connection details")
}
if !hasDiff {
n.metricRecorder.SetReconcileTime(mg.GetName())
n.metricRecorder.SetReconcileTime(metrics.NameForManaged(mg))
}
if !specUpdateRequired {
resource.SetUpToDateCondition(mg, !hasDiff)
@ -564,13 +592,9 @@ func (n *terraformPluginFrameworkExternalClient) Delete(ctx context.Context, _ x
}
func (n *terraformPluginFrameworkExternalClient) setExternalName(mg xpresource.Managed, stateValueMap map[string]interface{}) (bool, error) {
id, ok := stateValueMap["id"]
if !ok || id.(string) == "" {
return false, nil
}
newName, err := n.config.ExternalName.GetExternalNameFn(stateValueMap)
if err != nil {
return false, errors.Wrapf(err, "failed to compute the external-name from the state map of the resource with the ID %s", id)
return false, errors.Wrap(err, "failed to compute the external-name from the state map")
}
oldName := meta.GetExternalName(mg)
// we have to make sure the newly set external-name is recorded

View File

@ -8,9 +8,9 @@ import (
"context"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/provider"
@ -25,9 +25,9 @@ import (
"github.com/pkg/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/resource/fake"
"github.com/crossplane/upjet/pkg/terraform"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/resource/fake"
"github.com/crossplane/upjet/v2/pkg/terraform"
)
func newBaseObject() fake.Terraformed {
@ -532,6 +532,51 @@ type mockTPFProviderServer struct {
ReadDataSourceFn func(ctx context.Context, request *tfprotov5.ReadDataSourceRequest) (*tfprotov5.ReadDataSourceResponse, error)
}
func (m *mockTPFProviderServer) UpgradeResourceIdentity(_ context.Context, _ *tfprotov5.UpgradeResourceIdentityRequest) (*tfprotov5.UpgradeResourceIdentityResponse, error) {
// TODO implement me
panic("implement me")
}
func (m *mockTPFProviderServer) GetResourceIdentitySchemas(_ context.Context, _ *tfprotov5.GetResourceIdentitySchemasRequest) (*tfprotov5.GetResourceIdentitySchemasResponse, error) {
// TODO implement me
panic("implement me")
}
func (m *mockTPFProviderServer) MoveResourceState(_ context.Context, _ *tfprotov5.MoveResourceStateRequest) (*tfprotov5.MoveResourceStateResponse, error) {
// TODO implement me
panic("implement me")
}
func (m *mockTPFProviderServer) CallFunction(_ context.Context, _ *tfprotov5.CallFunctionRequest) (*tfprotov5.CallFunctionResponse, error) {
// TODO implement me
panic("implement me")
}
func (m *mockTPFProviderServer) GetFunctions(_ context.Context, _ *tfprotov5.GetFunctionsRequest) (*tfprotov5.GetFunctionsResponse, error) {
// TODO implement me
panic("implement me")
}
func (m *mockTPFProviderServer) ValidateEphemeralResourceConfig(_ context.Context, _ *tfprotov5.ValidateEphemeralResourceConfigRequest) (*tfprotov5.ValidateEphemeralResourceConfigResponse, error) {
// TODO implement me
panic("implement me")
}
func (m *mockTPFProviderServer) OpenEphemeralResource(_ context.Context, _ *tfprotov5.OpenEphemeralResourceRequest) (*tfprotov5.OpenEphemeralResourceResponse, error) {
// TODO implement me
panic("implement me")
}
func (m *mockTPFProviderServer) RenewEphemeralResource(_ context.Context, _ *tfprotov5.RenewEphemeralResourceRequest) (*tfprotov5.RenewEphemeralResourceResponse, error) {
// TODO implement me
panic("implement me")
}
func (m *mockTPFProviderServer) CloseEphemeralResource(_ context.Context, _ *tfprotov5.CloseEphemeralResourceRequest) (*tfprotov5.CloseEphemeralResourceResponse, error) {
// TODO implement me
panic("implement me")
}
func (m *mockTPFProviderServer) GetMetadata(_ context.Context, _ *tfprotov5.GetMetadataRequest) (*tfprotov5.GetMetadataResponse, error) {
// TODO implement me
panic("implement me")

View File

@ -10,12 +10,12 @@ import (
"strings"
"time"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1"
"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
"github.com/crossplane/crossplane-runtime/v2/pkg/meta"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/hashicorp/go-cty/cty"
tfdiag "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@ -25,11 +25,11 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/metrics"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/pkg/resource/json"
"github.com/crossplane/upjet/pkg/terraform"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/metrics"
"github.com/crossplane/upjet/v2/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/resource/json"
"github.com/crossplane/upjet/v2/pkg/terraform"
)
type TerraformPluginSDKConnector struct {
@ -128,6 +128,10 @@ func getExtendedParameters(ctx context.Context, tr resource.Terraformed, externa
if err != nil {
return nil, errors.Wrap(err, "cannot get merged parameters")
}
params, err = cfg.ApplyTFConversions(params, config.ToTerraform)
if err != nil {
return nil, errors.Wrap(err, "cannot apply tf conversions")
}
if err = resource.GetSensitiveParameters(ctx, &APISecretClient{kube: kube}, tr, params, tr.GetConnectionDetailsMapping()); err != nil {
return nil, errors.Wrap(err, "cannot store sensitive parameters into params")
}
@ -156,7 +160,7 @@ func getExtendedParameters(ctx context.Context, tr resource.Terraformed, externa
params["tags_all"] = params["tags"]
}
}
return cfg.ApplyTFConversions(params, config.ToTerraform)
return params, nil
}
func (c *TerraformPluginSDKConnector) processParamsWithHCLParser(schemaMap map[string]*schema.Schema, params map[string]any) map[string]any {
@ -225,8 +229,8 @@ func (c *TerraformPluginSDKConnector) applyHCLParserToParam(sc *schema.Schema, p
}
func (c *TerraformPluginSDKConnector) Connect(ctx context.Context, mg xpresource.Managed) (managed.ExternalClient, error) { //nolint:gocyclo
c.metricRecorder.ObserveReconcileDelay(mg.GetObjectKind().GroupVersionKind(), mg.GetName())
logger := c.logger.WithValues("uid", mg.GetUID(), "name", mg.GetName(), "gvk", mg.GetObjectKind().GroupVersionKind().String())
c.metricRecorder.ObserveReconcileDelay(mg.GetObjectKind().GroupVersionKind(), metrics.NameForManaged(mg))
logger := c.logger.WithValues("uid", mg.GetUID(), "name", mg.GetName(), "namespace", mg.GetNamespace(), "gvk", mg.GetObjectKind().GroupVersionKind().String())
logger.Debug("Connecting to the service provider")
start := time.Now()
ts, err := c.getTerraformSetup(ctx, c.kube, mg)
@ -241,7 +245,7 @@ func (c *TerraformPluginSDKConnector) Connect(ctx context.Context, mg xpresource
externalName := meta.GetExternalName(tr)
params, err := getExtendedParameters(ctx, tr, externalName, c.config, ts, c.isManagementPoliciesEnabled, c.kube)
if err != nil {
return nil, errors.Wrapf(err, "failed to get the extended parameters for resource %q", mg.GetName())
return nil, errors.Wrapf(err, "failed to get the extended parameters for resource %q", client.ObjectKeyFromObject(mg))
}
params = c.processParamsWithHCLParser(c.config.TerraformResource.Schema, params)
@ -422,6 +426,11 @@ func (n *terraformPluginSDKExternal) getResourceDataDiff(tr resource.Terraformed
if err != nil {
return nil, errors.Wrap(err, "failed to get *terraform.InstanceDiff")
}
// Sanitize Identity field in Diff.
// This causes continuous diff loop.
if instanceDiff != nil {
instanceDiff.Identity = nil
}
if n.config.TerraformCustomDiff != nil {
instanceDiff, err = n.config.TerraformCustomDiff(instanceDiff, s, resourceConfig)
if err != nil {
@ -557,7 +566,7 @@ func (n *terraformPluginSDKExternal) Observe(ctx context.Context, mg xpresource.
}
if !hasDiff {
n.metricRecorder.SetReconcileTime(mg.GetName())
n.metricRecorder.SetReconcileTime(metrics.NameForManaged(mg))
}
if !specUpdateRequired {
resource.SetUpToDateCondition(mg, !hasDiff)

View File

@ -9,10 +9,10 @@ import (
"testing"
"time"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/crossplane/crossplane-runtime/v2/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@ -21,9 +21,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/resource/fake"
"github.com/crossplane/upjet/pkg/terraform"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/resource/fake"
"github.com/crossplane/upjet/v2/pkg/terraform"
)
var (

View File

@ -7,7 +7,7 @@ package controller
import (
"context"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/pkg/errors"
)

View File

@ -9,7 +9,7 @@ import (
"sync"
"time"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/event"
@ -54,7 +54,7 @@ func NewEventHandler(opts ...Option) *EventHandler {
// RequestReconcile requeues a reconciliation request for the specified name.
// Returns true if the reconcile request was successfully queued.
func (e *EventHandler) RequestReconcile(rateLimiterName, name string, failureLimit *int) bool {
func (e *EventHandler) RequestReconcile(rateLimiterName string, name types.NamespacedName, failureLimit *int) bool {
e.mu.Lock()
defer e.mu.Unlock()
if e.queue == nil {
@ -62,9 +62,7 @@ func (e *EventHandler) RequestReconcile(rateLimiterName, name string, failureLim
}
logger := e.logger.WithValues("name", name)
item := reconcile.Request{
NamespacedName: types.NamespacedName{
Name: name,
},
NamespacedName: name,
}
var when time.Duration = 0
if rateLimiterName != NoRateLimiter {
@ -86,7 +84,7 @@ func (e *EventHandler) RequestReconcile(rateLimiterName, name string, failureLim
// Forget indicates that the reconcile retries is finished for
// the specified name.
func (e *EventHandler) Forget(rateLimiterName, name string) {
func (e *EventHandler) Forget(rateLimiterName string, name types.NamespacedName) {
e.mu.RLock()
defer e.mu.RUnlock()
rateLimiter := e.rateLimiterMap[rateLimiterName]
@ -94,9 +92,7 @@ func (e *EventHandler) Forget(rateLimiterName, name string) {
return
}
rateLimiter.Forget(reconcile.Request{
NamespacedName: types.NamespacedName{
Name: name,
},
NamespacedName: name,
})
}
@ -110,24 +106,24 @@ func (e *EventHandler) setQueue(limitingInterface workqueue.TypedRateLimitingInt
func (e *EventHandler) Create(ctx context.Context, ev event.CreateEvent, limitingInterface workqueue.TypedRateLimitingInterface[reconcile.Request]) {
e.setQueue(limitingInterface)
e.logger.Debug("Calling the inner handler for Create event.", "name", ev.Object.GetName(), "queueLength", limitingInterface.Len())
e.logger.Debug("Calling the inner handler for Create event.", "name", ev.Object.GetName(), "namespace", ev.Object.GetNamespace(), "queueLength", limitingInterface.Len())
e.innerHandler.Create(ctx, ev, limitingInterface)
}
func (e *EventHandler) Update(ctx context.Context, ev event.UpdateEvent, limitingInterface workqueue.TypedRateLimitingInterface[reconcile.Request]) {
e.setQueue(limitingInterface)
e.logger.Debug("Calling the inner handler for Update event.", "name", ev.ObjectOld.GetName(), "queueLength", limitingInterface.Len())
e.logger.Debug("Calling the inner handler for Update event.", "name", ev.ObjectOld.GetName(), "namespace", ev.ObjectOld.GetNamespace(), "queueLength", limitingInterface.Len())
e.innerHandler.Update(ctx, ev, limitingInterface)
}
func (e *EventHandler) Delete(ctx context.Context, ev event.DeleteEvent, limitingInterface workqueue.TypedRateLimitingInterface[reconcile.Request]) {
e.setQueue(limitingInterface)
e.logger.Debug("Calling the inner handler for Delete event.", "name", ev.Object.GetName(), "queueLength", limitingInterface.Len())
e.logger.Debug("Calling the inner handler for Delete event.", "name", ev.Object.GetName(), "namespace", ev.Object.GetNamespace(), "queueLength", limitingInterface.Len())
e.innerHandler.Delete(ctx, ev, limitingInterface)
}
func (e *EventHandler) Generic(ctx context.Context, ev event.GenericEvent, limitingInterface workqueue.TypedRateLimitingInterface[reconcile.Request]) {
e.setQueue(limitingInterface)
e.logger.Debug("Calling the inner handler for Generic event.", "name", ev.Object.GetName(), "queueLength", limitingInterface.Len())
e.logger.Debug("Calling the inner handler for Generic event.", "name", ev.Object.GetName(), "namespace", ev.Object.GetNamespace(), "queueLength", limitingInterface.Len())
e.innerHandler.Generic(ctx, ev, limitingInterface)
}

View File

@ -7,9 +7,11 @@ package controller
import (
"context"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/pkg/terraform"
"k8s.io/apimachinery/pkg/types"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/terraform"
)
// TODO(muvaf): It's a bit weird that the functions return the struct of a
@ -40,7 +42,7 @@ type Store interface {
// CallbackProvider provides functions that can be called with the result of
// async operations.
type CallbackProvider interface {
Create(name string) terraform.CallbackFn
Update(name string) terraform.CallbackFn
Destroy(name string) terraform.CallbackFn
Create(name types.NamespacedName) terraform.CallbackFn
Update(name types.NamespacedName) terraform.CallbackFn
Destroy(name types.NamespacedName) terraform.CallbackFn
}

View File

@ -8,14 +8,14 @@ import (
"sync"
"sync/atomic"
"github.com/crossplane/crossplane-runtime/pkg/logging"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
tfsdk "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"k8s.io/apimachinery/pkg/types"
"github.com/crossplane/upjet/pkg/resource"
"github.com/crossplane/upjet/pkg/terraform"
"github.com/crossplane/upjet/v2/pkg/resource"
"github.com/crossplane/upjet/v2/pkg/terraform"
)
// AsyncTracker holds information for a managed resource to track the
@ -189,7 +189,7 @@ func (ops *OperationTrackerStore) Tracker(tr resource.Terraformed) *AsyncTracker
defer ops.mu.Unlock()
tracker, ok := ops.store[tr.GetUID()]
if !ok {
l := ops.logger.WithValues("trackerUID", tr.GetUID(), "resourceName", tr.GetName(), "gvk", tr.GetObjectKind().GroupVersionKind().String())
l := ops.logger.WithValues("trackerUID", tr.GetUID(), "resourceName", tr.GetName(), "resourceNamespace", tr.GetNamespace(), "gvk", tr.GetObjectKind().GroupVersionKind().String())
ops.store[tr.GetUID()] = NewAsyncTracker(WithAsyncTrackerLogger(l))
tracker = ops.store[tr.GetUID()]
}

View File

@ -5,14 +5,12 @@
package controller
import (
"crypto/tls"
"time"
"github.com/crossplane/crossplane-runtime/pkg/controller"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/crossplane/crossplane-runtime/v2/pkg/controller"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/terraform"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/terraform"
)
// Options contains incriminating options for a given Upjet controller instance.
@ -34,14 +32,6 @@ type Options struct {
// preparing the auth token for Terraform CLI.
SetupFn terraform.SetupFn
// SecretStoreConfigGVK is the GroupVersionKind for the Secret StoreConfig
// resource. Setting this enables External Secret Stores for the controller
// by adding connection.DetailsManager as a ConnectionPublisher.
SecretStoreConfigGVK *schema.GroupVersionKind
// ESSOptions for External Secret Stores.
ESSOptions *ESSOptions
// PollJitter adds the specified jitter to the configured reconcile period
// of the up-to-date resources in managed.Reconciler.
PollJitter time.Duration
@ -50,9 +40,3 @@ type Options struct {
// provider's controllerruntime.Manager.
StartWebhooks bool
}
// ESSOptions for External Secret Stores.
type ESSOptions struct {
TLSConfig *tls.Config
TLSSecretName *string
}

View File

@ -19,8 +19,8 @@ import (
kyaml "k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/yaml"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/config/conversion"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/config/conversion"
)
// ConvertSingletonListToEmbeddedObject generates the example manifests for

View File

@ -14,17 +14,17 @@ import (
"sort"
"strings"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
xpmeta "github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath"
xpmeta "github.com/crossplane/crossplane-runtime/v2/pkg/meta"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/pkg/errors"
"sigs.k8s.io/yaml"
"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/registry/reference"
"github.com/crossplane/upjet/pkg/resource/json"
tjtypes "github.com/crossplane/upjet/pkg/types"
"github.com/crossplane/upjet/pkg/types/name"
"github.com/crossplane/upjet/v2/pkg/config"
"github.com/crossplane/upjet/v2/pkg/registry/reference"
"github.com/crossplane/upjet/v2/pkg/resource/json"
tjtypes "github.com/crossplane/upjet/v2/pkg/types"
"github.com/crossplane/upjet/v2/pkg/types/name"
)
var (
@ -42,27 +42,53 @@ const (
// Generates example manifests for Terraform resources under examples-generated.
type Generator struct {
reference.Injector
rootDir string
exampleDir string
configResources map[string]*config.Resource
resources map[string]*reference.PavedWithManifest
exampleNamespace string
localSecretRefs bool
}
type GeneratorOption func(*Generator)
// WithLocalSecretRefs configures the example generator to
// generate examples with local secret references,
// i.e. no namespace specified.
func WithLocalSecretRefs() GeneratorOption {
return func(g *Generator) {
g.localSecretRefs = true
}
}
// WithNamespacedExamples configures the example generator to
// generate examples with the default namespace
func WithNamespacedExamples() GeneratorOption {
return func(g *Generator) {
g.exampleNamespace = defaultNamespace
}
}
// NewGenerator returns a configured Generator
func NewGenerator(rootDir, modulePath, shortName string, configResources map[string]*config.Resource) *Generator {
return &Generator{
func NewGenerator(exampleDir, apisModulePath, shortName string, configResources map[string]*config.Resource, opts ...GeneratorOption) *Generator {
g := &Generator{
Injector: reference.Injector{
ModulePath: modulePath,
ModulePath: apisModulePath,
ProviderShortName: shortName,
},
rootDir: rootDir,
exampleDir: exampleDir,
configResources: configResources,
resources: make(map[string]*reference.PavedWithManifest),
}
for _, opt := range opts {
opt(g)
}
return g
}
// StoreExamples stores the generated example manifests under examples-generated in
// their respective API groups.
func (eg *Generator) StoreExamples() error { // nolint:gocyclo
func (eg *Generator) StoreExamples() error { //nolint:gocyclo
for rn, pm := range eg.resources {
manifestDir := filepath.Dir(pm.ManifestPath)
if err := os.MkdirAll(manifestDir, 0750); err != nil {
@ -98,7 +124,7 @@ func (eg *Generator) StoreExamples() error { // nolint:gocyclo
// e.g. meta.upbound.io/example-id: ec2/v1beta1/instance
eGroup := fmt.Sprintf("%s/%s/%s", strings.ToLower(r.ShortGroup), r.Version, strings.ToLower(r.Kind))
pmd := paveCRManifest(exampleParams, dr.Config,
reference.NewRefPartsFromResourceName(dn).ExampleName, dr.Group, dr.Version, eGroup)
reference.NewRefPartsFromResourceName(dn).ExampleName, dr.Group, dr.Version, eGroup, eg.exampleNamespace, eg.localSecretRefs)
if err := eg.writeManifest(&buff, pmd, context); err != nil {
return errors.Wrapf(err, "cannot store example manifest for %s dependency: %s", rn, dn)
}
@ -115,10 +141,10 @@ func (eg *Generator) StoreExamples() error { // nolint:gocyclo
return nil
}
func paveCRManifest(exampleParams map[string]any, r *config.Resource, eName, group, version, eGroup string) *reference.PavedWithManifest {
func paveCRManifest(exampleParams map[string]any, r *config.Resource, eName, group, version, eGroup, namespace string, localSecretRefs bool) *reference.PavedWithManifest {
delete(exampleParams, "depends_on")
delete(exampleParams, "lifecycle")
transformFields(r, exampleParams, r.ExternalName.OmittedFields, "")
transformFields(r, exampleParams, r.ExternalName.OmittedFields, "", localSecretRefs)
metadata := map[string]any{
"labels": map[string]string{
labelExampleName: eName,
@ -127,6 +153,9 @@ func paveCRManifest(exampleParams map[string]any, r *config.Resource, eName, gro
annotationExampleGroup: eGroup,
},
}
if namespace != "" {
metadata["namespace"] = namespace
}
example := map[string]any{
"apiVersion": fmt.Sprintf("%s/%s", group, version),
"kind": r.Kind,
@ -184,8 +213,8 @@ func (eg *Generator) Generate(group, version string, r *config.Resource) error {
groupPrefix := strings.ToLower(strings.Split(group, ".")[0])
// e.g. gvk = ec2/v1beta1/instance
gvk := fmt.Sprintf("%s/%s/%s", groupPrefix, version, strings.ToLower(r.Kind))
pm := paveCRManifest(rm.Examples[0].Paved.UnstructuredContent(), r, rm.Examples[0].Name, group, version, gvk)
manifestDir := filepath.Join(eg.rootDir, "examples-generated", groupPrefix, r.Version)
pm := paveCRManifest(rm.Examples[0].Paved.UnstructuredContent(), r, rm.Examples[0].Name, group, version, gvk, eg.exampleNamespace, eg.localSecretRefs)
manifestDir := filepath.Join(eg.exampleDir, groupPrefix, r.Version)
pm.ManifestPath = filepath.Join(manifestDir, fmt.Sprintf("%s.yaml", strings.ToLower(r.Kind)))
eg.resources[fmt.Sprintf("%s.%s", r.Name, reference.Wildcard)] = pm
return nil
@ -206,7 +235,7 @@ func isStatus(r *config.Resource, attr string) bool {
return tjtypes.IsObservation(s)
}
func transformFields(r *config.Resource, params map[string]any, omittedFields []string, namePrefix string) { // nolint:gocyclo
func transformFields(r *config.Resource, params map[string]any, omittedFields []string, namePrefix string, localSecretRefs bool) { //nolint:gocyclo
for n := range params {
hName := getHierarchicalName(namePrefix, n)
if isStatus(r, hName) {
@ -224,7 +253,7 @@ func transformFields(r *config.Resource, params map[string]any, omittedFields []
for n, v := range params {
switch pT := v.(type) {
case map[string]any:
transformFields(r, pT, omittedFields, getHierarchicalName(namePrefix, n))
transformFields(r, pT, omittedFields, getHierarchicalName(namePrefix, n), localSecretRefs)
case []any:
for _, e := range pT {
@ -232,7 +261,7 @@ func transformFields(r *config.Resource, params map[string]any, omittedFields []
if !ok {
continue
}
transformFields(r, eM, omittedFields, getHierarchicalName(namePrefix, n))
transformFields(r, eM, omittedFields, getHierarchicalName(namePrefix, n), localSecretRefs)
}
}
}
@ -250,11 +279,14 @@ func transformFields(r *config.Resource, params map[string]any, omittedFields []
switch {
case sch.Sensitive:
secretName, secretKey := getSecretRef(v)
params[fn.LowerCamelComputed+"SecretRef"] = getRefField(v, map[string]any{
ref := map[string]any{
"name": secretName,
"namespace": defaultNamespace,
"key": secretKey,
})
}
if !localSecretRefs {
ref["namespace"] = defaultNamespace
}
params[fn.LowerCamelComputed+"SecretRef"] = getRefField(v, ref)
case r.References[fieldPath] != config.Reference{}:
switch v.(type) {
case []any:

View File

@ -6,10 +6,11 @@ package metrics
import (
"context"
"fmt"
"sync"
"time"
"github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/v2/pkg/resource"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -124,6 +125,13 @@ type Observations struct {
observeReconcileDelay bool
}
func NameForManaged(mg resource.Managed) string {
if mg.GetNamespace() == "" {
return mg.GetName()
}
return fmt.Sprintf("%s/%s", mg.GetNamespace(), mg.GetName())
}
func NewMetricRecorder(gvk schema.GroupVersionKind, c cluster.Cluster, pollInterval time.Duration) *MetricRecorder {
return &MetricRecorder{
gvk: gvk,
@ -174,7 +182,7 @@ func (r *MetricRecorder) Start(ctx context.Context) error {
obj = final.Obj
}
managed := obj.(resource.Managed)
r.observations.Delete(managed.GetName())
r.observations.Delete(NameForManaged(managed))
},
})
if err != nil {

View File

@ -1,347 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"strconv"
v1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composite"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
const (
stepPauseManaged step = iota
stepPauseComposites
stepCreateNewManaged
stepNewCompositions
stepEditComposites
stepEditClaims
stepDeletionPolicyOrphan
stepRemoveFinalizers
stepDeleteOldManaged
stepStartManaged
stepStartComposites
// this must be the last step
stepAPIEnd
)
func getAPIMigrationSteps() []step {
steps := make([]step, 0, stepAPIEnd)
for i := step(0); i < stepAPIEnd; i++ {
steps = append(steps, i)
}
return steps
}
func getAPIMigrationStepsFileSystemMode() []step {
return []step{
stepCreateNewManaged,
stepNewCompositions,
stepEditComposites,
stepEditClaims,
stepStartManaged,
stepStartComposites,
// this must be the last step
stepAPIEnd,
}
}
func (pg *PlanGenerator) addStepsForManagedResource(u *UnstructuredWithMetadata) error {
if u.Metadata.Category != CategoryManaged {
if _, ok, err := toManagedResource(pg.registry.scheme, u.Object); err != nil || !ok {
// not a managed resource or unable to determine
// whether it's a managed resource
return nil //nolint:nilerr
}
}
qName := getQualifiedName(u.Object)
if err := pg.stepPauseManagedResource(u, qName); err != nil {
return err
}
if err := pg.stepOrphanManagedResource(u, qName); err != nil {
return err
}
if err := pg.stepRemoveFinalizersManagedResource(u, qName); err != nil {
return err
}
pg.stepDeleteOldManagedResource(u)
orphaned, err := pg.stepOrhanMR(*u)
if err != nil {
return err
}
if !orphaned {
return nil
}
_, err = pg.stepRevertOrhanMR(*u)
return err
}
func (pg *PlanGenerator) stepStartManagedResource(u *UnstructuredWithMetadata) error {
if !pg.stepEnabled(stepStartManaged) {
return nil
}
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepStartManaged).Name, getQualifiedName(u.Object))
pg.stepAPI(stepStartManaged).Patch.Files = append(pg.stepAPI(stepStartManaged).Patch.Files, u.Metadata.Path)
return pg.pause(*u, false)
}
func (pg *PlanGenerator) stepPauseManagedResource(u *UnstructuredWithMetadata, qName string) error {
if !pg.stepEnabled(stepPauseManaged) {
return nil
}
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepPauseManaged).Name, qName)
pg.stepAPI(stepPauseManaged).Patch.Files = append(pg.stepAPI(stepPauseManaged).Patch.Files, u.Metadata.Path)
return pg.pause(*u, true)
}
func (pg *PlanGenerator) stepPauseComposite(u *UnstructuredWithMetadata) error {
if !pg.stepEnabled(stepPauseComposites) {
return nil
}
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepPauseComposites).Name, getQualifiedName(u.Object))
pg.stepAPI(stepPauseComposites).Patch.Files = append(pg.stepAPI(stepPauseComposites).Patch.Files, u.Metadata.Path)
return pg.pause(*u, true)
}
func (pg *PlanGenerator) stepOrphanManagedResource(u *UnstructuredWithMetadata, qName string) error {
if !pg.stepEnabled(stepDeletionPolicyOrphan) {
return nil
}
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepDeletionPolicyOrphan).Name, qName)
pg.stepAPI(stepDeletionPolicyOrphan).Patch.Files = append(pg.stepAPI(stepDeletionPolicyOrphan).Patch.Files, u.Metadata.Path)
return errors.Wrap(pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(u.Object, map[string]any{
"spec": map[string]any{
"deletionPolicy": string(v1.DeletionOrphan),
},
}),
},
Metadata: u.Metadata,
}), errResourceOrphan)
}
func (pg *PlanGenerator) stepRemoveFinalizersManagedResource(u *UnstructuredWithMetadata, qName string) error {
if !pg.stepEnabled(stepRemoveFinalizers) {
return nil
}
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepRemoveFinalizers).Name, qName)
pg.stepAPI(stepRemoveFinalizers).Patch.Files = append(pg.stepAPI(stepRemoveFinalizers).Patch.Files, u.Metadata.Path)
return pg.removeFinalizers(*u)
}
func (pg *PlanGenerator) stepDeleteOldManagedResource(u *UnstructuredWithMetadata) {
if !pg.stepEnabled(stepDeleteOldManaged) {
return
}
pg.stepAPI(stepDeleteOldManaged).Delete.Resources = append(pg.stepAPI(stepDeleteOldManaged).Delete.Resources,
Resource{
GroupVersionKind: FromGroupVersionKind(u.Object.GroupVersionKind()),
Name: u.Object.GetName(),
})
}
func (pg *PlanGenerator) stepNewManagedResource(u *UnstructuredWithMetadata) error {
if !pg.stepEnabled(stepCreateNewManaged) {
return nil
}
meta.AddAnnotations(&u.Object, map[string]string{meta.AnnotationKeyReconciliationPaused: "true"})
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepCreateNewManaged).Name, getQualifiedName(u.Object))
pg.stepAPI(stepCreateNewManaged).Apply.Files = append(pg.stepAPI(stepCreateNewManaged).Apply.Files, u.Metadata.Path)
if err := pg.target.Put(*u); err != nil {
return errors.Wrap(err, errResourceOutput)
}
return nil
}
func (pg *PlanGenerator) stepNewComposition(u *UnstructuredWithMetadata) error {
if !pg.stepEnabled(stepNewCompositions) {
return nil
}
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepNewCompositions).Name, getQualifiedName(u.Object))
pg.stepAPI(stepNewCompositions).Apply.Files = append(pg.stepAPI(stepNewCompositions).Apply.Files, u.Metadata.Path)
if err := pg.target.Put(*u); err != nil {
return errors.Wrap(err, errCompositionOutput)
}
return nil
}
func (pg *PlanGenerator) stepStartComposites(composites []UnstructuredWithMetadata) error {
if !pg.stepEnabled(stepStartComposites) {
return nil
}
for _, u := range composites {
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepStartComposites).Name, getQualifiedName(u.Object))
pg.stepAPI(stepStartComposites).Patch.Files = append(pg.stepAPI(stepStartComposites).Patch.Files, u.Metadata.Path)
if err := pg.pause(u, false); err != nil {
return errors.Wrap(err, errCompositeOutput)
}
}
return nil
}
func (pg *PlanGenerator) pause(u UnstructuredWithMetadata, isPaused bool) error {
return errors.Wrap(pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(u.Object, map[string]any{
"metadata": map[string]any{
"annotations": map[string]any{
meta.AnnotationKeyReconciliationPaused: strconv.FormatBool(isPaused),
},
},
}),
},
Metadata: Metadata{
Path: u.Metadata.Path,
},
}), errPause)
}
func (pg *PlanGenerator) removeFinalizers(u UnstructuredWithMetadata) error {
return errors.Wrap(pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(u.Object, map[string]any{
"metadata": map[string]any{
"finalizers": []any{},
},
}),
},
Metadata: Metadata{
Path: u.Metadata.Path,
},
}), errResourceRemoveFinalizer)
}
func (pg *PlanGenerator) stepEditComposites(composites []UnstructuredWithMetadata, convertedMap map[corev1.ObjectReference][]UnstructuredWithMetadata, convertedComposition map[string]string) error {
if !pg.stepEnabled(stepEditComposites) {
return nil
}
for _, u := range composites {
cp := composite.Unstructured{Unstructured: u.Object}
refs := cp.GetResourceReferences()
// compute new spec.resourceRefs so that the XR references the new MRs
newRefs := make([]corev1.ObjectReference, 0, len(refs))
for _, ref := range refs {
converted, ok := convertedMap[ref]
if !ok {
newRefs = append(newRefs, ref)
continue
}
for _, o := range converted {
gvk := o.Object.GroupVersionKind()
newRefs = append(newRefs, corev1.ObjectReference{
Kind: gvk.Kind,
Name: o.Object.GetName(),
APIVersion: gvk.GroupVersion().String(),
})
}
}
cp.SetResourceReferences(newRefs)
// compute new spec.compositionRef
if ref := cp.GetCompositionReference(); ref != nil && convertedComposition[ref.Name] != "" {
ref.Name = convertedComposition[ref.Name]
cp.SetCompositionReference(ref)
}
spec := u.Object.Object["spec"].(map[string]any)
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepEditComposites).Name, getQualifiedName(u.Object))
pg.stepAPI(stepEditComposites).Patch.Files = append(pg.stepAPI(stepEditComposites).Patch.Files, u.Metadata.Path)
if err := pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(u.Object, map[string]any{
"spec": map[string]any{
keyResourceRefs: spec[keyResourceRefs],
keyCompositionRef: spec[keyCompositionRef]},
}),
},
Metadata: u.Metadata,
}); err != nil {
return errors.Wrap(err, errCompositeOutput)
}
}
return nil
}
func (pg *PlanGenerator) stepEditClaims(claims []UnstructuredWithMetadata, convertedComposition map[string]string) error {
if !pg.stepEnabled(stepEditClaims) {
return nil
}
for _, u := range claims {
cm := claim.Unstructured{Unstructured: u.Object}
if ref := cm.GetCompositionReference(); ref != nil && convertedComposition[ref.Name] != "" {
ref.Name = convertedComposition[ref.Name]
cm.SetCompositionReference(ref)
}
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepEditClaims).Name, getQualifiedName(u.Object))
pg.stepAPI(stepEditClaims).Patch.Files = append(pg.stepAPI(stepEditClaims).Patch.Files, u.Metadata.Path)
if err := pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(u.Object, map[string]any{
"spec": map[string]any{
keyCompositionRef: u.Object.Object["spec"].(map[string]any)[keyCompositionRef],
},
}),
},
Metadata: u.Metadata,
}); err != nil {
return errors.Wrap(err, errClaimOutput)
}
}
return nil
}
// NOTE: to cover different migration scenarios, we may use
// "migration templates" instead of a static plan. But a static plan should be
// fine as a start.
func (pg *PlanGenerator) stepAPI(s step) *Step { //nolint:gocyclo // all steps under a single clause for readability
stepKey := strconv.Itoa(int(s))
if pg.Plan.Spec.stepMap[stepKey] != nil {
return pg.Plan.Spec.stepMap[stepKey]
}
pg.Plan.Spec.stepMap[stepKey] = &Step{}
switch s { //nolint:exhaustive
case stepPauseManaged:
setPatchStep("pause-managed", pg.Plan.Spec.stepMap[stepKey])
case stepPauseComposites:
setPatchStep("pause-composites", pg.Plan.Spec.stepMap[stepKey])
case stepCreateNewManaged:
setApplyStep("create-new-managed", pg.Plan.Spec.stepMap[stepKey])
case stepNewCompositions:
setApplyStep("new-compositions", pg.Plan.Spec.stepMap[stepKey])
case stepEditComposites:
setPatchStep("edit-composites", pg.Plan.Spec.stepMap[stepKey])
case stepEditClaims:
setPatchStep("edit-claims", pg.Plan.Spec.stepMap[stepKey])
case stepDeletionPolicyOrphan:
setPatchStep("deletion-policy-orphan", pg.Plan.Spec.stepMap[stepKey])
case stepRemoveFinalizers:
setPatchStep("remove-finalizers", pg.Plan.Spec.stepMap[stepKey])
case stepDeleteOldManaged:
setDeleteStep("delete-old-managed", pg.Plan.Spec.stepMap[stepKey])
case stepStartManaged:
setPatchStep("start-managed", pg.Plan.Spec.stepMap[stepKey])
case stepStartComposites:
setPatchStep("start-composites", pg.Plan.Spec.stepMap[stepKey])
default:
panic(fmt.Sprintf(errInvalidStepFmt, s))
}
return pg.Plan.Spec.stepMap[stepKey]
}

View File

@ -1,32 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
const (
errEditCategory = "failed to put the edited resource of category %q: %s"
)
func (pg *PlanGenerator) stepEditCategory(source UnstructuredWithMetadata, t *UnstructuredWithMetadata) error {
s := pg.stepConfiguration(stepOrphanMRs)
t.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(t.Object))
s.Patch.Files = append(s.Patch.Files, t.Metadata.Path)
patchMap, err := computeJSONMergePathDoc(source.Object, t.Object)
if err != nil {
return err
}
return errors.Wrapf(pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(t.Object, patchMap),
},
Metadata: t.Metadata,
}), errEditCategory, source.Metadata.Category, source.Object.GetName())
}

View File

@ -1,170 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"strconv"
xpmetav1 "github.com/crossplane/crossplane/apis/pkg/meta/v1"
xpmetav1alpha1 "github.com/crossplane/crossplane/apis/pkg/meta/v1alpha1"
"github.com/pkg/errors"
)
const (
// configuration migration steps follow any existing API migration steps
stepBackupMRs = iota + stepAPIEnd + 1
stepBackupComposites
stepBackupClaims
stepOrphanMRs
stepNewFamilyProvider
stepCheckHealthFamilyProvider
stepNewServiceScopedProvider
stepCheckHealthNewServiceScopedProvider
stepConfigurationPackageDisableDepResolution
stepEditPackageLock
stepDeleteMonolithicProvider
stepActivateFamilyProviderRevision
stepCheckInstallationFamilyProviderRevision
stepActivateServiceScopedProviderRevision
stepCheckInstallationServiceScopedProviderRevision
stepEditConfigurationMetadata
stepBuildConfiguration
stepPushConfiguration
stepEditConfigurationPackage
stepConfigurationPackageEnableDepResolution
stepRevertOrphanMRs
stepConfigurationEnd
)
func getConfigurationMigrationSteps() []step {
steps := make([]step, 0, stepConfigurationEnd-stepAPIEnd-1)
for i := stepAPIEnd + 1; i < stepConfigurationEnd; i++ {
steps = append(steps, i)
}
return steps
}
const (
errConfigurationMetadataOutput = "failed to output configuration YAML document"
)
func (pg *PlanGenerator) convertConfigurationMetadata(o UnstructuredWithMetadata) error {
isConverted := false
conf, err := toConfigurationMetadata(o.Object)
if err != nil {
return err
}
for _, confConv := range pg.registry.configurationMetaConverters {
if confConv.re == nil || confConv.converter == nil || !confConv.re.MatchString(o.Object.GetName()) {
continue
}
switch o.Object.GroupVersionKind().Version {
case "v1alpha1":
err = confConv.converter.ConfigurationMetadataV1Alpha1(conf.(*xpmetav1alpha1.Configuration))
default:
err = confConv.converter.ConfigurationMetadataV1(conf.(*xpmetav1.Configuration))
}
if err != nil {
return errors.Wrapf(err, "failed to call converter on Configuration: %s", conf.GetName())
}
// TODO: if a configuration converter only converts a specific version,
// (or does not convert the given configuration),
// we will have a false positive. Better to compute and check
// a diff here.
isConverted = true
}
if !isConverted {
return nil
}
return pg.stepEditConfigurationMetadata(o, &UnstructuredWithMetadata{
Object: ToSanitizedUnstructured(conf),
Metadata: o.Metadata,
})
}
func (pg *PlanGenerator) stepConfiguration(s step) *Step {
return pg.stepConfigurationWithSubStep(s, false)
}
func (pg *PlanGenerator) configurationSubStep(s step) string {
ss := -1
subStep := pg.subSteps[s]
if subStep != "" {
s, err := strconv.Atoi(subStep)
if err == nil {
ss = s
}
}
pg.subSteps[s] = strconv.Itoa(ss + 1)
return pg.subSteps[s]
}
func (pg *PlanGenerator) stepConfigurationWithSubStep(s step, newSubStep bool) *Step { //nolint:gocyclo // easy to follow all steps here
stepKey := strconv.Itoa(int(s))
if newSubStep {
stepKey = fmt.Sprintf("%s.%s", stepKey, pg.configurationSubStep(s))
}
if pg.Plan.Spec.stepMap[stepKey] != nil {
return pg.Plan.Spec.stepMap[stepKey]
}
pg.Plan.Spec.stepMap[stepKey] = &Step{}
switch s { //nolint:exhaustive
case stepOrphanMRs:
setPatchStep("deletion-policy-orphan", pg.Plan.Spec.stepMap[stepKey])
case stepRevertOrphanMRs:
setPatchStep("deletion-policy-delete", pg.Plan.Spec.stepMap[stepKey])
case stepNewFamilyProvider:
setApplyStep("new-ssop", pg.Plan.Spec.stepMap[stepKey])
case stepNewServiceScopedProvider:
setApplyStep("new-ssop", pg.Plan.Spec.stepMap[stepKey])
case stepConfigurationPackageDisableDepResolution:
setPatchStep("disable-dependency-resolution", pg.Plan.Spec.stepMap[stepKey])
case stepConfigurationPackageEnableDepResolution:
setPatchStep("enable-dependency-resolution", pg.Plan.Spec.stepMap[stepKey])
case stepEditConfigurationPackage:
setPatchStep("edit-configuration-package", pg.Plan.Spec.stepMap[stepKey])
case stepEditPackageLock:
setPatchStep("edit-package-lock", pg.Plan.Spec.stepMap[stepKey])
case stepDeleteMonolithicProvider:
setDeleteStep("delete-monolithic-provider", pg.Plan.Spec.stepMap[stepKey])
case stepActivateFamilyProviderRevision:
setPatchStep("activate-ssop", pg.Plan.Spec.stepMap[stepKey])
case stepActivateServiceScopedProviderRevision:
setPatchStep("activate-ssop", pg.Plan.Spec.stepMap[stepKey])
case stepEditConfigurationMetadata:
setExecStep("edit-configuration-metadata", pg.Plan.Spec.stepMap[stepKey])
case stepBackupMRs:
setExecStep("backup-managed-resources", pg.Plan.Spec.stepMap[stepKey])
case stepBackupComposites:
setExecStep("backup-composite-resources", pg.Plan.Spec.stepMap[stepKey])
case stepBackupClaims:
setExecStep("backup-claim-resources", pg.Plan.Spec.stepMap[stepKey])
case stepCheckHealthFamilyProvider:
setExecStep("wait-for-healthy", pg.Plan.Spec.stepMap[stepKey])
case stepCheckHealthNewServiceScopedProvider:
setExecStep("wait-for-healthy", pg.Plan.Spec.stepMap[stepKey])
case stepCheckInstallationFamilyProviderRevision:
setExecStep("wait-for-installed", pg.Plan.Spec.stepMap[stepKey])
case stepCheckInstallationServiceScopedProviderRevision:
setExecStep("wait-for-installed", pg.Plan.Spec.stepMap[stepKey])
case stepBuildConfiguration:
setExecStep("build-configuration", pg.Plan.Spec.stepMap[stepKey])
case stepPushConfiguration:
setExecStep("push-configuration", pg.Plan.Spec.stepMap[stepKey])
default:
panic(fmt.Sprintf(errInvalidStepFmt, s))
}
return pg.Plan.Spec.stepMap[stepKey]
}
func (pg *PlanGenerator) stepEditConfigurationMetadata(source UnstructuredWithMetadata, target *UnstructuredWithMetadata) error {
s := pg.stepConfiguration(stepEditConfigurationMetadata)
target.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(target.Object))
s.Exec.Args = []string{"-c", fmt.Sprintf("cp %s %s", target.Metadata.Path, source.Metadata.Path)}
return errors.Wrap(pg.target.Put(*target), errConfigurationMetadataOutput)
}

View File

@ -1,143 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
v1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
const (
errSetDeletionPolicyFmt = "failed to put the patch file to set the deletion policy to %q: %s"
errEditConfigurationPackageFmt = `failed to put the edited Configuration package: %s`
)
func (pg *PlanGenerator) convertConfigurationPackage(o UnstructuredWithMetadata) error {
pkg, err := toConfigurationPackageV1(o.Object)
if err != nil {
return err
}
// add step for disabling the dependency resolution
// for the configuration package
s := pg.stepConfiguration(stepConfigurationPackageDisableDepResolution)
p := fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(o.Object))
s.Patch.Files = append(s.Patch.Files, p)
if err := pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(o.Object, map[string]any{
"spec": map[string]any{
"skipDependencyResolution": true,
},
}),
},
Metadata: Metadata{
Path: p,
},
}); err != nil {
return err
}
// add step for enabling the dependency resolution
// for the configuration package
s = pg.stepConfiguration(stepConfigurationPackageEnableDepResolution)
p = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(o.Object))
s.Patch.Files = append(s.Patch.Files, p)
if err := pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(o.Object, map[string]any{
"spec": map[string]any{
"skipDependencyResolution": false,
},
}),
},
Metadata: Metadata{
Path: p,
},
}); err != nil {
return err
}
// add the step for editing the configuration package
for _, pkgConv := range pg.registry.configurationPackageConverters {
if pkgConv.re == nil || pkgConv.converter == nil || !pkgConv.re.MatchString(pkg.Spec.Package) {
continue
}
err := pkgConv.converter.ConfigurationPackageV1(pkg)
if err != nil {
return errors.Wrapf(err, "failed to call converter on Configuration package: %s", pkg.Spec.Package)
}
// TODO: if a converter only converts a specific version,
// (or does not convert the given configuration),
// we will have a false positive. Better to compute and check
// a diff here.
target := &UnstructuredWithMetadata{
Object: ToSanitizedUnstructured(pkg),
Metadata: o.Metadata,
}
if err := pg.stepEditConfigurationPackage(o, target); err != nil {
return err
}
}
return nil
}
func (pg *PlanGenerator) stepEditConfigurationPackage(source UnstructuredWithMetadata, t *UnstructuredWithMetadata) error {
if !pg.stepEnabled(stepEditConfigurationPackage) {
return nil
}
s := pg.stepConfigurationWithSubStep(stepEditConfigurationPackage, true)
t.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(t.Object))
s.Patch.Files = append(s.Patch.Files, t.Metadata.Path)
patchMap, err := computeJSONMergePathDoc(source.Object, t.Object)
if err != nil {
return err
}
return errors.Wrapf(pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(t.Object, patchMap),
},
Metadata: t.Metadata,
}), errEditConfigurationPackageFmt, t.Object.GetName())
}
func (pg *PlanGenerator) stepOrhanMR(u UnstructuredWithMetadata) (bool, error) {
return pg.stepSetDeletionPolicy(u, stepOrphanMRs, v1.DeletionOrphan, true)
}
func (pg *PlanGenerator) stepRevertOrhanMR(u UnstructuredWithMetadata) (bool, error) {
return pg.stepSetDeletionPolicy(u, stepRevertOrphanMRs, v1.DeletionDelete, false)
}
func (pg *PlanGenerator) stepSetDeletionPolicy(u UnstructuredWithMetadata, step step, policy v1.DeletionPolicy, checkCurrentPolicy bool) (bool, error) {
if !pg.stepEnabled(step) {
return false, nil
}
pv := fieldpath.Pave(u.Object.Object)
p, err := pv.GetString("spec.deletionPolicy")
if err != nil && !fieldpath.IsNotFound(err) {
return false, errors.Wrapf(err, "failed to get the current deletion policy from MR: %s", u.Object.GetName())
}
if checkCurrentPolicy && err == nil && v1.DeletionPolicy(p) == policy {
return false, nil
}
s := pg.stepConfiguration(step)
u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(u.Object))
s.Patch.Files = append(s.Patch.Files, u.Metadata.Path)
return true, errors.Wrapf(pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(u.Object, map[string]any{
"spec": map[string]any{
"deletionPolicy": string(policy),
},
}),
},
Metadata: u.Metadata,
}), errSetDeletionPolicyFmt, policy, u.Object.GetName())
}

View File

@ -1,304 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
xpmeta "github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane/apis/apiextensions/v1"
xpmetav1 "github.com/crossplane/crossplane/apis/pkg/meta/v1"
xpmetav1alpha1 "github.com/crossplane/crossplane/apis/pkg/meta/v1alpha1"
xppkgv1 "github.com/crossplane/crossplane/apis/pkg/v1"
xppkgv1beta1 "github.com/crossplane/crossplane/apis/pkg/v1beta1"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/json"
k8sjson "sigs.k8s.io/json"
)
const (
errFromUnstructured = "failed to convert from unstructured.Unstructured to the managed resource type"
errFromUnstructuredConfMeta = "failed to convert from unstructured.Unstructured to Crossplane Configuration metadata"
errFromUnstructuredConfPackage = "failed to convert from unstructured.Unstructured to Crossplane Configuration package"
errFromUnstructuredProvider = "failed to convert from unstructured.Unstructured to Crossplane Provider package"
errFromUnstructuredLock = "failed to convert from unstructured.Unstructured to Crossplane package lock"
errToUnstructured = "failed to convert from the managed resource type to unstructured.Unstructured"
errRawExtensionUnmarshal = "failed to unmarshal runtime.RawExtension"
errFmtPavedDelete = "failed to delete fieldpath %q from paved"
metadataAnnotationPaveKey = "metadata.annotations['%s']"
)
// CopyInto copies values of fields from the migration `source` object
// into the migration `target` object and fills in the target object's
// TypeMeta using the supplied `targetGVK`. While copying fields from
// migration source to migration target, the fields at the paths
// specified with `skipFieldPaths` array are skipped. This is a utility
// that can be used in the migration resource converter implementations.
// If a certain field with the same name in both the `source` and the `target`
// objects has different types in `source` and `target`, then it must be
// included in the `skipFieldPaths` and it must manually be handled in the
// conversion function.
func CopyInto(source any, target any, targetGVK schema.GroupVersionKind, skipFieldPaths ...string) (any, error) {
u := ToSanitizedUnstructured(source)
paved := fieldpath.Pave(u.Object)
skipFieldPaths = append(skipFieldPaths, "apiVersion", "kind",
fmt.Sprintf(metadataAnnotationPaveKey, xpmeta.AnnotationKeyExternalCreatePending),
fmt.Sprintf(metadataAnnotationPaveKey, xpmeta.AnnotationKeyExternalCreateSucceeded),
fmt.Sprintf(metadataAnnotationPaveKey, xpmeta.AnnotationKeyExternalCreateFailed),
fmt.Sprintf(metadataAnnotationPaveKey, corev1.LastAppliedConfigAnnotation),
)
for _, p := range skipFieldPaths {
if err := paved.DeleteField(p); err != nil {
return nil, errors.Wrapf(err, errFmtPavedDelete, p)
}
}
u.SetGroupVersionKind(targetGVK)
return target, errors.Wrap(runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, target), errFromUnstructured)
}
// sanitizeResource removes certain fields from the unstructured object.
// It turns out that certain fields, such as `metadata.creationTimestamp`
// are still serialized even if they have zero-values. This function
// removes such fields. We also unconditionally sanitize `status`
// so that the controller will populate it back.
func sanitizeResource(m map[string]any) map[string]any {
delete(m, "status")
if _, ok := m["metadata"]; !ok {
return m
}
metadata := m["metadata"].(map[string]any)
if v := metadata["creationTimestamp"]; v == nil {
delete(metadata, "creationTimestamp")
}
if len(metadata) == 0 {
delete(m, "metadata")
}
removeNilValuedKeys(m)
return m
}
// removeNilValuedKeys removes nil values from the specified map so that
// the serialized manifest do not contain corresponding superfluous YAML
// nulls.
func removeNilValuedKeys(m map[string]interface{}) {
for k, v := range m {
if v == nil {
delete(m, k)
continue
}
switch c := v.(type) {
case map[string]any:
removeNilValuedKeys(c)
case []any:
for _, e := range c {
if cm, ok := e.(map[string]interface{}); ok {
removeNilValuedKeys(cm)
}
}
}
}
}
// ToSanitizedUnstructured converts the specified managed resource to an
// unstructured.Unstructured. Before the converted object is
// returned, it's sanitized by removing certain fields
// (like status, metadata.creationTimestamp).
func ToSanitizedUnstructured(mg any) unstructured.Unstructured {
m, err := runtime.DefaultUnstructuredConverter.ToUnstructured(mg)
if err != nil {
panic(errors.Wrap(err, errToUnstructured))
}
return unstructured.Unstructured{
Object: sanitizeResource(m),
}
}
// FromRawExtension attempts to convert a runtime.RawExtension into
// an unstructured.Unstructured.
func FromRawExtension(r runtime.RawExtension) (unstructured.Unstructured, error) {
var m map[string]interface{}
if err := json.Unmarshal(r.Raw, &m); err != nil {
return unstructured.Unstructured{}, errors.Wrap(err, errRawExtensionUnmarshal)
}
return unstructured.Unstructured{
Object: m,
}, nil
}
// FromGroupVersionKind converts a schema.GroupVersionKind into
// a migration.GroupVersionKind.
func FromGroupVersionKind(gvk schema.GroupVersionKind) GroupVersionKind {
return GroupVersionKind{
Group: gvk.Group,
Version: gvk.Version,
Kind: gvk.Kind,
}
}
// ToComposition converts the specified unstructured.Unstructured to
// a Crossplane Composition.
// Workaround for:
// https://github.com/kubernetes-sigs/structured-merge-diff/issues/230
func ToComposition(u unstructured.Unstructured) (*xpv1.Composition, error) {
buff, err := json.Marshal(u.Object)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal map to JSON")
}
c := &xpv1.Composition{}
return c, errors.Wrap(k8sjson.UnmarshalCaseSensitivePreserveInts(buff, c), "failed to unmarshal into a v1.Composition")
}
func addGVK(u unstructured.Unstructured, target map[string]any) map[string]any {
if target == nil {
target = make(map[string]any)
}
target["apiVersion"] = u.GetAPIVersion()
target["kind"] = u.GetKind()
return target
}
func addNameGVK(u unstructured.Unstructured, target map[string]any) map[string]any {
target = addGVK(u, target)
m := target["metadata"]
if m == nil {
m = make(map[string]any)
}
metadata := m.(map[string]any)
metadata["name"] = u.GetName()
if len(u.GetNamespace()) != 0 {
metadata["namespace"] = u.GetNamespace()
}
target["metadata"] = m
return target
}
func toManagedResource(c runtime.ObjectCreater, u unstructured.Unstructured) (resource.Managed, bool, error) {
gvk := u.GroupVersionKind()
if gvk == xpv1.CompositionGroupVersionKind {
return nil, false, nil
}
obj, err := c.New(gvk)
if err != nil {
return nil, false, errors.Wrapf(err, errFmtNewObject, gvk)
}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, obj); err != nil {
return nil, false, errors.Wrap(err, errFromUnstructured)
}
mg, ok := obj.(resource.Managed)
return mg, ok, nil
}
func toConfigurationPackageV1(u unstructured.Unstructured) (*xppkgv1.Configuration, error) {
conf := &xppkgv1.Configuration{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, conf); err != nil {
return nil, errors.Wrap(err, errFromUnstructuredConfPackage)
}
return conf, nil
}
func toConfigurationMetadataV1(u unstructured.Unstructured) (*xpmetav1.Configuration, error) {
conf := &xpmetav1.Configuration{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, conf); err != nil {
return nil, errors.Wrap(err, errFromUnstructuredConfMeta)
}
return conf, nil
}
func toConfigurationMetadataV1Alpha1(u unstructured.Unstructured) (*xpmetav1alpha1.Configuration, error) {
conf := &xpmetav1alpha1.Configuration{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, conf); err != nil {
return nil, errors.Wrap(err, errFromUnstructuredConfMeta)
}
return conf, nil
}
func toConfigurationMetadata(u unstructured.Unstructured) (metav1.Object, error) {
var conf metav1.Object
var err error
switch u.GroupVersionKind().Version {
case "v1alpha1":
conf, err = toConfigurationMetadataV1Alpha1(u)
default:
conf, err = toConfigurationMetadataV1(u)
}
return conf, err
}
func toProviderPackage(u unstructured.Unstructured) (*xppkgv1.Provider, error) {
pkg := &xppkgv1.Provider{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, pkg); err != nil {
return nil, errors.Wrap(err, errFromUnstructuredProvider)
}
return pkg, nil
}
func getCategory(u unstructured.Unstructured) Category {
switch u.GroupVersionKind() {
case xpv1.CompositionGroupVersionKind:
return CategoryComposition
default:
return categoryUnknown
}
}
func toPackageLock(u unstructured.Unstructured) (*xppkgv1beta1.Lock, error) {
lock := &xppkgv1beta1.Lock{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, lock); err != nil {
return nil, errors.Wrap(err, errFromUnstructuredLock)
}
return lock, nil
}
// ConvertComposedTemplatePatchesMap converts the composed templates with given conversionMap
// Key of the conversionMap points to the source field
// Value of the conversionMap points to the target field
func ConvertComposedTemplatePatchesMap(sourceTemplate xpv1.ComposedTemplate, conversionMap map[string]string) []xpv1.Patch {
var patchesToAdd []xpv1.Patch
for _, p := range sourceTemplate.Patches {
switch p.Type { //nolint:exhaustive
case xpv1.PatchTypeFromCompositeFieldPath, xpv1.PatchTypeCombineFromComposite, "":
{
if p.ToFieldPath != nil {
if to, ok := conversionMap[*p.ToFieldPath]; ok {
patchesToAdd = append(patchesToAdd, xpv1.Patch{
Type: p.Type,
FromFieldPath: p.FromFieldPath,
ToFieldPath: &to,
Transforms: p.Transforms,
Policy: p.Policy,
Combine: p.Combine,
})
}
}
}
case xpv1.PatchTypeToCompositeFieldPath, xpv1.PatchTypeCombineToComposite:
{
if p.FromFieldPath != nil {
if to, ok := conversionMap[*p.FromFieldPath]; ok {
patchesToAdd = append(patchesToAdd, xpv1.Patch{
Type: p.Type,
FromFieldPath: &to,
ToFieldPath: p.ToFieldPath,
Transforms: p.Transforms,
Policy: p.Policy,
Combine: p.Combine,
})
}
}
}
}
}
return patchesToAdd
}

View File

@ -1,21 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import "fmt"
type errUnsupportedStepType struct {
planStep Step
}
func (e errUnsupportedStepType) Error() string {
return fmt.Sprintf("executor does not support steps of type %q in step: %s", e.planStep.Type, e.planStep.Name)
}
func NewUnsupportedStepTypeError(s Step) error {
return errUnsupportedStepType{
planStep: s,
}
}

View File

@ -1,80 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"github.com/pkg/errors"
)
func (pg *PlanGenerator) stepBackupAllResources() {
pg.stepBackupManagedResources()
pg.stepBackupCompositeResources()
pg.stepBackupClaims()
}
func (pg *PlanGenerator) stepBackupManagedResources() {
s := pg.stepConfiguration(stepBackupMRs)
s.Exec.Args = []string{"-c", "kubectl get managed -o yaml > backup/managed-resources.yaml"}
}
func (pg *PlanGenerator) stepBackupCompositeResources() {
s := pg.stepConfiguration(stepBackupComposites)
s.Exec.Args = []string{"-c", "kubectl get composite -o yaml > backup/composite-resources.yaml"}
}
func (pg *PlanGenerator) stepBackupClaims() {
s := pg.stepConfiguration(stepBackupClaims)
s.Exec.Args = []string{"-c", "kubectl get claim --all-namespaces -o yaml > backup/claim-resources.yaml"}
}
func (pg *PlanGenerator) stepCheckHealthOfNewProvider(source UnstructuredWithMetadata, targets []*UnstructuredWithMetadata) error {
for _, t := range targets {
var s *Step
isFamilyConfig, err := checkContainsFamilyConfigProvider(targets)
if err != nil {
return errors.Wrapf(err, "could not decide whether the provider family config")
}
if isFamilyConfig {
s = pg.stepConfigurationWithSubStep(stepCheckHealthFamilyProvider, true)
} else {
s = pg.stepConfigurationWithSubStep(stepCheckHealthNewServiceScopedProvider, true)
}
s.Exec.Args = []string{"-c", fmt.Sprintf("kubectl wait provider.pkg %s --for condition=Healthy", t.Object.GetName())}
t.Object.Object = addGVK(source.Object, t.Object.Object)
t.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(t.Object))
}
return nil
}
func (pg *PlanGenerator) stepCheckInstallationOfNewProvider(source UnstructuredWithMetadata, targets []*UnstructuredWithMetadata) error {
for _, t := range targets {
var s *Step
isFamilyConfig, err := checkContainsFamilyConfigProvider(targets)
if err != nil {
return errors.Wrapf(err, "could not decide whether the provider family config")
}
if isFamilyConfig {
s = pg.stepConfigurationWithSubStep(stepCheckInstallationFamilyProviderRevision, true)
} else {
s = pg.stepConfigurationWithSubStep(stepCheckInstallationServiceScopedProviderRevision, true)
}
s.Exec.Args = []string{"-c", fmt.Sprintf("kubectl wait provider.pkg %s --for condition=Installed", t.Object.GetName())}
t.Object.Object = addGVK(source.Object, t.Object.Object)
t.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(t.Object))
}
return nil
}
func (pg *PlanGenerator) stepBuildConfiguration() {
s := pg.stepConfiguration(stepBuildConfiguration)
s.Exec.Args = []string{"-c", "up xpkg build --package-root={{PKG_ROOT}} --examples-root={{EXAMPLES_ROOT}} -o {{PKG_PATH}}"}
}
func (pg *PlanGenerator) stepPushConfiguration() {
s := pg.stepConfiguration(stepPushConfiguration)
s.Exec.Args = []string{"-c", "up xpkg push {{TARGET_CONFIGURATION_PACKAGE}} -f {{PKG_PATH}}"}
}

View File

@ -1,621 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/crossplane/crossplane-runtime/pkg/resource (interfaces: Managed)
// Package mocks is a generated GoMock package.
package mocks
import (
reflect "reflect"
v1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
gomock "github.com/golang/mock/gomock"
v10 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
)
// MockManaged is a mock of Managed interface.
type MockManaged struct {
ctrl *gomock.Controller
recorder *MockManagedMockRecorder
}
// MockManagedMockRecorder is the mock recorder for MockManaged.
type MockManagedMockRecorder struct {
mock *MockManaged
}
// NewMockManaged creates a new mock instance.
func NewMockManaged(ctrl *gomock.Controller) *MockManaged {
mock := &MockManaged{ctrl: ctrl}
mock.recorder = &MockManagedMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockManaged) EXPECT() *MockManagedMockRecorder {
return m.recorder
}
// DeepCopyObject mocks base method.
func (m *MockManaged) DeepCopyObject() runtime.Object {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeepCopyObject")
ret0, _ := ret[0].(runtime.Object)
return ret0
}
// DeepCopyObject indicates an expected call of DeepCopyObject.
func (mr *MockManagedMockRecorder) DeepCopyObject() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeepCopyObject", reflect.TypeOf((*MockManaged)(nil).DeepCopyObject))
}
// GetAnnotations mocks base method.
func (m *MockManaged) GetAnnotations() map[string]string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAnnotations")
ret0, _ := ret[0].(map[string]string)
return ret0
}
// GetAnnotations indicates an expected call of GetAnnotations.
func (mr *MockManagedMockRecorder) GetAnnotations() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAnnotations", reflect.TypeOf((*MockManaged)(nil).GetAnnotations))
}
// GetCondition mocks base method.
func (m *MockManaged) GetCondition(arg0 v1.ConditionType) v1.Condition {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCondition", arg0)
ret0, _ := ret[0].(v1.Condition)
return ret0
}
// GetCondition indicates an expected call of GetCondition.
func (mr *MockManagedMockRecorder) GetCondition(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCondition", reflect.TypeOf((*MockManaged)(nil).GetCondition), arg0)
}
// GetCreationTimestamp mocks base method.
func (m *MockManaged) GetCreationTimestamp() v10.Time {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCreationTimestamp")
ret0, _ := ret[0].(v10.Time)
return ret0
}
// GetCreationTimestamp indicates an expected call of GetCreationTimestamp.
func (mr *MockManagedMockRecorder) GetCreationTimestamp() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCreationTimestamp", reflect.TypeOf((*MockManaged)(nil).GetCreationTimestamp))
}
// GetDeletionGracePeriodSeconds mocks base method.
func (m *MockManaged) GetDeletionGracePeriodSeconds() *int64 {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetDeletionGracePeriodSeconds")
ret0, _ := ret[0].(*int64)
return ret0
}
// GetDeletionGracePeriodSeconds indicates an expected call of GetDeletionGracePeriodSeconds.
func (mr *MockManagedMockRecorder) GetDeletionGracePeriodSeconds() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeletionGracePeriodSeconds", reflect.TypeOf((*MockManaged)(nil).GetDeletionGracePeriodSeconds))
}
// GetDeletionPolicy mocks base method.
func (m *MockManaged) GetDeletionPolicy() v1.DeletionPolicy {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetDeletionPolicy")
ret0, _ := ret[0].(v1.DeletionPolicy)
return ret0
}
// GetDeletionPolicy indicates an expected call of GetDeletionPolicy.
func (mr *MockManagedMockRecorder) GetDeletionPolicy() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeletionPolicy", reflect.TypeOf((*MockManaged)(nil).GetDeletionPolicy))
}
// GetDeletionTimestamp mocks base method.
func (m *MockManaged) GetDeletionTimestamp() *v10.Time {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetDeletionTimestamp")
ret0, _ := ret[0].(*v10.Time)
return ret0
}
// GetDeletionTimestamp indicates an expected call of GetDeletionTimestamp.
func (mr *MockManagedMockRecorder) GetDeletionTimestamp() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeletionTimestamp", reflect.TypeOf((*MockManaged)(nil).GetDeletionTimestamp))
}
// GetFinalizers mocks base method.
func (m *MockManaged) GetFinalizers() []string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetFinalizers")
ret0, _ := ret[0].([]string)
return ret0
}
// GetFinalizers indicates an expected call of GetFinalizers.
func (mr *MockManagedMockRecorder) GetFinalizers() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFinalizers", reflect.TypeOf((*MockManaged)(nil).GetFinalizers))
}
// GetGenerateName mocks base method.
func (m *MockManaged) GetGenerateName() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetGenerateName")
ret0, _ := ret[0].(string)
return ret0
}
// GetGenerateName indicates an expected call of GetGenerateName.
func (mr *MockManagedMockRecorder) GetGenerateName() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGenerateName", reflect.TypeOf((*MockManaged)(nil).GetGenerateName))
}
// GetGeneration mocks base method.
func (m *MockManaged) GetGeneration() int64 {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetGeneration")
ret0, _ := ret[0].(int64)
return ret0
}
// GetGeneration indicates an expected call of GetGeneration.
func (mr *MockManagedMockRecorder) GetGeneration() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGeneration", reflect.TypeOf((*MockManaged)(nil).GetGeneration))
}
// GetLabels mocks base method.
func (m *MockManaged) GetLabels() map[string]string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetLabels")
ret0, _ := ret[0].(map[string]string)
return ret0
}
// GetLabels indicates an expected call of GetLabels.
func (mr *MockManagedMockRecorder) GetLabels() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLabels", reflect.TypeOf((*MockManaged)(nil).GetLabels))
}
// GetManagedFields mocks base method.
func (m *MockManaged) GetManagedFields() []v10.ManagedFieldsEntry {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetManagedFields")
ret0, _ := ret[0].([]v10.ManagedFieldsEntry)
return ret0
}
// GetManagedFields indicates an expected call of GetManagedFields.
func (mr *MockManagedMockRecorder) GetManagedFields() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetManagedFields", reflect.TypeOf((*MockManaged)(nil).GetManagedFields))
}
// GetManagementPolicies mocks base method.
func (m *MockManaged) GetManagementPolicies() v1.ManagementPolicies {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetManagementPolicies")
ret0, _ := ret[0].(v1.ManagementPolicies)
return ret0
}
// GetManagementPolicies indicates an expected call of GetManagementPolicies.
func (mr *MockManagedMockRecorder) GetManagementPolicies() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetManagementPolicies", reflect.TypeOf((*MockManaged)(nil).GetManagementPolicies))
}
// GetName mocks base method.
func (m *MockManaged) GetName() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetName")
ret0, _ := ret[0].(string)
return ret0
}
// GetName indicates an expected call of GetName.
func (mr *MockManagedMockRecorder) GetName() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockManaged)(nil).GetName))
}
// GetNamespace mocks base method.
func (m *MockManaged) GetNamespace() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetNamespace")
ret0, _ := ret[0].(string)
return ret0
}
// GetNamespace indicates an expected call of GetNamespace.
func (mr *MockManagedMockRecorder) GetNamespace() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNamespace", reflect.TypeOf((*MockManaged)(nil).GetNamespace))
}
// GetObjectKind mocks base method.
func (m *MockManaged) GetObjectKind() schema.ObjectKind {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetObjectKind")
ret0, _ := ret[0].(schema.ObjectKind)
return ret0
}
// GetObjectKind indicates an expected call of GetObjectKind.
func (mr *MockManagedMockRecorder) GetObjectKind() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectKind", reflect.TypeOf((*MockManaged)(nil).GetObjectKind))
}
// GetOwnerReferences mocks base method.
func (m *MockManaged) GetOwnerReferences() []v10.OwnerReference {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetOwnerReferences")
ret0, _ := ret[0].([]v10.OwnerReference)
return ret0
}
// GetOwnerReferences indicates an expected call of GetOwnerReferences.
func (mr *MockManagedMockRecorder) GetOwnerReferences() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOwnerReferences", reflect.TypeOf((*MockManaged)(nil).GetOwnerReferences))
}
// GetProviderConfigReference mocks base method.
func (m *MockManaged) GetProviderConfigReference() *v1.Reference {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetProviderConfigReference")
ret0, _ := ret[0].(*v1.Reference)
return ret0
}
// GetProviderConfigReference indicates an expected call of GetProviderConfigReference.
func (mr *MockManagedMockRecorder) GetProviderConfigReference() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProviderConfigReference", reflect.TypeOf((*MockManaged)(nil).GetProviderConfigReference))
}
// GetPublishConnectionDetailsTo mocks base method.
func (m *MockManaged) GetPublishConnectionDetailsTo() *v1.PublishConnectionDetailsTo {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPublishConnectionDetailsTo")
ret0, _ := ret[0].(*v1.PublishConnectionDetailsTo)
return ret0
}
// GetPublishConnectionDetailsTo indicates an expected call of GetPublishConnectionDetailsTo.
func (mr *MockManagedMockRecorder) GetPublishConnectionDetailsTo() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPublishConnectionDetailsTo", reflect.TypeOf((*MockManaged)(nil).GetPublishConnectionDetailsTo))
}
// GetResourceVersion mocks base method.
func (m *MockManaged) GetResourceVersion() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetResourceVersion")
ret0, _ := ret[0].(string)
return ret0
}
// GetResourceVersion indicates an expected call of GetResourceVersion.
func (mr *MockManagedMockRecorder) GetResourceVersion() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetResourceVersion", reflect.TypeOf((*MockManaged)(nil).GetResourceVersion))
}
// GetSelfLink mocks base method.
func (m *MockManaged) GetSelfLink() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSelfLink")
ret0, _ := ret[0].(string)
return ret0
}
// GetSelfLink indicates an expected call of GetSelfLink.
func (mr *MockManagedMockRecorder) GetSelfLink() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSelfLink", reflect.TypeOf((*MockManaged)(nil).GetSelfLink))
}
// GetUID mocks base method.
func (m *MockManaged) GetUID() types.UID {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUID")
ret0, _ := ret[0].(types.UID)
return ret0
}
// GetUID indicates an expected call of GetUID.
func (mr *MockManagedMockRecorder) GetUID() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUID", reflect.TypeOf((*MockManaged)(nil).GetUID))
}
// GetWriteConnectionSecretToReference mocks base method.
func (m *MockManaged) GetWriteConnectionSecretToReference() *v1.SecretReference {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetWriteConnectionSecretToReference")
ret0, _ := ret[0].(*v1.SecretReference)
return ret0
}
// GetWriteConnectionSecretToReference indicates an expected call of GetWriteConnectionSecretToReference.
func (mr *MockManagedMockRecorder) GetWriteConnectionSecretToReference() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWriteConnectionSecretToReference", reflect.TypeOf((*MockManaged)(nil).GetWriteConnectionSecretToReference))
}
// SetAnnotations mocks base method.
func (m *MockManaged) SetAnnotations(arg0 map[string]string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetAnnotations", arg0)
}
// SetAnnotations indicates an expected call of SetAnnotations.
func (mr *MockManagedMockRecorder) SetAnnotations(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAnnotations", reflect.TypeOf((*MockManaged)(nil).SetAnnotations), arg0)
}
// SetConditions mocks base method.
func (m *MockManaged) SetConditions(arg0 ...v1.Condition) {
m.ctrl.T.Helper()
varargs := []interface{}{}
for _, a := range arg0 {
varargs = append(varargs, a)
}
m.ctrl.Call(m, "SetConditions", varargs...)
}
// SetConditions indicates an expected call of SetConditions.
func (mr *MockManagedMockRecorder) SetConditions(arg0 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetConditions", reflect.TypeOf((*MockManaged)(nil).SetConditions), arg0...)
}
// SetCreationTimestamp mocks base method.
func (m *MockManaged) SetCreationTimestamp(arg0 v10.Time) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetCreationTimestamp", arg0)
}
// SetCreationTimestamp indicates an expected call of SetCreationTimestamp.
func (mr *MockManagedMockRecorder) SetCreationTimestamp(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCreationTimestamp", reflect.TypeOf((*MockManaged)(nil).SetCreationTimestamp), arg0)
}
// SetDeletionGracePeriodSeconds mocks base method.
func (m *MockManaged) SetDeletionGracePeriodSeconds(arg0 *int64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetDeletionGracePeriodSeconds", arg0)
}
// SetDeletionGracePeriodSeconds indicates an expected call of SetDeletionGracePeriodSeconds.
func (mr *MockManagedMockRecorder) SetDeletionGracePeriodSeconds(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeletionGracePeriodSeconds", reflect.TypeOf((*MockManaged)(nil).SetDeletionGracePeriodSeconds), arg0)
}
// SetDeletionPolicy mocks base method.
func (m *MockManaged) SetDeletionPolicy(arg0 v1.DeletionPolicy) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetDeletionPolicy", arg0)
}
// SetDeletionPolicy indicates an expected call of SetDeletionPolicy.
func (mr *MockManagedMockRecorder) SetDeletionPolicy(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeletionPolicy", reflect.TypeOf((*MockManaged)(nil).SetDeletionPolicy), arg0)
}
// SetDeletionTimestamp mocks base method.
func (m *MockManaged) SetDeletionTimestamp(arg0 *v10.Time) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetDeletionTimestamp", arg0)
}
// SetDeletionTimestamp indicates an expected call of SetDeletionTimestamp.
func (mr *MockManagedMockRecorder) SetDeletionTimestamp(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeletionTimestamp", reflect.TypeOf((*MockManaged)(nil).SetDeletionTimestamp), arg0)
}
// SetFinalizers mocks base method.
func (m *MockManaged) SetFinalizers(arg0 []string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetFinalizers", arg0)
}
// SetFinalizers indicates an expected call of SetFinalizers.
func (mr *MockManagedMockRecorder) SetFinalizers(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFinalizers", reflect.TypeOf((*MockManaged)(nil).SetFinalizers), arg0)
}
// SetGenerateName mocks base method.
func (m *MockManaged) SetGenerateName(arg0 string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetGenerateName", arg0)
}
// SetGenerateName indicates an expected call of SetGenerateName.
func (mr *MockManagedMockRecorder) SetGenerateName(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetGenerateName", reflect.TypeOf((*MockManaged)(nil).SetGenerateName), arg0)
}
// SetGeneration mocks base method.
func (m *MockManaged) SetGeneration(arg0 int64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetGeneration", arg0)
}
// SetGeneration indicates an expected call of SetGeneration.
func (mr *MockManagedMockRecorder) SetGeneration(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetGeneration", reflect.TypeOf((*MockManaged)(nil).SetGeneration), arg0)
}
// SetLabels mocks base method.
func (m *MockManaged) SetLabels(arg0 map[string]string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetLabels", arg0)
}
// SetLabels indicates an expected call of SetLabels.
func (mr *MockManagedMockRecorder) SetLabels(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLabels", reflect.TypeOf((*MockManaged)(nil).SetLabels), arg0)
}
// SetManagedFields mocks base method.
func (m *MockManaged) SetManagedFields(arg0 []v10.ManagedFieldsEntry) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetManagedFields", arg0)
}
// SetManagedFields indicates an expected call of SetManagedFields.
func (mr *MockManagedMockRecorder) SetManagedFields(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetManagedFields", reflect.TypeOf((*MockManaged)(nil).SetManagedFields), arg0)
}
// SetManagementPolicies mocks base method.
func (m *MockManaged) SetManagementPolicies(arg0 v1.ManagementPolicies) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetManagementPolicies", arg0)
}
// SetManagementPolicies indicates an expected call of SetManagementPolicies.
func (mr *MockManagedMockRecorder) SetManagementPolicies(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetManagementPolicies", reflect.TypeOf((*MockManaged)(nil).SetManagementPolicies), arg0)
}
// SetName mocks base method.
func (m *MockManaged) SetName(arg0 string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetName", arg0)
}
// SetName indicates an expected call of SetName.
func (mr *MockManagedMockRecorder) SetName(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetName", reflect.TypeOf((*MockManaged)(nil).SetName), arg0)
}
// SetNamespace mocks base method.
func (m *MockManaged) SetNamespace(arg0 string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetNamespace", arg0)
}
// SetNamespace indicates an expected call of SetNamespace.
func (mr *MockManagedMockRecorder) SetNamespace(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNamespace", reflect.TypeOf((*MockManaged)(nil).SetNamespace), arg0)
}
// SetOwnerReferences mocks base method.
func (m *MockManaged) SetOwnerReferences(arg0 []v10.OwnerReference) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetOwnerReferences", arg0)
}
// SetOwnerReferences indicates an expected call of SetOwnerReferences.
func (mr *MockManagedMockRecorder) SetOwnerReferences(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetOwnerReferences", reflect.TypeOf((*MockManaged)(nil).SetOwnerReferences), arg0)
}
// SetProviderConfigReference mocks base method.
func (m *MockManaged) SetProviderConfigReference(arg0 *v1.Reference) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetProviderConfigReference", arg0)
}
// SetProviderConfigReference indicates an expected call of SetProviderConfigReference.
func (mr *MockManagedMockRecorder) SetProviderConfigReference(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetProviderConfigReference", reflect.TypeOf((*MockManaged)(nil).SetProviderConfigReference), arg0)
}
// SetPublishConnectionDetailsTo mocks base method.
func (m *MockManaged) SetPublishConnectionDetailsTo(arg0 *v1.PublishConnectionDetailsTo) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetPublishConnectionDetailsTo", arg0)
}
// SetPublishConnectionDetailsTo indicates an expected call of SetPublishConnectionDetailsTo.
func (mr *MockManagedMockRecorder) SetPublishConnectionDetailsTo(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPublishConnectionDetailsTo", reflect.TypeOf((*MockManaged)(nil).SetPublishConnectionDetailsTo), arg0)
}
// SetResourceVersion mocks base method.
func (m *MockManaged) SetResourceVersion(arg0 string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetResourceVersion", arg0)
}
// SetResourceVersion indicates an expected call of SetResourceVersion.
func (mr *MockManagedMockRecorder) SetResourceVersion(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetResourceVersion", reflect.TypeOf((*MockManaged)(nil).SetResourceVersion), arg0)
}
// SetSelfLink mocks base method.
func (m *MockManaged) SetSelfLink(arg0 string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetSelfLink", arg0)
}
// SetSelfLink indicates an expected call of SetSelfLink.
func (mr *MockManagedMockRecorder) SetSelfLink(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSelfLink", reflect.TypeOf((*MockManaged)(nil).SetSelfLink), arg0)
}
// SetUID mocks base method.
func (m *MockManaged) SetUID(arg0 types.UID) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetUID", arg0)
}
// SetUID indicates an expected call of SetUID.
func (mr *MockManagedMockRecorder) SetUID(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUID", reflect.TypeOf((*MockManaged)(nil).SetUID), arg0)
}
// SetWriteConnectionSecretToReference mocks base method.
func (m *MockManaged) SetWriteConnectionSecretToReference(arg0 *v1.SecretReference) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetWriteConnectionSecretToReference", arg0)
}
// SetWriteConnectionSecretToReference indicates an expected call of SetWriteConnectionSecretToReference.
func (mr *MockManagedMockRecorder) SetWriteConnectionSecretToReference(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteConnectionSecretToReference", reflect.TypeOf((*MockManaged)(nil).SetWriteConnectionSecretToReference), arg0)
}

View File

@ -1,130 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
//go:generate go run github.com/golang/mock/mockgen -copyright_file ../../../hack/boilerplate.txt -destination=./mocks/mock.go -package mocks github.com/crossplane/crossplane-runtime/pkg/resource Managed
package fake
import (
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/crossplane/upjet/pkg/migration/fake/mocks"
)
const (
MigrationSourceGroup = "fakesourceapi"
MigrationSourceVersion = "v1alpha1"
MigrationSourceKind = "VPC"
MigrationTargetGroup = "faketargetapi"
MigrationTargetVersion = "v1alpha1"
MigrationTargetKind = "VPC"
)
var (
MigrationSourceGVK = schema.GroupVersionKind{
Group: MigrationSourceGroup,
Version: MigrationSourceVersion,
Kind: MigrationSourceKind,
}
MigrationTargetGVK = schema.GroupVersionKind{
Group: MigrationTargetGroup,
Version: MigrationTargetVersion,
Kind: MigrationTargetKind,
}
)
type MigrationSourceObject struct {
mocks.MockManaged
// cannot inline v1.TypeMeta here as mocks.MockManaged is also inlined
APIVersion string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty"`
// cannot inline v1.ObjectMeta here as mocks.MockManaged is also inlined
ObjectMeta ObjectMeta `json:"metadata,omitempty"`
Spec SourceSpec `json:"spec"`
Status Status `json:"status,omitempty"`
}
type SourceSpec struct {
xpv1.ResourceSpec `json:",inline"`
ForProvider SourceSpecParameters `json:"forProvider"`
}
type EmbeddedParameter struct {
Param *string `json:"param,omitempty"`
}
type SourceSpecParameters struct {
Region *string `json:"region,omitempty"`
CIDRBlock string `json:"cidrBlock"`
Tags []Tag `json:"tags,omitempty"`
TestParam *EmbeddedParameter `json:",inline"`
}
type Tag struct {
Key string `json:"key"`
Value string `json:"value"`
}
type Status struct {
xpv1.ResourceStatus `json:",inline"`
AtProvider Observation `json:"atProvider,omitempty"`
}
type Observation struct{}
func (m *MigrationSourceObject) GetName() string {
return m.ObjectMeta.Name
}
type MigrationTargetObject struct {
mocks.MockManaged
// cannot inline v1.TypeMeta here as mocks.MockManaged is also inlined
APIVersion string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty"`
// cannot inline v1.ObjectMeta here as mocks.MockManaged is also inlined
ObjectMeta ObjectMeta `json:"metadata,omitempty"`
Spec TargetSpec `json:"spec"`
Status Status `json:"status,omitempty"`
}
type ObjectMeta struct {
Name string `json:"name,omitempty"`
GenerateName string `json:"generateName,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}
type TargetSpec struct {
xpv1.ResourceSpec `json:",inline"`
ForProvider TargetSpecParameters `json:"forProvider"`
}
type TargetSpecParameters struct {
Region *string `json:"region,omitempty"`
CIDRBlock string `json:"cidrBlock"`
Tags map[string]string `json:"tags,omitempty"`
TestParam EmbeddedParameter `json:",inline"`
}
type targetObjectKind struct{}
func (t *targetObjectKind) SetGroupVersionKind(_ schema.GroupVersionKind) {}
func (t *targetObjectKind) GroupVersionKind() schema.GroupVersionKind {
return MigrationTargetGVK
}
func (m *MigrationTargetObject) GetObjectKind() schema.ObjectKind {
return &targetObjectKind{}
}
func (m *MigrationTargetObject) GetName() string {
return m.ObjectMeta.Name
}
func (m *MigrationTargetObject) GetGenerateName() string {
return m.ObjectMeta.GenerateName
}

View File

@ -1,178 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"bytes"
"fmt"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/spf13/afero"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/yaml"
sigsyaml "sigs.k8s.io/yaml"
)
var (
_ Source = &FileSystemSource{}
_ Target = &FileSystemTarget{}
)
// FileSystemSource is a source implementation to read resources from filesystem
type FileSystemSource struct {
index int
items []UnstructuredWithMetadata
afero afero.Afero
}
// FileSystemSourceOption allows you to configure FileSystemSource
type FileSystemSourceOption func(*FileSystemSource)
// FsWithFileSystem configures the filesystem to use. Used mostly for testing.
func FsWithFileSystem(f afero.Fs) FileSystemSourceOption {
return func(fs *FileSystemSource) {
fs.afero = afero.Afero{Fs: f}
}
}
// NewFileSystemSource returns a FileSystemSource
func NewFileSystemSource(dir string, opts ...FileSystemSourceOption) (*FileSystemSource, error) {
fs := &FileSystemSource{
afero: afero.Afero{Fs: afero.NewOsFs()},
}
for _, f := range opts {
f(fs)
}
if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return errors.Wrap(err, fmt.Sprintf("cannot read %s", path))
}
if info.IsDir() {
return nil
}
data, err := fs.afero.ReadFile(path)
if err != nil {
return errors.Wrap(err, "cannot read source file")
}
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewBufferString(string(data)), 1024)
u := &unstructured.Unstructured{}
if err := decoder.Decode(&u); err != nil {
return errors.Wrap(err, "cannot decode read data")
}
fs.items = append(fs.items, UnstructuredWithMetadata{
Object: *u,
Metadata: Metadata{
Path: path,
Category: getCategory(*u),
},
})
return nil
}); err != nil {
return nil, errors.Wrap(err, "cannot read source directory")
}
return fs, nil
}
// HasNext checks the next item
func (fs *FileSystemSource) HasNext() (bool, error) {
return fs.index < len(fs.items), nil
}
// Next returns the next item of slice
func (fs *FileSystemSource) Next() (UnstructuredWithMetadata, error) {
if hasNext, _ := fs.HasNext(); hasNext {
item := fs.items[fs.index]
fs.index++
return item, nil
}
return UnstructuredWithMetadata{}, errors.New("no more elements")
}
// Reset resets the source so that resources can be reread from the beginning.
func (fs *FileSystemSource) Reset() error {
fs.index = 0
return nil
}
// FileSystemTarget is a target implementation to write/patch/delete resources to file system
type FileSystemTarget struct {
afero afero.Afero
parent string
}
// FileSystemTargetOption allows you to configure FileSystemTarget
type FileSystemTargetOption func(*FileSystemTarget)
// FtWithFileSystem configures the filesystem to use. Used mostly for testing.
func FtWithFileSystem(f afero.Fs) FileSystemTargetOption {
return func(ft *FileSystemTarget) {
ft.afero = afero.Afero{Fs: f}
}
}
// WithParentDirectory configures the parent directory for the FileSystemTarget
func WithParentDirectory(parent string) FileSystemTargetOption {
return func(ft *FileSystemTarget) {
ft.parent = parent
}
}
// NewFileSystemTarget returns a FileSystemTarget
func NewFileSystemTarget(opts ...FileSystemTargetOption) *FileSystemTarget {
ft := &FileSystemTarget{
afero: afero.Afero{Fs: afero.NewOsFs()},
}
for _, f := range opts {
f(ft)
}
return ft
}
// Put writes input to filesystem
func (ft *FileSystemTarget) Put(o UnstructuredWithMetadata) error {
b, err := sigsyaml.Marshal(o.Object.Object)
if err != nil {
return errors.Wrap(err, "cannot marshal object")
}
if err := os.MkdirAll(filepath.Join(ft.parent, filepath.Dir(o.Metadata.Path)), 0o750); err != nil {
return errors.Wrapf(err, "cannot mkdirall: %s", filepath.Dir(o.Metadata.Path))
}
if o.Metadata.Parents != "" {
f, err := ft.afero.OpenFile(filepath.Join(ft.parent, o.Metadata.Path), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return errors.Wrap(err, "cannot open file")
}
defer f.Close() //nolint:errcheck
if _, err = fmt.Fprintf(f, "\n---\n\n%s", string(b)); err != nil {
return errors.Wrap(err, "cannot write file")
}
} else {
f, err := ft.afero.Create(filepath.Join(ft.parent, o.Metadata.Path))
if err != nil {
return errors.Wrap(err, "cannot create file")
}
if _, err := f.Write(b); err != nil {
return errors.Wrap(err, "cannot write file")
}
}
return nil
}
// Delete deletes a file from filesystem
func (ft *FileSystemTarget) Delete(o UnstructuredWithMetadata) error {
return ft.afero.Remove(o.Metadata.Path)
}

View File

@ -1,260 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/pkg/errors"
"github.com/spf13/afero"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
var (
unstructuredAwsVpc = map[string]interface{}{
"apiVersion": "ec2.aws.crossplane.io/v1beta1",
"kind": "VPC",
"metadata": map[string]interface{}{
"name": "sample-vpc",
},
"spec": map[string]interface{}{
"forProvider": map[string]interface{}{
"region": "us-west-1",
"cidrBlock": "172.16.0.0/16",
},
},
}
unstructuredResourceGroup = map[string]interface{}{
"apiVersion": "azure.crossplane.io/v1beta1",
"kind": "ResourceGroup",
"metadata": map[string]interface{}{
"name": "example-resources",
},
"spec": map[string]interface{}{
"forProvider": map[string]interface{}{
"location": "West Europe",
},
},
}
)
func TestNewFileSystemSource(t *testing.T) {
type args struct {
dir string
a func() afero.Afero
}
type want struct {
fs *FileSystemSource
err error
}
cases := map[string]struct {
args
want
}{
"Successful": {
args: args{
dir: "testdata",
a: func() afero.Afero {
fss := afero.Afero{Fs: afero.NewMemMapFs()}
_ = fss.WriteFile("testdata/source/awsvpc.yaml",
[]byte("apiVersion: ec2.aws.crossplane.io/v1beta1\nkind: VPC\nmetadata:\n name: sample-vpc\nspec:\n forProvider:\n cidrBlock: 172.16.0.0/16\n region: us-west-1\n"),
0600)
_ = fss.WriteFile("testdata/source/resourcegroup.yaml",
[]byte("apiVersion: azure.crossplane.io/v1beta1\nkind: ResourceGroup\nmetadata:\n name: example-resources\nspec:\n forProvider:\n location: West Europe\n"),
0600)
return fss
},
},
want: want{
fs: &FileSystemSource{
index: 0,
items: []UnstructuredWithMetadata{
{
Object: unstructured.Unstructured{
Object: unstructuredAwsVpc,
},
Metadata: Metadata{
Path: "testdata/source/awsvpc.yaml",
},
},
{
Object: unstructured.Unstructured{
Object: unstructuredResourceGroup,
},
Metadata: Metadata{
Path: "testdata/source/resourcegroup.yaml",
},
},
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
files := tc.args.a()
fs, err := NewFileSystemSource("testdata/source", FsWithFileSystem(files))
if err != nil {
t.Fatalf("Failed to initialize a new FileSystemSource: %v", err)
}
if diff := cmp.Diff(tc.want.err, err); diff != "" {
t.Errorf("\nNext(...): -want, +got:\n%s", diff)
}
if diff := cmp.Diff(tc.want.fs.items, fs.items); diff != "" {
t.Errorf("\nNext(...): -want, +got:\n%s", diff)
}
})
}
}
func TestFileSystemTarget_Put(t *testing.T) {
type args struct {
o UnstructuredWithMetadata
a func() afero.Afero
}
type want struct {
data string
err error
}
cases := map[string]struct {
args
want
}{
"Write": {
args: args{
o: UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "ec2.aws.upbound.io/v1beta1",
"kind": "VPC",
"metadata": map[string]interface{}{
"name": "sample-vpc",
},
"spec": map[string]interface{}{
"forProvider": map[string]interface{}{
"region": "us-west-1",
"cidrBlock": "172.16.0.0/16",
},
},
},
},
Metadata: Metadata{
Path: "testdata/source/awsvpc.yaml",
},
},
a: func() afero.Afero {
return afero.Afero{Fs: afero.NewMemMapFs()}
},
},
want: want{
data: "apiVersion: ec2.aws.upbound.io/v1beta1\nkind: VPC\nmetadata:\n name: sample-vpc\nspec:\n forProvider:\n cidrBlock: 172.16.0.0/16\n region: us-west-1\n",
err: nil,
},
},
"Append": {
args: args{
o: UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "azure.crossplane.io/v1beta1",
"kind": "ResourceGroup",
"metadata": map[string]interface{}{
"name": "example-resources",
},
"spec": map[string]interface{}{
"forProvider": map[string]interface{}{
"location": "West Europe",
},
},
},
},
Metadata: Metadata{
Path: "testdata/source/awsvpc.yaml",
Parents: "parent metadata",
},
},
a: func() afero.Afero {
fss := afero.Afero{Fs: afero.NewMemMapFs()}
_ = fss.WriteFile("testdata/source/awsvpc.yaml",
[]byte("apiVersion: ec2.aws.upbound.io/v1beta1\nkind: VPC\nmetadata:\n name: sample-vpc\nspec:\n forProvider:\n cidrBlock: 172.16.0.0/16\n region: us-west-1\n"),
0600)
return fss
},
},
want: want{
data: "apiVersion: ec2.aws.upbound.io/v1beta1\nkind: VPC\nmetadata:\n name: sample-vpc\nspec:\n forProvider:\n cidrBlock: 172.16.0.0/16\n region: us-west-1\n\n---\n\napiVersion: azure.crossplane.io/v1beta1\nkind: ResourceGroup\nmetadata:\n name: example-resources\nspec:\n forProvider:\n location: West Europe\n",
err: nil,
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
files := tc.args.a()
ft := NewFileSystemTarget(FtWithFileSystem(files))
if err := ft.Put(tc.args.o); err != nil {
t.Error(err)
}
b, err := ft.afero.ReadFile("testdata/source/awsvpc.yaml")
if diff := cmp.Diff(tc.want.err, err); diff != "" {
t.Errorf("\nNext(...): -want, +got:\n%s", diff)
}
if diff := cmp.Diff(tc.want.data, string(b)); diff != "" {
t.Errorf("\nNext(...): -want, +got:\n%s", diff)
}
})
}
}
func TestFileSystemTarget_Delete(t *testing.T) {
type args struct {
o UnstructuredWithMetadata
a func() afero.Afero
}
type want struct {
err error
}
cases := map[string]struct {
args
want
}{
"Successful": {
args: args{
o: UnstructuredWithMetadata{
Metadata: Metadata{
Path: "testdata/source/awsvpc.yaml",
},
},
a: func() afero.Afero {
fss := afero.Afero{Fs: afero.NewMemMapFs()}
_ = fss.WriteFile("testdata/source/awsvpc.yaml",
[]byte("apiVersion: ec2.aws.upbound.io/v1beta1\nkind: VPC\nmetadata:\n name: sample-vpc\nspec:\n forProvider:\n cidrBlock: 172.16.0.0/16\n region: us-west-1\n"),
0600)
return fss
},
},
want: want{
err: errors.New(fmt.Sprintf("%s: %s", "open testdata/source/awsvpc.yaml", afero.ErrFileNotFound)),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
files := tc.args.a()
ft := NewFileSystemTarget(FtWithFileSystem(files))
if err := ft.Delete(tc.args.o); err != nil {
t.Error(err)
}
_, err := ft.afero.ReadFile("testdata/source/awsvpc.yaml")
if diff := cmp.Diff(tc.want.err.Error(), err.Error()); diff != "" {
t.Errorf("\nNext(...): -want, +got:\n%s", diff)
}
})
}
}

View File

@ -1,111 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"os"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/pkg/errors"
"k8s.io/utils/exec"
)
const (
errForkExecutorNotSupported = "step type should be Exec or step's manualExecution should be non-empty"
errStepFailedFmt = "failed to execute the step %q"
)
var _ Executor = &forkExecutor{}
// forkExecutor executes Exec steps or steps with the `manualExecution` hints
// by forking processes.
type forkExecutor struct {
executor exec.Interface
logger logging.Logger
cwd string
}
// ForkExecutorOption allows you to configure forkExecutor objects.
type ForkExecutorOption func(executor *forkExecutor)
// WithLogger sets the logger of forkExecutor.
func WithLogger(l logging.Logger) ForkExecutorOption {
return func(e *forkExecutor) {
e.logger = l
}
}
// WithExecutor sets the executor of ForkExecutor.
func WithExecutor(e exec.Interface) ForkExecutorOption {
return func(fe *forkExecutor) {
fe.executor = e
}
}
// WithWorkingDir sets the current working directory for the executor.
func WithWorkingDir(dir string) ForkExecutorOption {
return func(e *forkExecutor) {
e.cwd = dir
}
}
// NewForkExecutor returns a new fork executor using a process forker.
func NewForkExecutor(opts ...ForkExecutorOption) Executor {
fe := &forkExecutor{
executor: exec.New(),
logger: logging.NewNopLogger(),
}
for _, f := range opts {
f(fe)
}
return fe
}
func (f forkExecutor) Init(_ map[string]any) error {
return nil
}
func (f forkExecutor) Step(s Step, ctx map[string]any) error {
var cmd exec.Cmd
switch {
case s.Type == StepTypeExec:
f.logger.Debug("Command to be executed", "command", s.Exec.Command, "args", s.Exec.Args)
return errors.Wrapf(f.exec(ctx, f.executor.Command(s.Exec.Command, s.Exec.Args...)), errStepFailedFmt, s.Name)
// TODO: we had better have separate executors to handle the other types of
// steps
case len(s.ManualExecution) != 0:
for _, c := range s.ManualExecution {
f.logger.Debug("Command to be executed", "command", "sh", "args", []string{"-c", c})
cmd = f.executor.Command("sh", "-c", c)
if err := f.exec(ctx, cmd); err != nil {
return errors.Wrapf(err, errStepFailedFmt, s.Name)
}
}
return nil
default:
return errors.Wrap(NewUnsupportedStepTypeError(s), errForkExecutorNotSupported)
}
}
func (f forkExecutor) exec(ctx map[string]any, cmd exec.Cmd) error {
cmd.SetEnv(os.Environ())
if f.cwd != "" {
cmd.SetDir(f.cwd)
}
buff, err := cmd.CombinedOutput()
logMsg := "Successfully executed command"
if err != nil {
logMsg = "Command execution failed"
}
f.logger.Debug(logMsg, "output", string(buff))
if ctx != nil {
ctx[KeyContextDiagnostics] = buff
}
return errors.Wrapf(err, "failed to execute command")
}
func (f forkExecutor) Destroy() error {
return nil
}

View File

@ -1,110 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"testing"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/pkg/errors"
k8sExec "k8s.io/utils/exec"
testingexec "k8s.io/utils/exec/testing"
)
var (
backupManagedStep = Step{
Name: "backup-managed-resources",
Type: StepTypeExec,
Exec: &ExecStep{
Command: "sh",
Args: []string{"-c", "kubectl get managed -o yaml"},
},
}
wrongCommand = Step{
Name: "wrong-command",
Type: StepTypeExec,
Exec: &ExecStep{
Command: "sh",
Args: []string{"-c", "nosuchcommand"},
},
}
wrongStepType = Step{
Name: "wrong-step-type",
Type: StepTypeDelete,
}
)
var errorWrongCommand = errors.New("exit status 127")
func newFakeExec(err error) *testingexec.FakeExec {
return &testingexec.FakeExec{
CommandScript: []testingexec.FakeCommandAction{
func(_ string, _ ...string) k8sExec.Cmd {
return &testingexec.FakeCmd{
CombinedOutputScript: []testingexec.FakeAction{
func() ([]byte, []byte, error) {
return nil, nil, err
},
},
}
},
},
}
}
func TestForkExecutorStep(t *testing.T) {
type args struct {
step Step
fakeExec *testingexec.FakeExec
}
type want struct {
err error
}
cases := map[string]struct {
args
want
}{
"Success": {
args: args{
step: backupManagedStep,
fakeExec: &testingexec.FakeExec{DisableScripts: true},
},
want: want{
nil,
},
},
"Failure": {
args: args{
step: wrongCommand,
fakeExec: newFakeExec(errorWrongCommand),
},
want: want{
errors.Wrap(errorWrongCommand, `failed to execute the step "wrong-command": failed to execute command`),
},
},
"WrongStepType": {
args: args{
step: wrongStepType,
},
want: want{
errors.Wrap(NewUnsupportedStepTypeError(wrongStepType), `step type should be Exec or step's manualExecution should be non-empty`),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
fe := NewForkExecutor(WithExecutor(tc.fakeExec))
err := fe.Step(tc.step, nil)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nStep(...): -want error, +got error:\n%s", name, diff)
}
})
}
}

View File

@ -1,172 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane/apis/apiextensions/v1"
xpmetav1 "github.com/crossplane/crossplane/apis/pkg/meta/v1"
xpmetav1alpha1 "github.com/crossplane/crossplane/apis/pkg/meta/v1alpha1"
xppkgv1 "github.com/crossplane/crossplane/apis/pkg/v1"
xppkgv1beta1 "github.com/crossplane/crossplane/apis/pkg/v1beta1"
)
// ResourceConverter converts a managed resource from
// the migration source provider's schema to the migration target
// provider's schema.
type ResourceConverter interface {
// Resource takes a managed resource and returns zero or more managed
// resources to be created.
Resource(mg resource.Managed) ([]resource.Managed, error)
}
// ComposedTemplateConverter converts a Composition's ComposedTemplate
// from the migration source provider's schema to the migration target
// provider's schema. Conversion of the `Base` must be handled by
// a ResourceConverter.
type ComposedTemplateConverter interface {
// ComposedTemplate receives a migration source v1.ComposedTemplate
// that has been converted, by a resource converter, to the
// v1.ComposedTemplates with the new shapes specified in the
// `convertedTemplates` argument.
// Conversion of the v1.ComposedTemplate.Bases is handled
// via ResourceConverter.Resource and ComposedTemplate must only
// convert the other fields (`Patches`, `ConnectionDetails`,
// `PatchSet`s, etc.)
// Returns any errors encountered.
ComposedTemplate(sourceTemplate xpv1.ComposedTemplate, convertedTemplates ...*xpv1.ComposedTemplate) error
}
// CompositionConverter converts a managed resource and a Composition's
// ComposedTemplate that composes a managed resource of the same kind
// from the migration source provider's schema to the migration target
// provider's schema.
type CompositionConverter interface {
ResourceConverter
ComposedTemplateConverter
}
// PatchSetConverter converts patch sets of Compositions.
// Any registered PatchSetConverters
// will be called before any resource or ComposedTemplate conversion is done.
// The rationale is to convert the Composition-wide patch sets before
// any resource-specific conversions so that migration targets can
// automatically inherit converted patch sets if their schemas match them.
// Registered PatchSetConverters will be called in the order
// they are registered.
type PatchSetConverter interface {
// PatchSets converts the `spec.patchSets` of a Composition
// from the migration source provider's schema to the migration target
// provider's schema.
PatchSets(psMap map[string]*xpv1.PatchSet) error
}
// ConfigurationMetadataConverter converts a Crossplane Configuration's metadata.
type ConfigurationMetadataConverter interface {
// ConfigurationMetadataV1 takes a Crossplane Configuration v1 metadata,
// converts it, and stores the converted metadata in its argument.
// Returns any errors encountered during the conversion.
ConfigurationMetadataV1(configuration *xpmetav1.Configuration) error
// ConfigurationMetadataV1Alpha1 takes a Crossplane Configuration v1alpha1
// metadata, converts it, and stores the converted metadata in its
// argument. Returns any errors encountered during the conversion.
ConfigurationMetadataV1Alpha1(configuration *xpmetav1alpha1.Configuration) error
}
// ConfigurationPackageConverter converts a Crossplane configuration package.
type ConfigurationPackageConverter interface {
// ConfigurationPackageV1 takes a Crossplane Configuration v1 package,
// converts it possibly to multiple packages and returns
// the converted configuration package.
// Returns any errors encountered during the conversion.
ConfigurationPackageV1(pkg *xppkgv1.Configuration) error
}
// ProviderPackageConverter converts a Crossplane provider package.
type ProviderPackageConverter interface {
// ProviderPackageV1 takes a Crossplane Provider v1 package,
// converts it possibly to multiple packages and returns the
// converted provider packages.
// Returns any errors encountered during the conversion.
ProviderPackageV1(pkg xppkgv1.Provider) ([]xppkgv1.Provider, error)
}
// PackageLockConverter converts a Crossplane package lock.
type PackageLockConverter interface {
// PackageLockV1Beta1 takes a Crossplane v1beta1 package lock,
// converts it, and stores the converted lock in its argument.
// Returns any errors encountered during the conversion.
PackageLockV1Beta1(lock *xppkgv1beta1.Lock) error
}
// Source is a source for reading resource manifests
type Source interface {
// HasNext returns `true` if the Source implementation has a next manifest
// available to return with a call to Next. Any errors encountered while
// determining whether a next manifest exists will also be reported.
HasNext() (bool, error)
// Next returns the next resource manifest available or
// any errors encountered while reading the next resource manifest.
Next() (UnstructuredWithMetadata, error)
// Reset resets the Source so that it can read the manifests
// from the beginning. There is no guarantee that the Source
// will return the same set of manifests or it will return
// them in the same order after a reset.
Reset() error
}
// Target is a target where resource manifests can be manipulated
// (e.g., added, deleted, patched, etc.)
type Target interface {
// Put writes a resource manifest to this Target
Put(o UnstructuredWithMetadata) error
// Delete deletes a resource manifest from this Target
Delete(o UnstructuredWithMetadata) error
}
// Executor is a migration plan executor.
type Executor interface {
// Init initializes an executor using the supplied executor specific
// configuration data.
Init(config map[string]any) error
// Step asks the executor to execute the next step passing any available
// context from the previous step, and returns any new context to be passed
// to the next step if there exists one.
Step(s Step, ctx map[string]any) error
// Destroy is called when all the steps have been executed,
// or a step has returned an error, and we would like to stop
// executing the plan.
Destroy() error
}
// UnstructuredPreProcessor allows manifests read by the Source
// to be pre-processed before the converters are run
// It's not possible to do any conversions via the pre-processors,
// and they only allow migrators to extract information from
// the manifests read by the Source before any converters are run.
type UnstructuredPreProcessor interface {
// PreProcess is called for a manifest read by the Source
// before any converters are run.
PreProcess(u UnstructuredWithMetadata) error
}
// ManagedPreProcessor allows manifests read by the Source
// to be pre-processed before the converters are run.
// These pre-processors will work for GVKs that have ResourceConverter
// registered.
type ManagedPreProcessor interface {
// ResourcePreProcessor is called for a manifest read by the Source
// before any converters are run.
ResourcePreProcessor(mg resource.Managed) error
}
// CategoricalConverter is a converter that converts resources of a given
// Category. Because it receives an unstructured argument, it should be
// used for implementing generic conversion functions acting on a specific
// category, such as setting a deletion policy on all the managed resources
// observed by the migration Source.
type CategoricalConverter interface {
Convert(u *UnstructuredWithMetadata) error
}

View File

@ -1,331 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"context"
"fmt"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/discovery"
"k8s.io/client-go/discovery/cached/disk"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"k8s.io/client-go/restmapper"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)
const (
errKubernetesSourceInit = "failed to initialize the migration Kubernetes source"
errCategoryGetFmt = "failed to get resources of category %q"
)
var (
_ Source = &KubernetesSource{}
defaultCacheDir = filepath.Join(homedir.HomeDir(), ".kube", "cache")
)
// KubernetesSource is a source implementation to read resources from Kubernetes
// cluster.
type KubernetesSource struct {
registry *Registry
categories []Category
index int
items []UnstructuredWithMetadata
dynamicClient dynamic.Interface
cachedDiscoveryClient discovery.CachedDiscoveryInterface
restMapper meta.RESTMapper
categoryExpander restmapper.CategoryExpander
cacheDir string
restConfig *rest.Config
}
// KubernetesSourceOption sets an option for a KubernetesSource.
type KubernetesSourceOption func(source *KubernetesSource)
// WithCacheDir sets the cache directory for the disk cached discovery client
// used by a KubernetesSource.
func WithCacheDir(cacheDir string) KubernetesSourceOption {
return func(s *KubernetesSource) {
s.cacheDir = cacheDir
}
}
// WithRegistry configures a KubernetesSource to use the specified registry
// for determining the GVKs of resources which will be read from the
// Kubernetes API server.
func WithRegistry(r *Registry) KubernetesSourceOption {
return func(s *KubernetesSource) {
s.registry = r
}
}
// WithCategories configures a KubernetesSource so that it will fetch
// all resources belonging to the specified categories.
func WithCategories(c []Category) KubernetesSourceOption {
return func(s *KubernetesSource) {
s.categories = c
}
}
// NewKubernetesSourceFromKubeConfig initializes a new KubernetesSource using
// the specified kube config file and KubernetesSourceOptions.
func NewKubernetesSourceFromKubeConfig(kubeconfigPath string, opts ...KubernetesSourceOption) (*KubernetesSource, error) {
ks := &KubernetesSource{}
for _, o := range opts {
o(ks)
}
var err error
ks.restConfig, err = clientcmd.BuildConfigFromFlags("", kubeconfigPath)
if err != nil {
return nil, errors.Wrap(err, "cannot create rest config object")
}
ks.restConfig.ContentConfig = resource.UnstructuredPlusDefaultContentConfig()
ks.dynamicClient, err = InitializeDynamicClient(kubeconfigPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to initialize a Kubernetes dynamic client from kubeconfig: %s", kubeconfigPath)
}
ks.cachedDiscoveryClient, err = InitializeDiscoveryClient(kubeconfigPath, ks.cacheDir)
if err != nil {
return nil, errors.Wrapf(err, "failed to initialize a Kubernetes discovery client from kubeconfig: %s", kubeconfigPath)
}
return ks, errors.Wrap(ks.init(), errKubernetesSourceInit)
}
// NewKubernetesSource returns a KubernetesSource
// DynamicClient is used here to query resources.
// Elements of gvks (slice of GroupVersionKind) are passed to the Dynamic Client
// in a loop to get list of resources.
// An example element of gvks slice:
// Group: "ec2.aws.upbound.io",
// Version: "v1beta1",
// Kind: "VPC",
func NewKubernetesSource(dynamicClient dynamic.Interface, discoveryClient discovery.CachedDiscoveryInterface, opts ...KubernetesSourceOption) (*KubernetesSource, error) {
ks := &KubernetesSource{
dynamicClient: dynamicClient,
cachedDiscoveryClient: discoveryClient,
}
for _, o := range opts {
o(ks)
}
return ks, errors.Wrap(ks.init(), errKubernetesSourceInit)
}
func (ks *KubernetesSource) init() error {
ks.restMapper = restmapper.NewDeferredDiscoveryRESTMapper(ks.cachedDiscoveryClient)
ks.categoryExpander = restmapper.NewDiscoveryCategoryExpander(ks.cachedDiscoveryClient)
for _, c := range ks.categories {
if err := ks.getCategoryResources(c); err != nil {
return errors.Wrapf(err, "cannot get resources of the category: %s", c)
}
}
if ks.registry == nil {
return nil
}
if err := ks.getGVKResources(ks.registry.claimTypes, CategoryClaim); err != nil {
return errors.Wrap(err, "cannot get claims")
}
if err := ks.getGVKResources(ks.registry.compositeTypes, CategoryComposite); err != nil {
return errors.Wrap(err, "cannot get composites")
}
if err := ks.getGVKResources(ks.registry.GetCompositionGVKs(), CategoryComposition); err != nil {
return errors.Wrap(err, "cannot get compositions")
}
if err := ks.getGVKResources(ks.registry.GetCrossplanePackageGVKs(), CategoryCrossplanePackage); err != nil {
return errors.Wrap(err, "cannot get Crossplane packages")
}
return errors.Wrap(ks.getGVKResources(ks.registry.GetManagedResourceGVKs(), CategoryManaged), "cannot get managed resources")
}
func (ks *KubernetesSource) getMappingFor(gr schema.GroupResource) (*meta.RESTMapping, error) {
r := fmt.Sprintf("%s.%s", gr.Resource, gr.Group)
fullySpecifiedGVR, groupResource := schema.ParseResourceArg(r)
gvk := schema.GroupVersionKind{}
if fullySpecifiedGVR != nil {
gvk, _ = ks.restMapper.KindFor(*fullySpecifiedGVR)
}
if gvk.Empty() {
gvk, _ = ks.restMapper.KindFor(groupResource.WithVersion(""))
}
if !gvk.Empty() {
return ks.restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
}
fullySpecifiedGVK, groupKind := schema.ParseKindArg(r)
if fullySpecifiedGVK == nil {
gvk := groupKind.WithVersion("")
fullySpecifiedGVK = &gvk
}
if !fullySpecifiedGVK.Empty() {
if mapping, err := ks.restMapper.RESTMapping(fullySpecifiedGVK.GroupKind(), fullySpecifiedGVK.Version); err == nil {
return mapping, nil
}
}
mapping, err := ks.restMapper.RESTMapping(groupKind, gvk.Version)
if err != nil {
if meta.IsNoMatchError(err) {
return nil, errors.Errorf("the server doesn't have a resource type %q", groupResource.Resource)
}
return nil, err
}
return mapping, nil
}
// parts of this implement are taken from the implementation of
// the "kubectl get" command:
// https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/kubectl/pkg/cmd/get
func (ks *KubernetesSource) getCategoryResources(c Category) error {
if ks.restConfig == nil {
return errors.New("rest.Config not initialized")
}
grs, _ := ks.categoryExpander.Expand(c.String())
for _, gr := range grs {
mapping, err := ks.getMappingFor(gr)
if err != nil {
return errors.Wrapf(err, errCategoryGetFmt, c.String())
}
gv := mapping.GroupVersionKind.GroupVersion()
ks.restConfig.GroupVersion = &gv
if len(gv.Group) == 0 {
ks.restConfig.APIPath = "/api"
} else {
ks.restConfig.APIPath = "/apis"
}
client, err := rest.RESTClientFor(ks.restConfig)
if err != nil {
return errors.Wrapf(err, errCategoryGetFmt, c.String())
}
helper := resource.NewHelper(client, mapping)
list, err := helper.List("", mapping.GroupVersionKind.GroupVersion().String(), &metav1.ListOptions{})
if err != nil {
return errors.Wrapf(err, errCategoryGetFmt, c.String())
}
ul, ok := list.(*unstructured.UnstructuredList)
if !ok {
return errors.New("expecting list to be of type *unstructured.UnstructuredList")
}
for _, u := range ul.Items {
ks.items = append(ks.items, UnstructuredWithMetadata{
Object: u,
Metadata: Metadata{
Path: string(u.GetUID()),
Category: c,
},
})
}
}
return nil
}
func (ks *KubernetesSource) getGVKResources(gvks []schema.GroupVersionKind, category Category) error {
processed := map[schema.GroupVersionKind]struct{}{}
for _, gvk := range gvks {
if _, ok := processed[gvk]; ok {
continue
}
m, err := ks.restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return errors.Wrapf(err, "cannot get REST mappings for GVK: %s", gvk.String())
}
if err := ks.getResourcesFor(m.Resource, category); err != nil {
return errors.Wrapf(err, "cannot get resources for GVK: %s", gvk.String())
}
processed[gvk] = struct{}{}
}
return nil
}
func (ks *KubernetesSource) getResourcesFor(gvr schema.GroupVersionResource, category Category) error {
ri := ks.dynamicClient.Resource(gvr)
unstructuredList, err := ri.List(context.TODO(), metav1.ListOptions{})
if err != nil {
return errors.Wrapf(err, "cannot list resources of GVR: %s", gvr.String())
}
for _, u := range unstructuredList.Items {
ks.items = append(ks.items, UnstructuredWithMetadata{
Object: u,
Metadata: Metadata{
Path: string(u.GetUID()),
Category: category,
},
})
}
return nil
}
// HasNext checks the next item
func (ks *KubernetesSource) HasNext() (bool, error) {
return ks.index < len(ks.items), nil
}
// Next returns the next item of slice
func (ks *KubernetesSource) Next() (UnstructuredWithMetadata, error) {
if hasNext, _ := ks.HasNext(); hasNext {
item := ks.items[ks.index]
ks.index++
return item, nil
}
return UnstructuredWithMetadata{}, errors.New("no more elements")
}
// Reset resets the source so that resources can be reread from the beginning.
func (ks *KubernetesSource) Reset() error {
ks.index = 0
return nil
}
// InitializeDynamicClient returns a dynamic client
func InitializeDynamicClient(kubeconfigPath string) (dynamic.Interface, error) {
config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
if err != nil {
return nil, errors.Wrap(err, "cannot create rest config object")
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, errors.Wrap(err, "cannot initialize dynamic client")
}
return dynamicClient, nil
}
func InitializeDiscoveryClient(kubeconfigPath, cacheDir string) (*disk.CachedDiscoveryClient, error) {
config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
if err != nil {
return nil, errors.Wrap(err, "cannot create rest config object")
}
if cacheDir == "" {
cacheDir = defaultCacheDir
}
httpCacheDir := filepath.Join(cacheDir, "http")
discoveryCacheDir := computeDiscoverCacheDir(filepath.Join(cacheDir, "discovery"), config.Host)
return disk.NewCachedDiscoveryClientForConfig(config, discoveryCacheDir, httpCacheDir, 10*time.Minute)
}
// overlyCautiousIllegalFileCharacters matches characters that *might* not be supported. Windows is really restrictive, so this is really restrictive
var overlyCautiousIllegalFileCharacters = regexp.MustCompile(`[^(\w/.)]`)
// computeDiscoverCacheDir takes the parentDir and the host and comes up with a "usually non-colliding" name.
func computeDiscoverCacheDir(parentDir, host string) string {
// strip the optional scheme from host if its there:
schemelessHost := strings.Replace(strings.Replace(host, "https://", "", 1), "http://", "", 1)
// now do a simple collapse of non-AZ09 characters. Collisions are possible but unlikely. Even if we do collide the problem is short lived
safeHost := overlyCautiousIllegalFileCharacters.ReplaceAllString(schemelessHost, "_")
return filepath.Join(parentDir, safeHost)
}

View File

@ -1,156 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"cmp"
"slices"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/test"
gocmp "github.com/google/go-cmp/cmp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/discovery/cached/memory"
"k8s.io/client-go/dynamic/fake"
fakeclientset "k8s.io/client-go/kubernetes/fake"
)
func TestNewKubernetesSource(t *testing.T) {
type args struct {
gvks []schema.GroupVersionKind
}
type want struct {
ks *KubernetesSource
err error
}
cases := map[string]struct {
args
want
}{
"Successful": {
args: args{
gvks: []schema.GroupVersionKind{
{
Group: "ec2.aws.crossplane.io",
Version: "v1beta1",
Kind: "VPC",
},
{
Group: "azure.crossplane.io",
Version: "v1beta1",
Kind: "ResourceGroup",
},
},
},
want: want{
ks: &KubernetesSource{
items: []UnstructuredWithMetadata{
{
Object: unstructured.Unstructured{
Object: unstructuredAwsVpc,
},
Metadata: Metadata{
Category: CategoryManaged,
},
},
{
Object: unstructured.Unstructured{
Object: unstructuredResourceGroup,
},
Metadata: Metadata{
Category: CategoryManaged,
},
},
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
s := runtime.NewScheme()
r := NewRegistry(s)
// register a dummy converter for the test GVKs
r.resourceConverters = map[schema.GroupVersionKind]ResourceConverter{}
for _, gvk := range tc.args.gvks {
r.resourceConverters[gvk] = nil
}
dynamicClient := fake.NewSimpleDynamicClientWithCustomListKinds(s,
map[schema.GroupVersionResource]string{
{
Group: "ec2.aws.crossplane.io",
Version: "v1beta1",
Resource: "vpcs",
}: "VPCList",
{
Group: "azure.crossplane.io",
Version: "v1beta1",
Resource: "resourcegroups",
}: "ResourceGroupList",
},
&unstructured.Unstructured{Object: unstructuredAwsVpc},
&unstructured.Unstructured{Object: unstructuredResourceGroup})
client := fakeclientset.NewSimpleClientset(
&unstructured.Unstructured{Object: unstructuredAwsVpc},
&unstructured.Unstructured{Object: unstructuredResourceGroup},
)
client.Fake.Resources = []*metav1.APIResourceList{
{
GroupVersion: "ec2.aws.crossplane.io/v1beta1",
APIResources: []metav1.APIResource{
{
Name: "vpcs",
Kind: "VPC",
},
},
},
{
GroupVersion: "azure.crossplane.io/v1beta1",
APIResources: []metav1.APIResource{
{
Name: "resourcegroups",
Kind: "ResourceGroup",
},
},
},
}
ks, err := NewKubernetesSource(dynamicClient, memory.NewMemCacheClient(client.Discovery()), WithRegistry(r))
if diff := gocmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\nNext(...): -want, +got:\n%s", diff)
}
slices.SortFunc(tc.want.ks.items, compareUnstructuredWithMetadata)
slices.SortFunc(ks.items, compareUnstructuredWithMetadata)
if diff := gocmp.Diff(tc.want.ks.items, ks.items); diff != "" {
t.Errorf("\nNext(...): -want, +got:\n%s", diff)
}
})
}
}
// compareUnstructuredWithMetadata is used for sorting UnstructuredWithMetadata
// slices based on their namespaced names and GVKs.
func compareUnstructuredWithMetadata(a, b UnstructuredWithMetadata) int {
switch {
case a.Object.Object == nil && b.Object.Object == nil:
return 0 // not comparable
case a.Object.Object == nil && b.Object.Object != nil:
return -1
case a.Object.Object != nil && b.Object.Object == nil:
return 1
}
fS := func(x *UnstructuredWithMetadata) string {
return types.NamespacedName{
Namespace: x.Object.GetNamespace(),
Name: x.Object.GetName(),
}.String() + ":" + x.Object.GetObjectKind().GroupVersionKind().String()
}
return cmp.Compare(fS(&a), fS(&b))
}

View File

@ -1,60 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func (pg *PlanGenerator) convertPackageLock(o UnstructuredWithMetadata) error {
lock, err := toPackageLock(o.Object)
if err != nil {
return err
}
isConverted := false
for _, lockConv := range pg.registry.packageLockConverters {
if lockConv.re == nil || lockConv.converter == nil || !lockConv.re.MatchString(lock.GetName()) {
continue
}
if err := lockConv.converter.PackageLockV1Beta1(lock); err != nil {
return errors.Wrapf(err, "failed to call converter on package lock: %s", lock.GetName())
}
// TODO: if a lock converter does not convert the given lock,
// we will have a false positive. Better to compute and check
// a diff here.
isConverted = true
}
if !isConverted {
return nil
}
target := &UnstructuredWithMetadata{
Object: ToSanitizedUnstructured(lock),
Metadata: o.Metadata,
}
if err := pg.stepEditPackageLock(o, target); err != nil {
return err
}
return nil
}
func (pg *PlanGenerator) stepEditPackageLock(source UnstructuredWithMetadata, t *UnstructuredWithMetadata) error {
// add step for editing the package lock
s := pg.stepConfiguration(stepEditPackageLock)
t.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(t.Object))
s.Patch.Files = append(s.Patch.Files, t.Metadata.Path)
patchMap, err := computeJSONMergePathDoc(source.Object, t.Object)
if err != nil {
return err
}
return errors.Wrapf(pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(t.Object, patchMap),
},
Metadata: t.Metadata,
}), errEditConfigurationPackageFmt, t.Object.GetName())
}

View File

@ -1,346 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"log"
"reflect"
"regexp"
"strings"
xpv1 "github.com/crossplane/crossplane/apis/apiextensions/v1"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
var (
regexIndex = regexp.MustCompile(`(.+)\[(.+)]`)
regexJSONTag = regexp.MustCompile(`([^,]+)?(,.+)?`)
)
const (
jsonTagInlined = ",inline"
)
// isConverted looks up the specified name in the list of already converted
// patch sets.
func isConverted(convertedPS []string, psName string) bool {
for _, n := range convertedPS {
if psName == n {
return true
}
}
return false
}
// removeInvalidPatches removes the (inherited) patches from
// a (split) migration target composed template. The migration target composed
// templates inherit patches from migration source templates by default, and
// this function is responsible for removing patches (including references to
// patch sets) that do not conform to the target composed template's schema.
func (pg *PlanGenerator) removeInvalidPatches(gvkSource, gvkTarget schema.GroupVersionKind, patchSets []xpv1.PatchSet, targetTemplate *xpv1.ComposedTemplate, convertedPS []string) error { //nolint:gocyclo // complexity (11) just above the threshold (10)
c := pg.registry.scheme
source, err := c.New(gvkSource)
if err != nil {
return errors.Wrapf(err, "failed to instantiate a new source object with GVK: %s", gvkSource.String())
}
target, err := c.New(gvkTarget)
if err != nil {
return errors.Wrapf(err, "failed to instantiate a new target object with GVK: %s", gvkTarget.String())
}
newPatches := make([]xpv1.Patch, 0, len(targetTemplate.Patches))
var patches []xpv1.Patch
for _, p := range targetTemplate.Patches {
s := source
switch p.Type { //nolint:exhaustive
case xpv1.PatchTypePatchSet:
ps := getNamedPatchSet(p.PatchSetName, patchSets)
if ps == nil {
// something is wrong with the patchset ref,
// we will just remove the ref
continue
}
if isConverted(convertedPS, ps.Name) {
// then do not use the source schema as the patch set
// is already converted
s = target
}
// assert each of the patches in the set
// conform the target schema
patches = ps.Patches
default:
patches = []xpv1.Patch{p}
}
keep := true
for _, p := range patches {
ok, err := assertPatchSchemaConformance(p, s, target)
if err != nil {
err := errors.Wrap(err, "failed to check whether the patch conforms to the target schema")
if pg.ErrorOnInvalidPatchSchema {
return err
}
log.Printf("Excluding the patch from the migration target because conformance checking has failed with: %v\n", err)
// if we could not check the patch's schema conformance
// and the plan generator is configured not to error,
// assume the patch does not conform to the schema
ok = false
}
if !ok {
keep = false
break
}
}
if keep {
newPatches = append(newPatches, p)
}
}
targetTemplate.Patches = newPatches
return nil
}
// assertPatchSchemaConformance asserts that the specified patch actually
// conforms the specified target schema. We also assert the patch conforms
// to the migration source schema, which prevents an invalid patch from being
// preserved after the conversion.
func assertPatchSchemaConformance(p xpv1.Patch, source, target any) (bool, error) {
var targetPath *string
// because this is defaulting logic and what we default can be overridden
// later in the convert, the type switch is not exhaustive
// TODO: consider processing other patch types
switch p.Type { //nolint:exhaustive
case xpv1.PatchTypeFromCompositeFieldPath, "": // the default type
targetPath = p.ToFieldPath
case xpv1.PatchTypeToCompositeFieldPath:
targetPath = p.FromFieldPath
}
if targetPath == nil {
return false, nil
}
ok, err := assertNameAndTypeAtPath(reflect.TypeOf(source), reflect.TypeOf(target), splitPathComponents(*targetPath))
return ok, errors.Wrapf(err, "failed to assert patch schema for path: %s", *targetPath)
}
// splitPathComponents splits a fieldpath expression into its path components,
// e.g., `m[a.b.c].a.b.c` is split into `m[a.b.c]`, `a`, `b`, `c`.
func splitPathComponents(path string) []string {
components := strings.Split(path, ".")
result := make([]string, 0, len(components))
indexedExpression := false
for _, c := range components {
switch {
case indexedExpression:
result[len(result)-1] = fmt.Sprintf("%s.%s", result[len(result)-1], c)
if strings.Contains(c, "]") {
indexedExpression = false
}
default:
result = append(result, c)
if strings.Contains(c, "[") && !strings.Contains(c, "]") {
indexedExpression = true
}
}
}
return result
}
func isRawExtension(source, target reflect.Type) bool {
reType := reflect.TypeOf(runtime.RawExtension{})
rePtrType := reflect.TypeOf(&runtime.RawExtension{})
return (source == reType && target == reType) || (source == rePtrType && target == rePtrType)
}
// assertNameAndTypeAtPath asserts that the migration source and target
// templates both have the same kind for the type at the specified path.
// Also validates the specific path is valid for the source.
func assertNameAndTypeAtPath(source, target reflect.Type, pathComponents []string) (bool, error) { //nolint:gocyclo
if len(pathComponents) < 1 {
return compareKinds(source, target), nil
}
// if both source and target are runtime.RawExtensions,
// then stop traversing the type hierarchy.
if isRawExtension(source, target) {
return true, nil
}
pathComponent := pathComponents[0]
if len(pathComponent) == 0 {
return false, errors.Errorf("failed to compare source and target structs. Invalid path: %s", strings.Join(pathComponents, "."))
}
m := regexIndex.FindStringSubmatch(pathComponent)
if m != nil {
// then a map component or a slicing component
pathComponent = m[1]
}
// assert the source and the target types
fSource, err := getFieldWithSerializedName(source, pathComponent)
if err != nil {
return false, errors.Wrapf(err, "failed to assert source struct field kind at path: %s", strings.Join(pathComponents, "."))
}
if fSource == nil {
// then source field could not be found
return false, errors.Errorf("struct field %q does not exist for the source type %q at path: %s", pathComponent, source.String(), strings.Join(pathComponents, "."))
}
// now assert that this field actually exists for the target type
// with the same type
fTarget, err := getFieldWithSerializedName(target, pathComponent)
if err != nil {
return false, errors.Wrapf(err, "failed to assert target struct field kind at path: %s", strings.Join(pathComponents, "."))
}
if fTarget == nil || !fTarget.IsExported() || !compareKinds(fSource.Type, fTarget.Type) {
return false, nil
}
nextSource, nextTarget := fSource.Type, fTarget.Type
if m != nil {
// parents are of map or slice type
nextSource = nextSource.Elem()
nextTarget = nextTarget.Elem()
}
return assertNameAndTypeAtPath(nextSource, nextTarget, pathComponents[1:])
}
// compareKinds compares the kinds of the specified types
// dereferencing (following) pointer types.
func compareKinds(s, t reflect.Type) bool {
if s.Kind() == reflect.Pointer {
s = s.Elem()
}
if t.Kind() == reflect.Pointer {
t = t.Elem()
}
return s.Kind() == t.Kind()
}
// getFieldWithSerializedName returns the field of a struct (if it exists)
// with the specified serialized (JSON) name. Returns a nil (and a nil error)
// if a field with the specified serialized name is not found
// in the specified type.
func getFieldWithSerializedName(t reflect.Type, name string) (*reflect.StructField, error) { //nolint:gocyclo
if t.Kind() == reflect.Pointer {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return nil, errors.Errorf("type is not a struct: %s", t.Name())
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
serializedName := f.Name
inlined := false
if fTag, ok := f.Tag.Lookup("json"); ok {
if m := regexJSONTag.FindStringSubmatch(fTag); m != nil && len(m[1]) > 0 {
serializedName = m[1]
}
if strings.HasSuffix(fTag, jsonTagInlined) {
inlined = true
}
}
if name == serializedName {
return &f, nil
}
if inlined {
inlinedType := f.Type
if inlinedType.Kind() == reflect.Pointer {
inlinedType = inlinedType.Elem()
}
if inlinedType.Kind() == reflect.Struct {
sf, err := getFieldWithSerializedName(inlinedType, name)
if err != nil {
return nil, errors.Wrapf(err, "failed to search for field %q in inlined type: %s", name, inlinedType.String())
}
if sf != nil {
return sf, nil
}
}
}
}
return nil, nil // not found
}
// getNamedPatchSet returns the patch set with the specified name
// from the specified patch set slice. Returns nil if a patch set
// with the given name is not found.
func getNamedPatchSet(name *string, patchSets []xpv1.PatchSet) *xpv1.PatchSet {
if name == nil {
// if name is not specified, do not attempt to find a named patchset
return nil
}
for _, ps := range patchSets {
if *name == ps.Name {
return &ps
}
}
return nil
}
// getConvertedPatchSetNames returns the names of patch sets that have been
// converted by a PatchSetConverter.
func getConvertedPatchSetNames(newPatchSets, oldPatchSets []xpv1.PatchSet) []string {
converted := make([]string, 0, len(newPatchSets))
for _, n := range newPatchSets {
found := false
for _, o := range oldPatchSets {
if o.Name != n.Name {
continue
}
found = true
if !reflect.DeepEqual(o, n) {
converted = append(converted, n.Name)
}
break
}
if !found {
converted = append(converted, n.Name)
}
}
return converted
}
// convertToMap converts the given slice of patch sets to a map of
// patch sets keyed by their names.
func convertToMap(ps []xpv1.PatchSet) map[string]*xpv1.PatchSet {
m := make(map[string]*xpv1.PatchSet, len(ps))
for _, p := range ps {
// Crossplane dereferences the last patch set with the same name,
// so override with the last patch set with the same name.
m[p.Name] = p.DeepCopy()
}
return m
}
// convertFromMap converts the specified map of patch sets back to a slice.
// If filterDeleted is set, previously existing patch sets in the Composition
// which have been removed from the map are also removed from the resulting
// slice, and eventually from the Composition. PatchSetConverters are
// allowed to remove patch sets, whereas Composition converters are
// not, as Composition converters have a local view of the patch sets and
// don't know about the other composed templates that may be sharing
// patch sets with them.
func convertFromMap(psMap map[string]*xpv1.PatchSet, oldPS []xpv1.PatchSet, filterDeleted bool) []xpv1.PatchSet {
result := make([]xpv1.PatchSet, 0, len(psMap))
for _, ps := range oldPS {
if filterDeleted && psMap[ps.Name] == nil {
// then patch set has been deleted
continue
}
if psMap[ps.Name] == nil {
result = append(result, ps)
continue
}
result = append(result, *psMap[ps.Name])
delete(psMap, ps.Name)
}
// add the new patch sets
for _, ps := range psMap {
if ps == nil {
continue
}
result = append(result, *ps)
}
return result
}

View File

@ -1,79 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"testing"
"github.com/google/go-cmp/cmp"
)
func TestSplitPathComponents(t *testing.T) {
tests := map[string]struct {
want []string
}{
`m['a.b.c']`: {
want: []string{`m['a.b.c']`},
},
`m["a.b.c"]`: {
want: []string{`m["a.b.c"]`},
},
`m[a.b.c]`: {
want: []string{`m[a.b.c]`},
},
`m[a.b.c.d.e]`: {
want: []string{`m[a.b.c.d.e]`},
},
`m['a.b.c.d.e']`: {
want: []string{`m['a.b.c.d.e']`},
},
`m['a.b']`: {
want: []string{`m['a.b']`},
},
`m['a']`: {
want: []string{`m['a']`},
},
`m['a'].b`: {
want: []string{`m['a']`, `b`},
},
`a`: {
want: []string{`a`},
},
`a.b`: {
want: []string{`a`, `b`},
},
`a.b.c`: {
want: []string{`a`, `b`, `c`},
},
`a.b.c.m['a.b.c']`: {
want: []string{`a`, `b`, `c`, `m['a.b.c']`},
},
`a.b.m['a.b.c'].c`: {
want: []string{`a`, `b`, `m['a.b.c']`, `c`},
},
`m['a.b.c'].a.b.c`: {
want: []string{`m['a.b.c']`, `a`, `b`, `c`},
},
`m[a.b.c].a.b.c`: {
want: []string{`m[a.b.c]`, `a`, `b`, `c`},
},
`m[0]`: {
want: []string{`m[0]`},
},
`a.b.c.m[0]`: {
want: []string{`a`, `b`, `c`, `m[0]`},
},
`m[0].a.b.c`: {
want: []string{`m[0]`, `a`, `b`, `c`},
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
if diff := cmp.Diff(tt.want, splitPathComponents(name)); diff != "" {
t.Errorf("splitPathComponents(%s): -want, +got:\n%s\n", name, diff)
}
})
}
}

View File

@ -1,129 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import "github.com/pkg/errors"
const (
// KeyContextDiagnostics is the executor step context key for
// storing any extra diagnostics information from
// the executor.
KeyContextDiagnostics = "diagnostics"
)
// PlanExecutor drives the execution of a plan's steps and
// uses the configured `executors` to execute those steps.
type PlanExecutor struct {
executors []Executor
plan Plan
callback ExecutorCallback
}
// Action represents an action to be taken by the PlanExecutor.
// An Action is dictated by a ExecutorCallback implementation
// to the PlanExecutor for each step.
type Action int
const (
// ActionContinue tells the PlanExecutor to continue with the execution
// of a Step.
ActionContinue Action = iota
// ActionSkip tells the PlanExecutor to skip the execution
// of the current Step.
ActionSkip
// ActionCancel tells the PlanExecutor to stop executing
// the Steps of a Plan.
ActionCancel
// ActionRepeat tells the PlanExecutor to repeat the execution
// of the current Step.
ActionRepeat
)
// CallbackResult is the type of a value returned from one of the callback
// methods of ExecutorCallback implementations.
type CallbackResult struct {
Action Action
}
// PlanExecutorOption is a mutator function for setting an option of a
// PlanExecutor.
type PlanExecutorOption func(executor *PlanExecutor)
// WithExecutorCallback configures an ExecutorCallback for a PlanExecutor
// to be notified as the Plan's Step's are executed.
func WithExecutorCallback(cb ExecutorCallback) PlanExecutorOption {
return func(pe *PlanExecutor) {
pe.callback = cb
}
}
// ExecutorCallback is the interface for the callback implementations
// to be notified while executing each Step of a migration Plan.
type ExecutorCallback interface {
// StepToExecute is called just before a migration Plan's Step is executed.
// Can be used to cancel the execution of the Plan, or to continue/skip
// the Step's execution.
StepToExecute(s Step, index int) CallbackResult
// StepSucceeded is called after a migration Plan's Step is
// successfully executed.
// Can be used to cancel the execution of the Plan, or to
// continue/skip/repeat the Step's execution.
StepSucceeded(s Step, index int, diagnostics any) CallbackResult
// StepFailed is called after a migration Plan's Step has
// failed to execute.
// Can be used to cancel the execution of the Plan, or to
// continue/skip/repeat the Step's execution.
StepFailed(s Step, index int, diagnostics any, err error) CallbackResult
}
// NewPlanExecutor returns a new plan executor for executing the steps
// of a migration plan.
func NewPlanExecutor(plan Plan, executors []Executor, opts ...PlanExecutorOption) *PlanExecutor {
pe := &PlanExecutor{
executors: executors,
plan: plan,
}
for _, o := range opts {
o(pe)
}
return pe
}
func (pe *PlanExecutor) Execute() error { //nolint:gocyclo // easier to follow this way
ctx := make(map[string]any)
for i := 0; i < len(pe.plan.Spec.Steps); i++ {
var r CallbackResult
if pe.callback != nil {
r = pe.callback.StepToExecute(pe.plan.Spec.Steps[i], i)
switch r.Action {
case ActionCancel:
return nil
case ActionSkip:
continue
case ActionContinue, ActionRepeat:
}
}
err := pe.executors[0].Step(pe.plan.Spec.Steps[i], ctx)
diag := ctx[KeyContextDiagnostics]
if err != nil {
if pe.callback != nil {
r = pe.callback.StepFailed(pe.plan.Spec.Steps[i], i, diag, err)
}
} else if pe.callback != nil {
r = pe.callback.StepSucceeded(pe.plan.Spec.Steps[i], i, diag)
}
switch r.Action {
case ActionCancel:
return errors.Wrapf(err, "failed to execute step %q at index %d", pe.plan.Spec.Steps[i].Name, i)
case ActionContinue, ActionSkip:
continue
case ActionRepeat:
i--
}
}
return nil
}

View File

@ -1,536 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"reflect"
"time"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane/apis/apiextensions/v1"
xpmetav1 "github.com/crossplane/crossplane/apis/pkg/meta/v1"
xpmetav1alpha1 "github.com/crossplane/crossplane/apis/pkg/meta/v1alpha1"
xppkgv1 "github.com/crossplane/crossplane/apis/pkg/v1"
xppkgv1beta1 "github.com/crossplane/crossplane/apis/pkg/v1beta1"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/rand"
)
const (
errSourceHasNext = "failed to generate migration plan: Could not check next object from source"
errSourceNext = "failed to generate migration plan: Could not get next object from source"
errPreProcessFmt = "failed to pre-process the manifest of category %q"
errSourceReset = "failed to generate migration plan: Could not get reset the source"
errUnstructuredConvert = "failed to convert from unstructured object to v1.Composition"
errUnstructuredMarshal = "failed to marshal unstructured object to JSON"
errResourceMigrate = "failed to migrate resource"
errCompositePause = "failed to pause composite resource"
errCompositesEdit = "failed to edit composite resources"
errCompositesStart = "failed to start composite resources"
errCompositionMigrateFmt = "failed to migrate the composition: %s"
errConfigurationMetadataMigrateFmt = "failed to migrate the configuration metadata: %s"
errConfigurationPackageMigrateFmt = "failed to migrate the configuration package: %s"
errProviderMigrateFmt = "failed to migrate the Provider package: %s"
errLockMigrateFmt = "failed to migrate the package lock: %s"
errComposedTemplateBase = "failed to migrate the base of a composed template"
errComposedTemplateMigrate = "failed to migrate the composed templates of the composition"
errResourceOutput = "failed to output migrated resource"
errResourceOrphan = "failed to orphan managed resource"
errResourceRemoveFinalizer = "failed to remove finalizers of managed resource"
errCompositionOutput = "failed to output migrated composition"
errCompositeOutput = "failed to output migrated composite"
errClaimOutput = "failed to output migrated claim"
errClaimsEdit = "failed to edit claims"
errPlanGeneration = "failed to generate the migration plan"
errPause = "failed to store a paused manifest"
errMissingGVK = "managed resource is missing its GVK. Resource converters must set GVKs on any managed resources they newly generate."
)
const (
versionV010 = "0.1.0"
keyCompositionRef = "compositionRef"
keyResourceRefs = "resourceRefs"
)
// PlanGeneratorOption configures a PlanGenerator
type PlanGeneratorOption func(generator *PlanGenerator)
// WithErrorOnInvalidPatchSchema returns a PlanGeneratorOption for configuring
// whether the PlanGenerator should error and stop the migration plan
// generation in case an error is encountered while checking a patch
// statement's conformance to the migration source or target.
func WithErrorOnInvalidPatchSchema(e bool) PlanGeneratorOption {
return func(pg *PlanGenerator) {
pg.ErrorOnInvalidPatchSchema = e
}
}
// WithSkipGVKs configures the set of GVKs to skip for conversion
// during a migration.
func WithSkipGVKs(gvk ...schema.GroupVersionKind) PlanGeneratorOption {
return func(pg *PlanGenerator) {
pg.SkipGVKs = gvk
}
}
// WithMultipleSources can be used to configure multiple sources for a
// PlanGenerator.
func WithMultipleSources(source ...Source) PlanGeneratorOption {
return func(pg *PlanGenerator) {
pg.source = &sources{backends: source}
}
}
// WithEnableConfigurationMigrationSteps enables only
// the configuration migration steps.
// TODO: to be replaced with a higher abstraction encapsulating
// migration scenarios.
func WithEnableConfigurationMigrationSteps() PlanGeneratorOption {
return func(pg *PlanGenerator) {
pg.enabledSteps = getConfigurationMigrationSteps()
}
}
func WithEnableOnlyFileSystemAPISteps() PlanGeneratorOption {
return func(pg *PlanGenerator) {
pg.enabledSteps = getAPIMigrationStepsFileSystemMode()
}
}
type sources struct {
backends []Source
i int
}
func (s *sources) HasNext() (bool, error) {
if s.i >= len(s.backends) {
return false, nil
}
ok, err := s.backends[s.i].HasNext()
if err != nil || ok {
return ok, err
}
s.i++
return s.HasNext()
}
func (s *sources) Next() (UnstructuredWithMetadata, error) {
return s.backends[s.i].Next()
}
func (s *sources) Reset() error {
for _, src := range s.backends {
if err := src.Reset(); err != nil {
return err
}
}
s.i = 0
return nil
}
// PlanGenerator generates a migration.Plan reading the manifests available
// from `source`, converting managed resources and compositions using the
// available `migration.Converter`s registered in the `registry` and
// writing the output manifests to the specified `target`.
type PlanGenerator struct {
source Source
target Target
registry *Registry
subSteps map[step]string
enabledSteps []step
// Plan is the migration.Plan whose steps are expected
// to complete a migration when they're executed in order.
Plan Plan
// ErrorOnInvalidPatchSchema errors and stops plan generation in case
// an error is encountered while checking the conformance of a patch
// statement against the migration source or the migration target.
ErrorOnInvalidPatchSchema bool
// GVKs of managed resources that
// should be skipped for conversion during the migration, if no
// converters are registered for them. If any of the GVK components
// is left empty, it will be a wildcard component.
// Exact matching with an empty group name is not possible.
SkipGVKs []schema.GroupVersionKind
}
// NewPlanGenerator constructs a new PlanGenerator using the specified
// Source and Target and the default converter Registry.
func NewPlanGenerator(registry *Registry, source Source, target Target, opts ...PlanGeneratorOption) PlanGenerator {
pg := &PlanGenerator{
source: &sources{backends: []Source{source}},
target: target,
registry: registry,
subSteps: map[step]string{},
enabledSteps: getAPIMigrationSteps(),
}
for _, o := range opts {
o(pg)
}
return *pg
}
// GeneratePlan generates a migration plan for the manifests available from
// the configured Source and writing them to the configured Target using the
// configured converter Registry. The generated Plan is available in the
// PlanGenerator.Plan variable if the generation is successful
// (i.e., no errors are reported).
func (pg *PlanGenerator) GeneratePlan() error {
pg.Plan.Spec.stepMap = make(map[string]*Step)
pg.Plan.Version = versionV010
defer pg.commitSteps()
if err := pg.preProcess(); err != nil {
return err
}
if err := pg.source.Reset(); err != nil {
return errors.Wrap(err, errSourceReset)
}
return errors.Wrap(pg.convert(), errPlanGeneration)
}
func (pg *PlanGenerator) preProcess() error {
if len(pg.registry.unstructuredPreProcessors) == 0 {
return nil
}
for hasNext, err := pg.source.HasNext(); ; hasNext, err = pg.source.HasNext() {
if err != nil {
return errors.Wrap(err, errSourceHasNext)
}
if !hasNext {
break
}
o, err := pg.source.Next()
if err != nil {
return errors.Wrap(err, errSourceNext)
}
for _, pp := range pg.registry.unstructuredPreProcessors[o.Metadata.Category] {
if err := pp.PreProcess(o); err != nil {
return errors.Wrapf(err, errPreProcessFmt, o.Metadata.Category)
}
}
}
return nil
}
func (pg *PlanGenerator) convertPatchSets(o UnstructuredWithMetadata) ([]string, error) {
var converted []string
for _, psConv := range pg.registry.patchSetConverters {
if psConv.re == nil || psConv.converter == nil {
continue
}
if !psConv.re.MatchString(o.Object.GetName()) {
continue
}
c, err := ToComposition(o.Object)
if err != nil {
return nil, errors.Wrap(err, errUnstructuredConvert)
}
oldPatchSets := make([]xpv1.PatchSet, len(c.Spec.PatchSets))
for i, ps := range c.Spec.PatchSets {
oldPatchSets[i] = *ps.DeepCopy()
}
psMap := convertToMap(c.Spec.PatchSets)
if err := psConv.converter.PatchSets(psMap); err != nil {
return nil, errors.Wrapf(err, "failed to call PatchSet converter on Composition: %s", c.GetName())
}
newPatchSets := convertFromMap(psMap, oldPatchSets, true)
converted = append(converted, getConvertedPatchSetNames(newPatchSets, oldPatchSets)...)
pv := fieldpath.Pave(o.Object.Object)
if err := pv.SetValue("spec.patchSets", newPatchSets); err != nil {
return nil, errors.Wrapf(err, "failed to set converted patch sets on Composition: %s", c.GetName())
}
}
return converted, nil
}
func (pg *PlanGenerator) categoricalConvert(u *UnstructuredWithMetadata) error {
if u.Metadata.Category == categoryUnknown {
return nil
}
source := *u
source.Object = *u.Object.DeepCopy()
converters := pg.registry.categoricalConverters[u.Metadata.Category]
if converters == nil {
return nil
}
// TODO: if a categorical converter does not convert the given object,
// we will have a false positive. Better to compute and check
// a diff here.
for _, converter := range converters {
if err := converter.Convert(u); err != nil {
return errors.Wrapf(err, "failed to convert unstructured object of category: %s", u.Metadata.Category)
}
}
return pg.stepEditCategory(source, u)
}
func (pg *PlanGenerator) convert() error { //nolint: gocyclo
convertedMR := make(map[corev1.ObjectReference][]UnstructuredWithMetadata)
convertedComposition := make(map[string]string)
var composites []UnstructuredWithMetadata
var claims []UnstructuredWithMetadata
for hasNext, err := pg.source.HasNext(); ; hasNext, err = pg.source.HasNext() {
if err != nil {
return errors.Wrap(err, errSourceHasNext)
}
if !hasNext {
break
}
o, err := pg.source.Next()
if err != nil {
return errors.Wrap(err, errSourceNext)
}
if err := pg.categoricalConvert(&o); err != nil {
return err
}
switch gvk := o.Object.GroupVersionKind(); gvk {
case xppkgv1.ConfigurationGroupVersionKind:
if err := pg.convertConfigurationPackage(o); err != nil {
return errors.Wrapf(err, errConfigurationPackageMigrateFmt, o.Object.GetName())
}
case xpmetav1.ConfigurationGroupVersionKind, xpmetav1alpha1.ConfigurationGroupVersionKind:
if err := pg.convertConfigurationMetadata(o); err != nil {
return errors.Wrapf(err, errConfigurationMetadataMigrateFmt, o.Object.GetName())
}
pg.stepBackupAllResources()
pg.stepBuildConfiguration()
pg.stepPushConfiguration()
case xpv1.CompositionGroupVersionKind:
target, converted, err := pg.convertComposition(o)
if err != nil {
return errors.Wrapf(err, errCompositionMigrateFmt, o.Object.GetName())
}
if converted {
migratedName := fmt.Sprintf("%s-migrated", o.Object.GetName())
convertedComposition[o.Object.GetName()] = migratedName
target.Object.SetName(migratedName)
if err := pg.stepNewComposition(target); err != nil {
return errors.Wrapf(err, errCompositionMigrateFmt, o.Object.GetName())
}
}
case xppkgv1.ProviderGroupVersionKind:
isConverted, err := pg.convertProviderPackage(o)
if err != nil {
return errors.Wrap(err, errProviderMigrateFmt)
}
if isConverted {
if err := pg.stepDeleteMonolith(o); err != nil {
return err
}
}
case xppkgv1beta1.LockGroupVersionKind:
if err := pg.convertPackageLock(o); err != nil {
return errors.Wrapf(err, errLockMigrateFmt, o.Object.GetName())
}
default:
if o.Metadata.Category == CategoryComposite {
if err := pg.stepPauseComposite(&o); err != nil {
return errors.Wrap(err, errCompositePause)
}
composites = append(composites, o)
continue
}
if o.Metadata.Category == CategoryClaim {
claims = append(claims, o)
continue
}
targets, converted, err := pg.convertResource(o, false)
if err != nil {
return errors.Wrap(err, errResourceMigrate)
}
if converted {
convertedMR[corev1.ObjectReference{
Kind: gvk.Kind,
Name: o.Object.GetName(),
APIVersion: gvk.GroupVersion().String(),
}] = targets
for _, tu := range targets {
tu := tu
if err := pg.stepNewManagedResource(&tu); err != nil {
return errors.Wrap(err, errResourceMigrate)
}
if err := pg.stepStartManagedResource(&tu); err != nil {
return errors.Wrap(err, errResourceMigrate)
}
}
} else if _, ok, _ := toManagedResource(pg.registry.scheme, o.Object); ok {
if err := pg.stepStartManagedResource(&o); err != nil {
return errors.Wrap(err, errResourceMigrate)
}
}
}
if err := pg.addStepsForManagedResource(&o); err != nil {
return err
}
}
if err := pg.stepEditComposites(composites, convertedMR, convertedComposition); err != nil {
return errors.Wrap(err, errCompositesEdit)
}
if err := pg.stepStartComposites(composites); err != nil {
return errors.Wrap(err, errCompositesStart)
}
if err := pg.stepEditClaims(claims, convertedComposition); err != nil {
return errors.Wrap(err, errClaimsEdit)
}
return nil
}
func (pg *PlanGenerator) convertResource(o UnstructuredWithMetadata, compositionContext bool) ([]UnstructuredWithMetadata, bool, error) {
gvk := o.Object.GroupVersionKind()
conv := pg.registry.resourceConverters[gvk]
if conv == nil {
return []UnstructuredWithMetadata{o}, false, nil
}
// we have already ensured that the GVK belongs to a managed resource type
mg, _, err := toManagedResource(pg.registry.scheme, o.Object)
if err != nil {
return nil, false, errors.Wrap(err, errResourceMigrate)
}
if pg.registry.resourcePreProcessors != nil {
for _, pp := range pg.registry.resourcePreProcessors {
if err = pp.ResourcePreProcessor(mg); err != nil {
return nil, false, errors.Wrap(err, errResourceMigrate)
}
}
}
resources, err := conv.Resource(mg)
if err != nil {
return nil, false, errors.Wrap(err, errResourceMigrate)
}
if err := assertGVK(resources); err != nil {
return nil, true, errors.Wrap(err, errResourceMigrate)
}
if !compositionContext {
assertMetadataName(mg.GetName(), resources)
}
converted := make([]UnstructuredWithMetadata, 0, len(resources))
for _, mg := range resources {
converted = append(converted, UnstructuredWithMetadata{
Object: ToSanitizedUnstructured(mg),
Metadata: o.Metadata,
})
}
return converted, true, nil
}
func assertGVK(resources []resource.Managed) error {
for _, r := range resources {
if reflect.ValueOf(r.GetObjectKind().GroupVersionKind()).IsZero() {
return errors.New(errMissingGVK)
}
}
return nil
}
func assertMetadataName(parentName string, resources []resource.Managed) {
for i, r := range resources {
if len(r.GetName()) != 0 || len(r.GetGenerateName()) != 0 {
continue
}
resources[i].SetGenerateName(fmt.Sprintf("%s-", parentName))
}
}
func (pg *PlanGenerator) convertComposition(o UnstructuredWithMetadata) (*UnstructuredWithMetadata, bool, error) { //nolint:gocyclo
convertedPS, err := pg.convertPatchSets(o)
if err != nil {
return nil, false, errors.Wrap(err, "failed to convert patch sets")
}
comp, err := ToComposition(o.Object)
if err != nil {
return nil, false, errors.Wrap(err, errUnstructuredConvert)
}
var targetResources []*xpv1.ComposedTemplate
isConverted := false
for _, cmp := range comp.Spec.Resources {
u, err := FromRawExtension(cmp.Base)
if err != nil {
return nil, false, errors.Wrapf(err, errCompositionMigrateFmt, o.Object.GetName())
}
gvk := u.GroupVersionKind()
converted, ok, err := pg.convertResource(UnstructuredWithMetadata{
Object: u,
Metadata: o.Metadata,
}, true)
if err != nil {
return nil, false, errors.Wrap(err, errComposedTemplateBase)
}
isConverted = isConverted || ok
cmps := make([]*xpv1.ComposedTemplate, 0, len(converted))
sourceNameUsed := false
for _, u := range converted {
buff, err := u.Object.MarshalJSON()
if err != nil {
return nil, false, errors.Wrap(err, errUnstructuredMarshal)
}
c := cmp.DeepCopy()
c.Base = runtime.RawExtension{
Raw: buff,
}
if err := pg.setDefaultsOnTargetTemplate(cmp.Name, &sourceNameUsed, gvk, u.Object.GroupVersionKind(), c, comp.Spec.PatchSets, convertedPS); err != nil {
return nil, false, errors.Wrap(err, errComposedTemplateMigrate)
}
cmps = append(cmps, c)
}
conv := pg.registry.templateConverters[gvk]
if conv != nil {
if err := conv.ComposedTemplate(cmp, cmps...); err != nil {
return nil, false, errors.Wrap(err, errComposedTemplateMigrate)
}
}
targetResources = append(targetResources, cmps...)
}
comp.Spec.Resources = make([]xpv1.ComposedTemplate, 0, len(targetResources))
for _, cmp := range targetResources {
comp.Spec.Resources = append(comp.Spec.Resources, *cmp)
}
return &UnstructuredWithMetadata{
Object: ToSanitizedUnstructured(&comp),
Metadata: o.Metadata,
}, isConverted, nil
}
func (pg *PlanGenerator) isGVKSkipped(sourceGVK schema.GroupVersionKind) bool {
for _, gvk := range pg.SkipGVKs {
if (len(gvk.Group) == 0 || gvk.Group == sourceGVK.Group) &&
(len(gvk.Version) == 0 || gvk.Version == sourceGVK.Version) &&
(len(gvk.Kind) == 0 || gvk.Kind == sourceGVK.Kind) {
return true
}
}
return false
}
func (pg *PlanGenerator) setDefaultsOnTargetTemplate(sourceName *string, sourceNameUsed *bool, gvkSource, gvkTarget schema.GroupVersionKind, target *xpv1.ComposedTemplate, patchSets []xpv1.PatchSet, convertedPS []string) error {
if pg.isGVKSkipped(gvkSource) {
return nil
}
// remove invalid patches that do not conform to the migration target's schema
if err := pg.removeInvalidPatches(gvkSource, gvkTarget, patchSets, target, convertedPS); err != nil {
return errors.Wrap(err, "failed to set the defaults on the migration target composed template")
}
if *sourceNameUsed || gvkSource.Kind != gvkTarget.Kind {
if sourceName != nil && len(*sourceName) > 0 {
targetName := fmt.Sprintf("%s-%s", *sourceName, rand.String(5))
target.Name = &targetName
}
} else {
*sourceNameUsed = true
}
return nil
}
func init() {
rand.Seed(time.Now().UnixNano())
}

View File

@ -1,646 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"bytes"
"os"
"path/filepath"
"regexp"
"testing"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/test"
v1 "github.com/crossplane/crossplane/apis/apiextensions/v1"
xpmetav1 "github.com/crossplane/crossplane/apis/pkg/meta/v1"
xpmetav1alpha1 "github.com/crossplane/crossplane/apis/pkg/meta/v1alpha1"
xppkgv1 "github.com/crossplane/crossplane/apis/pkg/v1"
xppkgv1beta1 "github.com/crossplane/crossplane/apis/pkg/v1beta1"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/utils/ptr"
k8syaml "sigs.k8s.io/yaml"
"github.com/crossplane/upjet/pkg/migration/fake"
)
func TestGeneratePlan(t *testing.T) {
type fields struct {
source Source
target *testTarget
registry *Registry
opts []PlanGeneratorOption
}
type want struct {
err error
migrationPlanPath string
// names of resource files to be loaded
migratedResourceNames []string
preProcessResults map[Category][]string
}
tests := map[string]struct {
fields fields
want want
}{
"EmptyPlan": {
fields: fields{
source: newTestSource(map[string]Metadata{}),
target: newTestTarget(),
registry: getRegistry(),
},
want: want{},
},
"PreProcess": {
fields: fields{
source: newTestSource(map[string]Metadata{
"testdata/plan/composition.yaml": {Category: CategoryComposition},
}),
target: newTestTarget(),
registry: getRegistry(withPreProcessor(CategoryComposition, &preProcessor{})),
},
want: want{
preProcessResults: map[Category][]string{
CategoryComposition: {"example.compositions.apiextensions.crossplane.io_v1"},
},
},
},
"PlanWithManagedResourceAndClaim": {
fields: fields{
source: newTestSource(map[string]Metadata{
"testdata/plan/sourcevpc.yaml": {Category: CategoryManaged},
"testdata/plan/claim.yaml": {Category: CategoryClaim},
"testdata/plan/composition.yaml": {},
"testdata/plan/xrd.yaml": {},
"testdata/plan/xr.yaml": {Category: CategoryComposite}}),
target: newTestTarget(),
registry: getRegistry(
withPreProcessor(CategoryManaged, &preProcessor{}),
withDelegatingConverter(fake.MigrationSourceGVK, delegatingConverter{
rFn: func(mg xpresource.Managed) ([]xpresource.Managed, error) {
s := mg.(*fake.MigrationSourceObject)
t := &fake.MigrationTargetObject{}
if _, err := CopyInto(s, t, fake.MigrationTargetGVK, "spec.forProvider.tags", "mockManaged"); err != nil {
return nil, err
}
t.Spec.ForProvider.Tags = make(map[string]string, len(s.Spec.ForProvider.Tags))
for _, tag := range s.Spec.ForProvider.Tags {
v := tag.Value
t.Spec.ForProvider.Tags[tag.Key] = v
}
return []xpresource.Managed{
t,
}, nil
},
cmpFn: func(_ v1.ComposedTemplate, convertedTemplates ...*v1.ComposedTemplate) error {
// convert patches in the migration target composed templates
for i := range convertedTemplates {
convertedTemplates[i].Patches = append([]v1.Patch{
{FromFieldPath: ptrFromString("spec.parameters.tagValue"),
ToFieldPath: ptrFromString(`spec.forProvider.tags["key1"]`),
}, {
FromFieldPath: ptrFromString("spec.parameters.tagValue"),
ToFieldPath: ptrFromString(`spec.forProvider.tags["key2"]`),
},
}, convertedTemplates[i].Patches...)
}
return nil
},
}),
withPatchSetConverter(patchSetConverter{
re: AllCompositions,
converter: &testConverter{},
})),
},
want: want{
migrationPlanPath: "testdata/plan/generated/migration_plan.yaml",
migratedResourceNames: []string{
"pause-managed/sample-vpc.vpcs.fakesourceapi.yaml",
"edit-claims/my-resource.myresources.test.com.yaml",
"start-managed/sample-vpc.vpcs.faketargetapi.yaml",
"pause-composites/my-resource-dwjgh.xmyresources.test.com.yaml",
"edit-composites/my-resource-dwjgh.xmyresources.test.com.yaml",
"deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi.yaml",
"remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml",
"new-compositions/example-migrated.compositions.apiextensions.crossplane.io.yaml",
"start-composites/my-resource-dwjgh.xmyresources.test.com.yaml",
"create-new-managed/sample-vpc.vpcs.faketargetapi.yaml",
},
preProcessResults: map[Category][]string{
CategoryManaged: {"sample-vpc.vpcs.fakesourceapi_v1alpha1"},
},
},
},
"PlanWithManagedResourceAndClaimForFileSystemMode": {
fields: fields{
source: newTestSource(map[string]Metadata{
"testdata/plan/sourcevpc.yaml": {Category: CategoryManaged},
"testdata/plan/claim.yaml": {Category: CategoryClaim},
"testdata/plan/composition.yaml": {},
"testdata/plan/xrd.yaml": {},
"testdata/plan/xr.yaml": {Category: CategoryComposite}}),
target: newTestTarget(),
registry: getRegistry(
withPreProcessor(CategoryManaged, &preProcessor{}),
withDelegatingConverter(fake.MigrationSourceGVK, delegatingConverter{
rFn: func(mg xpresource.Managed) ([]xpresource.Managed, error) {
s := mg.(*fake.MigrationSourceObject)
t := &fake.MigrationTargetObject{}
if _, err := CopyInto(s, t, fake.MigrationTargetGVK, "spec.forProvider.tags", "mockManaged"); err != nil {
return nil, err
}
t.Spec.ForProvider.Tags = make(map[string]string, len(s.Spec.ForProvider.Tags))
for _, tag := range s.Spec.ForProvider.Tags {
v := tag.Value
t.Spec.ForProvider.Tags[tag.Key] = v
}
return []xpresource.Managed{
t,
}, nil
},
cmpFn: func(_ v1.ComposedTemplate, convertedTemplates ...*v1.ComposedTemplate) error {
// convert patches in the migration target composed templates
for i := range convertedTemplates {
convertedTemplates[i].Patches = append([]v1.Patch{
{FromFieldPath: ptrFromString("spec.parameters.tagValue"),
ToFieldPath: ptrFromString(`spec.forProvider.tags["key1"]`),
}, {
FromFieldPath: ptrFromString("spec.parameters.tagValue"),
ToFieldPath: ptrFromString(`spec.forProvider.tags["key2"]`),
},
}, convertedTemplates[i].Patches...)
}
return nil
},
}),
withPatchSetConverter(patchSetConverter{
re: AllCompositions,
converter: &testConverter{},
})),
opts: []PlanGeneratorOption{WithEnableOnlyFileSystemAPISteps()},
},
want: want{
migrationPlanPath: "testdata/plan/generated/migration_plan_filesystem.yaml",
migratedResourceNames: []string{
"edit-claims/my-resource.myresources.test.com.yaml",
"start-managed/sample-vpc.vpcs.faketargetapi.yaml",
"edit-composites/my-resource-dwjgh.xmyresources.test.com.yaml",
"new-compositions/example-migrated.compositions.apiextensions.crossplane.io.yaml",
"start-composites/my-resource-dwjgh.xmyresources.test.com.yaml",
"create-new-managed/sample-vpc.vpcs.faketargetapi.yaml",
},
preProcessResults: map[Category][]string{
CategoryManaged: {"sample-vpc.vpcs.fakesourceapi_v1alpha1"},
},
},
},
"PlanWithConfigurationMetaV1": {
fields: fields{
source: newTestSource(map[string]Metadata{
"testdata/plan/configurationv1.yaml": {}}),
target: newTestTarget(),
registry: getRegistry(
withConfigurationMetadataConverter(configurationMetadataConverter{
re: AllConfigurations,
converter: &configurationMetaTestConverter{},
})),
opts: []PlanGeneratorOption{WithEnableConfigurationMigrationSteps()},
},
want: want{
migrationPlanPath: "testdata/plan/generated/configurationv1_migration_plan.yaml",
migratedResourceNames: []string{
"edit-configuration-metadata/platform-ref-aws.configurations.meta.pkg.crossplane.io_v1.yaml",
},
},
},
"PlanWithConfigurationMetaV1Alpha1": {
fields: fields{
source: newTestSource(map[string]Metadata{
"testdata/plan/configurationv1alpha1.yaml": {}}),
target: newTestTarget(),
registry: getRegistry(
withConfigurationMetadataConverter(configurationMetadataConverter{
re: AllConfigurations,
converter: &configurationMetaTestConverter{},
})),
opts: []PlanGeneratorOption{WithEnableConfigurationMigrationSteps()},
},
want: want{
migrationPlanPath: "testdata/plan/generated/configurationv1alpha1_migration_plan.yaml",
migratedResourceNames: []string{
"edit-configuration-metadata/platform-ref-aws.configurations.meta.pkg.crossplane.io_v1alpha1.yaml",
},
},
},
"PlanWithProviderPackageV1": {
fields: fields{
source: newTestSource(map[string]Metadata{
"testdata/plan/providerv1.yaml": {}}),
target: newTestTarget(),
registry: getRegistry(
withProviderPackageConverter(providerPackageConverter{
re: regexp.MustCompile(`xpkg.upbound.io/upbound/provider-aws:.+`),
converter: &monolithProviderToFamilyConfigConverter{},
}),
withProviderPackageConverter(providerPackageConverter{
re: regexp.MustCompile(`xpkg.upbound.io/upbound/provider-aws:.+`),
converter: &monolithicProviderToSSOPConverter{},
})),
opts: []PlanGeneratorOption{WithEnableConfigurationMigrationSteps()},
},
want: want{
migrationPlanPath: "testdata/plan/generated/providerv1_migration_plan.yaml",
migratedResourceNames: []string{
"new-ssop/provider-family-aws.providers.pkg.crossplane.io_v1.yaml",
"new-ssop/provider-aws-ec2.providers.pkg.crossplane.io_v1.yaml",
"new-ssop/provider-aws-eks.providers.pkg.crossplane.io_v1.yaml",
"activate-ssop/provider-family-aws.providers.pkg.crossplane.io_v1.yaml",
"activate-ssop/provider-aws-ec2.providers.pkg.crossplane.io_v1.yaml",
"activate-ssop/provider-aws-eks.providers.pkg.crossplane.io_v1.yaml",
},
},
},
"PlanForConfigurationPackageMigration": {
fields: fields{
source: newTestSource(map[string]Metadata{
"testdata/plan/providerv1.yaml": {},
"testdata/plan/configurationv1.yaml": {},
"testdata/plan/configurationpkgv1.yaml": {},
"testdata/plan/lockv1beta1.yaml": {},
"testdata/plan/sourcevpc.yaml": {Category: CategoryManaged},
"testdata/plan/sourcevpc2.yaml": {Category: CategoryManaged},
}),
target: newTestTarget(),
registry: getRegistry(
withConfigurationMetadataConverter(configurationMetadataConverter{
re: AllConfigurations,
converter: &configurationMetaTestConverter{},
}),
withConfigurationPackageConverter(configurationPackageConverter{
re: regexp.MustCompile(`xpkg.upbound.io/upbound/provider-ref-aws:.+`),
converter: &configurationPackageTestConverter{},
}),
withProviderPackageConverter(providerPackageConverter{
re: regexp.MustCompile(`xpkg.upbound.io/upbound/provider-aws:.+`),
converter: &monolithProviderToFamilyConfigConverter{},
}),
withProviderPackageConverter(providerPackageConverter{
re: regexp.MustCompile(`xpkg.upbound.io/upbound/provider-aws:.+`),
converter: &monolithicProviderToSSOPConverter{},
}),
withPackageLockConverter(packageLockConverter{
re: CrossplaneLockName,
converter: &lockConverter{},
}),
),
opts: []PlanGeneratorOption{WithEnableConfigurationMigrationSteps()},
},
want: want{
migrationPlanPath: "testdata/plan/generated/configurationv1_pkg_migration_plan.yaml",
migratedResourceNames: []string{
"disable-dependency-resolution/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml",
"edit-configuration-package/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml",
"enable-dependency-resolution/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml",
"edit-configuration-metadata/platform-ref-aws.configurations.meta.pkg.crossplane.io_v1.yaml",
"new-ssop/provider-family-aws.providers.pkg.crossplane.io_v1.yaml",
"new-ssop/provider-aws-ec2.providers.pkg.crossplane.io_v1.yaml",
"new-ssop/provider-aws-eks.providers.pkg.crossplane.io_v1.yaml",
"activate-ssop/provider-family-aws.providers.pkg.crossplane.io_v1.yaml",
"activate-ssop/provider-aws-ec2.providers.pkg.crossplane.io_v1.yaml",
"activate-ssop/provider-aws-eks.providers.pkg.crossplane.io_v1.yaml",
"edit-package-lock/lock.locks.pkg.crossplane.io_v1beta1.yaml",
"deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi_v1alpha1.yaml",
"deletion-policy-delete/sample-vpc.vpcs.fakesourceapi_v1alpha1.yaml",
},
},
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
pg := NewPlanGenerator(tt.fields.registry, tt.fields.source, tt.fields.target, tt.fields.opts...)
err := pg.GeneratePlan()
// compare error state
if diff := cmp.Diff(tt.want.err, err, test.EquateErrors()); diff != "" {
t.Fatalf("GeneratePlan(): -wantError, +gotError: %s", diff)
}
if err != nil {
return
}
// compare preprocessor results
for c, results := range tt.want.preProcessResults {
pps := tt.fields.registry.unstructuredPreProcessors[c]
if len(pps) != 1 {
t.Fatalf("One pre-processor must have been registered for category: %s", c)
}
pp := pps[0].(*preProcessor)
if diff := cmp.Diff(results, pp.results); diff != "" {
t.Errorf("GeneratePlan(): -wantPreProcessorResults, +gotPreProcessorResults: %s", diff)
}
}
// compare generated plan with the expected plan
p, err := loadPlan(tt.want.migrationPlanPath)
if err != nil {
t.Fatalf("Failed to load plan file from path %s: %v", tt.want.migrationPlanPath, err)
}
if diff := cmp.Diff(p, &pg.Plan, cmpopts.IgnoreUnexported(Spec{})); diff != "" {
t.Errorf("GeneratePlan(): -wantPlan, +gotPlan: %s", diff)
}
// compare generated migration files with the expected ones
for _, name := range tt.want.migratedResourceNames {
path := filepath.Join("testdata/plan/generated", name)
buff, err := os.ReadFile(path)
if err != nil {
t.Fatalf("Failed to read a generated migration resource from path %s: %v", path, err)
}
u := unstructured.Unstructured{}
if err := k8syaml.Unmarshal(buff, &u); err != nil {
t.Fatalf("Failed to unmarshal a generated migration resource from path %s: %v", path, err)
}
gU, ok := tt.fields.target.targetManifests[name]
if !ok {
t.Errorf("GeneratePlan(): Expected generated migration resource file not found: %s", name)
continue
}
removeNilValuedKeys(u.Object)
if diff := cmp.Diff(u, gU.Object); diff != "" {
t.Errorf("GeneratePlan(): -wantMigratedResource, +gotMigratedResource with name %q: %s", name, diff)
}
delete(tt.fields.target.targetManifests, name)
}
// check for unexpected generated migration files
for name := range tt.fields.target.targetManifests {
t.Errorf("GeneratePlan(): Unexpected generated migration file: %s", name)
}
})
}
}
type testSource struct {
sourceManifests map[string]Metadata
paths []string
index int
}
func newTestSource(sourceManifests map[string]Metadata) *testSource {
result := &testSource{sourceManifests: sourceManifests}
result.paths = make([]string, 0, len(result.sourceManifests))
for k := range result.sourceManifests {
result.paths = append(result.paths, k)
}
return result
}
func (f *testSource) HasNext() (bool, error) {
return f.index <= len(f.paths)-1, nil
}
func (f *testSource) Next() (UnstructuredWithMetadata, error) {
um := UnstructuredWithMetadata{
Metadata: f.sourceManifests[f.paths[f.index]],
Object: unstructured.Unstructured{},
}
um.Metadata.Path = f.paths[f.index]
buff, err := os.ReadFile(f.paths[f.index])
if err != nil {
return um, err
}
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewBufferString(string(buff)), 1024)
if err := decoder.Decode(&um.Object); err != nil {
return um, err
}
f.index++
return um, nil
}
func (f *testSource) Reset() error {
f.index = 0
return nil
}
type testTarget struct {
targetManifests map[string]UnstructuredWithMetadata
}
func newTestTarget() *testTarget {
return &testTarget{
targetManifests: make(map[string]UnstructuredWithMetadata),
}
}
func (f *testTarget) Put(o UnstructuredWithMetadata) error {
f.targetManifests[o.Metadata.Path] = o
return nil
}
func (f *testTarget) Delete(o UnstructuredWithMetadata) error {
delete(f.targetManifests, o.Metadata.Path)
return nil
}
// can be utilized to populate test artifacts
/*func (f *testTarget) dumpFiles(parentDir string) error {
for f, u := range f.targetManifests {
path := filepath.Join(parentDir, f)
buff, err := k8syaml.Marshal(u.Object.Object)
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return err
}
if err := os.WriteFile(path, buff, 0o600); err != nil {
return err
}
}
return nil
}*/
type testConverter struct{}
func (f *testConverter) PatchSets(psMap map[string]*v1.PatchSet) error {
psMap["ps1"].Patches[0].ToFieldPath = ptrFromString(`spec.forProvider.tags["key3"]`)
psMap["ps6"].Patches[0].ToFieldPath = ptrFromString(`spec.forProvider.tags["key4"]`)
return nil
}
func ptrFromString(s string) *string {
return &s
}
type registryOption func(*Registry)
func withDelegatingConverter(gvk schema.GroupVersionKind, d delegatingConverter) registryOption {
return func(r *Registry) {
r.RegisterAPIConversionFunctions(gvk, d.rFn, d.cmpFn, nil)
}
}
func withPatchSetConverter(c patchSetConverter) registryOption {
return func(r *Registry) {
r.RegisterPatchSetConverter(c.re, c.converter)
}
}
func withConfigurationMetadataConverter(c configurationMetadataConverter) registryOption {
return func(r *Registry) {
r.RegisterConfigurationMetadataConverter(c.re, c.converter)
}
}
func withConfigurationPackageConverter(c configurationPackageConverter) registryOption {
return func(r *Registry) {
r.RegisterConfigurationPackageConverter(c.re, c.converter)
}
}
func withProviderPackageConverter(c providerPackageConverter) registryOption {
return func(r *Registry) {
r.RegisterProviderPackageConverter(c.re, c.converter)
}
}
func withPackageLockConverter(c packageLockConverter) registryOption {
return func(r *Registry) {
r.RegisterPackageLockConverter(c.re, c.converter)
}
}
func withPreProcessor(c Category, pp UnstructuredPreProcessor) registryOption {
return func(r *Registry) {
r.RegisterPreProcessor(c, pp)
}
}
func getRegistry(opts ...registryOption) *Registry {
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName(fake.MigrationSourceGVK, &fake.MigrationSourceObject{})
scheme.AddKnownTypeWithName(fake.MigrationTargetGVK, &fake.MigrationTargetObject{})
r := NewRegistry(scheme)
for _, o := range opts {
o(r)
}
return r
}
func loadPlan(planPath string) (*Plan, error) {
if planPath == "" {
return emptyPlan(), nil
}
buff, err := os.ReadFile(planPath)
if err != nil {
return nil, err
}
p := &Plan{}
return p, k8syaml.Unmarshal(buff, p)
}
func emptyPlan() *Plan {
return &Plan{
Version: versionV010,
}
}
type configurationPackageTestConverter struct{}
func (c *configurationPackageTestConverter) ConfigurationPackageV1(pkg *xppkgv1.Configuration) error {
pkg.Spec.Package = "xpkg.upbound.io/upbound/provider-ref-aws:v0.2.0-ssop"
return nil
}
type configurationMetaTestConverter struct{}
func (cc *configurationMetaTestConverter) ConfigurationMetadataV1(c *xpmetav1.Configuration) error {
c.Spec.DependsOn = []xpmetav1.Dependency{
{
Provider: ptrFromString("xpkg.upbound.io/upbound/provider-aws-eks"),
Version: ">=v0.17.0",
},
}
return nil
}
func (cc *configurationMetaTestConverter) ConfigurationMetadataV1Alpha1(c *xpmetav1alpha1.Configuration) error {
c.Spec.DependsOn = []xpmetav1alpha1.Dependency{
{
Provider: ptrFromString("xpkg.upbound.io/upbound/provider-aws-eks"),
Version: ">=v0.17.0",
},
}
return nil
}
type monolithProviderToFamilyConfigConverter struct{}
func (c *monolithProviderToFamilyConfigConverter) ProviderPackageV1(_ xppkgv1.Provider) ([]xppkgv1.Provider, error) {
ap := xppkgv1.ManualActivation
return []xppkgv1.Provider{
{
ObjectMeta: metav1.ObjectMeta{
Name: "provider-family-aws",
},
Spec: xppkgv1.ProviderSpec{
PackageSpec: xppkgv1.PackageSpec{
Package: "xpkg.upbound.io/upbound/provider-family-aws:v0.37.0",
RevisionActivationPolicy: &ap,
},
},
},
}, nil
}
type monolithicProviderToSSOPConverter struct{}
func (c *monolithicProviderToSSOPConverter) ProviderPackageV1(_ xppkgv1.Provider) ([]xppkgv1.Provider, error) {
ap := xppkgv1.ManualActivation
return []xppkgv1.Provider{
{
ObjectMeta: metav1.ObjectMeta{
Name: "provider-aws-ec2",
},
Spec: xppkgv1.ProviderSpec{
PackageSpec: xppkgv1.PackageSpec{
Package: "xpkg.upbound.io/upbound/provider-aws-ec2:v0.37.0",
RevisionActivationPolicy: &ap,
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "provider-aws-eks",
},
Spec: xppkgv1.ProviderSpec{
PackageSpec: xppkgv1.PackageSpec{
Package: "xpkg.upbound.io/upbound/provider-aws-eks:v0.37.0",
RevisionActivationPolicy: &ap,
},
},
},
}, nil
}
type lockConverter struct{}
func (p *lockConverter) PackageLockV1Beta1(lock *xppkgv1beta1.Lock) error {
lock.Packages = append(lock.Packages, xppkgv1beta1.LockPackage{
Name: "test-provider",
Type: ptr.To(xppkgv1beta1.ProviderPackageType),
Source: "xpkg.upbound.io/upbound/test-provider",
Version: "vX.Y.Z",
})
return nil
}
type preProcessor struct {
results []string
}
func (pp *preProcessor) PreProcess(u UnstructuredWithMetadata) error {
pp.results = append(pp.results, getVersionedName(u.Object))
return nil
}

View File

@ -1,177 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"sort"
"strconv"
"strings"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/jsonmergepatch"
"k8s.io/apimachinery/pkg/util/rand"
)
type step int
const (
errMarshalSourceForPatch = "failed to marshal source object for computing JSON merge patch"
errMarshalTargetForPatch = "failed to marshal target object for computing JSON merge patch"
errMergePatch = "failed to compute the JSON merge patch document"
errMergePatchMap = "failed to unmarshal the JSON merge patch document into map"
errInvalidStepFmt = "invalid step ID: %d"
)
func setApplyStep(name string, s *Step) {
s.Name = name
s.Type = StepTypeApply
s.Apply = &ApplyStep{}
}
func setPatchStep(name string, s *Step) {
s.Name = name
s.Type = StepTypePatch
s.Patch = &PatchStep{}
s.Patch.Type = PatchTypeMerge
}
func setDeleteStep(name string, s *Step) {
s.Name = name
s.Type = StepTypeDelete
deletePolicy := FinalizerPolicyRemove
s.Delete = &DeleteStep{
Options: &DeleteOptions{
FinalizerPolicy: &deletePolicy,
},
}
}
func setExecStep(name string, s *Step) {
s.Name = name
s.Type = StepTypeExec
s.Exec = &ExecStep{
Command: "sh",
}
}
func (pg *PlanGenerator) commitSteps() { //nolint: gocyclo
if len(pg.Plan.Spec.stepMap) == 0 {
return
}
pg.Plan.Spec.Steps = make([]Step, 0, len(pg.Plan.Spec.stepMap))
keys := make([]string, 0, len(pg.Plan.Spec.stepMap))
for s := range pg.Plan.Spec.stepMap {
keys = append(keys, s)
}
// keys slice consist of the step keys of enabled migration steps.
// step keys are the string representation of integer or float64 numbers,
// which correspond to the step execution order (greater number executes later)
// therefore needs to be sorted according to their numeric values.
// otherwise, sorting the strings directly causes faulty behavior e.g "1" < "10" < "2"
// sorting will panic if a non-numeric step key is found in keys
sort.SliceStable(keys, func(i, j int) bool {
fi, err := strconv.ParseFloat(keys[i], 64)
if err != nil {
panic(err)
}
fj, err := strconv.ParseFloat(keys[j], 64)
if err != nil {
panic(err)
}
return fi < fj
})
addManualExecution := true
switch t := pg.source.(type) {
case *sources:
for _, source := range t.backends {
if _, ok := source.(*FileSystemSource); ok {
addManualExecution = false
break
}
}
case *FileSystemSource:
addManualExecution = false
}
if addManualExecution {
for _, s := range keys {
AddManualExecution(pg.Plan.Spec.stepMap[s])
pg.Plan.Spec.Steps = append(pg.Plan.Spec.Steps, *pg.Plan.Spec.stepMap[s])
}
} else {
for _, s := range keys {
pg.Plan.Spec.Steps = append(pg.Plan.Spec.Steps, *pg.Plan.Spec.stepMap[s])
}
}
}
// AddManualExecution sets the manual execution hint for
// the specified step.
func AddManualExecution(s *Step) {
switch s.Type {
case StepTypeExec:
s.ManualExecution = []string{fmt.Sprintf("%s %s %q", s.Exec.Command, s.Exec.Args[0], strings.Join(s.Exec.Args[1:], " "))}
case StepTypePatch:
for _, f := range s.Patch.Files {
s.ManualExecution = append(s.ManualExecution, fmt.Sprintf("kubectl patch --type='%s' -f %s --patch-file %s", s.Patch.Type, f, f))
}
case StepTypeApply:
for _, f := range s.Apply.Files {
s.ManualExecution = append(s.ManualExecution, fmt.Sprintf("kubectl apply -f %s", f))
}
case StepTypeDelete:
for _, r := range s.Delete.Resources {
s.ManualExecution = append(s.ManualExecution, fmt.Sprintf("kubectl delete %s %s", strings.Join([]string{r.Kind, r.Group}, "."), r.Name))
}
}
}
func (pg *PlanGenerator) stepEnabled(s step) bool {
for _, i := range pg.enabledSteps {
if i == s {
return true
}
}
return false
}
func computeJSONMergePathDoc(source, target unstructured.Unstructured) (map[string]any, error) {
sourceBuff, err := source.MarshalJSON()
if err != nil {
return nil, errors.Wrap(err, errMarshalSourceForPatch)
}
targetBuff, err := target.MarshalJSON()
if err != nil {
return nil, errors.Wrap(err, errMarshalTargetForPatch)
}
patch, err := jsonmergepatch.CreateThreeWayJSONMergePatch(sourceBuff, targetBuff, sourceBuff)
if err != nil {
return nil, errors.Wrap(err, errMergePatch)
}
var result map[string]any
return result, errors.Wrap(json.Unmarshal(patch, &result), errMergePatchMap)
}
func getQualifiedName(u unstructured.Unstructured) string {
namePrefix := u.GetName()
if len(namePrefix) == 0 {
namePrefix = fmt.Sprintf("%s%s", u.GetGenerateName(), rand.String(5))
}
gvk := u.GroupVersionKind()
return fmt.Sprintf("%s.%ss.%s", namePrefix, strings.ToLower(gvk.Kind), gvk.Group)
}
func getVersionedName(u unstructured.Unstructured) string {
v := u.GroupVersionKind().Version
qName := getQualifiedName(u)
if v == "" {
return qName
}
return fmt.Sprintf("%s_%s", qName, v)
}

View File

@ -1,146 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"fmt"
"strings"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
const (
errPutSSOPPackageFmt = "failed to put the SSOP package: %s"
errActivateSSOP = "failed to put the activated SSOP package: %s"
)
func (pg *PlanGenerator) convertProviderPackage(o UnstructuredWithMetadata) (bool, error) { //nolint:gocyclo
pkg, err := toProviderPackage(o.Object)
if err != nil {
return false, err
}
isConverted := false
for _, pkgConv := range pg.registry.providerPackageConverters {
if pkgConv.re == nil || pkgConv.converter == nil || !pkgConv.re.MatchString(pkg.Spec.Package) {
continue
}
targetPkgs, err := pkgConv.converter.ProviderPackageV1(*pkg)
if err != nil {
return false, errors.Wrapf(err, "failed to call converter on Provider package: %s", pkg.Spec.Package)
}
if len(targetPkgs) == 0 {
continue
}
// TODO: if a configuration converter only converts a specific version,
// (or does not convert the given configuration),
// we will have a false positive. Better to compute and check
// a diff here.
isConverted = true
converted := make([]*UnstructuredWithMetadata, 0, len(targetPkgs))
for _, p := range targetPkgs {
p := p
converted = append(converted, &UnstructuredWithMetadata{
Object: ToSanitizedUnstructured(&p),
Metadata: o.Metadata,
})
}
if err := pg.stepNewSSOPs(o, converted); err != nil {
return false, err
}
if err := pg.stepActivateSSOPs(converted); err != nil {
return false, err
}
if err := pg.stepCheckHealthOfNewProvider(o, converted); err != nil {
return false, err
}
if err := pg.stepCheckInstallationOfNewProvider(o, converted); err != nil {
return false, err
}
}
return isConverted, nil
}
func (pg *PlanGenerator) stepDeleteMonolith(source UnstructuredWithMetadata) error {
// delete the monolithic provider package
s := pg.stepConfigurationWithSubStep(stepDeleteMonolithicProvider, true)
source.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(source.Object))
s.Delete.Resources = []Resource{
{
GroupVersionKind: FromGroupVersionKind(source.Object.GroupVersionKind()),
Name: source.Object.GetName(),
},
}
return nil
}
// add steps for the new SSOPs
func (pg *PlanGenerator) stepNewSSOPs(source UnstructuredWithMetadata, targets []*UnstructuredWithMetadata) error {
var s *Step
isFamilyConfig, err := checkContainsFamilyConfigProvider(targets)
if err != nil {
return errors.Wrapf(err, "could not decide whether the provider family config")
}
if isFamilyConfig {
s = pg.stepConfigurationWithSubStep(stepNewFamilyProvider, true)
} else {
s = pg.stepConfigurationWithSubStep(stepNewServiceScopedProvider, true)
}
for _, t := range targets {
t.Object.Object = addGVK(source.Object, t.Object.Object)
t.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(t.Object))
s.Apply.Files = append(s.Apply.Files, t.Metadata.Path)
if err := pg.target.Put(*t); err != nil {
return errors.Wrapf(err, errPutSSOPPackageFmt, t.Metadata.Path)
}
}
return nil
}
// add steps for activating SSOPs
func (pg *PlanGenerator) stepActivateSSOPs(targets []*UnstructuredWithMetadata) error {
var s *Step
isFamilyConfig, err := checkContainsFamilyConfigProvider(targets)
if err != nil {
return errors.Wrapf(err, "could not decide whether the provider family config")
}
if isFamilyConfig {
s = pg.stepConfigurationWithSubStep(stepActivateFamilyProviderRevision, true)
} else {
s = pg.stepConfigurationWithSubStep(stepActivateServiceScopedProviderRevision, true)
}
for _, t := range targets {
t.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(t.Object))
s.Patch.Files = append(s.Patch.Files, t.Metadata.Path)
if err := pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(t.Object, map[string]any{
"spec": map[string]any{
"revisionActivationPolicy": "Automatic",
},
}),
},
Metadata: t.Metadata,
}); err != nil {
return errors.Wrapf(err, errActivateSSOP, t.Metadata.Path)
}
}
return nil
}
func checkContainsFamilyConfigProvider(targets []*UnstructuredWithMetadata) (bool, error) {
for _, t := range targets {
paved := fieldpath.Pave(t.Object.Object)
pkg, err := paved.GetString("spec.package")
if err != nil {
return false, errors.Wrap(err, "could not get package of provider")
}
if strings.Contains(pkg, "provider-family") {
return true, nil
}
}
return false, nil
}

View File

@ -1,542 +0,0 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0
package migration
import (
"regexp"
"github.com/crossplane/crossplane-runtime/pkg/resource"
xpv1 "github.com/crossplane/crossplane/apis/apiextensions/v1"
xpmetav1 "github.com/crossplane/crossplane/apis/pkg/meta/v1"
xpmetav1alpha1 "github.com/crossplane/crossplane/apis/pkg/meta/v1alpha1"
xppkgv1 "github.com/crossplane/crossplane/apis/pkg/v1"
xppkgv1beta1 "github.com/crossplane/crossplane/apis/pkg/v1beta1"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
var (
// AllCompositions matches all v1.Composition names.
AllCompositions = regexp.MustCompile(`.*`)
// AllConfigurations matches all metav1.Configuration names.
AllConfigurations = regexp.MustCompile(`.*`)
// CrossplaneLockName is the Crossplane package lock's `metadata.name`
CrossplaneLockName = regexp.MustCompile(`^lock$`)
)
const (
errAddToScheme = "failed to register types with the registry's scheme"
errFmtNewObject = "failed to instantiate a new runtime.Object using runtime.Scheme for: %s"
errFmtNotManagedResource = "specified GVK does not belong to a managed resource: %s"
)
type patchSetConverter struct {
// re is the regular expression against which a Composition's name
// will be matched to determine whether the conversion function
// will be invoked.
re *regexp.Regexp
// converter is the PatchSetConverter to be run on the Composition's
// patch sets.
converter PatchSetConverter
}
type configurationMetadataConverter struct {
// re is the regular expression against which a Configuration's name
// will be matched to determine whether the conversion function
// will be invoked.
re *regexp.Regexp
// converter is the ConfigurationMetadataConverter to be run on the Configuration's
// metadata.
converter ConfigurationMetadataConverter
}
type configurationPackageConverter struct {
// re is the regular expression against which a Configuration package's
// reference will be matched to determine whether the conversion function
// will be invoked.
re *regexp.Regexp
// converter is the ConfigurationPackageConverter to be run on the
// Configuration package.
converter ConfigurationPackageConverter
}
type providerPackageConverter struct {
// re is the regular expression against which a Provider package's
// reference will be matched to determine whether the conversion function
// will be invoked.
re *regexp.Regexp
// converter is the ProviderPackageConverter to be run on the
// Provider package.
converter ProviderPackageConverter
}
type packageLockConverter struct {
// re is the regular expression against which a package Lock's name
// will be matched to determine whether the conversion function
// will be invoked.
re *regexp.Regexp
// converter is the PackageLockConverter to be run on the package Lock.
converter PackageLockConverter
}
// Registry is a registry of `migration.Converter`s keyed with
// the associated `schema.GroupVersionKind`s and an associated
// runtime.Scheme with which the corresponding types are registered.
type Registry struct {
unstructuredPreProcessors map[Category][]UnstructuredPreProcessor
resourcePreProcessors []ManagedPreProcessor
resourceConverters map[schema.GroupVersionKind]ResourceConverter
templateConverters map[schema.GroupVersionKind]ComposedTemplateConverter
patchSetConverters []patchSetConverter
configurationMetaConverters []configurationMetadataConverter
configurationPackageConverters []configurationPackageConverter
providerPackageConverters []providerPackageConverter
packageLockConverters []packageLockConverter
categoricalConverters map[Category][]CategoricalConverter
scheme *runtime.Scheme
claimTypes []schema.GroupVersionKind
compositeTypes []schema.GroupVersionKind
}
// NewRegistry returns a new Registry initialized with
// the specified runtime.Scheme.
func NewRegistry(scheme *runtime.Scheme) *Registry {
return &Registry{
resourceConverters: make(map[schema.GroupVersionKind]ResourceConverter),
templateConverters: make(map[schema.GroupVersionKind]ComposedTemplateConverter),
categoricalConverters: make(map[Category][]CategoricalConverter),
unstructuredPreProcessors: make(map[Category][]UnstructuredPreProcessor),
scheme: scheme,
}
}
// make sure a converter is being registered for a managed resource,
// and it's registered with our runtime scheme.
// This will be needed, during runtime, for properly converting resources.
func (r *Registry) assertManagedResource(gvk schema.GroupVersionKind) {
obj, err := r.scheme.New(gvk)
if err != nil {
panic(errors.Wrapf(err, errFmtNewObject, gvk))
}
if _, ok := obj.(resource.Managed); !ok {
panic(errors.Errorf(errFmtNotManagedResource, gvk))
}
}
// RegisterResourceConverter registers the specified ResourceConverter
// for the specified GVK with this Registry.
func (r *Registry) RegisterResourceConverter(gvk schema.GroupVersionKind, conv ResourceConverter) {
r.assertManagedResource(gvk)
r.resourceConverters[gvk] = conv
}
// RegisterTemplateConverter registers the specified ComposedTemplateConverter
// for the specified GVK with this Registry.
func (r *Registry) RegisterTemplateConverter(gvk schema.GroupVersionKind, conv ComposedTemplateConverter) {
r.assertManagedResource(gvk)
r.templateConverters[gvk] = conv
}
// RegisterCompositionConverter is a convenience method for registering both
// a ResourceConverter and a ComposedTemplateConverter that act on the same
// managed resource kind and are implemented by the same type.
func (r *Registry) RegisterCompositionConverter(gvk schema.GroupVersionKind, conv CompositionConverter) {
r.RegisterResourceConverter(gvk, conv)
r.RegisterTemplateConverter(gvk, conv)
}
// RegisterPatchSetConverter registers the given PatchSetConverter for
// the compositions whose name match the given regular expression.
func (r *Registry) RegisterPatchSetConverter(re *regexp.Regexp, psConv PatchSetConverter) {
r.patchSetConverters = append(r.patchSetConverters, patchSetConverter{
re: re,
converter: psConv,
})
}
// RegisterConfigurationMetadataConverter registers the given ConfigurationMetadataConverter
// for the configurations whose name match the given regular expression.
func (r *Registry) RegisterConfigurationMetadataConverter(re *regexp.Regexp, confConv ConfigurationMetadataConverter) {
r.configurationMetaConverters = append(r.configurationMetaConverters, configurationMetadataConverter{
re: re,
converter: confConv,
})
}
// RegisterConfigurationMetadataV1ConversionFunction registers the specified
// ConfigurationMetadataV1ConversionFn for the v1 configurations whose name match
// the given regular expression.
func (r *Registry) RegisterConfigurationMetadataV1ConversionFunction(re *regexp.Regexp, confConversionFn ConfigurationMetadataV1ConversionFn) {
r.RegisterConfigurationMetadataConverter(re, &delegatingConverter{
confMetaV1Fn: confConversionFn,
})
}
// RegisterConfigurationMetadataV1Alpha1ConversionFunction registers the specified
// ConfigurationMetadataV1Alpha1ConversionFn for the v1alpha1 configurations
// whose name match the given regular expression.
func (r *Registry) RegisterConfigurationMetadataV1Alpha1ConversionFunction(re *regexp.Regexp, confConversionFn ConfigurationMetadataV1Alpha1ConversionFn) {
r.RegisterConfigurationMetadataConverter(re, &delegatingConverter{
confMetaV1Alpha1Fn: confConversionFn,
})
}
// RegisterConfigurationPackageConverter registers the specified
// ConfigurationPackageConverter for the Configuration v1 packages whose reference
// match the given regular expression.
func (r *Registry) RegisterConfigurationPackageConverter(re *regexp.Regexp, pkgConv ConfigurationPackageConverter) {
r.configurationPackageConverters = append(r.configurationPackageConverters, configurationPackageConverter{
re: re,
converter: pkgConv,
})
}
// RegisterConfigurationPackageV1ConversionFunction registers the specified
// ConfigurationPackageV1ConversionFn for the Configuration v1 packages whose reference
// match the given regular expression.
func (r *Registry) RegisterConfigurationPackageV1ConversionFunction(re *regexp.Regexp, confConversionFn ConfigurationPackageV1ConversionFn) {
r.RegisterConfigurationPackageConverter(re, &delegatingConverter{
confPackageV1Fn: confConversionFn,
})
}
// RegisterProviderPackageConverter registers the given ProviderPackageConverter
// for the provider packages whose references match the given regular expression.
func (r *Registry) RegisterProviderPackageConverter(re *regexp.Regexp, pkgConv ProviderPackageConverter) {
r.providerPackageConverters = append(r.providerPackageConverters, providerPackageConverter{
re: re,
converter: pkgConv,
})
}
// RegisterProviderPackageV1ConversionFunction registers the specified
// ProviderPackageV1ConversionFn for the provider v1 packages whose reference
// match the given regular expression.
func (r *Registry) RegisterProviderPackageV1ConversionFunction(re *regexp.Regexp, pkgConversionFn ProviderPackageV1ConversionFn) {
r.RegisterProviderPackageConverter(re, &delegatingConverter{
providerPackageV1Fn: pkgConversionFn,
})
}
// RegisterPackageLockConverter registers the given PackageLockConverter.
func (r *Registry) RegisterPackageLockConverter(re *regexp.Regexp, lockConv PackageLockConverter) {
r.packageLockConverters = append(r.packageLockConverters, packageLockConverter{
re: re,
converter: lockConv,
})
}
// RegisterCategoricalConverter registers the specified CategoricalConverter
// for the specified Category of resources.
func (r *Registry) RegisterCategoricalConverter(c Category, converter CategoricalConverter) {
r.categoricalConverters[c] = append(r.categoricalConverters[c], converter)
}
// RegisterCategoricalConverterFunction registers the specified
// CategoricalConverterFunctionFn for the specified Category.
func (r *Registry) RegisterCategoricalConverterFunction(c Category, converterFn CategoricalConverterFunctionFn) {
r.RegisterCategoricalConverter(c, &delegatingConverter{
categoricalConverterFn: converterFn,
})
}
// RegisterPackageLockV1Beta1ConversionFunction registers the specified
// RegisterPackageLockV1Beta1ConversionFunction for the package v1beta1 locks.
func (r *Registry) RegisterPackageLockV1Beta1ConversionFunction(re *regexp.Regexp, lockConversionFn PackageLockV1Beta1ConversionFn) {
r.RegisterPackageLockConverter(re, &delegatingConverter{
packageLockV1Beta1Fn: lockConversionFn,
})
}
// AddToScheme registers types with this Registry's runtime.Scheme
func (r *Registry) AddToScheme(sb func(scheme *runtime.Scheme) error) error {
return errors.Wrap(sb(r.scheme), errAddToScheme)
}
// AddCompositionTypes registers the Composition types with
// the registry's scheme. Only the v1 API of Compositions
// is currently supported.
func (r *Registry) AddCompositionTypes() error {
return r.AddToScheme(xpv1.AddToScheme)
}
// AddCrossplanePackageTypes registers the
// {Provider,Configuration,Lock, etc.}.pkg types with
// the registry's scheme.
func (r *Registry) AddCrossplanePackageTypes() error {
if err := r.AddToScheme(xppkgv1beta1.AddToScheme); err != nil {
return err
}
return r.AddToScheme(xppkgv1.AddToScheme)
}
// AddClaimType registers a new composite resource claim type
// with the given GVK
func (r *Registry) AddClaimType(gvk schema.GroupVersionKind) {
r.claimTypes = append(r.claimTypes, gvk)
}
// AddCompositeType registers a new composite resource type with the given GVK
func (r *Registry) AddCompositeType(gvk schema.GroupVersionKind) {
r.compositeTypes = append(r.compositeTypes, gvk)
}
// GetManagedResourceGVKs returns a list of all registered managed resource
// GVKs
func (r *Registry) GetManagedResourceGVKs() []schema.GroupVersionKind {
gvks := make([]schema.GroupVersionKind, 0, len(r.resourceConverters)+len(r.templateConverters))
for gvk := range r.resourceConverters {
gvks = append(gvks, gvk)
}
for gvk := range r.templateConverters {
gvks = append(gvks, gvk)
}
return gvks
}
// GetCompositionGVKs returns the registered Composition GVKs.
func (r *Registry) GetCompositionGVKs() []schema.GroupVersionKind {
// Composition types are registered with this registry's scheme
if _, ok := r.scheme.AllKnownTypes()[xpv1.CompositionGroupVersionKind]; ok {
return []schema.GroupVersionKind{xpv1.CompositionGroupVersionKind}
}
return nil
}
// GetCrossplanePackageGVKs returns the registered Crossplane package GVKs.
func (r *Registry) GetCrossplanePackageGVKs() []schema.GroupVersionKind {
if r.scheme.AllKnownTypes()[xppkgv1.ProviderGroupVersionKind] == nil ||
r.scheme.AllKnownTypes()[xppkgv1.ConfigurationGroupVersionKind] == nil ||
r.scheme.AllKnownTypes()[xppkgv1beta1.LockGroupVersionKind] == nil {
return nil
}
return []schema.GroupVersionKind{
xppkgv1.ProviderGroupVersionKind,
xppkgv1.ConfigurationGroupVersionKind,
xppkgv1beta1.LockGroupVersionKind,
}
}
// GetAllRegisteredGVKs returns a list of registered GVKs
// including v1.CompositionGroupVersionKind,
// metav1.ConfigurationGroupVersionKind,
// metav1alpha1.ConfigurationGroupVersionKind
// pkg.ConfigurationGroupVersionKind,
// pkg.ProviderGroupVersionKind,
// pkg.LockGroupVersionKind.
func (r *Registry) GetAllRegisteredGVKs() []schema.GroupVersionKind {
gvks := make([]schema.GroupVersionKind, 0, len(r.claimTypes)+len(r.compositeTypes)+len(r.resourceConverters)+len(r.templateConverters)+1)
gvks = append(gvks, r.claimTypes...)
gvks = append(gvks, r.compositeTypes...)
gvks = append(gvks, r.GetManagedResourceGVKs()...)
gvks = append(gvks, xpv1.CompositionGroupVersionKind, xpmetav1.ConfigurationGroupVersionKind, xpmetav1alpha1.ConfigurationGroupVersionKind,
xppkgv1.ConfigurationGroupVersionKind, xppkgv1.ProviderGroupVersionKind, xppkgv1beta1.LockGroupVersionKind)
return gvks
}
// ResourceConversionFn is a function that converts the specified migration
// source managed resource to one or more migration target managed resources.
type ResourceConversionFn func(mg resource.Managed) ([]resource.Managed, error)
// ComposedTemplateConversionFn is a function that converts from the specified
// migration source v1.ComposedTemplate to one or more migration
// target v1.ComposedTemplates.
type ComposedTemplateConversionFn func(sourceTemplate xpv1.ComposedTemplate, convertedTemplates ...*xpv1.ComposedTemplate) error
// PatchSetsConversionFn is a function that converts
// the `spec.patchSets` of a Composition from the migration source provider's
// schema to the migration target provider's schema.
type PatchSetsConversionFn func(psMap map[string]*xpv1.PatchSet) error
// ConfigurationMetadataV1ConversionFn is a function that converts the specified
// migration source Configuration v1 metadata to the migration target
// Configuration metadata.
type ConfigurationMetadataV1ConversionFn func(configuration *xpmetav1.Configuration) error
// ConfigurationMetadataV1Alpha1ConversionFn is a function that converts the specified
// migration source Configuration v1alpha1 metadata to the migration target
// Configuration metadata.
type ConfigurationMetadataV1Alpha1ConversionFn func(configuration *xpmetav1alpha1.Configuration) error
// PackageLockV1Beta1ConversionFn is a function that converts the specified
// migration source package v1beta1 lock to the migration target
// package lock.
type PackageLockV1Beta1ConversionFn func(pkg *xppkgv1beta1.Lock) error
// ConfigurationPackageV1ConversionFn is a function that converts the specified
// migration source Configuration v1 package to the migration target
// Configuration package(s).
type ConfigurationPackageV1ConversionFn func(pkg *xppkgv1.Configuration) error
// ProviderPackageV1ConversionFn is a function that converts the specified
// migration source provider v1 package to the migration target
// Provider package(s).
type ProviderPackageV1ConversionFn func(pkg xppkgv1.Provider) ([]xppkgv1.Provider, error)
// CategoricalConverterFunctionFn is a function that converts resources of a
// Category. Because it receives an unstructured argument, it should be
// used for implementing generic conversion functions acting on a specific
// category.
type CategoricalConverterFunctionFn func(u *UnstructuredWithMetadata) error
type delegatingConverter struct {
rFn ResourceConversionFn
cmpFn ComposedTemplateConversionFn
psFn PatchSetsConversionFn
confMetaV1Fn ConfigurationMetadataV1ConversionFn
confMetaV1Alpha1Fn ConfigurationMetadataV1Alpha1ConversionFn
confPackageV1Fn ConfigurationPackageV1ConversionFn
providerPackageV1Fn ProviderPackageV1ConversionFn
packageLockV1Beta1Fn PackageLockV1Beta1ConversionFn
categoricalConverterFn CategoricalConverterFunctionFn
}
func (d *delegatingConverter) Convert(u *UnstructuredWithMetadata) error {
if d.categoricalConverterFn == nil {
return nil
}
return d.categoricalConverterFn(u)
}
func (d *delegatingConverter) ConfigurationPackageV1(pkg *xppkgv1.Configuration) error {
if d.confPackageV1Fn == nil {
return nil
}
return d.confPackageV1Fn(pkg)
}
func (d *delegatingConverter) PackageLockV1Beta1(lock *xppkgv1beta1.Lock) error {
if d.packageLockV1Beta1Fn == nil {
return nil
}
return d.packageLockV1Beta1Fn(lock)
}
func (d *delegatingConverter) ProviderPackageV1(pkg xppkgv1.Provider) ([]xppkgv1.Provider, error) {
if d.providerPackageV1Fn == nil {
return []xppkgv1.Provider{pkg}, nil
}
return d.providerPackageV1Fn(pkg)
}
func (d *delegatingConverter) ConfigurationMetadataV1(c *xpmetav1.Configuration) error {
if d.confMetaV1Fn == nil {
return nil
}
return d.confMetaV1Fn(c)
}
func (d *delegatingConverter) ConfigurationMetadataV1Alpha1(c *xpmetav1alpha1.Configuration) error {
if d.confMetaV1Alpha1Fn == nil {
return nil
}
return d.confMetaV1Alpha1Fn(c)
}
func (d *delegatingConverter) PatchSets(psMap map[string]*xpv1.PatchSet) error {
if d.psFn == nil {
return nil
}
return d.psFn(psMap)
}
// Resource takes a managed resource and returns zero or more managed
// resources to be created by calling the configured ResourceConversionFn.
func (d *delegatingConverter) Resource(mg resource.Managed) ([]resource.Managed, error) {
if d.rFn == nil {
return []resource.Managed{mg}, nil
}
return d.rFn(mg)
}
// ComposedTemplate converts from the specified migration source
// v1.ComposedTemplate to the migration target schema by calling the configured
// ComposedTemplateConversionFn.
func (d *delegatingConverter) ComposedTemplate(sourceTemplate xpv1.ComposedTemplate, convertedTemplates ...*xpv1.ComposedTemplate) error {
if d.cmpFn == nil {
return nil
}
return d.cmpFn(sourceTemplate, convertedTemplates...)
}
// DefaultCompositionConverter is a generic composition converter
// conversionMap: is fieldpath map for conversion
// Key of the conversionMap points to the source field
// Value of the conversionMap points to the target field
// Example: "spec.forProvider.assumeRolePolicyDocument": "spec.forProvider.assumeRolePolicy",
// fns are functions that manipulate the patchsets
func DefaultCompositionConverter(conversionMap map[string]string, fns ...func(sourceTemplate xpv1.ComposedTemplate) ([]xpv1.Patch, error)) ComposedTemplateConversionFn {
return func(sourceTemplate xpv1.ComposedTemplate, convertedTemplates ...*xpv1.ComposedTemplate) error {
var patchesToAdd []xpv1.Patch
for _, fn := range fns {
patches, err := fn(sourceTemplate)
if err != nil {
return errors.Wrap(err, "cannot run the patch sets converter function")
}
patchesToAdd = append(patchesToAdd, patches...)
}
patchesToAdd = append(patchesToAdd, ConvertComposedTemplatePatchesMap(sourceTemplate, conversionMap)...)
for i := range convertedTemplates {
convertedTemplates[i].Patches = append(convertedTemplates[i].Patches, patchesToAdd...)
}
return nil
}
}
// RegisterAPIConversionFunctions registers the supplied ResourceConversionFn and
// ComposedTemplateConversionFn for the specified GVK, and the supplied
// PatchSetsConversionFn for all the discovered Compositions.
// The specified GVK must belong to a Crossplane managed resource type and
// the type must already have been registered with this registry's scheme
// by calling Registry.AddToScheme.
func (r *Registry) RegisterAPIConversionFunctions(gvk schema.GroupVersionKind, rFn ResourceConversionFn, cmpFn ComposedTemplateConversionFn, psFn PatchSetsConversionFn) {
d := &delegatingConverter{
rFn: rFn,
cmpFn: cmpFn,
psFn: psFn,
}
r.RegisterPatchSetConverter(AllCompositions, d)
r.RegisterCompositionConverter(gvk, d)
}
// RegisterConversionFunctions registers the supplied ResourceConversionFn and
// ComposedTemplateConversionFn for the specified GVK, and the supplied
// PatchSetsConversionFn for all the discovered Compositions.
// The specified GVK must belong to a Crossplane managed resource type and
// the type must already have been registered with this registry's scheme
// by calling Registry.AddToScheme.
// Deprecated: Use RegisterAPIConversionFunctions instead.
func (r *Registry) RegisterConversionFunctions(gvk schema.GroupVersionKind, rFn ResourceConversionFn, cmpFn ComposedTemplateConversionFn, psFn PatchSetsConversionFn) {
r.RegisterAPIConversionFunctions(gvk, rFn, cmpFn, psFn)
}
func (r *Registry) RegisterPreProcessor(category Category, pp UnstructuredPreProcessor) {
r.unstructuredPreProcessors[category] = append(r.unstructuredPreProcessors[category], pp)
}
// PreProcessor is a function type to convert pre-processor functions to
// UnstructuredPreProcessor.
type PreProcessor func(u UnstructuredWithMetadata) error
func (pp PreProcessor) PreProcess(u UnstructuredWithMetadata) error {
if pp == nil {
return nil
}
return pp(u)
}
func (r *Registry) RegisterResourcePreProcessor(pp ManagedPreProcessor) {
r.resourcePreProcessors = append(r.resourcePreProcessors, pp)
}
type ResourcePreProcessor func(mg resource.Managed) error
func (pp ResourcePreProcessor) ResourcePreProcessor(mg resource.Managed) error {
if pp == nil {
return nil
}
return pp(mg)
}

View File

@ -1,15 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: test.com/v1alpha1
kind: MyResource
metadata:
name: my-resource
namespace: upbound-system
spec:
compositionRef:
name: example
parameters:
tagValue: demo-test
region: us-west-1

View File

@ -1,78 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
labels:
purpose: example
name: example
spec:
compositeTypeRef:
apiVersion: test.com/v1alpha1
kind: XMyResource
patchSets:
- name: not-referenced
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: spec.forProvider.myTag
- name: ps1
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: spec.forProvider.tags[2].value
- name: ps2
patches:
- fromFieldPath: "spec.parameters.region"
toFieldPath: spec.forProvider.region
- name: ps3
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: metadata.labels[a.b.c.d/tag-value]
- name: ps4
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: metadata.labels['a.b.c.d.e/tag-value']
- name: ps5
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: metadata.labels["a.b.c.d.e.f/tag-value"]
- name: ps6
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: spec.forProvider.tags[3].value
resources:
- base:
apiVersion: fakesourceapi/v1alpha1
kind: VPC
spec:
forProvider:
cidrBlock: "192.168.0.0/16"
region: "us-west-1"
tags:
- key: key1
value: val1
- key: key2
value: val2
- key: key3
value: val3
name: vpc
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: spec.forProvider.tags[0].value
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: spec.forProvider.tags[1].value
- type: PatchSet
patchSetName: ps1
- type: PatchSet
patchSetName: ps2
- type: PatchSet
patchSetName: ps3
- type: PatchSet
patchSetName: ps4
- type: PatchSet
patchSetName: ps5
- type: PatchSet
patchSetName: ps6
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: spec.forProvider.param

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1
kind: Configuration
metadata:
name: platform-ref-aws
spec:
package: xpkg.upbound.io/upbound/provider-ref-aws:v0.1.0

View File

@ -1,40 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: meta.pkg.crossplane.io/v1
kind: Configuration
metadata:
name: platform-ref-aws
annotations:
meta.crossplane.io/maintainer: Upbound <support@upbound.io>
meta.crossplane.io/source: github.com/upbound/platform-ref-aws
meta.crossplane.io/license: Apache-2.0
meta.crossplane.io/description: |
This reference platform Configuration for Kubernetes and Data Services
is a starting point to build, run, and operate your own internal cloud
platform and offer a self-service console and API to your internal teams.
meta.crossplane.io/readme: |
This reference platform `Configuration` for Kubernetes and Data Services
is a starting point to build, run, and operate your own internal cloud
platform and offer a self-service console and API to your internal teams.
It provides platform APIs to provision fully configured EKS clusters,
with secure networking, and stateful cloud services (RDS) designed to
securely connect to the nodes in each EKS cluster -- all composed using
cloud service primitives from the [Upbound Official AWS
Provider](https://marketplace.upbound.io/providers/upbound/provider-aws). App
deployments can securely connect to the infrastructure they need using
secrets distributed directly to the app namespace.
To learn more checkout the [GitHub
repo](https://github.com/upbound/platform-ref-aws/) that you can copy and
customize to meet the exact needs of your organization!
spec:
crossplane:
version: ">=v1.7.0-0"
dependsOn:
- provider: xpkg.upbound.io/upbound/provider-aws
version: ">=v0.15.0"
- provider: xpkg.upbound.io/crossplane-contrib/provider-helm
version: ">=v0.12.0"

View File

@ -1,40 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: meta.pkg.crossplane.io/v1alpha1
kind: Configuration
metadata:
name: platform-ref-aws
annotations:
meta.crossplane.io/maintainer: Upbound <support@upbound.io>
meta.crossplane.io/source: github.com/upbound/platform-ref-aws
meta.crossplane.io/license: Apache-2.0
meta.crossplane.io/description: |
This reference platform Configuration for Kubernetes and Data Services
is a starting point to build, run, and operate your own internal cloud
platform and offer a self-service console and API to your internal teams.
meta.crossplane.io/readme: |
This reference platform `Configuration` for Kubernetes and Data Services
is a starting point to build, run, and operate your own internal cloud
platform and offer a self-service console and API to your internal teams.
It provides platform APIs to provision fully configured EKS clusters,
with secure networking, and stateful cloud services (RDS) designed to
securely connect to the nodes in each EKS cluster -- all composed using
cloud service primitives from the [Upbound Official AWS
Provider](https://marketplace.upbound.io/providers/upbound/provider-aws). App
deployments can securely connect to the infrastructure they need using
secrets distributed directly to the app namespace.
To learn more checkout the [GitHub
repo](https://github.com/upbound/platform-ref-aws/) that you can copy and
customize to meet the exact needs of your organization!
spec:
crossplane:
version: ">=v1.7.0-0"
dependsOn:
- provider: xpkg.upbound.io/upbound/provider-aws
version: ">=v0.15.0"
- provider: xpkg.upbound.io/crossplane-contrib/provider-helm
version: ">=v0.12.0"

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-ec2
spec:
revisionActivationPolicy: Automatic

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-eks
spec:
revisionActivationPolicy: Automatic

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-family-aws
spec:
revisionActivationPolicy: Automatic

View File

@ -1,67 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
spec:
steps:
- exec:
command: sh
args:
- "-c"
- "kubectl get managed -o yaml > backup/managed-resources.yaml"
name: backup-managed-resources
manualExecution:
- sh -c "kubectl get managed -o yaml > backup/managed-resources.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "kubectl get composite -o yaml > backup/composite-resources.yaml"
name: backup-composite-resources
manualExecution:
- sh -c "kubectl get composite -o yaml > backup/composite-resources.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "kubectl get claim --all-namespaces -o yaml > backup/claim-resources.yaml"
name: backup-claim-resources
manualExecution:
- sh -c "kubectl get claim --all-namespaces -o yaml > backup/claim-resources.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "cp edit-configuration-metadata/platform-ref-aws.configurations.meta.pkg.crossplane.io_v1.yaml testdata/plan/configurationv1.yaml"
name: edit-configuration-metadata
manualExecution:
- sh -c "cp edit-configuration-metadata/platform-ref-aws.configurations.meta.pkg.crossplane.io_v1.yaml testdata/plan/configurationv1.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "up xpkg build --package-root={{PKG_ROOT}} --examples-root={{EXAMPLES_ROOT}} -o {{PKG_PATH}}"
name: build-configuration
manualExecution:
- sh -c "up xpkg build --package-root={{PKG_ROOT}} --examples-root={{EXAMPLES_ROOT}} -o {{PKG_PATH}}"
type: Exec
- exec:
command: sh
args:
- "-c"
- "up xpkg push {{TARGET_CONFIGURATION_PACKAGE}} -f {{PKG_PATH}}"
name: push-configuration
manualExecution:
- sh -c "up xpkg push {{TARGET_CONFIGURATION_PACKAGE}} -f {{PKG_PATH}}"
type: Exec
version: 0.1.0

View File

@ -1,232 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
spec:
steps:
- exec:
command: sh
args:
- "-c"
- "kubectl get managed -o yaml > backup/managed-resources.yaml"
name: backup-managed-resources
manualExecution:
- sh -c "kubectl get managed -o yaml > backup/managed-resources.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "kubectl get composite -o yaml > backup/composite-resources.yaml"
name: backup-composite-resources
manualExecution:
- sh -c "kubectl get composite -o yaml > backup/composite-resources.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "kubectl get claim --all-namespaces -o yaml > backup/claim-resources.yaml"
name: backup-claim-resources
manualExecution:
- sh -c "kubectl get claim --all-namespaces -o yaml > backup/claim-resources.yaml"
type: Exec
- patch:
type: merge
files:
- deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi_v1alpha1.yaml
name: deletion-policy-orphan
manualExecution:
- "kubectl patch --type='merge' -f deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi_v1alpha1.yaml --patch-file deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi_v1alpha1.yaml"
type: Patch
- apply:
files:
- new-ssop/provider-family-aws.providers.pkg.crossplane.io_v1.yaml
name: new-ssop
manualExecution:
- "kubectl apply -f new-ssop/provider-family-aws.providers.pkg.crossplane.io_v1.yaml"
type: Apply
- exec:
command: sh
args:
- "-c"
- "kubectl wait provider.pkg provider-family-aws --for condition=Healthy"
name: wait-for-healthy
manualExecution:
- sh -c "kubectl wait provider.pkg provider-family-aws --for condition=Healthy"
type: Exec
- apply:
files:
- new-ssop/provider-aws-ec2.providers.pkg.crossplane.io_v1.yaml
- new-ssop/provider-aws-eks.providers.pkg.crossplane.io_v1.yaml
name: new-ssop
manualExecution:
- "kubectl apply -f new-ssop/provider-aws-ec2.providers.pkg.crossplane.io_v1.yaml"
- "kubectl apply -f new-ssop/provider-aws-eks.providers.pkg.crossplane.io_v1.yaml"
type: Apply
- exec:
command: sh
args:
- "-c"
- "kubectl wait provider.pkg provider-aws-ec2 --for condition=Healthy"
name: wait-for-healthy
manualExecution:
- sh -c "kubectl wait provider.pkg provider-aws-ec2 --for condition=Healthy"
type: Exec
- exec:
command: sh
args:
- "-c"
- "kubectl wait provider.pkg provider-aws-eks --for condition=Healthy"
name: wait-for-healthy
manualExecution:
- sh -c "kubectl wait provider.pkg provider-aws-eks --for condition=Healthy"
type: Exec
- patch:
type: merge
files:
- disable-dependency-resolution/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml
name: disable-dependency-resolution
manualExecution:
- "kubectl patch --type='merge' -f disable-dependency-resolution/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml --patch-file disable-dependency-resolution/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml"
type: Patch
- patch:
type: merge
files:
- edit-package-lock/lock.locks.pkg.crossplane.io_v1beta1.yaml
name: edit-package-lock
manualExecution:
- "kubectl patch --type='merge' -f edit-package-lock/lock.locks.pkg.crossplane.io_v1beta1.yaml --patch-file edit-package-lock/lock.locks.pkg.crossplane.io_v1beta1.yaml"
type: Patch
- delete:
options:
finalizerPolicy: Remove
resources:
- group: pkg.crossplane.io
kind: Provider
name: provider-aws
version: v1
name: delete-monolithic-provider
manualExecution:
- "kubectl delete Provider.pkg.crossplane.io provider-aws"
type: Delete
- patch:
type: merge
files:
- activate-ssop/provider-family-aws.providers.pkg.crossplane.io_v1.yaml
name: activate-ssop
manualExecution:
- "kubectl patch --type='merge' -f activate-ssop/provider-family-aws.providers.pkg.crossplane.io_v1.yaml --patch-file activate-ssop/provider-family-aws.providers.pkg.crossplane.io_v1.yaml"
type: Patch
- exec:
command: sh
args:
- "-c"
- "kubectl wait provider.pkg provider-family-aws --for condition=Installed"
name: wait-for-installed
manualExecution:
- sh -c "kubectl wait provider.pkg provider-family-aws --for condition=Installed"
type: Exec
- patch:
type: merge
files:
- activate-ssop/provider-aws-ec2.providers.pkg.crossplane.io_v1.yaml
- activate-ssop/provider-aws-eks.providers.pkg.crossplane.io_v1.yaml
name: activate-ssop
manualExecution:
- "kubectl patch --type='merge' -f activate-ssop/provider-aws-ec2.providers.pkg.crossplane.io_v1.yaml --patch-file activate-ssop/provider-aws-ec2.providers.pkg.crossplane.io_v1.yaml"
- "kubectl patch --type='merge' -f activate-ssop/provider-aws-eks.providers.pkg.crossplane.io_v1.yaml --patch-file activate-ssop/provider-aws-eks.providers.pkg.crossplane.io_v1.yaml"
type: Patch
- exec:
command: sh
args:
- "-c"
- "kubectl wait provider.pkg provider-aws-ec2 --for condition=Installed"
name: wait-for-installed
manualExecution:
- sh -c "kubectl wait provider.pkg provider-aws-ec2 --for condition=Installed"
type: Exec
- exec:
command: sh
args:
- "-c"
- "kubectl wait provider.pkg provider-aws-eks --for condition=Installed"
name: wait-for-installed
manualExecution:
- sh -c "kubectl wait provider.pkg provider-aws-eks --for condition=Installed"
type: Exec
- exec:
command: sh
args:
- "-c"
- "cp edit-configuration-metadata/platform-ref-aws.configurations.meta.pkg.crossplane.io_v1.yaml testdata/plan/configurationv1.yaml"
name: edit-configuration-metadata
manualExecution:
- sh -c "cp edit-configuration-metadata/platform-ref-aws.configurations.meta.pkg.crossplane.io_v1.yaml testdata/plan/configurationv1.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "up xpkg build --package-root={{PKG_ROOT}} --examples-root={{EXAMPLES_ROOT}} -o {{PKG_PATH}}"
name: build-configuration
manualExecution:
- sh -c "up xpkg build --package-root={{PKG_ROOT}} --examples-root={{EXAMPLES_ROOT}} -o {{PKG_PATH}}"
type: Exec
- exec:
command: sh
args:
- "-c"
- "up xpkg push {{TARGET_CONFIGURATION_PACKAGE}} -f {{PKG_PATH}}"
name: push-configuration
manualExecution:
- sh -c "up xpkg push {{TARGET_CONFIGURATION_PACKAGE}} -f {{PKG_PATH}}"
type: Exec
- patch:
type: merge
files:
- edit-configuration-package/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml
name: edit-configuration-package
manualExecution:
- "kubectl patch --type='merge' -f edit-configuration-package/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml --patch-file edit-configuration-package/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml"
type: Patch
- patch:
type: merge
files:
- enable-dependency-resolution/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml
name: enable-dependency-resolution
manualExecution:
- "kubectl patch --type='merge' -f enable-dependency-resolution/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml --patch-file enable-dependency-resolution/platform-ref-aws.configurations.pkg.crossplane.io_v1.yaml"
type: Patch
- patch:
type: merge
files:
- deletion-policy-delete/sample-vpc.vpcs.fakesourceapi_v1alpha1.yaml
name: deletion-policy-delete
manualExecution:
- "kubectl patch --type='merge' -f deletion-policy-delete/sample-vpc.vpcs.fakesourceapi_v1alpha1.yaml --patch-file deletion-policy-delete/sample-vpc.vpcs.fakesourceapi_v1alpha1.yaml"
type: Patch
version: 0.1.0

View File

@ -1,67 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
spec:
steps:
- exec:
command: sh
args:
- "-c"
- "kubectl get managed -o yaml > backup/managed-resources.yaml"
name: backup-managed-resources
manualExecution:
- sh -c "kubectl get managed -o yaml > backup/managed-resources.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "kubectl get composite -o yaml > backup/composite-resources.yaml"
name: backup-composite-resources
manualExecution:
- sh -c "kubectl get composite -o yaml > backup/composite-resources.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "kubectl get claim --all-namespaces -o yaml > backup/claim-resources.yaml"
name: backup-claim-resources
manualExecution:
- sh -c "kubectl get claim --all-namespaces -o yaml > backup/claim-resources.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "cp edit-configuration-metadata/platform-ref-aws.configurations.meta.pkg.crossplane.io_v1alpha1.yaml testdata/plan/configurationv1alpha1.yaml"
name: edit-configuration-metadata
manualExecution:
- sh -c "cp edit-configuration-metadata/platform-ref-aws.configurations.meta.pkg.crossplane.io_v1alpha1.yaml testdata/plan/configurationv1alpha1.yaml"
type: Exec
- exec:
command: sh
args:
- "-c"
- "up xpkg build --package-root={{PKG_ROOT}} --examples-root={{EXAMPLES_ROOT}} -o {{PKG_PATH}}"
name: build-configuration
manualExecution:
- sh -c "up xpkg build --package-root={{PKG_ROOT}} --examples-root={{EXAMPLES_ROOT}} -o {{PKG_PATH}}"
type: Exec
- exec:
command: sh
args:
- "-c"
- "up xpkg push {{TARGET_CONFIGURATION_PACKAGE}} -f {{PKG_PATH}}"
name: push-configuration
manualExecution:
- sh -c "up xpkg push {{TARGET_CONFIGURATION_PACKAGE}} -f {{PKG_PATH}}"
type: Exec
version: 0.1.0

View File

@ -1,19 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: faketargetapi/v1alpha1
kind: VPC
metadata:
annotations:
crossplane.io/paused: "true"
name: sample-vpc
mockManaged:
ctrl: null
recorder: null
spec:
forProvider:
cidrBlock: 172.16.0.0/16
region: us-west-1
tags:
tag1: value1

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: fakesourceapi/v1alpha1
kind: VPC
metadata:
name: sample-vpc
spec:
deletionPolicy: Delete

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: fakesourceapi/v1alpha1
kind: VPC
metadata:
name: sample-vpc
spec:
deletionPolicy: Orphan

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: fakesourceapi/v1alpha1
kind: VPC
metadata:
name: sample-vpc
spec:
deletionPolicy: Orphan

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1
kind: Configuration
metadata:
name: platform-ref-aws
spec:
skipDependencyResolution: true

View File

@ -1,12 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: test.com/v1alpha1
kind: MyResource
metadata:
name: my-resource
namespace: upbound-system
spec:
compositionRef:
name: example-migrated

View File

@ -1,15 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: test.com/v1alpha1
kind: XMyResource
metadata:
name: my-resource-dwjgh
spec:
compositionRef:
name: example-migrated
resourceRefs:
- apiVersion: faketargetapi/v1alpha1
kind: VPC
name: sample-vpc

View File

@ -1,38 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: meta.pkg.crossplane.io/v1
kind: Configuration
metadata:
name: platform-ref-aws
annotations:
meta.crossplane.io/maintainer: Upbound <support@upbound.io>
meta.crossplane.io/source: github.com/upbound/platform-ref-aws
meta.crossplane.io/license: Apache-2.0
meta.crossplane.io/description: |
This reference platform Configuration for Kubernetes and Data Services
is a starting point to build, run, and operate your own internal cloud
platform and offer a self-service console and API to your internal teams.
meta.crossplane.io/readme: |
This reference platform `Configuration` for Kubernetes and Data Services
is a starting point to build, run, and operate your own internal cloud
platform and offer a self-service console and API to your internal teams.
It provides platform APIs to provision fully configured EKS clusters,
with secure networking, and stateful cloud services (RDS) designed to
securely connect to the nodes in each EKS cluster -- all composed using
cloud service primitives from the [Upbound Official AWS
Provider](https://marketplace.upbound.io/providers/upbound/provider-aws). App
deployments can securely connect to the infrastructure they need using
secrets distributed directly to the app namespace.
To learn more checkout the [GitHub
repo](https://github.com/upbound/platform-ref-aws/) that you can copy and
customize to meet the exact needs of your organization!
spec:
crossplane:
version: ">=v1.7.0-0"
dependsOn:
- provider: xpkg.upbound.io/upbound/provider-aws-eks
version: ">=v0.17.0"

View File

@ -1,38 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: meta.pkg.crossplane.io/v1alpha1
kind: Configuration
metadata:
name: platform-ref-aws
annotations:
meta.crossplane.io/maintainer: Upbound <support@upbound.io>
meta.crossplane.io/source: github.com/upbound/platform-ref-aws
meta.crossplane.io/license: Apache-2.0
meta.crossplane.io/description: |
This reference platform Configuration for Kubernetes and Data Services
is a starting point to build, run, and operate your own internal cloud
platform and offer a self-service console and API to your internal teams.
meta.crossplane.io/readme: |
This reference platform `Configuration` for Kubernetes and Data Services
is a starting point to build, run, and operate your own internal cloud
platform and offer a self-service console and API to your internal teams.
It provides platform APIs to provision fully configured EKS clusters,
with secure networking, and stateful cloud services (RDS) designed to
securely connect to the nodes in each EKS cluster -- all composed using
cloud service primitives from the [Upbound Official AWS
Provider](https://marketplace.upbound.io/providers/upbound/provider-aws). App
deployments can securely connect to the infrastructure they need using
secrets distributed directly to the app namespace.
To learn more checkout the [GitHub
repo](https://github.com/upbound/platform-ref-aws/) that you can copy and
customize to meet the exact needs of your organization!
spec:
crossplane:
version: ">=v1.7.0-0"
dependsOn:
- provider: xpkg.upbound.io/upbound/provider-aws-eks
version: ">=v0.17.0"

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1
kind: Configuration
metadata:
name: platform-ref-aws
spec:
package: xpkg.upbound.io/upbound/provider-ref-aws:v0.2.0-ssop

View File

@ -1,17 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1beta1
kind: Lock
metadata:
name: lock
packages:
- name: provider-aws
type: Provider
source: xpkg.upbound.io/upbound/provider-aws
version: v0.36.0
- name: test-provider
type: Provider
source: xpkg.upbound.io/upbound/test-provider
version: vX.Y.Z

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1
kind: Configuration
metadata:
name: platform-ref-aws
spec:
skipDependencyResolution: false

View File

@ -1,108 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
spec:
steps:
- patch:
type: merge
files:
- pause-managed/sample-vpc.vpcs.fakesourceapi.yaml
name: pause-managed
manualExecution:
- "kubectl patch --type='merge' -f pause-managed/sample-vpc.vpcs.fakesourceapi.yaml --patch-file pause-managed/sample-vpc.vpcs.fakesourceapi.yaml"
type: Patch
- patch:
type: merge
files:
- pause-composites/my-resource-dwjgh.xmyresources.test.com.yaml
name: pause-composites
manualExecution:
- "kubectl patch --type='merge' -f pause-composites/my-resource-dwjgh.xmyresources.test.com.yaml --patch-file pause-composites/my-resource-dwjgh.xmyresources.test.com.yaml"
type: Patch
- apply:
files:
- create-new-managed/sample-vpc.vpcs.faketargetapi.yaml
name: create-new-managed
manualExecution:
- "kubectl apply -f create-new-managed/sample-vpc.vpcs.faketargetapi.yaml"
type: Apply
- apply:
files:
- new-compositions/example-migrated.compositions.apiextensions.crossplane.io.yaml
name: new-compositions
manualExecution:
- "kubectl apply -f new-compositions/example-migrated.compositions.apiextensions.crossplane.io.yaml"
type: Apply
- patch:
type: merge
files:
- edit-composites/my-resource-dwjgh.xmyresources.test.com.yaml
name: edit-composites
manualExecution:
- "kubectl patch --type='merge' -f edit-composites/my-resource-dwjgh.xmyresources.test.com.yaml --patch-file edit-composites/my-resource-dwjgh.xmyresources.test.com.yaml"
type: Patch
- patch:
type: merge
files:
- edit-claims/my-resource.myresources.test.com.yaml
name: edit-claims
manualExecution:
- "kubectl patch --type='merge' -f edit-claims/my-resource.myresources.test.com.yaml --patch-file edit-claims/my-resource.myresources.test.com.yaml"
type: Patch
- patch:
type: merge
files:
- deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi.yaml
name: deletion-policy-orphan
manualExecution:
- "kubectl patch --type='merge' -f deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi.yaml --patch-file deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi.yaml"
type: Patch
- patch:
type: merge
files:
- remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml
name: remove-finalizers
manualExecution:
- "kubectl patch --type='merge' -f remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml --patch-file remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml"
type: Patch
- delete:
options:
finalizerPolicy: Remove
resources:
- group: fakesourceapi
kind: VPC
name: sample-vpc
version: v1alpha1
name: delete-old-managed
manualExecution:
- "kubectl delete VPC.fakesourceapi sample-vpc"
type: Delete
- patch:
type: merge
files:
- start-managed/sample-vpc.vpcs.faketargetapi.yaml
name: start-managed
manualExecution:
- "kubectl patch --type='merge' -f start-managed/sample-vpc.vpcs.faketargetapi.yaml --patch-file start-managed/sample-vpc.vpcs.faketargetapi.yaml"
type: Patch
- patch:
type: merge
files:
- start-composites/my-resource-dwjgh.xmyresources.test.com.yaml
name: start-composites
manualExecution:
- "kubectl patch --type='merge' -f start-composites/my-resource-dwjgh.xmyresources.test.com.yaml --patch-file start-composites/my-resource-dwjgh.xmyresources.test.com.yaml"
type: Patch
version: 0.1.0

View File

@ -1,59 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
spec:
steps:
- apply:
files:
- create-new-managed/sample-vpc.vpcs.faketargetapi.yaml
name: create-new-managed
manualExecution:
- "kubectl apply -f create-new-managed/sample-vpc.vpcs.faketargetapi.yaml"
type: Apply
- apply:
files:
- new-compositions/example-migrated.compositions.apiextensions.crossplane.io.yaml
name: new-compositions
manualExecution:
- "kubectl apply -f new-compositions/example-migrated.compositions.apiextensions.crossplane.io.yaml"
type: Apply
- patch:
type: merge
files:
- edit-composites/my-resource-dwjgh.xmyresources.test.com.yaml
name: edit-composites
manualExecution:
- "kubectl patch --type='merge' -f edit-composites/my-resource-dwjgh.xmyresources.test.com.yaml --patch-file edit-composites/my-resource-dwjgh.xmyresources.test.com.yaml"
type: Patch
- patch:
type: merge
files:
- edit-claims/my-resource.myresources.test.com.yaml
name: edit-claims
manualExecution:
- "kubectl patch --type='merge' -f edit-claims/my-resource.myresources.test.com.yaml --patch-file edit-claims/my-resource.myresources.test.com.yaml"
type: Patch
- patch:
type: merge
files:
- start-managed/sample-vpc.vpcs.faketargetapi.yaml
name: start-managed
manualExecution:
- "kubectl patch --type='merge' -f start-managed/sample-vpc.vpcs.faketargetapi.yaml --patch-file start-managed/sample-vpc.vpcs.faketargetapi.yaml"
type: Patch
- patch:
type: merge
files:
- start-composites/my-resource-dwjgh.xmyresources.test.com.yaml
name: start-composites
manualExecution:
- "kubectl patch --type='merge' -f start-composites/my-resource-dwjgh.xmyresources.test.com.yaml --patch-file start-composites/my-resource-dwjgh.xmyresources.test.com.yaml"
type: Patch
version: 0.1.0

View File

@ -1,78 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
labels:
purpose: example
name: example-migrated
spec:
compositeTypeRef:
apiVersion: test.com/v1alpha1
kind: XMyResource
patchSets:
- name: not-referenced
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: spec.forProvider.myTag
- name: ps1
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: spec.forProvider.tags["key3"]
- name: ps2
patches:
- fromFieldPath: "spec.parameters.region"
toFieldPath: spec.forProvider.region
- name: ps3
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: metadata.labels[a.b.c.d/tag-value]
- name: ps4
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: metadata.labels['a.b.c.d.e/tag-value']
- name: ps5
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: metadata.labels["a.b.c.d.e.f/tag-value"]
- name: ps6
patches:
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: spec.forProvider.tags["key4"]
resources:
- base:
apiVersion: faketargetapi/v1alpha1
kind: VPC
mockManaged:
ctrl: null
recorder: null
spec:
forProvider:
cidrBlock: 192.168.0.0/16
region: us-west-1
tags:
key1: val1
key2: val2
key3: val3
name: vpc
patches:
- fromFieldPath: spec.parameters.tagValue
toFieldPath: spec.forProvider.tags["key1"]
- fromFieldPath: spec.parameters.tagValue
toFieldPath: spec.forProvider.tags["key2"]
- type: PatchSet
patchSetName: ps1
- type: PatchSet
patchSetName: ps2
- type: PatchSet
patchSetName: ps3
- type: PatchSet
patchSetName: ps4
- type: PatchSet
patchSetName: ps5
- type: PatchSet
patchSetName: ps6
- fromFieldPath: "spec.parameters.tagValue"
toFieldPath: spec.forProvider.param

View File

@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-ec2
spec:
package: xpkg.upbound.io/upbound/provider-aws-ec2:v0.37.0
revisionActivationPolicy: Manual

View File

@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-eks
spec:
package: xpkg.upbound.io/upbound/provider-aws-eks:v0.37.0
revisionActivationPolicy: Manual

View File

@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-family-aws
spec:
package: xpkg.upbound.io/upbound/provider-family-aws:v0.37.0
revisionActivationPolicy: Manual

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: test.com/v1alpha1
kind: XMyResource
metadata:
annotations:
crossplane.io/paused: "true"
name: my-resource-dwjgh

View File

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
#
# SPDX-License-Identifier: Apache-2.0
apiVersion: fakesourceapi/v1alpha1
kind: VPC
metadata:
annotations:
crossplane.io/paused: "true"
name: sample-vpc

Some files were not shown because too many files have changed in this diff Show More