ELIOT Command Line Interface Commit
[eliot.git] / blueprints / common / elcli / elcli / cmd / util / common.go
1 /*
2 Copyright 2019 The ELIOT Team .
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 package util
18
19 import (
20         "bytes"
21         "fmt"
22         "io"
23         "os"
24         "os/exec"
25         "strings"
26         "sync"
27         
28
29         "github.com/spf13/pflag"
30         types "elcli/cmd/common"
31 )
32
33 //Constants used by installers
34 const (
35         UbuntuOSType = "ubuntu"
36         CentOSType   = "centos"
37
38         DefaultDownloadURL = "https://download.docker.com"
39         DockerPreqReqList  = "apt-transport-https ca-certificates curl gnupg-agent software-properties-common"
40
41         KubernetesDownloadURL = "https://apt.kubernetes.io/"
42         KubernetesGPGURL      = "https://packages.cloud.google.com/apt/doc/apt-key.gpg"
43
44         KubeAPIServerName          = "kube-apiserver"
45 )
46
47 //AddToolVals gets the value and default values of each flags and collects them in temporary cache
48 func AddToolVals(f *pflag.Flag, flagData map[string]types.FlagData) {
49         flagData[f.Name] = types.FlagData{Val: f.Value.String(), DefVal: f.DefValue}
50 }
51
52 //CheckIfAvailable checks is val of a flag is empty then return the default value
53 func CheckIfAvailable(val, defval string) string {
54         if val == "" {
55                 return defval
56         }
57         return val
58 }
59
60 //Common struct contains OS and Tool version properties and also embeds OS interface
61 type Common struct {
62         types.OSTypeInstaller
63         OSVersion   string
64         ToolVersion string
65         KubeConfig  string
66 }
67
68 //SetOSInterface defines a method to set the implemtation of the OS interface
69 func (co *Common) SetOSInterface(intf types.OSTypeInstaller) {
70         co.OSTypeInstaller = intf
71 }
72
73 //Command defines commands to be executed and captures std out and std error
74 type Command struct {
75         Cmd    *exec.Cmd
76         StdOut []byte
77         StdErr []byte
78 }
79
80 //ExecuteCommand executes the command and captures the output in stdOut
81 func (cm *Command) ExecuteCommand() {
82         var err error
83         cm.StdOut, err = cm.Cmd.Output()
84         if err != nil {
85                 fmt.Println("Output failed: ", err)
86                 cm.StdErr = []byte(err.Error())
87         }
88 }
89
90 //GetStdOutput gets StdOut field
91 func (cm Command) GetStdOutput() string {
92         if len(cm.StdOut) != 0 {
93                 return strings.TrimRight(string(cm.StdOut), "\n")
94         }
95         return ""
96 }
97
98 //GetStdErr gets StdErr field
99 func (cm Command) GetStdErr() string {
100         if len(cm.StdErr) != 0 {
101                 return strings.TrimRight(string(cm.StdErr), "\n")
102         }
103         return ""
104 }
105
106 //ExecuteCmdShowOutput captures both StdOut and StdErr after exec.cmd().
107 //It helps in the commands where it takes some time for execution.
108 func (cm Command) ExecuteCmdShowOutput() error {
109         var stdoutBuf, stderrBuf bytes.Buffer
110         stdoutIn, _ := cm.Cmd.StdoutPipe()
111         stderrIn, _ := cm.Cmd.StderrPipe()
112
113         var errStdout, errStderr error
114         stdout := io.MultiWriter(os.Stdout, &stdoutBuf)
115         stderr := io.MultiWriter(os.Stderr, &stderrBuf)
116         err := cm.Cmd.Start()
117         if err != nil {
118                 return fmt.Errorf("failed to start '%s' because of error : %s", strings.Join(cm.Cmd.Args, " "), err.Error())
119         }
120
121         var wg sync.WaitGroup
122         wg.Add(1)
123
124         go func() {
125                 _, errStdout = io.Copy(stdout, stdoutIn)
126                 wg.Done()
127         }()
128
129         _, errStderr = io.Copy(stderr, stderrIn)
130         wg.Wait()
131
132         err = cm.Cmd.Wait()
133         if err != nil {
134                 return fmt.Errorf("failed to run '%s' because of error : %s", strings.Join(cm.Cmd.Args, " "), err.Error())
135         }
136         if errStdout != nil || errStderr != nil {
137                 return fmt.Errorf("failed to capture stdout or stderr")
138         }
139
140         cm.StdOut, cm.StdErr = stdoutBuf.Bytes(), stderrBuf.Bytes()
141         return nil
142 }
143
144 //GetOSVersion gets the OS name
145 func GetOSVersion() string {
146         c := &Command{Cmd: exec.Command("sh", "-c", ". /etc/os-release && echo $ID")}
147         c.ExecuteCommand()
148         return c.GetStdOutput()
149 }
150
151 //GetOSInterface helps in returning OS specific object which implements OSTypeInstaller interface.
152 func GetOSInterface() types.OSTypeInstaller {
153
154         switch GetOSVersion() {
155         case UbuntuOSType:
156                 return &UbuntuOS{}
157         case CentOSType:
158                 return &CentOS{}
159         default:
160                 panic("This OS version is currently un-supported by keadm")
161         }
162 }
163
164 // IsCloudCore identifies if the node is having cloudcore and kube-apiserver already running.
165 // If so, then return true, else it can used as edge node and initialise it.
166 func IsCloudCore() (types.ModuleRunning, error) {
167         //osType := GetOSInterface()
168
169         //If any of cloudcore or K8S API server is running, then we believe the node is cloud node
170
171         return types.NoneRunning, nil
172 }
173
174 //IsK8SClusterRunning check whether Kubernetes Master is running already on the server in which ELIOT Setup command is executed
175 //Currently there is no check on the ELIOT Edge Nodes. 
176 func IsK8SClusterRunning() (bool, error) {
177         shK8SClusterRunning := fmt.Sprintf("ps aux | grep kube- | grep -v grep | wc -l")
178         cmd := &Command {Cmd : exec.Command ("sh" , "-c" ,shK8SClusterRunning)}
179         cmd.ExecuteCommand()
180         stdOut:= cmd.GetStdOutput()
181         errOut:= cmd.GetStdErr()
182
183         if errOut != "" {
184                 return false, fmt.Errorf("%s", errOut)
185         }
186         if stdOut != "" {
187                 return true, nil
188         }
189         return false,nil
190
191 }
192
193 // runCommandWithShell executes the given command with "sh -c".
194 // It returns an error if the command outputs anything on the stderr.
195 func runCommandWithShell(command string) (string, error) {
196         cmd := &Command{Cmd: exec.Command("sh", "-c", command)}
197         err := cmd.ExecuteCmdShowOutput()
198         if err != nil {
199                 return "", err
200         }
201         errout := cmd.GetStdErr()
202         if errout != "" {
203                 return "", fmt.Errorf("%s", errout)
204         }
205         return cmd.GetStdOutput(), nil
206 }