From e506364eedb2f915c0507617c88eba6f8a91a353 Mon Sep 17 00:00:00 2001 From: Alper Rifat Ulucinar Date: Wed, 24 May 2023 10:22:02 +0300 Subject: [PATCH] Add support for converting Crossplane Configurations - Add the migration.Executor interface Signed-off-by: Alper Rifat Ulucinar --- pkg/migration/converter.go | 49 +++++++++++ pkg/migration/errors.go | 31 +++++++ pkg/migration/interfaces.go | 29 ++++++ pkg/migration/plan_generator.go | 66 ++++++++++---- pkg/migration/plan_generator_test.go | 58 +++++++++++- pkg/migration/registry.go | 88 ++++++++++++++++--- .../testdata/plan/configurationv1.yaml | 36 ++++++++ .../testdata/plan/configurationv1alpha1.yaml | 36 ++++++++ 8 files changed, 362 insertions(+), 31 deletions(-) create mode 100644 pkg/migration/errors.go create mode 100644 pkg/migration/testdata/plan/configurationv1.yaml create mode 100644 pkg/migration/testdata/plan/configurationv1alpha1.yaml diff --git a/pkg/migration/converter.go b/pkg/migration/converter.go index e2937c96..9638d859 100644 --- a/pkg/migration/converter.go +++ b/pkg/migration/converter.go @@ -16,8 +16,12 @@ package migration import ( "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" "github.com/pkg/errors" + 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" @@ -27,6 +31,7 @@ import ( const ( errFromUnstructured = "failed to convert from unstructured.Unstructured to the managed resource type" + errFromUnstructuredConf = "failed to convert from unstructured.Unstructured to Crossplane Configuration metadata" errToUnstructured = "failed to convert from the managed resource type to unstructured.Unstructured" errRawExtensionUnmarshal = "failed to unmarshal runtime.RawExtension" @@ -165,3 +170,47 @@ func addNameGVK(u unstructured.Unstructured, target map[string]any) map[string]a 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 toConfigurationV1(u unstructured.Unstructured) (*xpmetav1.Configuration, error) { + conf := &xpmetav1.Configuration{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, conf); err != nil { + return nil, errors.Wrap(err, errFromUnstructuredConf) + } + return conf, nil +} + +func toConfigurationV1Alpha1(u unstructured.Unstructured) (*xpmetav1alpha1.Configuration, error) { + conf := &xpmetav1alpha1.Configuration{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, conf); err != nil { + return nil, errors.Wrap(err, errFromUnstructuredConf) + } + return conf, nil +} + +func toConfiguration(u unstructured.Unstructured) (metav1.Object, error) { + var conf metav1.Object + var err error + switch u.GroupVersionKind().Version { + case "v1alpha1": + conf, err = toConfigurationV1Alpha1(u) + default: + conf, err = toConfigurationV1(u) + } + return conf, err +} diff --git a/pkg/migration/errors.go b/pkg/migration/errors.go new file mode 100644 index 00000000..b28b7b9c --- /dev/null +++ b/pkg/migration/errors.go @@ -0,0 +1,31 @@ +// Copyright 2023 Upbound Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +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, + } +} diff --git a/pkg/migration/interfaces.go b/pkg/migration/interfaces.go index 12bc1f86..b79a5a48 100644 --- a/pkg/migration/interfaces.go +++ b/pkg/migration/interfaces.go @@ -17,6 +17,8 @@ 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" ) // ResourceConverter converts a managed resource from @@ -69,6 +71,18 @@ type PatchSetConverter interface { PatchSets(psMap map[string]*xpv1.PatchSet) error } +// ConfigurationConverter converts a Crossplane Configuration's metadata. +type ConfigurationConverter interface { + // ConfigurationV1 takes a Crossplane Configuration v1 metadata, + // converts it, and stores the converted metadata in its argument. + // Returns any errors encountered during the conversion. + ConfigurationV1(configuration *xpmetav1.Configuration) error + // ConfigurationV1Alpha1 takes a Crossplane Configuration v1alpha1 + // metadata, converts it, and stores the converted metadata in its + // argument. Returns any errors encountered during the conversion. + ConfigurationV1Alpha1(configuration *xpmetav1alpha1.Configuration) error +} + // Source is a source for reading resource manifests type Source interface { // HasNext returns `true` if the Source implementation has a next manifest @@ -88,3 +102,18 @@ type Target interface { // 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 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 any) (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 +} diff --git a/pkg/migration/plan_generator.go b/pkg/migration/plan_generator.go index 05996041..4bc49aac 100644 --- a/pkg/migration/plan_generator.go +++ b/pkg/migration/plan_generator.go @@ -21,6 +21,10 @@ import ( "strings" "time" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + xpmetav1alpha1 "github.com/crossplane/crossplane/apis/pkg/meta/v1alpha1" + v1 "github.com/crossplane/crossplane-runtime/apis/common/v1" "github.com/crossplane/crossplane-runtime/pkg/fieldpath" "github.com/crossplane/crossplane-runtime/pkg/meta" @@ -28,6 +32,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim" "github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composite" xpv1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + xpmetav1 "github.com/crossplane/crossplane/apis/pkg/meta/v1" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -45,7 +50,8 @@ const ( errCompositePause = "failed to pause composite resource" errCompositesEdit = "failed to edit composite resources" errCompositesStart = "failed to start composite resources" - errCompositionMigrate = "failed to migrate the composition" + errCompositionMigrateFmt = "failed to migrate the composition: %s" + errConfigurationMigrateFmt = "failed to migrate the configuration: %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" @@ -197,17 +203,25 @@ func (pg *PlanGenerator) convert() error { //nolint: gocyclo return errors.Wrap(err, errSourceNext) } switch gvk := o.Object.GroupVersionKind(); gvk { + case xpmetav1.ConfigurationGroupVersionKind, xpmetav1alpha1.ConfigurationGroupVersionKind: + target, converted, err := pg.convertConfiguration(o) + if err != nil { + return errors.Wrapf(err, errConfigurationMigrateFmt, o.Object.GetName()) + } + if converted { + fmt.Printf("converted configuration: %v\n", target) + } case xpv1.CompositionGroupVersionKind: target, converted, err := pg.convertComposition(o) if err != nil { - return errors.Wrap(err, errCompositionMigrate) + 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.Wrap(err, errCompositionMigrate) + return errors.Wrapf(err, errCompositionMigrateFmt, o.Object.GetName()) } } default: @@ -314,20 +328,38 @@ func assertMetadataName(parentName string, resources []resource.Managed) { } } -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) +func (pg *PlanGenerator) convertConfiguration(o UnstructuredWithMetadata) (*UnstructuredWithMetadata, bool, error) { + isConverted := false + var conf metav1.Object + var err error + for _, confConv := range pg.registry.configurationConverters { + if confConv.re == nil || confConv.converter == nil || !confConv.re.MatchString(o.Object.GetName()) { + continue + } + + conf, err = toConfiguration(o.Object) + if err != nil { + return nil, false, err + } + switch o.Object.GroupVersionKind().Version { + case "v1alpha1": + err = confConv.converter.ConfigurationV1Alpha1(conf.(*xpmetav1alpha1.Configuration)) + default: + err = confConv.converter.ConfigurationV1(conf.(*xpmetav1.Configuration)) + } + if err != nil { + return nil, false, 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 } - mg, ok := obj.(resource.Managed) - return mg, ok, nil + return &UnstructuredWithMetadata{ + Object: ToSanitizedUnstructured(conf), + Metadata: o.Metadata, + }, isConverted, nil } func (pg *PlanGenerator) convertComposition(o UnstructuredWithMetadata) (*UnstructuredWithMetadata, bool, error) { // nolint:gocyclo @@ -344,7 +376,7 @@ func (pg *PlanGenerator) convertComposition(o UnstructuredWithMetadata) (*Unstru for _, cmp := range comp.Spec.Resources { u, err := FromRawExtension(cmp.Base) if err != nil { - return nil, false, errors.Wrap(err, errCompositionMigrate) + return nil, false, errors.Wrapf(err, errCompositionMigrateFmt, o.Object.GetName()) } gvk := u.GroupVersionKind() converted, ok, err := pg.convertResource(UnstructuredWithMetadata{ diff --git a/pkg/migration/plan_generator_test.go b/pkg/migration/plan_generator_test.go index d240db27..9fd06eef 100644 --- a/pkg/migration/plan_generator_test.go +++ b/pkg/migration/plan_generator_test.go @@ -20,9 +20,12 @@ import ( "path/filepath" "testing" + xpmetav1alpha1 "github.com/crossplane/crossplane/apis/pkg/meta/v1alpha1" + 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" "github.com/google/go-cmp/cmp" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -95,7 +98,7 @@ func TestGeneratePlan(t *testing.T) { re: AllCompositions, converter: &testConverter{}, }, - }), + }, nil), }, want: want{ migrationPlanPath: "testdata/plan/generated/migration_plan.yaml", @@ -112,6 +115,34 @@ func TestGeneratePlan(t *testing.T) { }, }, }, + "PlanWithConfigurationV1": { + fields: fields{ + source: newTestSource(map[string]Metadata{ + "testdata/plan/configurationv1.yaml": {}}), + target: newTestTarget(), + registry: getRegistryWithConverters(nil, nil, []configurationConverter{ + { + re: AllConfigurations, + converter: &testConverter{}, + }, + }), + }, + want: want{}, + }, + "PlanWithConfigurationV1Alpha1": { + fields: fields{ + source: newTestSource(map[string]Metadata{ + "testdata/plan/configurationv1alpha1.yaml": {}}), + target: newTestTarget(), + registry: getRegistryWithConverters(nil, nil, []configurationConverter{ + { + re: AllConfigurations, + converter: &testConverter{}, + }, + }), + }, + want: want{}, + }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { @@ -239,6 +270,26 @@ func (f *testTarget) Delete(o UnstructuredWithMetadata) error { type testConverter struct{} +func (f *testConverter) ConfigurationV1(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 (f *testConverter) ConfigurationV1Alpha1(c *xpmetav1alpha1.Configuration) error { + c.Spec.DependsOn = []xpmetav1alpha1.Dependency{ + { + Provider: ptrFromString("xpkg.upbound.io/upbound/provider-aws-eks"), + Version: ">=v0.17.0", + }, + } + return nil +} + 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"]`) @@ -249,7 +300,7 @@ func ptrFromString(s string) *string { return &s } -func getRegistryWithConverters(converters map[schema.GroupVersionKind]delegatingConverter, psConverters []patchSetConverter) *Registry { +func getRegistryWithConverters(converters map[schema.GroupVersionKind]delegatingConverter, psConverters []patchSetConverter, confConverters []configurationConverter) *Registry { scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(fake.MigrationSourceGVK, &fake.MigrationSourceObject{}) scheme.AddKnownTypeWithName(fake.MigrationTargetGVK, &fake.MigrationTargetObject{}) @@ -257,6 +308,9 @@ func getRegistryWithConverters(converters map[schema.GroupVersionKind]delegating for _, c := range psConverters { r.RegisterPatchSetConverter(c.re, c.converter) } + for _, c := range confConverters { + r.RegisterConfigurationConverter(c.re, c.converter) + } for gvk, d := range converters { r.RegisterConversionFunctions(gvk, d.rFn, d.cmpFn, nil) } diff --git a/pkg/migration/registry.go b/pkg/migration/registry.go index a1f022b2..f1448fcd 100644 --- a/pkg/migration/registry.go +++ b/pkg/migration/registry.go @@ -19,6 +19,8 @@ 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" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -27,6 +29,8 @@ import ( var ( // AllCompositions matches all v1.Composition names. AllCompositions = regexp.MustCompile(`.*`) + // AllConfigurations matches all metav1.Configuration names. + AllConfigurations = regexp.MustCompile(`.*`) ) const ( @@ -45,16 +49,27 @@ type patchSetConverter struct { converter PatchSetConverter } +type configurationConverter 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 ConfigurationConverter to be run on the Configuration's + // metadata. + converter ConfigurationConverter +} + // 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 { - resourceConverters map[schema.GroupVersionKind]ResourceConverter - templateConverters map[schema.GroupVersionKind]ComposedTemplateConverter - patchSetConverters []patchSetConverter - scheme *runtime.Scheme - claimTypes []schema.GroupVersionKind - compositeTypes []schema.GroupVersionKind + resourceConverters map[schema.GroupVersionKind]ResourceConverter + templateConverters map[schema.GroupVersionKind]ComposedTemplateConverter + patchSetConverters []patchSetConverter + configurationConverters []configurationConverter + scheme *runtime.Scheme + claimTypes []schema.GroupVersionKind + compositeTypes []schema.GroupVersionKind } // NewRegistry returns a new Registry initialized with @@ -102,7 +117,7 @@ func (r *Registry) RegisterCompositionConverter(gvk schema.GroupVersionKind, con r.RegisterTemplateConverter(gvk, conv) } -// RegisterPatchSetConverter registers the given PatchSetConversionFn for +// 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{ @@ -111,6 +126,27 @@ func (r *Registry) RegisterPatchSetConverter(re *regexp.Regexp, psConv PatchSetC }) } +// RegisterConfigurationConverter registers the given ConfigurationConverter +// for the configurations whose name match the given regular expression. +func (r *Registry) RegisterConfigurationConverter(re *regexp.Regexp, confConv ConfigurationConverter) { + r.configurationConverters = append(r.configurationConverters, configurationConverter{ + re: re, + converter: confConv, + }) +} + +func (r *Registry) RegisterConfigurationV1ConversionFunction(re *regexp.Regexp, confConversionFn ConfigurationV1ConversionFn) { + r.RegisterConfigurationConverter(re, &delegatingConverter{ + confV1Fn: confConversionFn, + }) +} + +func (r *Registry) RegisterConfigurationV1Alpha1ConversionFunction(re *regexp.Regexp, confConversionFn ConfigurationV1Alpha1ConversionFn) { + r.RegisterConfigurationConverter(re, &delegatingConverter{ + confV1Alpha1Fn: confConversionFn, + }) +} + // 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) @@ -156,13 +192,15 @@ func (r *Registry) GetCompositionGVKs() []schema.GroupVersionKind { } // GetAllRegisteredGVKs returns a list of registered GVKs -// including v1.CompositionGroupVersionKind +// including v1.CompositionGroupVersionKind, +// metav1.ConfigurationGroupVersionKind and +// metav1alpha1.ConfigurationGroupVersionKind. 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) + gvks = append(gvks, xpv1.CompositionGroupVersionKind, xpmetav1.ConfigurationGroupVersionKind, xpmetav1alpha1.ConfigurationGroupVersionKind) return gvks } @@ -180,10 +218,36 @@ type ComposedTemplateConversionFn func(sourceTemplate xpv1.ComposedTemplate, con // schema to the migration target provider's schema. type PatchSetsConversionFn func(psMap map[string]*xpv1.PatchSet) error +// ConfigurationV1ConversionFn is a function that converts the specified +// migration source Configuration v1 metadata to the migration target +// Configuration metadata. +type ConfigurationV1ConversionFn func(configuration *xpmetav1.Configuration) error + +// ConfigurationV1Alpha1ConversionFn is a function that converts the specified +// migration source Configuration v1alpha1 metadata to the migration target +// Configuration metadata. +type ConfigurationV1Alpha1ConversionFn func(configuration *xpmetav1alpha1.Configuration) error + type delegatingConverter struct { - rFn ResourceConversionFn - cmpFn ComposedTemplateConversionFn - psFn PatchSetsConversionFn + rFn ResourceConversionFn + cmpFn ComposedTemplateConversionFn + psFn PatchSetsConversionFn + confV1Fn ConfigurationV1ConversionFn + confV1Alpha1Fn ConfigurationV1Alpha1ConversionFn +} + +func (d *delegatingConverter) ConfigurationV1(c *xpmetav1.Configuration) error { + if d.confV1Fn == nil { + return nil + } + return d.confV1Fn(c) +} + +func (d *delegatingConverter) ConfigurationV1Alpha1(c *xpmetav1alpha1.Configuration) error { + if d.confV1Alpha1Fn == nil { + return nil + } + return d.confV1Alpha1Fn(c) } func (d *delegatingConverter) PatchSets(psMap map[string]*xpv1.PatchSet) error { diff --git a/pkg/migration/testdata/plan/configurationv1.yaml b/pkg/migration/testdata/plan/configurationv1.yaml new file mode 100644 index 00000000..809b0a26 --- /dev/null +++ b/pkg/migration/testdata/plan/configurationv1.yaml @@ -0,0 +1,36 @@ +apiVersion: meta.pkg.crossplane.io/v1 +kind: Configuration +metadata: + name: platform-ref-aws + annotations: + meta.crossplane.io/maintainer: Upbound + 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" \ No newline at end of file diff --git a/pkg/migration/testdata/plan/configurationv1alpha1.yaml b/pkg/migration/testdata/plan/configurationv1alpha1.yaml new file mode 100644 index 00000000..117faf74 --- /dev/null +++ b/pkg/migration/testdata/plan/configurationv1alpha1.yaml @@ -0,0 +1,36 @@ +apiVersion: meta.pkg.crossplane.io/v1alpha1 +kind: Configuration +metadata: + name: platform-ref-aws + annotations: + meta.crossplane.io/maintainer: Upbound + 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" \ No newline at end of file