github.com/pingcap/tiup@v1.15.1/components/cluster/command/import.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package command
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  
    22  	"github.com/fatih/color"
    23  	"github.com/pingcap/tiup/pkg/cluster/ansible"
    24  	"github.com/pingcap/tiup/pkg/cluster/ctxt"
    25  	"github.com/pingcap/tiup/pkg/cluster/spec"
    26  	"github.com/pingcap/tiup/pkg/tui"
    27  	"github.com/pingcap/tiup/pkg/utils"
    28  	"github.com/spf13/cobra"
    29  )
    30  
    31  func newImportCmd() *cobra.Command {
    32  	var (
    33  		ansibleDir        string
    34  		inventoryFileName string
    35  		ansibleCfgFile    string
    36  		rename            string
    37  		noBackup          bool
    38  	)
    39  
    40  	cmd := &cobra.Command{
    41  		Use:   "import",
    42  		Short: "Import an exist TiDB cluster from TiDB-Ansible",
    43  		RunE: func(cmd *cobra.Command, args []string) error {
    44  			// Use current directory as ansibleDir by default
    45  			if ansibleDir == "" {
    46  				cwd, err := os.Getwd()
    47  				if err != nil {
    48  					return err
    49  				}
    50  				ansibleDir = cwd
    51  			}
    52  
    53  			ctx := ctxt.New(
    54  				context.Background(),
    55  				gOpt.Concurrency,
    56  				log,
    57  			)
    58  
    59  			// migrate cluster metadata from Ansible inventory
    60  			clsName, clsMeta, inv, err := ansible.ReadInventory(ctx, ansibleDir, inventoryFileName)
    61  			if err != nil {
    62  				return err
    63  			}
    64  
    65  			// Rename the imported cluster
    66  			if rename != "" {
    67  				clsName = rename
    68  			}
    69  			if clsName == "" {
    70  				return fmt.Errorf("cluster name should not be empty")
    71  			}
    72  
    73  			exist, err := tidbSpec.Exist(clsName)
    74  			if err != nil {
    75  				return err
    76  			}
    77  
    78  			if exist {
    79  				return errDeployNameDuplicate.
    80  					New("Cluster name '%s' is duplicated", clsName).
    81  					WithProperty(tui.SuggestionFromFormat(
    82  						fmt.Sprintf("Please use --rename `NAME` to specify another name (You can use `%s list` to see all clusters)", tui.OsArgs0())))
    83  			}
    84  
    85  			// prompt for backups
    86  			backupDir := spec.ClusterPath(clsName, "ansible-backup")
    87  			backupFile := filepath.Join(ansibleDir, fmt.Sprintf("tiup-%s.bak", inventoryFileName))
    88  			prompt := fmt.Sprintf("The ansible directory will be moved to %s after import.", backupDir)
    89  			if noBackup {
    90  				log.Infof("The '--no-backup' flag is set, the ansible directory will be kept at its current location.")
    91  				prompt = fmt.Sprintf("The inventory file will be renamed to %s after import.", backupFile)
    92  			}
    93  			log.Warnf("TiDB-Ansible and TiUP Cluster can NOT be used together, please DO NOT try to use ansible to manage the imported cluster anymore to avoid metadata conflict.")
    94  			log.Infof(prompt)
    95  			if !skipConfirm {
    96  				err = tui.PromptForConfirmOrAbortError("Do you want to continue? [y/N]: ")
    97  				if err != nil {
    98  					return err
    99  				}
   100  			}
   101  
   102  			if !skipConfirm {
   103  				err = tui.PromptForConfirmOrAbortError(
   104  					"Prepared to import TiDB %s cluster %s.\nDo you want to continue? [y/N]:",
   105  					clsMeta.Version,
   106  					clsName)
   107  				if err != nil {
   108  					return err
   109  				}
   110  			}
   111  
   112  			// parse config and import nodes
   113  			if err = ansible.ParseAndImportInventory(
   114  				ctx,
   115  				ansibleDir,
   116  				ansibleCfgFile,
   117  				clsMeta,
   118  				inv,
   119  				gOpt.SSHTimeout,
   120  				gOpt.SSHType,
   121  			); err != nil {
   122  				return err
   123  			}
   124  
   125  			// copy SSH key to TiUP profile directory
   126  			if err = utils.MkdirAll(spec.ClusterPath(clsName, "ssh"), 0755); err != nil {
   127  				return err
   128  			}
   129  			srcKeyPathPriv := ansible.SSHKeyPath()
   130  			srcKeyPathPub := srcKeyPathPriv + ".pub"
   131  			dstKeyPathPriv := spec.ClusterPath(clsName, "ssh", "id_rsa")
   132  			dstKeyPathPub := dstKeyPathPriv + ".pub"
   133  			if err = utils.Copy(srcKeyPathPriv, dstKeyPathPriv); err != nil {
   134  				return err
   135  			}
   136  			if err = utils.Copy(srcKeyPathPub, dstKeyPathPub); err != nil {
   137  				return err
   138  			}
   139  
   140  			// copy config files form deployment servers
   141  			if err = ansible.ImportConfig(ctx, clsName, clsMeta, gOpt); err != nil {
   142  				return err
   143  			}
   144  
   145  			// copy config detail to meta file
   146  			if err = ansible.LoadConfig(clsName, clsMeta); err != nil {
   147  				return err
   148  			}
   149  
   150  			if err = spec.SaveClusterMeta(clsName, clsMeta); err != nil {
   151  				return err
   152  			}
   153  
   154  			// comment config to avoid duplicated copy
   155  			if err = ansible.CommentConfig(clsName); err != nil {
   156  				return err
   157  			}
   158  
   159  			// backup ansible files
   160  			if noBackup {
   161  				// rename original TiDB-Ansible inventory file
   162  				if err = utils.Move(filepath.Join(ansibleDir, inventoryFileName), backupFile); err != nil {
   163  					return err
   164  				}
   165  				log.Infof("Ansible inventory renamed to %s.", color.HiCyanString(backupFile))
   166  			} else {
   167  				// move original TiDB-Ansible directory to a staged location
   168  				if err = utils.Move(ansibleDir, backupDir); err != nil {
   169  					return err
   170  				}
   171  				log.Infof("Ansible inventory saved in %s.", color.HiCyanString(backupDir))
   172  			}
   173  
   174  			log.Infof("Cluster %s imported.", clsName)
   175  			fmt.Printf("Try `%s` to show node list and status of the cluster.\n",
   176  				color.HiYellowString("%s display %s", tui.OsArgs0(), clsName))
   177  			return nil
   178  		},
   179  	}
   180  
   181  	cmd.Flags().StringVarP(&ansibleDir, "dir", "d", "", "The path to TiDB-Ansible directory")
   182  	cmd.Flags().StringVar(&inventoryFileName, "inventory", ansible.AnsibleInventoryFile, "The name of inventory file")
   183  	cmd.Flags().StringVar(&ansibleCfgFile, "ansible-config", ansible.AnsibleConfigFile, "The path to ansible.cfg")
   184  	cmd.Flags().StringVarP(&rename, "rename", "r", "", "Rename the imported cluster to `NAME`")
   185  	cmd.Flags().BoolVar(&noBackup, "no-backup", false, "Don't backup ansible dir, useful when there're multiple inventory files")
   186  
   187  	return cmd
   188  }