Skip to content

Commit

Permalink
consistent requirement checks (#29)
Browse files Browse the repository at this point in the history
- consistent requirement checks and some cleanup
- Adjusted requirement checks to use spinner logs for warnings
- Adjusted minikube version warning to include the version numbers as it was hard to guess which version es recommended
- Moved kubectl helper file into dedicated package
- Used verbose flag for kubectl and minikube commands
- Moved gliph in spinner message to start of line
- introduced new gliphs for spinner
- Have a better vmdriver default for windows
- Using simple spinner only for windows
  • Loading branch information
a-thaler authored Feb 26, 2019
1 parent f3bfa3f commit c0b3d26
Show file tree
Hide file tree
Showing 20 changed files with 608 additions and 495 deletions.
6 changes: 0 additions & 6 deletions internal/const_unix.go

This file was deleted.

6 changes: 0 additions & 6 deletions internal/const_windows.go

This file was deleted.

177 changes: 0 additions & 177 deletions internal/kubectl.go

This file was deleted.

205 changes: 205 additions & 0 deletions internal/kubectl/kubectl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package kubectl

import (
"bufio"
"bytes"
"fmt"
"os/exec"
"regexp"
"strings"
"time"

"github.com/Masterminds/semver"
yaml "gopkg.in/yaml.v2"
)

const (
kubectlVersion string = "1.11.0"
sleep = 5 * time.Second
)

//RunCmd executes a kubectl command with given arguments
func RunCmd(verbose bool, args ...string) (string, error) {
cmd := exec.Command("kubectl", args[0:]...)
return execCmd(cmd, strings.Join(args, " "), verbose)
}

//RunApplyCmd executes a kubectl apply command with given resources
func RunApplyCmd(resources []map[string]interface{}, verbose bool) (string, error) {
cmd := exec.Command("kubectl", "apply", "-f", "-")
buf := &bytes.Buffer{}
enc := yaml.NewEncoder(buf)
for _, y := range resources {
err := enc.Encode(y)
if err != nil {
return "", err
}
}
err := enc.Close()
if err != nil {
return "", err
}
cmd.Stdin = buf
return execCmd(cmd, fmt.Sprintf("apply -f -%s", resources), verbose)
}

func execCmd(cmd *exec.Cmd, inputText string, verbose bool) (string, error) {
out, err := cmd.CombinedOutput()
unquotedOut := strings.Replace(string(out), "'", "", -1)
if err != nil {
if verbose {
fmt.Printf("\nExecuted command:\n kubectl %s\nwith output:\n %s\nand error:\n %s\n", inputText, string(out), err)
}
return unquotedOut, fmt.Errorf("Failed executing kubectl command 'kubectl %s' with output '%s' and error message '%s'", inputText, out, err)
}
if verbose {
fmt.Printf("\nExecuted command:\n kubectl %s\nwith output:\n %s\n", inputText, string(out))
}
return unquotedOut, nil
}

//WaitForPodReady waits till a pod is deployed and has status 'running'.
// The pod gets identified by the namespace and a lebel key=value pair.
func WaitForPodReady(namespace string, labelName string, labelValue string, verbose bool) error {
for {
isDeployed, err := IsPodDeployed(namespace, labelName, labelValue, verbose)
if err != nil {
return err
}
if isDeployed {
break
}
time.Sleep(sleep)
}

for {
isReady, err := IsPodReady(namespace, labelName, labelValue, verbose)
if err != nil {
return err
}
if isReady {
break
}
time.Sleep(sleep)
}
return nil
}

//WaitForPodGone waits till a pod is not existent anymore.
// The pod gets identified by the namespace and a lebel key=value pair.
func WaitForPodGone(namespace string, labelName string, labelValue string, verbose bool) error {
for {
check, err := IsPodDeployed(namespace, labelName, labelValue, verbose)
if err != nil {
return err
}
if !check {
break
}
time.Sleep(sleep)
}
return nil
}

//IsPodDeployed checks if a pod is deployed.
// It will not wait till it is deployed.
// The pod gets identified by the namespace and a label key=value pair.
func IsPodDeployed(namespace string, labelName string, labelValue string, verbose bool) (bool, error) {
return IsResourceDeployed("pod", namespace, labelName, labelValue, verbose)
}

//IsResourceDeployed checks if a kubernetes resource is deployed.
// It will not wait till it is deployed.
// The resource gets identified by the namespace and a lebel key=value pair.
func IsResourceDeployed(resource string, namespace string, labelName string, labelValue string, verbose bool) (bool, error) {
resourceNames, err := RunCmd(verbose, "get", resource, "-n", namespace, "-l", labelName+"="+labelValue, "-o", "jsonpath='{.items[*].metadata.name}'")
if err != nil {
return false, err
}
if resourceNames == "" {
return false, nil
}
return true, nil
}

//IsClusterResourceDeployed checks if a kubernetes cluster resource is deployed.
// It will not wait till it is deployed.
// The resource gets identified by a lebel key=value pair.
func IsClusterResourceDeployed(resource string, labelName string, labelValue string, verbose bool) (bool, error) {
resourceNames, err := RunCmd(verbose, "get", resource, "-l", labelName+"="+labelValue, "-o", "jsonpath='{.items[*].metadata.name}'")
if err != nil {
return false, err
}
if resourceNames == "" {
return false, nil
}
return true, nil
}

//IsPodReady checks if a pod is deployed and running.
// It will not wait till it is deployed or running.
// The pod gets identified by the namespace and a lebel key=value pair.
func IsPodReady(namespace string, labelName string, labelValue string, verbose bool) (bool, error) {
podNames, err := RunCmd(verbose, "get", "pods", "-n", namespace, "-l", labelName+"="+labelValue, "-o", "jsonpath='{.items[*].metadata.name}'")
if err != nil {
return false, err
}

if podNames == "" {
return false, nil
}

scanner := bufio.NewScanner(strings.NewReader(podNames))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
if err := scanner.Err(); err != nil {
return false, err
}

pod := scanner.Text()
containerStatus, err := RunCmd(verbose, "get", "pod", pod, "-n", namespace, "-o", "jsonpath='{.status.containerStatuses[0].ready}'")
if err != nil {
return false, err
}

if containerStatus != "true" {
events, err := RunCmd(verbose, "get", "event", "-n", namespace, "-o", "go-template='{{range .items}}{{if eq .involvedObject.name \"'"+pod+"'\"}}{{.message}}{{\"\\n\"}}{{end}}{{end}}'")
if err != nil {
fmt.Printf("Error while checking for pod events '%s'\n‚", err)
}
if events != "" {
fmt.Printf("Status '%s'", events)
}
return false, nil
}
}
return true, nil
}

//CheckVersion assures that the kubectl version used is compatible
func CheckVersion(verbose bool) (string, error) {
versionText, err := RunCmd(verbose, "version", "--client", "--short")
if err != nil {
return "", err
}

exp, _ := regexp.Compile("Client Version: v(.*)")
versionString := exp.FindStringSubmatch(versionText)
version, err := semver.NewVersion(versionString[1])
if err != nil {
return "", err
}

constraintString := "~" + kubectlVersion
constraint, err := semver.NewConstraint(constraintString)
if err != nil {
return "", err
}

check := constraint.Check(version)
if check {
return "", nil
}

return fmt.Sprintf("You are using an unsupported kubectl version '%s'. This may not work. It is recommended to use kubectl version '%s'", version, kubectlVersion), nil
}
Loading

0 comments on commit c0b3d26

Please sign in to comment.