diff --git a/cmd/cluster.go b/cmd/cluster.go index 6795165a..7afc37db 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -37,7 +37,7 @@ var clusterCmd = &cobra.Command{ ksVersion = "" } logger := util.InitLogger(opt.Verbose) - return install.CreateCluster(opt.ClusterCfgFile, opt.Kubernetes, ksVersion, logger, opt.Kubesphere, opt.Verbose, opt.SkipCheck, opt.SkipPullImages, opt.InCluster) + return install.CreateCluster(opt.ClusterCfgFile, opt.Kubernetes, ksVersion, logger, opt.Kubesphere, opt.Verbose, opt.SkipCheck, opt.SkipPullImages, opt.InCluster, opt.DownloadCmd) }, } @@ -49,6 +49,8 @@ func init() { clusterCmd.Flags().BoolVarP(&opt.Kubesphere, "with-kubesphere", "", false, "Deploy a specific version of kubesphere (default v3.0.0)") clusterCmd.Flags().BoolVarP(&opt.SkipCheck, "yes", "y", false, "Skip pre-check of the installation") clusterCmd.Flags().BoolVarP(&opt.SkipPullImages, "skip-pull-images", "", false, "Skip pre pull images") + clusterCmd.Flags().StringVarP(&opt.DownloadCmd, "download-cmd", "", "curl -L -o %s %s", + `The user defined command to download the necessary binary files. The first param '%s' is output path, the second param '%s', is the URL`) if err := setValidArgs(clusterCmd); err != nil { panic(fmt.Sprintf("Got error with the completion setting")) diff --git a/cmd/root.go b/cmd/root.go index 69639548..294c81f1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -39,6 +39,7 @@ type Options struct { SourcesDir string AddImagesRepo bool InCluster bool + DownloadCmd string } var ( diff --git a/pkg/cluster/preinstall/preinstall.go b/pkg/cluster/preinstall/preinstall.go index 7ee66cc2..1822903e 100644 --- a/pkg/cluster/preinstall/preinstall.go +++ b/pkg/cluster/preinstall/preinstall.go @@ -54,7 +54,7 @@ func FilesDownloadHTTP(mgr *manager.Manager, filepath, version, arch string) err kubectl.Url = fmt.Sprintf("https://kubernetes-release.pek3b.qingstor.com/release/%s/bin/linux/%s/kubectl", kubectl.Version, kubectl.Arch) kubecni.Url = fmt.Sprintf("https://containernetworking.pek3b.qingstor.com/plugins/releases/download/%s/cni-plugins-linux-%s-%s.tgz", kubecni.Version, kubecni.Arch, kubecni.Version) helm.Url = fmt.Sprintf("https://kubernetes-helm.pek3b.qingstor.com/linux-%s/%s/helm", helm.Arch, helm.Version) - helm.GetCmd = fmt.Sprintf("curl -o %s %s", helm.Path, helm.Url) + helm.GetCmd = mgr.DownloadCommand(helm.Path, helm.Url) } else { etcd.Url = fmt.Sprintf("https://github.com/coreos/etcd/releases/download/%s/etcd-%s-linux-%s.tar.gz", etcd.Version, etcd.Version, etcd.Arch) kubeadm.Url = fmt.Sprintf("https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/%s/kubeadm", kubeadm.Version, kubeadm.Arch) @@ -62,14 +62,15 @@ func FilesDownloadHTTP(mgr *manager.Manager, filepath, version, arch string) err kubectl.Url = fmt.Sprintf("https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/%s/kubectl", kubectl.Version, kubectl.Arch) kubecni.Url = fmt.Sprintf("https://github.com/containernetworking/plugins/releases/download/%s/cni-plugins-linux-%s-%s.tgz", kubecni.Version, kubecni.Arch, kubecni.Version) helm.Url = fmt.Sprintf("https://get.helm.sh/helm-%s-linux-%s.tar.gz", helm.Version, helm.Arch) - helm.GetCmd = fmt.Sprintf("curl -o %s/helm-%s-linux-%s.tar.gz %s && cd %s && tar -zxf helm-%s-linux-%s.tar.gz && mv linux-%s/helm . && rm -rf *linux-%s*", filepath, helm.Version, helm.Arch, helm.Url, filepath, helm.Version, helm.Arch, helm.Arch, helm.Arch) + getCmd := mgr.DownloadCommand(fmt.Sprintf("%s/helm-%s-linux-%s.tar.gz", filepath, helm.Version, helm.Arch), helm.Url) + helm.GetCmd = fmt.Sprintf("%s && cd %s && tar -zxf helm-%s-linux-%s.tar.gz && mv linux-%s/helm . && rm -rf *linux-%s*", getCmd, filepath, helm.Version, helm.Arch, helm.Arch, helm.Arch) } - kubeadm.GetCmd = fmt.Sprintf("curl -L -o %s %s", kubeadm.Path, kubeadm.Url) - kubelet.GetCmd = fmt.Sprintf("curl -L -o %s %s", kubelet.Path, kubelet.Url) - kubectl.GetCmd = fmt.Sprintf("curl -L -o %s %s", kubectl.Path, kubectl.Url) - kubecni.GetCmd = fmt.Sprintf("curl -L -o %s %s", kubecni.Path, kubecni.Url) - etcd.GetCmd = fmt.Sprintf("curl -L -o %s %s", etcd.Path, etcd.Url) + kubeadm.GetCmd = mgr.DownloadCommand(kubeadm.Path, kubeadm.Url) + kubelet.GetCmd = mgr.DownloadCommand(kubelet.Path, kubelet.Url) + kubectl.GetCmd = mgr.DownloadCommand(kubectl.Path, kubectl.Url) + kubecni.GetCmd = mgr.DownloadCommand(kubecni.Path, kubecni.Url) + etcd.GetCmd = mgr.DownloadCommand(etcd.Path, etcd.Url) binaries := []files.KubeBinary{kubeadm, kubelet, kubectl, helm, kubecni, etcd} @@ -78,37 +79,41 @@ func FilesDownloadHTTP(mgr *manager.Manager, filepath, version, arch string) err continue } mgr.Logger.Infoln(fmt.Sprintf("Downloading %s ...", binary.Name)) - if util.IsExist(binary.Path) == false { - for i := 5; i > 0; i-- { - if output, err := exec.Command("/bin/sh", "-c", binary.GetCmd).CombinedOutput(); err != nil { - fmt.Println(string(output)) - if kkzone != "cn" { - mgr.Logger.Warningln("Having a problem with accessing https://storage.googleapis.com? You can try again after setting environment 'export KKZONE=cn'") - } - return errors.New(fmt.Sprintf("Failed to download %s binary: %s", binary.Name, binary.GetCmd)) - } - - if err := SHA256Check(binary, version); err != nil { - if i == 1 { - return err - } - _ = exec.Command("/bin/sh", "-c", fmt.Sprintf("rm -f %s", binary.Path)).Run() - continue - } - break - } - } else { + if util.IsExist(binary.Path) { + // download it again if it's incorrect if err := SHA256Check(binary, version); err != nil { - return err + _ = exec.Command("/bin/sh", "-c", fmt.Sprintf("rm -f %s", binary.Path)).Run() + } else { + continue } } + + for i := 5; i > 0; i-- { + if output, err := exec.Command("/bin/sh", "-c", binary.GetCmd).CombinedOutput(); err != nil { + fmt.Println(string(output)) + + if kkzone != "cn" { + mgr.Logger.Warningln("Having a problem with accessing https://storage.googleapis.com? You can try again after setting environment 'export KKZONE=cn'") + } + return errors.New(fmt.Sprintf("Failed to download %s binary: %s", binary.Name, binary.GetCmd)) + } + + if err := SHA256Check(binary, version); err != nil { + if i == 1 { + return err + } + _ = exec.Command("/bin/sh", "-c", fmt.Sprintf("rm -f %s", binary.Path)).Run() + continue + } + break + } } if mgr.Cluster.KubeSphere.Version == "v2.1.1" { mgr.Logger.Infoln(fmt.Sprintf("Downloading %s ...", "helm2")) if util.IsExist(fmt.Sprintf("%s/helm2", filepath)) == false { - cmd := fmt.Sprintf("curl -o %s/helm2 %s", filepath, fmt.Sprintf("https://kubernetes-helm.pek3b.qingstor.com/linux-%s/%s/helm", helm.Arch, "v2.16.9")) + cmd := mgr.DownloadCommand(fmt.Sprintf("%s/helm2", filepath), fmt.Sprintf("https://kubernetes-helm.pek3b.qingstor.com/linux-%s/%s/helm", helm.Arch, "v2.16.9")) if output, err := exec.Command("/bin/sh", "-c", cmd).CombinedOutput(); err != nil { fmt.Println(string(output)) return errors.Wrap(err, "Failed to download helm2 binary") diff --git a/pkg/install/install.go b/pkg/install/install.go index b0f6924d..6f660aa6 100644 --- a/pkg/install/install.go +++ b/pkg/install/install.go @@ -42,7 +42,7 @@ import ( ) // CreateCluster is used to create cluster based on the given parameters or configuration file. -func CreateCluster(clusterCfgFile, k8sVersion, ksVersion string, logger *log.Logger, ksEnabled, verbose, skipCheck, skipPullImages, inCluster bool) error { +func CreateCluster(clusterCfgFile, k8sVersion, ksVersion string, logger *log.Logger, ksEnabled, verbose, skipCheck, skipPullImages, inCluster bool, downloadCmd string) error { currentDir, err := filepath.Abs(filepath.Dir(os.Args[0])) if err != nil { return errors.Wrap(err, "Failed to get current dir") @@ -70,7 +70,14 @@ func CreateCluster(clusterCfgFile, k8sVersion, ksVersion string, logger *log.Log clientset = c } - return Execute(executor.NewExecutor(&cfg.Spec, objName, logger, "", verbose, skipCheck, skipPullImages, false, inCluster, clientset)) + executorInstance := executor.NewExecutor(&cfg.Spec, objName, logger, "", verbose, skipCheck, skipPullImages, false, inCluster, clientset) + executorInstance.DownloadCommand = func(path, url string) string { + // this is an extension point for downloading tools, for example users can set the timeout, proxy or retry under + // some poor network environment. Or users even can choose another cli, it might be wget. + // perhaps we should have a build-in download function instead of totally rely on the external one + return fmt.Sprintf(downloadCmd, path, url) + } + return Execute(executorInstance) } // ExecTasks is used to schedule and execute installation tasks. diff --git a/pkg/util/executor/executor.go b/pkg/util/executor/executor.go index 9105285e..3e2c5a8c 100644 --- a/pkg/util/executor/executor.go +++ b/pkg/util/executor/executor.go @@ -29,16 +29,17 @@ import ( ) type Executor struct { - ObjName string - Cluster *kubekeyapiv1alpha1.ClusterSpec - Logger *log.Logger - SourcesDir string - Debug bool - SkipCheck bool - SkipPullImages bool - AddImagesRepo bool - InCluster bool - ClientSet *kubekeyclientset.Clientset + ObjName string + Cluster *kubekeyapiv1alpha1.ClusterSpec + Logger *log.Logger + SourcesDir string + Debug bool + SkipCheck bool + SkipPullImages bool + AddImagesRepo bool + InCluster bool + ClientSet *kubekeyclientset.Clientset + DownloadCommand func(path, url string) string } func NewExecutor(cluster *kubekeyapiv1alpha1.ClusterSpec, objName string, logger *log.Logger, sourcesDir string, debug, skipCheck, skipPullImages, addImagesRepo, inCluster bool, clientset *kubekeyclientset.Clientset) *Executor { @@ -82,6 +83,7 @@ func (executor *Executor) CreateManager() (*manager.Manager, error) { mgr.ObjName = executor.ObjName mgr.InCluster = executor.InCluster mgr.ClientSet = executor.ClientSet + mgr.DownloadCommand = executor.DownloadCommand if executor.Cluster.Kubernetes.ContainerManager == "" || executor.Cluster.Kubernetes.ContainerManager == "docker" { mgr.EtcdContainer = true } diff --git a/pkg/util/manager/manager.go b/pkg/util/manager/manager.go index 2272d3e1..5ef4d21c 100644 --- a/pkg/util/manager/manager.go +++ b/pkg/util/manager/manager.go @@ -26,30 +26,31 @@ import ( // Manager defines all the parameters needed for the installation. type Manager struct { - ObjName string - Cluster *kubekeyapiv1alpha1.ClusterSpec - Logger log.FieldLogger - Connector *ssh.Dialer - Runner *runner.Runner - AllNodes []kubekeyapiv1alpha1.HostCfg - EtcdNodes []kubekeyapiv1alpha1.HostCfg - MasterNodes []kubekeyapiv1alpha1.HostCfg - WorkerNodes []kubekeyapiv1alpha1.HostCfg - K8sNodes []kubekeyapiv1alpha1.HostCfg - EtcdContainer bool - ClusterHosts []string - WorkDir string - KsEnable bool - KsVersion string - Debug bool - SkipCheck bool - SkipPullImages bool - SourcesDir string - AddImagesRepo bool - InCluster bool - Kubeconfig string - Conditions []kubekeyapiv1alpha1.Condition - ClientSet *kubekeyclientset.Clientset + ObjName string + Cluster *kubekeyapiv1alpha1.ClusterSpec + Logger log.FieldLogger + Connector *ssh.Dialer + Runner *runner.Runner + AllNodes []kubekeyapiv1alpha1.HostCfg + EtcdNodes []kubekeyapiv1alpha1.HostCfg + MasterNodes []kubekeyapiv1alpha1.HostCfg + WorkerNodes []kubekeyapiv1alpha1.HostCfg + K8sNodes []kubekeyapiv1alpha1.HostCfg + EtcdContainer bool + ClusterHosts []string + WorkDir string + KsEnable bool + KsVersion string + Debug bool + SkipCheck bool + SkipPullImages bool + SourcesDir string + AddImagesRepo bool + InCluster bool + Kubeconfig string + Conditions []kubekeyapiv1alpha1.Condition + ClientSet *kubekeyclientset.Clientset + DownloadCommand func(path, url string) string } // Copy is used to create a copy for Manager.