mirror of
https://github.com/outbackdingo/kamaji.git
synced 2026-01-27 10:19:29 +00:00
* feat(migration): cleanup prior migration When using the annotation `kamaji.clastix.io/cleanup-prior-migration` with a true boolean value, Kamaji will perform a clean-up on the target DataStore to avoid stale resources when back and forth migrations occur. Signed-off-by: Dario Tranchitella <dario@tranchitella.eu> * docs: cleanup prior migration Signed-off-by: Dario Tranchitella <dario@tranchitella.eu> --------- Signed-off-by: Dario Tranchitella <dario@tranchitella.eu>
136 lines
4.4 KiB
Go
136 lines
4.4 KiB
Go
// Copyright 2022 Clastix Labs
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package migrate
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
|
"github.com/clastix/kamaji/internal/datastore"
|
|
)
|
|
|
|
func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
|
// CLI flags
|
|
var (
|
|
tenantControlPlane string
|
|
targetDataStore string
|
|
cleanupPriorMigration bool
|
|
timeout time.Duration
|
|
)
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "migrate",
|
|
Short: "Migrate the data of a TenantControlPlane to another compatible DataStore",
|
|
SilenceUsage: true,
|
|
RunE: func(*cobra.Command, []string) error {
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), timeout)
|
|
defer cancelFn()
|
|
|
|
log := ctrl.Log
|
|
|
|
log.Info("generating the controller-runtime client")
|
|
|
|
client, err := ctrlclient.New(ctrl.GetConfigOrDie(), ctrlclient.Options{
|
|
Scheme: scheme,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
parts := strings.Split(tenantControlPlane, string(types.Separator))
|
|
if len(parts) != 2 {
|
|
return fmt.Errorf("non well-formed namespaced name for the tenant control plane, expected <NAMESPACE>/NAME, fot %s", tenantControlPlane)
|
|
}
|
|
|
|
log.Info("retrieving the TenantControlPlane")
|
|
|
|
tcp := &kamajiv1alpha1.TenantControlPlane{}
|
|
if err = client.Get(ctx, types.NamespacedName{Namespace: parts[0], Name: parts[1]}, tcp); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info("retrieving the TenantControlPlane used DataStore")
|
|
|
|
originDs := &kamajiv1alpha1.DataStore{}
|
|
if err = client.Get(ctx, types.NamespacedName{Name: tcp.Status.Storage.DataStoreName}, originDs); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info("retrieving the target DataStore")
|
|
|
|
targetDs := &kamajiv1alpha1.DataStore{}
|
|
if err = client.Get(ctx, types.NamespacedName{Name: targetDataStore}, targetDs); err != nil {
|
|
return err
|
|
}
|
|
|
|
if tcp.Status.Storage.Driver != string(targetDs.Spec.Driver) {
|
|
return fmt.Errorf("migration between DataStore with different driver is not supported")
|
|
}
|
|
|
|
if tcp.Status.Storage.DataStoreName == targetDs.GetName() {
|
|
return fmt.Errorf("cannot migrate to the same DataStore")
|
|
}
|
|
|
|
log.Info("generating the origin storage connection")
|
|
|
|
originConnection, err := datastore.NewStorageConnection(ctx, client, *originDs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer originConnection.Close()
|
|
|
|
log.Info("generating the target storage connection")
|
|
|
|
targetConnection, err := datastore.NewStorageConnection(ctx, client, *targetDs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer targetConnection.Close()
|
|
|
|
if cleanupPriorMigration {
|
|
log.Info("Checking if target DataStore should be clean-up prior migration")
|
|
|
|
if exists, _ := targetConnection.DBExists(ctx, tcp.Status.Storage.Setup.Schema); exists {
|
|
log.Info("A colliding schema on target DataStore is present, cleaning up")
|
|
|
|
if dErr := targetConnection.DeleteDB(ctx, tcp.Status.Storage.Setup.Schema); dErr != nil {
|
|
return fmt.Errorf("error cleaning up prior migration: %s", dErr.Error())
|
|
}
|
|
|
|
log.Info("Cleaning up prior migration has been completed")
|
|
}
|
|
}
|
|
// Start migrating from the old Datastore to the new one
|
|
log.Info("migration from origin to target started")
|
|
|
|
if err = originConnection.Migrate(ctx, *tcp, targetConnection); err != nil {
|
|
return fmt.Errorf("unable to migrate data from %s to %s: %w", originDs.GetName(), targetDs.GetName(), err)
|
|
}
|
|
|
|
log.Info("migration completed")
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
cmd.Flags().StringVar(&tenantControlPlane, "tenant-control-plane", "", "Namespaced-name of the TenantControlPlane that must be migrated (e.g.: default/test)")
|
|
cmd.Flags().StringVar(&targetDataStore, "target-datastore", "", "Name of the Datastore to which the TenantControlPlane will be migrated")
|
|
cmd.Flags().BoolVar(&cleanupPriorMigration, "cleanup-prior-migration", false, "When set to true, migration job will drop existing data in the target DataStore: useful to avoid stale data when migrating back and forth between DataStores.")
|
|
cmd.Flags().DurationVar(&timeout, "timeout", 5*time.Minute, "Amount of time for the context timeout")
|
|
|
|
_ = cmd.MarkFlagRequired("tenant-control-plane")
|
|
_ = cmd.MarkFlagRequired("target-datastore")
|
|
|
|
return cmd
|
|
}
|