github.com/pingcap/tiup@v1.15.1/components/cluster/command/start.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  	"database/sql"
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/fatih/color"
    22  	"github.com/pingcap/tiup/pkg/cluster/spec"
    23  	"github.com/pingcap/tiup/pkg/cluster/task"
    24  	"github.com/pingcap/tiup/pkg/crypto/rand"
    25  	"github.com/pingcap/tiup/pkg/proxy"
    26  	"github.com/pingcap/tiup/pkg/utils"
    27  	"github.com/spf13/cobra"
    28  
    29  	// for sql/driver
    30  	_ "github.com/go-sql-driver/mysql"
    31  )
    32  
    33  func newStartCmd() *cobra.Command {
    34  	var (
    35  		initPasswd    bool
    36  		restoreLeader bool
    37  	)
    38  
    39  	cmd := &cobra.Command{
    40  		Use:   "start <cluster-name>",
    41  		Short: "Start a TiDB cluster",
    42  		RunE: func(cmd *cobra.Command, args []string) error {
    43  			if len(args) != 1 {
    44  				return cmd.Help()
    45  			}
    46  
    47  			if err := validRoles(gOpt.Roles); err != nil {
    48  				return err
    49  			}
    50  
    51  			clusterName := args[0]
    52  			clusterReport.ID = scrubClusterName(clusterName)
    53  			teleCommand = append(teleCommand, scrubClusterName(clusterName))
    54  
    55  			if err := cm.StartCluster(clusterName, gOpt, restoreLeader, func(b *task.Builder, metadata spec.Metadata) {
    56  				b.UpdateTopology(
    57  					clusterName,
    58  					tidbSpec.Path(clusterName),
    59  					metadata.(*spec.ClusterMeta),
    60  					nil, /* deleteNodeIds */
    61  				)
    62  			}); err != nil {
    63  				return err
    64  			}
    65  
    66  			// init password
    67  			if initPasswd {
    68  				pwd, err := initPassword(clusterName)
    69  				if err != nil {
    70  					log.Errorf("Failed to set root password of TiDB database to '%s'", pwd)
    71  					if strings.Contains(strings.ToLower(err.Error()), "error 1045") {
    72  						log.Errorf("Initializing is only working when the root password is empty")
    73  						log.Errorf(color.YellowString("Did you already set root password before?"))
    74  					}
    75  					return err
    76  				}
    77  				log.Warnf("The root password of TiDB database has been changed.")
    78  				fmt.Printf("The new password is: '%s'.\n", color.HiYellowString(pwd)) // use fmt to avoid printing to audit log
    79  				log.Warnf("Copy and record it to somewhere safe, %s, and will not be stored.", color.HiRedString("it is only displayed once"))
    80  				log.Warnf("The generated password %s.", color.HiRedString("can NOT be get and shown again"))
    81  			}
    82  			return nil
    83  		},
    84  		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
    85  			switch len(args) {
    86  			case 0:
    87  				return shellCompGetClusterName(cm, toComplete)
    88  			default:
    89  				return nil, cobra.ShellCompDirectiveNoFileComp
    90  			}
    91  		},
    92  	}
    93  
    94  	cmd.Flags().BoolVar(&initPasswd, "init", false, "Initialize a secure root password for the database")
    95  	cmd.Flags().BoolVar(&restoreLeader, "restore-leaders", false, "Allow leaders to be scheduled to stores after start")
    96  	cmd.Flags().StringSliceVarP(&gOpt.Roles, "role", "R", nil, "Only start specified roles")
    97  	cmd.Flags().StringSliceVarP(&gOpt.Nodes, "node", "N", nil, "Only start specified nodes")
    98  
    99  	_ = cmd.Flags().MarkHidden("restore-leaders")
   100  
   101  	return cmd
   102  }
   103  
   104  func initPassword(clusterName string) (string, error) {
   105  	metadata, err := spec.ClusterMetadata(clusterName)
   106  	if err != nil {
   107  		return "", err
   108  	}
   109  	tcpProxy := proxy.GetTCPProxy()
   110  
   111  	// generate password
   112  	pwd, err := rand.Password(18)
   113  	if err != nil {
   114  		return pwd, err
   115  	}
   116  
   117  	var lastErr error
   118  	for _, spec := range metadata.Topology.TiDBServers {
   119  		spec := spec
   120  		endpoint := utils.JoinHostPort(spec.Host, spec.Port)
   121  		if tcpProxy != nil {
   122  			closeC := tcpProxy.Run([]string{endpoint})
   123  			defer tcpProxy.Close(closeC)
   124  			endpoint = tcpProxy.GetEndpoints()[0]
   125  		}
   126  		db, err := createDB(endpoint)
   127  		if err != nil {
   128  			lastErr = err
   129  			continue
   130  		}
   131  		defer db.Close()
   132  
   133  		sqlStr := fmt.Sprintf("SET PASSWORD FOR 'root'@'%%' = '%s'; FLUSH PRIVILEGES;", pwd)
   134  		_, err = db.Exec(sqlStr)
   135  		if err != nil {
   136  			lastErr = err
   137  			continue
   138  		}
   139  		return pwd, nil
   140  	}
   141  
   142  	return pwd, lastErr
   143  }
   144  
   145  func createDB(endpoint string) (db *sql.DB, err error) {
   146  	dsn := fmt.Sprintf("root:@tcp(%s)/?charset=utf8mb4,utf8&multiStatements=true", endpoint)
   147  	db, err = sql.Open("mysql", dsn)
   148  
   149  	return
   150  }