/* Copyright 2019 The Kubeedge Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package util import ( "fmt" //"os" "os/exec" "strings" //types "github.com/kubeedge/kubeedge/keadm/app/cmd/common" types "elcli/cmd/common" ) const downloadRetryTimes int = 3 // Ubuntu releases const ( UbuntuXenial = "xenial" UbuntuBionic = "bionic" ) //UbuntuOS struct objects shall have information of the tools version to be installed //on Hosts having Ubuntu OS. //It implements OSTypeInstaller interface type UbuntuOS struct { DockerVersion string KubernetesVersion string KubeEdgeVersion string IsEdgeNode bool //True - Edgenode False - EliotCloudnode K8SImageRepository string K8SPodNetworkCidr string } //SetDockerVersion sets the Docker version for the objects instance func (u *UbuntuOS) SetDockerVersion(version string) { u.DockerVersion = version } //SetK8SVersionAndIsNodeFlag sets the K8S version for the objects instance //It also sets if this host shall act as edge node or not func (u *UbuntuOS) SetK8SVersionAndIsNodeFlag(version string, flag bool) { u.KubernetesVersion = version u.IsEdgeNode = flag } //SetK8SImageRepoAndPodNetworkCidr sets the K8S image Repository and pod network // cidr. func (u *UbuntuOS) SetK8SImageRepoAndPodNetworkCidr(repo, cidr string) { u.K8SImageRepository = repo u.K8SPodNetworkCidr = cidr } //SetKubeEdgeVersion sets the KubeEdge version for the objects instance func (u *UbuntuOS) SetKubeEdgeVersion(version string) { u.KubeEdgeVersion = version } //IsToolVerInRepo checks if the tool mentioned in available in OS repo or not func (u *UbuntuOS) IsToolVerInRepo(toolName, version string) (bool, error) { //Check if requested Docker or K8S components said version is available in OS repo or not chkToolVer := fmt.Sprintf("apt-cache madison '%s' | grep -w %s | head -1 | awk '{$1=$1};1' | cut -d' ' -f 3", toolName, version) cmd := &Command{Cmd: exec.Command("sh", "-c", chkToolVer)} cmd.ExecuteCommand() stdout := cmd.GetStdOutput() errout := cmd.GetStdErr() if errout != "" { return false, fmt.Errorf("%s", errout) } if stdout != "" { fmt.Println(toolName, stdout, "is available in OS repo") return true, nil } fmt.Println(toolName, "version", version, "not found in OS repo") return false, nil } func (u *UbuntuOS) addDockerRepositoryAndUpdate() error { //lsb_release -cs cmd := &Command{Cmd: exec.Command("sh", "-c", "lsb_release -cs")} cmd.ExecuteCommand() distVersion := cmd.GetStdOutput() if distVersion == "" { return fmt.Errorf("ubuntu dist version not available") } fmt.Println("Ubuntu distribution version is", distVersion) //'apt-get update' stdout, err := runCommandWithShell("apt-get update") if err != nil { return err } fmt.Println(stdout) //"curl -fsSL \"$DOWNLOAD_URL/linux/$lsb_dist/gpg\" | apt-key add" //Get the GPG key curl := fmt.Sprintf("curl -fsSL \"%s/linux/%s/gpg\" | apt-key add", DefaultDownloadURL, UbuntuOSType) cmd = &Command{Cmd: exec.Command("sh", "-c", curl)} cmd.ExecuteCommand() curlOutput := cmd.GetStdOutput() if curlOutput == "" { return fmt.Errorf("not able add the apt key") } fmt.Println(curlOutput) //Add the repo in OS source.list aptRepo := fmt.Sprintf("deb [arch=$(dpkg --print-architecture)] %s/linux/%s %s stable", DefaultDownloadURL, UbuntuOSType, distVersion) updtRepo := fmt.Sprintf("echo \"%s\" > /etc/apt/sources.list.d/docker.list", aptRepo) cmd = &Command{Cmd: exec.Command("sh", "-c", updtRepo)} cmd.ExecuteCommand() updtRepoErr := cmd.GetStdErr() if updtRepoErr != "" { return fmt.Errorf("not able add update repo due to error : %s", updtRepoErr) } //Do an apt-get update stdout, err = runCommandWithShell("apt-get update") if err != nil { return err } fmt.Println(stdout) return nil } //IsDockerInstalled checks if docker is installed in the host or not func (u *UbuntuOS) IsDockerInstalled(defVersion string) (types.InstallState, error) { cmd := &Command{Cmd: exec.Command("sh", "-c", "docker -v | cut -d ' ' -f3 | cut -d ',' -f1")} cmd.ExecuteCommand() str := cmd.GetStdOutput() if strings.Contains(str, u.DockerVersion) { return types.AlreadySameVersionExist, nil } if err := u.addDockerRepositoryAndUpdate(); err != nil { return types.VersionNAInRepo, err } if str == "" { return types.NewInstallRequired, nil } isReqVerAvail, err := u.IsToolVerInRepo("docker-ce", u.DockerVersion) if err != nil { return types.VersionNAInRepo, err } var isDefVerAvail bool if u.DockerVersion != defVersion { isDefVerAvail, err = u.IsToolVerInRepo("docker-ce", defVersion) if err != nil { return types.VersionNAInRepo, err } } if isReqVerAvail { return types.NewInstallRequired, nil } if isDefVerAvail { return types.DefVerInstallRequired, nil } return types.VersionNAInRepo, nil } //InstallDocker will install the specified docker in the host func (u *UbuntuOS) InstallDocker() error { fmt.Println("Installing ", u.DockerVersion, "version of docker") //Do an apt-get install instPreReq := fmt.Sprintf("apt-get install -y %s", DockerPreqReqList) stdout, err := runCommandWithShell(instPreReq) if err != nil { return err } fmt.Println(stdout) //Get the exact version string from OS repo, so that it can search and install. chkDockerVer := fmt.Sprintf("apt-cache madison 'docker-ce' | grep %s | head -1 | awk '{$1=$1};1' | cut -d' ' -f 3", u.DockerVersion) cmd := &Command{Cmd: exec.Command("sh", "-c", chkDockerVer)} cmd.ExecuteCommand() stdout = cmd.GetStdOutput() errout := cmd.GetStdErr() if errout != "" { return fmt.Errorf("%s", errout) } fmt.Println("Expected docker version to install is", stdout) //Install docker-ce dockerInst := fmt.Sprintf("apt-get install -y --allow-change-held-packages --allow-downgrades docker-ce=%s", stdout) stdout, err = runCommandWithShell(dockerInst) if err != nil { return err } fmt.Println(stdout) fmt.Println("Docker", u.DockerVersion, "version is installed in this Host") return nil } //IsK8SComponentInstalled checks if said K8S version is already installed in the host func (u *UbuntuOS) IsK8SComponentInstalled(component, defVersion string) (types.InstallState, error) { find := fmt.Sprintf("dpkg -l | grep %s | awk '{print $3}'", component) cmd := &Command{Cmd: exec.Command("sh", "-c", find)} cmd.ExecuteCommand() str := cmd.GetStdOutput() if strings.Contains(str, u.KubernetesVersion) { return types.AlreadySameVersionExist, nil } if err := u.addK8SRepositoryAndUpdate(); err != nil { return types.VersionNAInRepo, err } if str == "" { return types.NewInstallRequired, nil } isReqVerAvail, err := u.IsToolVerInRepo(component, u.KubernetesVersion) if err != nil { return types.VersionNAInRepo, err } var isDefVerAvail bool if u.KubernetesVersion != defVersion { isDefVerAvail, _ = u.IsToolVerInRepo(component, defVersion) if err != nil { return types.VersionNAInRepo, err } } if isReqVerAvail { return types.NewInstallRequired, nil } if isDefVerAvail { return types.DefVerInstallRequired, nil } return types.VersionNAInRepo, nil } func (u *UbuntuOS) addK8SRepositoryAndUpdate() error { //Get the distribution version cmd := &Command{Cmd: exec.Command("sh", "-c", "lsb_release -cs")} cmd.ExecuteCommand() distVersion := cmd.GetStdOutput() if distVersion == "" { return fmt.Errorf("ubuntu dist version not available") } fmt.Println("Ubuntu distribution version is", distVersion) distVersionForSuite := distVersion if distVersion == UbuntuBionic { // No bionic-specific version is available on apt.kubernetes.io. // Use xenial version instead. distVersionForSuite = UbuntuXenial } suite := fmt.Sprintf("kubernetes-%s", distVersionForSuite) fmt.Println("Deb suite to use:", suite) //Do an apt-get update stdout, err := runCommandWithShell("apt-get update") if err != nil { return err } fmt.Println(stdout) //curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - //Get the GPG key curl := fmt.Sprintf("curl -s %s | apt-key add -", KubernetesGPGURL) cmd = &Command{Cmd: exec.Command("sh", "-c", curl)} cmd.ExecuteCommand() curlOutput := cmd.GetStdOutput() curlErr := cmd.GetStdErr() if curlOutput == "" || curlErr != "" { return fmt.Errorf("not able add the apt key due to error : %s", curlErr) } fmt.Println(curlOutput) //Add K8S repo to local apt-get source.list aptRepo := fmt.Sprintf("deb %s %s main", KubernetesDownloadURL, suite) updtRepo := fmt.Sprintf("echo \"%s\" > /etc/apt/sources.list.d/kubernetes.list", aptRepo) cmd = &Command{Cmd: exec.Command("sh", "-c", updtRepo)} cmd.ExecuteCommand() updtRepoErr := cmd.GetStdErr() if updtRepoErr != "" { return fmt.Errorf("not able add update repo due to error : %s", updtRepoErr) } //Do an apt-get update stdout, err = runCommandWithShell("apt-get update") if err != nil { return err } fmt.Println(stdout) return nil } //InstallK8S will install kubeadm, kudectl and kubelet for the cloud node func (u *UbuntuOS) InstallK8S() error { k8sComponent := "kubeadm" fmt.Println("Installing", k8sComponent, u.KubernetesVersion, "version") //Get the exact version string from OS repo, so that it can search and install. chkKubeadmVer := fmt.Sprintf("apt-cache madison '%s' | grep %s | head -1 | awk '{$1=$1};1' | cut -d' ' -f 3", k8sComponent, u.KubernetesVersion) cmd := &Command{Cmd: exec.Command("sh", "-c", chkKubeadmVer)} cmd.ExecuteCommand() stdout := cmd.GetStdOutput() errout := cmd.GetStdErr() if errout != "" { return fmt.Errorf("%s", errout) } fmt.Println("Expected K8S('", k8sComponent, "') version to install is", stdout) //Install respective K8S components based on where it is being installed k8sInst := fmt.Sprintf("apt-get install -y --allow-change-held-packages --allow-downgrades kubeadm=%s kubelet=%s kubectl=%s", stdout, stdout, stdout) stdout, err := runCommandWithShell(k8sInst) if err != nil { return err } fmt.Println(stdout) fmt.Println(k8sComponent, "version", u.KubernetesVersion, "is installed in this Host") return nil } //StartK8Scluster will do "kubeadm init" and cluster will be started func (u *UbuntuOS) StartK8Scluster() error { var install bool cmd := &Command{Cmd: exec.Command("sh", "-c", "kubeadm version")} cmd.ExecuteCommand() str := cmd.GetStdOutput() if str != "" { install = true } else { install = false } if install == true { k8sInit := fmt.Sprintf("swapoff -a && kubeadm init --image-repository \"%s\" --pod-network-cidr=%s", u.K8SImageRepository, u.K8SPodNetworkCidr) stdout, err := runCommandWithShell(k8sInit) if err != nil { return err } fmt.Println(stdout) stdout, err = runCommandWithShell("mkdir -p $HOME/.kube && cp -r /etc/kubernetes/admin.conf $HOME/.kube/config && sudo chown $(id -u):$(id -g) $HOME/.kube/config") if err != nil { return err } fmt.Println(stdout) } else { return fmt.Errorf("kubeadm not installed in this host") } fmt.Println("Kubeadm init successfully executed") return nil } // // runCommandWithShell executes the given command with "sh -c". // // It returns an error if the command outputs anything on the stderr. // func runCommandWithShell(command string) (string, error) { // cmd := &Command{Cmd: exec.Command("sh", "-c", command)} // err := cmd.ExecuteCmdShowOutput() // if err != nil { // return "", err // } // errout := cmd.GetStdErr() // if errout != "" { // return "", fmt.Errorf("%s", errout) // } // return cmd.GetStdOutput(), nil // }