package cmd import ( "context" "fmt" "io/ioutil" "os" "path/filepath" "strings" "github.com/rancher/rke/cluster" "github.com/rancher/rke/hosts" "github.com/rancher/rke/log" "github.com/rancher/rke/pki" v3 "github.com/rancher/rke/types" "github.com/rancher/rke/util" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) var commonFlags = []cli.Flag{ cli.BoolFlag{ Name: "ssh-agent-auth", Usage: "Use SSH Agent Auth defined by SSH_AUTH_SOCK", }, cli.BoolFlag{ Name: "ignore-docker-version", Usage: "Disable Docker version check", }, } func resolveClusterFile(ctx *cli.Context) (string, string, error) { clusterFile := ctx.String("config") fp, err := filepath.Abs(clusterFile) if err != nil { return "", "", fmt.Errorf("failed to lookup current directory name: %v", err) } file, err := os.Open(fp) if err != nil { return "", "", fmt.Errorf("can not find cluster configuration file: %v", err) } defer file.Close() buf, err := ioutil.ReadAll(file) if err != nil { return "", "", fmt.Errorf("failed to read file: %v", err) } clusterFileBuff := string(buf) return clusterFileBuff, clusterFile, nil } func setOptionsFromCLI(c *cli.Context, rkeConfig *v3.RancherKubernetesEngineConfig) (*v3.RancherKubernetesEngineConfig, error) { // If true... override the file.. else let file value go through if c.Bool("ssh-agent-auth") { rkeConfig.SSHAgentAuth = c.Bool("ssh-agent-auth") } ignoreDockerVersion := c.Bool("ignore-docker-version") rkeConfig.IgnoreDockerVersion = &ignoreDockerVersion if c.Bool("s3") { if rkeConfig.Services.Etcd.BackupConfig == nil { rkeConfig.Services.Etcd.BackupConfig = &v3.BackupConfig{} } rkeConfig.Services.Etcd.BackupConfig.S3BackupConfig = setS3OptionsFromCLI(c) } return rkeConfig, nil } func ClusterInit(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig, dialersOptions hosts.DialersOptions, flags cluster.ExternalFlags) error { log.Infof(ctx, "Initiating Kubernetes cluster") var fullState *cluster.FullState stateFilePath := cluster.GetStateFilePath(flags.ClusterFilePath, flags.ConfigDir) if len(flags.CertificateDir) == 0 { flags.CertificateDir = cluster.GetCertificateDirPath(flags.ClusterFilePath, flags.ConfigDir) } rkeFullState, _ := cluster.ReadStateFile(ctx, stateFilePath) kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags, rkeFullState.DesiredState.EncryptionConfig) if err != nil { return err } if err := kubeCluster.SetupDialers(ctx, dialersOptions); err != nil { return err } err = checkLegacyCluster(ctx, kubeCluster, rkeFullState, flags) if err != nil { if strings.Contains(err.Error(), "aborting upgrade") { return err } log.Warnf(ctx, "[state] can't fetch legacy cluster state from Kubernetes: %v", err) } // check if certificate rotate or normal init if kubeCluster.RancherKubernetesEngineConfig.RotateCertificates != nil { fullState, err = rotateRKECertificates(ctx, kubeCluster, flags, rkeFullState) } else { fullState, err = cluster.RebuildState(ctx, kubeCluster, rkeFullState, flags) } if err != nil { return err } if fullState.DesiredState.EncryptionConfig != "" { kubeCluster.EncryptionConfig.EncryptionProviderFile = fullState.DesiredState.EncryptionConfig } rkeState := cluster.FullState{ DesiredState: fullState.DesiredState, CurrentState: fullState.CurrentState, } return rkeState.WriteStateFile(ctx, stateFilePath) } func setS3OptionsFromCLI(c *cli.Context) *v3.S3BackupConfig { endpoint := c.String("s3-endpoint") bucketName := c.String("bucket-name") region := c.String("region") accessKey := c.String("access-key") secretKey := c.String("secret-key") endpointCA := c.String("s3-endpoint-ca") folder := c.String("folder") var s3BackupBackend = &v3.S3BackupConfig{} if len(endpoint) != 0 { s3BackupBackend.Endpoint = endpoint } if len(bucketName) != 0 { s3BackupBackend.BucketName = bucketName } if len(region) != 0 { s3BackupBackend.Region = region } if len(accessKey) != 0 { s3BackupBackend.AccessKey = accessKey } if len(secretKey) != 0 { s3BackupBackend.SecretKey = secretKey } if len(endpointCA) != 0 { caStr, err := pki.ReadCertToStr(endpointCA) if err != nil { logrus.Warnf("Failed to read s3-endpoint-ca [%s]: %v", endpointCA, err) } else { s3BackupBackend.CustomCA = caStr } } if len(folder) != 0 { s3BackupBackend.Folder = folder } return s3BackupBackend } func checkLegacyCluster(ctx context.Context, kubeCluster *cluster.Cluster, fullState *cluster.FullState, flags cluster.ExternalFlags) error { stateFileExists, err := util.IsFileExists(kubeCluster.StateFilePath) if err != nil { return err } if stateFileExists { logrus.Debug("[state] previous state found, this is not a legacy cluster") return nil } logrus.Debug("[state] previous state not found, possible legacy cluster") return fetchAndUpdateStateFromLegacyCluster(ctx, kubeCluster, fullState, flags) } func fetchAndUpdateStateFromLegacyCluster(ctx context.Context, kubeCluster *cluster.Cluster, fullState *cluster.FullState, flags cluster.ExternalFlags) error { kubeConfigExists, err := util.IsFileExists(kubeCluster.LocalKubeConfigPath) if err != nil { return err } if !kubeConfigExists { // if kubeconfig doesn't exist and its a legacy cluster then error out if err := kubeCluster.TunnelHosts(ctx, flags); err != nil { return err } if recoveredCluster := cluster.GetStateFromNodes(ctx, kubeCluster); recoveredCluster != nil { return fmt.Errorf("This is a legacy cluster with no kube config, aborting upgrade. Please re-run rke up with rke 0.1.x to retrieve correct state") } return nil } // We have a kubeconfig and no current state. This is a legacy cluster or a new cluster with old kubeconfig // let's try to upgrade log.Infof(ctx, "[state] Possible legacy cluster detected, trying to upgrade") if err := cluster.RebuildKubeconfig(ctx, kubeCluster); err != nil { return err } recoveredCluster, err := cluster.GetStateFromKubernetes(ctx, kubeCluster) if err != nil { log.Warnf(ctx, "Failed to fetch state from kubernetes: %v", err) // try to fetch state from nodes err = kubeCluster.TunnelHosts(ctx, flags) if err != nil { return err } // fetching state/certs from nodes should be removed in rke 0.3.0 log.Infof(ctx, "[state] Fetching cluster state from Nodes") recoveredCluster = cluster.GetStateFromNodes(ctx, kubeCluster) } // if we found a recovered cluster, we will need override the current state recoveredCerts, err := cluster.GetClusterCertsFromKubernetes(ctx, kubeCluster) if err != nil { log.Warnf(ctx, "Failed to fetch certs from kubernetes: %v", err) // try to fetch certs from nodes recoveredCerts, err = cluster.GetClusterCertsFromNodes(ctx, kubeCluster) if err != nil { return fmt.Errorf("Failed to fetch cluster certs from nodes, aborting upgrade: %v", err) } } fullState.CurrentState.RancherKubernetesEngineConfig = kubeCluster.RancherKubernetesEngineConfig.DeepCopy() if recoveredCluster != nil { fullState.CurrentState.RancherKubernetesEngineConfig = recoveredCluster.RancherKubernetesEngineConfig.DeepCopy() } fullState.CurrentState.CertificatesBundle = recoveredCerts // we don't want to regenerate certificates fullState.DesiredState.CertificatesBundle = recoveredCerts return fullState.WriteStateFile(ctx, kubeCluster.StateFilePath) }