Improve `flux migrate` for live cluster migrations

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
pull/5558/head
Stefan Prodan 1 week ago
parent 69b4b85cd9
commit 0255957dd7
No known key found for this signature in database
GPG Key ID: 3299AEB0E4085BAF

@ -18,18 +18,21 @@ package main
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"io/fs" "io/fs"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/fluxcd/pkg/ssa"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry" "k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
@ -54,6 +57,7 @@ type APIVersions struct {
LatestVersions map[schema.GroupKind]string LatestVersions map[schema.GroupKind]string
} }
// TODO: Update this mapping when new Flux minor versions are released!
// latestAPIVersions contains the latest API versions for each GroupKind // latestAPIVersions contains the latest API versions for each GroupKind
// for each supported Flux version. We maintain the latest two minor versions. // for each supported Flux version. We maintain the latest two minor versions.
var latestAPIVersions = []APIVersions{ var latestAPIVersions = []APIVersions{
@ -132,35 +136,29 @@ The command has two modes of operation:
- Cluster mode (default): migrates all the Flux custom resources stored in Kubernetes etcd to their latest API version. - Cluster mode (default): migrates all the Flux custom resources stored in Kubernetes etcd to their latest API version.
- File system mode (-f): migrates the Flux custom resources defined in the manifests located in the specified path. - File system mode (-f): migrates the Flux custom resources defined in the manifests located in the specified path.
`,
Examples: Example: ` # Migrate all the Flux custom resources in the cluster.
# This uses the current kubeconfig context and requires cluster-admin permissions.
# Migrate all the Flux custom resources in the cluster.
flux migrate flux migrate
# Migrate all manifests in a Git repository. # Migrate all the Flux custom resources in a Git repository
# checked out in the current working directory.
flux migrate -f . flux migrate -f .
# Migrate the Flux custom resources defined in the manifests located in the ./manifest.yaml file. # Migrate all Flux custom resources defined in YAML and Helm YAML template files.
flux migrate --path=./manifest.yaml flux migrate -f . --extensions=.yml,.yaml,.tpl
# Migrate the Flux custom resources defined in the manifests located in the ./manifests directory.
flux migrate --path=./manifests
# Migrate the Flux custom resources defined in the manifests located in the ./manifests directory # Migrate the Flux custom resources to the latest API versions of Flux 2.6.
# skipping confirmation prompts. flux migrate -f . --version=2.6
flux migrate --path=./manifests --yes
# Simulate the migration process without making any changes, only applicable with --path. # Migrate the Flux custom resources defined in a multi-document YAML manifest file.
flux migrate --path=./manifests --dry-run flux migrate -f path/to/manifest.yaml
# Migrate the Flux custom resources defined in the manifests located in the ./manifests directory # Simulate the migration without making any changes.
# considering YAML and Helm YAML template files. flux migrate -f . --dry-run
flux migrate --path=./manifests --extensions=.yml --extensions=.yaml --extensions=.tpl
# Migrate the Flux custom resources defined in the manifests located in the ./manifests directory # Run the migration by skipping confirmation prompts.
# for Flux 2.6. This will migrate the custom resources to their latest API versions in Flux 2.6. flux migrate -f . --yes
flux migrate --path=./manifests --version=2.6
`, `,
RunE: runMigrateCmd, RunE: runMigrateCmd,
} }
@ -176,16 +174,16 @@ var migrateFlags struct {
func init() { func init() {
rootCmd.AddCommand(migrateCmd) rootCmd.AddCommand(migrateCmd)
migrateCmd.Flags().BoolVarP(&migrateFlags.yes, "yes", "y", false,
"skip confirmation prompts")
migrateCmd.Flags().BoolVar(&migrateFlags.dryRun, "dry-run", false,
"simulate the migration process without making any changes, only applicable with --path")
migrateCmd.Flags().StringVarP(&migrateFlags.path, "path", "f", "", migrateCmd.Flags().StringVarP(&migrateFlags.path, "path", "f", "",
"the path to the directory containing the manifests to migrate") "the path to the directory containing the manifests to migrate")
migrateCmd.Flags().StringArrayVarP(&migrateFlags.extensions, "extensions", "e", []string{".yaml", ".yml"}, migrateCmd.Flags().StringSliceVarP(&migrateFlags.extensions, "extensions", "e", []string{".yaml", ".yml"},
"the file extensions to consider when migrating manifests from the filesystem (only used with --path)") "the file extensions to consider when migrating manifests, only applicable --path")
migrateCmd.Flags().StringVarP(&migrateFlags.version, "version", "v", "", migrateCmd.Flags().StringVarP(&migrateFlags.version, "version", "v", "",
"the target Flux version to migrate custom resource API versions to (defaults to the version of the CLI)") "the target Flux minor version to migrate manifests to, only applicable with --path (defaults to the version of the CLI)")
migrateCmd.Flags().BoolVarP(&migrateFlags.yes, "yes", "y", false,
"skip confirmation prompts when migrating manifests, only applicable with --path")
migrateCmd.Flags().BoolVar(&migrateFlags.dryRun, "dry-run", false,
"simulate the migration of manifests without making any changes, only applicable with --path")
} }
func runMigrateCmd(*cobra.Command, []string) error { func runMigrateCmd(*cobra.Command, []string) error {
@ -319,7 +317,7 @@ func (c *ClusterMigrator) migrateCRD(ctx context.Context, name string) error {
return nil return nil
} }
// migrateCR migrates all CRs for the given CRD to the specified version by patching them with an empty patch. // migrateCR migrates all CRs for the given CRD to the specified version by patching them.
func (c *ClusterMigrator) migrateCR(ctx context.Context, crd *apiextensionsv1.CustomResourceDefinition, version string) error { func (c *ClusterMigrator) migrateCR(ctx context.Context, crd *apiextensionsv1.CustomResourceDefinition, version string) error {
list := &unstructured.UnstructuredList{} list := &unstructured.UnstructuredList{}
@ -339,16 +337,39 @@ func (c *ClusterMigrator) migrateCR(ctx context.Context, crd *apiextensionsv1.Cu
} }
for _, item := range list.Items { for _, item := range list.Items {
// patch the resource with an empty patch to update the version patches, err := ssa.PatchMigrateToVersion(&item, apiVersion)
if err := c.kubeClient.Patch( if err != nil {
ctx, return fmt.Errorf("failed to create migration patch for %s/%s/%s: %w",
&item,
client.RawPatch(client.Merge.Type(), []byte("{}")),
); err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf(" %s/%s/%s failed to migrate: %w",
item.GetKind(), item.GetNamespace(), item.GetName(), err) item.GetKind(), item.GetNamespace(), item.GetName(), err)
} }
if len(patches) == 0 {
// patch the resource with an empty patch to update the version
if err := c.kubeClient.Patch(
ctx,
&item,
client.RawPatch(client.Merge.Type(), []byte("{}")),
); err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf(" %s/%s/%s failed to migrate: %w",
item.GetKind(), item.GetNamespace(), item.GetName(), err)
}
} else {
// patch the resource to migrate the managed fields to the latest apiVersion
rawPatch, err := json.Marshal(patches)
if err != nil {
return fmt.Errorf("failed to marshal migration patch for %s/%s/%s: %w",
item.GetKind(), item.GetNamespace(), item.GetName(), err)
}
if err := c.kubeClient.Patch(
ctx,
&item,
client.RawPatch(types.JSONPatchType, rawPatch),
); err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf(" %s/%s/%s failed to migrate managed fields: %w",
item.GetKind(), item.GetNamespace(), item.GetName(), err)
}
}
logger.Successf("%s/%s/%s migrated to version %s", logger.Successf("%s/%s/%s migrated to version %s",
item.GetKind(), item.GetNamespace(), item.GetName(), version) item.GetKind(), item.GetNamespace(), item.GetName(), version)
} }

