@@ -18,18 +18,21 @@ package main
1818
1919import (
2020 "context"
21+ "encoding/json"
2122 "fmt"
2223 "io/fs"
2324 "os"
2425 "path/filepath"
2526 "strings"
2627
28+ "github.com/fluxcd/pkg/ssa"
2729 "github.com/manifoldco/promptui"
2830 "github.com/spf13/cobra"
2931 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
3032 apierrors "k8s.io/apimachinery/pkg/api/errors"
3133 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3234 "k8s.io/apimachinery/pkg/runtime/schema"
35+ "k8s.io/apimachinery/pkg/types"
3336 "k8s.io/client-go/util/retry"
3437 "sigs.k8s.io/controller-runtime/pkg/client"
3538
@@ -54,6 +57,7 @@ type APIVersions struct {
5457 LatestVersions map [schema.GroupKind ]string
5558}
5659
60+ // TODO: Update this mapping when new Flux minor versions are released!
5761// latestAPIVersions contains the latest API versions for each GroupKind
5862// for each supported Flux version. We maintain the latest two minor versions.
5963var latestAPIVersions = []APIVersions {
@@ -132,35 +136,29 @@ The command has two modes of operation:
132136
133137- Cluster mode (default): migrates all the Flux custom resources stored in Kubernetes etcd to their latest API version.
134138- File system mode (-f): migrates the Flux custom resources defined in the manifests located in the specified path.
135-
136- Examples:
137-
138- # Migrate all the Flux custom resources in the cluster.
139+ ` ,
140+ Example : ` # Migrate all the Flux custom resources in the cluster.
141+ # This uses the current kubeconfig context and requires cluster-admin permissions.
139142 flux migrate
140143
141- # Migrate all manifests in a Git repository.
144+ # Migrate all the Flux custom resources in a Git repository
145+ # checked out in the current working directory.
142146 flux migrate -f .
143147
144- # Migrate the Flux custom resources defined in the manifests located in the ./manifest.yaml file.
145- flux migrate --path=./manifest.yaml
146-
147- # Migrate the Flux custom resources defined in the manifests located in the ./manifests directory.
148- flux migrate --path=./manifests
148+ # Migrate all Flux custom resources defined in YAML and Helm YAML template files.
149+ flux migrate -f . --extensions=.yml,.yaml,.tpl
149150
150- # Migrate the Flux custom resources defined in the manifests located in the ./manifests directory
151- # skipping confirmation prompts.
152- flux migrate --path=./manifests --yes
151+ # Migrate the Flux custom resources to the latest API versions of Flux 2.6.
152+ flux migrate -f . --version=2.6
153153
154- # Simulate the migration process without making any changes, only applicable with --path .
155- flux migrate -- path=./manifests --dry-run
154+ # Migrate the Flux custom resources defined in a multi-document YAML manifest file .
155+ flux migrate -f path/to/manifest.yaml
156156
157- # Migrate the Flux custom resources defined in the manifests located in the ./manifests directory
158- # considering YAML and Helm YAML template files.
159- flux migrate --path=./manifests --extensions=.yml --extensions=.yaml --extensions=.tpl
157+ # Simulate the migration without making any changes.
158+ flux migrate -f . --dry-run
160159
161- # Migrate the Flux custom resources defined in the manifests located in the ./manifests directory
162- # for Flux 2.6. This will migrate the custom resources to their latest API versions in Flux 2.6.
163- flux migrate --path=./manifests --version=2.6
160+ # Run the migration by skipping confirmation prompts.
161+ flux migrate -f . --yes
164162` ,
165163 RunE : runMigrateCmd ,
166164}
@@ -176,16 +174,16 @@ var migrateFlags struct {
176174func init () {
177175 rootCmd .AddCommand (migrateCmd )
178176
179- migrateCmd .Flags ().BoolVarP (& migrateFlags .yes , "yes" , "y" , false ,
180- "skip confirmation prompts" )
181- migrateCmd .Flags ().BoolVar (& migrateFlags .dryRun , "dry-run" , false ,
182- "simulate the migration process without making any changes, only applicable with --path" )
183177 migrateCmd .Flags ().StringVarP (& migrateFlags .path , "path" , "f" , "" ,
184178 "the path to the directory containing the manifests to migrate" )
185- migrateCmd .Flags ().StringArrayVarP (& migrateFlags .extensions , "extensions" , "e" , []string {".yaml" , ".yml" },
186- "the file extensions to consider when migrating manifests from the filesystem ( only used with --path) " )
179+ migrateCmd .Flags ().StringSliceVarP (& migrateFlags .extensions , "extensions" , "e" , []string {".yaml" , ".yml" },
180+ "the file extensions to consider when migrating manifests, only applicable --path" )
187181 migrateCmd .Flags ().StringVarP (& migrateFlags .version , "version" , "v" , "" ,
188- "the target Flux version to migrate custom resource API versions to (defaults to the version of the CLI)" )
182+ "the target Flux minor version to migrate manifests to, only applicable with --path (defaults to the version of the CLI)" )
183+ migrateCmd .Flags ().BoolVarP (& migrateFlags .yes , "yes" , "y" , false ,
184+ "skip confirmation prompts when migrating manifests, only applicable with --path" )
185+ migrateCmd .Flags ().BoolVar (& migrateFlags .dryRun , "dry-run" , false ,
186+ "simulate the migration of manifests without making any changes, only applicable with --path" )
189187}
190188
191189func runMigrateCmd (* cobra.Command , []string ) error {
@@ -319,7 +317,7 @@ func (c *ClusterMigrator) migrateCRD(ctx context.Context, name string) error {
319317 return nil
320318}
321319
322- // migrateCR migrates all CRs for the given CRD to the specified version by patching them with an empty patch .
320+ // migrateCR migrates all CRs for the given CRD to the specified version by patching them.
323321func (c * ClusterMigrator ) migrateCR (ctx context.Context , crd * apiextensionsv1.CustomResourceDefinition , version string ) error {
324322 list := & unstructured.UnstructuredList {}
325323
@@ -339,16 +337,39 @@ func (c *ClusterMigrator) migrateCR(ctx context.Context, crd *apiextensionsv1.Cu
339337 }
340338
341339 for _ , item := range list .Items {
342- // patch the resource with an empty patch to update the version
343- if err := c .kubeClient .Patch (
344- ctx ,
345- & item ,
346- client .RawPatch (client .Merge .Type (), []byte ("{}" )),
347- ); err != nil && ! apierrors .IsNotFound (err ) {
348- return fmt .Errorf (" %s/%s/%s failed to migrate: %w" ,
340+ patches , err := ssa .PatchMigrateToVersion (& item , apiVersion )
341+ if err != nil {
342+ return fmt .Errorf ("failed to create migration patch for %s/%s/%s: %w" ,
349343 item .GetKind (), item .GetNamespace (), item .GetName (), err )
350344 }
351345
346+ if len (patches ) == 0 {
347+ // patch the resource with an empty patch to update the version
348+ if err := c .kubeClient .Patch (
349+ ctx ,
350+ & item ,
351+ client .RawPatch (client .Merge .Type (), []byte ("{}" )),
352+ ); err != nil && ! apierrors .IsNotFound (err ) {
353+ return fmt .Errorf (" %s/%s/%s failed to migrate: %w" ,
354+ item .GetKind (), item .GetNamespace (), item .GetName (), err )
355+ }
356+ } else {
357+ // patch the resource to migrate the managed fields to the latest apiVersion
358+ rawPatch , err := json .Marshal (patches )
359+ if err != nil {
360+ return fmt .Errorf ("failed to marshal migration patch for %s/%s/%s: %w" ,
361+ item .GetKind (), item .GetNamespace (), item .GetName (), err )
362+ }
363+ if err := c .kubeClient .Patch (
364+ ctx ,
365+ & item ,
366+ client .RawPatch (types .JSONPatchType , rawPatch ),
367+ ); err != nil && ! apierrors .IsNotFound (err ) {
368+ return fmt .Errorf (" %s/%s/%s failed to migrate managed fields: %w" ,
369+ item .GetKind (), item .GetNamespace (), item .GetName (), err )
370+ }
371+ }
372+
352373 logger .Successf ("%s/%s/%s migrated to version %s" ,
353374 item .GetKind (), item .GetNamespace (), item .GetName (), version )
354375 }
0 commit comments