From d1f5f70ffe477faafb1aa3a79343e00161d33fb4 Mon Sep 17 00:00:00 2001 From: Alper Rifat Ulucinar Date: Wed, 7 Jun 2023 19:07:41 +0300 Subject: [PATCH] Add support for Lock.pkg converters Signed-off-by: Alper Rifat Ulucinar --- pkg/migration/converter.go | 11 +-- pkg/migration/package_lock_steps.go | 70 +++++++++++++++++++ pkg/migration/plan_generator.go | 4 ++ pkg/migration/plan_generator_test.go | 25 +++++++ pkg/migration/plan_steps.go | 7 +- pkg/migration/registry.go | 24 +++++-- ...igurationv1_providerv1_migration_plan.yaml | 7 ++ .../lock.locks.pkg.crossplane.io_v1beta1.yaml | 14 ++++ pkg/migration/testdata/plan/lockv1beta1.yaml | 13 ++++ 9 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 pkg/migration/package_lock_steps.go create mode 100644 pkg/migration/testdata/plan/generated/edit-package-lock/lock.locks.pkg.crossplane.io_v1beta1.yaml create mode 100644 pkg/migration/testdata/plan/lockv1beta1.yaml diff --git a/pkg/migration/converter.go b/pkg/migration/converter.go index eb48e25d..6019060a 100644 --- a/pkg/migration/converter.go +++ b/pkg/migration/converter.go @@ -21,6 +21,7 @@ import ( 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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -35,9 +36,9 @@ const ( 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" + 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" ) @@ -251,10 +252,10 @@ func getCategory(u unstructured.Unstructured) Category { } } -/*func toPackageLock(u unstructured.Unstructured) (*xppkgv1beta1.Lock, error) { +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 -}*/ +} diff --git a/pkg/migration/package_lock_steps.go b/pkg/migration/package_lock_steps.go new file mode 100644 index 00000000..6a8ca78b --- /dev/null +++ b/pkg/migration/package_lock_steps.go @@ -0,0 +1,70 @@ +// 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" + + "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.Wrap(pg.target.Put(UnstructuredWithMetadata{ + Object: unstructured.Unstructured{ + Object: addNameGVK(t.Object, patchMap), + }, + Metadata: t.Metadata, + }), errEditConfigurationPackageFmt) +} diff --git a/pkg/migration/plan_generator.go b/pkg/migration/plan_generator.go index 3ac6ea84..08e7a2bb 100644 --- a/pkg/migration/plan_generator.go +++ b/pkg/migration/plan_generator.go @@ -48,6 +48,7 @@ const ( 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" @@ -256,6 +257,9 @@ func (pg *PlanGenerator) convert() error { //nolint: gocyclo } } 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 { diff --git a/pkg/migration/plan_generator_test.go b/pkg/migration/plan_generator_test.go index 826d65ea..d9898c5d 100644 --- a/pkg/migration/plan_generator_test.go +++ b/pkg/migration/plan_generator_test.go @@ -27,6 +27,7 @@ import ( 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" @@ -212,6 +213,7 @@ func TestGeneratePlan(t *testing.T) { "testdata/plan/providerv1.yaml": {}, "testdata/plan/configurationv1.yaml": {}, "testdata/plan/configurationpkgv1.yaml": {}, + "testdata/plan/lockv1beta1.yaml": {}, }), target: newTestTarget(), registry: getRegistry( @@ -230,6 +232,10 @@ func TestGeneratePlan(t *testing.T) { withProviderPackageConverter(providerPackageConverter{ re: regexp.MustCompile(`xpkg.upbound.io/upbound/provider-aws:.+`), converter: &monolithicProviderToSSOPConverter{}, + }), + withPackageLockConverter(packageLockConverter{ + re: CrossplaneLockName, + converter: &lockConverter{}, })), }, want: want{ @@ -245,6 +251,7 @@ func TestGeneratePlan(t *testing.T) { "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", }, }, }, @@ -433,6 +440,12 @@ func withProviderPackageConverter(c providerPackageConverter) registryOption { } } +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) @@ -546,6 +559,18 @@ func (c *monolithicProviderToSSOPConverter) ProviderPackageV1(_ xppkgv1.Provider }, nil } +type lockConverter struct{} + +func (p *lockConverter) PackageLockV1Beta1(lock *xppkgv1beta1.Lock) error { + lock.Packages = append(lock.Packages, xppkgv1beta1.LockPackage{ + Name: "test-provider", + Type: xppkgv1beta1.ProviderPackageType, + Source: "xpkg.upbound.io/upbound/test-provider", + Version: "vX.Y.Z", + }) + return nil +} + type preProcessor struct { results []string } diff --git a/pkg/migration/plan_steps.go b/pkg/migration/plan_steps.go index c2193382..f12fa784 100644 --- a/pkg/migration/plan_steps.go +++ b/pkg/migration/plan_steps.go @@ -19,14 +19,11 @@ import ( "sort" "strings" - "k8s.io/apimachinery/pkg/util/rand" - - "k8s.io/apimachinery/pkg/util/jsonmergepatch" - "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 diff --git a/pkg/migration/registry.go b/pkg/migration/registry.go index 2aeda29a..13773de7 100644 --- a/pkg/migration/registry.go +++ b/pkg/migration/registry.go @@ -33,6 +33,8 @@ var ( 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 ( @@ -81,6 +83,15 @@ type providerPackageConverter struct { 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. @@ -92,7 +103,7 @@ type Registry struct { configurationMetaConverters []configurationMetadataConverter configurationPackageConverters []configurationPackageConverter providerPackageConverters []providerPackageConverter - packageLockConverters []PackageLockConverter + packageLockConverters []packageLockConverter scheme *runtime.Scheme claimTypes []schema.GroupVersionKind compositeTypes []schema.GroupVersionKind @@ -218,14 +229,17 @@ func (r *Registry) RegisterProviderPackageV1ConversionFunction(re *regexp.Regexp } // RegisterPackageLockConverter registers the given PackageLockConverter. -func (r *Registry) RegisterPackageLockConverter(lockConv PackageLockConverter) { - r.packageLockConverters = append(r.packageLockConverters, lockConv) +func (r *Registry) RegisterPackageLockConverter(re *regexp.Regexp, lockConv PackageLockConverter) { + r.packageLockConverters = append(r.packageLockConverters, packageLockConverter{ + re: re, + converter: lockConv, + }) } // RegisterPackageLockV1Beta1ConversionFunction registers the specified // RegisterPackageLockV1Beta1ConversionFunction for the package v1beta1 locks. -func (r *Registry) RegisterPackageLockV1Beta1ConversionFunction(lockConversionFn PackageLockV1Beta1ConversionFn) { - r.RegisterPackageLockConverter(&delegatingConverter{ +func (r *Registry) RegisterPackageLockV1Beta1ConversionFunction(re *regexp.Regexp, lockConversionFn PackageLockV1Beta1ConversionFn) { + r.RegisterPackageLockConverter(re, &delegatingConverter{ packageLockV1Beta1Fn: lockConversionFn, }) } diff --git a/pkg/migration/testdata/plan/generated/configurationv1_providerv1_migration_plan.yaml b/pkg/migration/testdata/plan/generated/configurationv1_providerv1_migration_plan.yaml index a70677c1..057f9648 100644 --- a/pkg/migration/testdata/plan/generated/configurationv1_providerv1_migration_plan.yaml +++ b/pkg/migration/testdata/plan/generated/configurationv1_providerv1_migration_plan.yaml @@ -20,6 +20,13 @@ spec: name: disable-dependency-resolution type: Patch + - patch: + type: merge + files: + - edit-package-lock/lock.locks.pkg.crossplane.io_v1beta1.yaml + name: edit-package-lock + type: Patch + - delete: options: finalizerPolicy: Remove diff --git a/pkg/migration/testdata/plan/generated/edit-package-lock/lock.locks.pkg.crossplane.io_v1beta1.yaml b/pkg/migration/testdata/plan/generated/edit-package-lock/lock.locks.pkg.crossplane.io_v1beta1.yaml new file mode 100644 index 00000000..3a0cfdb5 --- /dev/null +++ b/pkg/migration/testdata/plan/generated/edit-package-lock/lock.locks.pkg.crossplane.io_v1beta1.yaml @@ -0,0 +1,14 @@ +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 + diff --git a/pkg/migration/testdata/plan/lockv1beta1.yaml b/pkg/migration/testdata/plan/lockv1beta1.yaml new file mode 100644 index 00000000..ac24f14e --- /dev/null +++ b/pkg/migration/testdata/plan/lockv1beta1.yaml @@ -0,0 +1,13 @@ +apiVersion: pkg.crossplane.io/v1beta1 +kind: Lock +metadata: + creationTimestamp: "2023-05-15T12:37:16Z" + generation: 9 + name: lock + resourceVersion: "10366" + uid: b1aabaaa-56bb-4356-ae45-1e3131cb9743 +packages: + - name: provider-aws + type: Provider + source: xpkg.upbound.io/upbound/provider-aws + version: v0.36.0