@ -28,7 +28,7 @@ require (
github.com/fluxcd/pkg/oci v0.56.0 github.com/fluxcd/pkg/oci v0.56.0
github.com/fluxcd/pkg/runtime v0.86.0 github.com/fluxcd/pkg/runtime v0.86.0
github.com/fluxcd/pkg/sourceignore v0.14.0 github.com/fluxcd/pkg/sourceignore v0.14.0
github.com/fluxcd/pkg/ssa v0.58.0 github.com/fluxcd/pkg/ssa v0.59.0
github.com/fluxcd/pkg/ssh v0.21.0 github.com/fluxcd/pkg/ssh v0.21.0
github.com/fluxcd/pkg/tar v0.14.0 github.com/fluxcd/pkg/tar v0.14.0
github.com/fluxcd/pkg/version v0.10.0 github.com/fluxcd/pkg/version v0.10.0

@ -214,8 +214,8 @@ github.com/fluxcd/pkg/runtime v0.86.0 h1:q7aBSerJwt0N9hpurPVElG+HWpVhZcs6t96bcNQ
github.com/fluxcd/pkg/runtime v0.86.0/go.mod h1:Wt9mUzQgMPQMu2D/wKl5pG4zh5vu/tfF5wq9pPobxOQ= github.com/fluxcd/pkg/runtime v0.86.0/go.mod h1:Wt9mUzQgMPQMu2D/wKl5pG4zh5vu/tfF5wq9pPobxOQ=
github.com/fluxcd/pkg/sourceignore v0.14.0 h1:ZiZzbXtXb/Qp7I7JCStsxOlX8ri8rWwCvmvIrJ0UzQQ= github.com/fluxcd/pkg/sourceignore v0.14.0 h1:ZiZzbXtXb/Qp7I7JCStsxOlX8ri8rWwCvmvIrJ0UzQQ=
github.com/fluxcd/pkg/sourceignore v0.14.0/go.mod h1:E3zKvyTyB+oQKqm/2I/jS6Rrt3B7fNuig/4bY2vi3bg= github.com/fluxcd/pkg/sourceignore v0.14.0/go.mod h1:E3zKvyTyB+oQKqm/2I/jS6Rrt3B7fNuig/4bY2vi3bg=
github.com/fluxcd/pkg/ssa v0.58.0 h1:W7m2LQFsZxPN9nn3lfGVDwXsZnIgCWWJ/+/K5hpzW+k= github.com/fluxcd/pkg/ssa v0.59.0 h1:c88Q5w9e0MgrEi3Z7/+FWEVvtJFaVHfA9sxreMJUR7g=
github.com/fluxcd/pkg/ssa v0.58.0/go.mod h1:iN/QDMqdJaVXKkqwbXqGa4PyWQwtyIy2WkeM2+9kfXA= github.com/fluxcd/pkg/ssa v0.59.0/go.mod h1:iN/QDMqdJaVXKkqwbXqGa4PyWQwtyIy2WkeM2+9kfXA=
github.com/fluxcd/pkg/ssh v0.21.0 h1:ZmyF0n9je0cTTkOpvFVgIhmdx9qtswnVE60TK4IzJh0= github.com/fluxcd/pkg/ssh v0.21.0 h1:ZmyF0n9je0cTTkOpvFVgIhmdx9qtswnVE60TK4IzJh0=
github.com/fluxcd/pkg/ssh v0.21.0/go.mod h1:nX+gvJOmjf0E7lxq5mKKzDIdPEL2jOUQZbkBMS+mDtk= github.com/fluxcd/pkg/ssh v0.21.0/go.mod h1:nX+gvJOmjf0E7lxq5mKKzDIdPEL2jOUQZbkBMS+mDtk=
github.com/fluxcd/pkg/tar v0.14.0 h1:9Gku8FIvPt2bixKldZnzXJ/t+7SloxePlzyVGOK8GVQ= github.com/fluxcd/pkg/tar v0.14.0 h1:9Gku8FIvPt2bixKldZnzXJ/t+7SloxePlzyVGOK8GVQ=

Loading…
Cancel
Save