github.phpd.cn/cilium/cilium@v1.6.12/test/helpers/vagrant.go (about)

     1  // Copyright 2017 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package helpers
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  	"os/exec"
    23  	"strings"
    24  
    25  	"github.com/cilium/cilium/test/config"
    26  	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
    27  
    28  	"github.com/onsi/ginkgo"
    29  	"github.com/sirupsen/logrus"
    30  )
    31  
    32  // CreateVM creates a new vagrant server.Receives a scope which indicates the
    33  // target server that needs to be created. In case of any error on vagrant
    34  // [provision|up|ssh-config] error will be returned.
    35  func CreateVM(scope string) error {
    36  	createCMD := "vagrant up %s --provision"
    37  
    38  	for _, v := range Status(scope) {
    39  		switch v {
    40  		case "running":
    41  			createCMD = "vagrant provision %s"
    42  		case "not_created":
    43  			createCMD = "vagrant up %s --provision"
    44  		default:
    45  			// Sometimes servers are stoped and not destroyed. Destroy VM just in case
    46  			DestroyVM(scope)
    47  		}
    48  	}
    49  	createCMD = fmt.Sprintf(createCMD, scope)
    50  	log.Infof("Vagrant:Create: running '%s'", createCMD)
    51  	cmd := getCmd(createCMD)
    52  	stdout, err := cmd.StdoutPipe()
    53  	if err != nil {
    54  		return fmt.Errorf("error getting stdout: %s", err)
    55  	}
    56  	stderr, err := cmd.StderrPipe()
    57  	if err != nil {
    58  		return fmt.Errorf("error getting stderr: %s", err)
    59  	}
    60  
    61  	globalWriter := ginkgoext.NewWriter(ginkgo.GinkgoWriter)
    62  
    63  	go io.Copy(globalWriter, stderr)
    64  	go io.Copy(globalWriter, stdout)
    65  
    66  	if err := cmd.Start(); err != nil {
    67  		log.WithFields(logrus.Fields{
    68  			"command": createCMD,
    69  			"err":     err,
    70  		}).Fatalf("Create error on start")
    71  		return err
    72  	}
    73  	result := cmd.Wait()
    74  	io.Copy(ginkgoext.GinkgoWriter, globalWriter.Buffer)
    75  	return result
    76  }
    77  
    78  // GetVagrantSSHMetadata returns a string containing the output of `vagrant ssh-config`
    79  // for the provided Vagrant of name vmName. Returns an error if
    80  // `vagrant ssh-config` fails to execute.
    81  func GetVagrantSSHMetadata(vmName string) ([]byte, error) {
    82  	// debugVMs is used when ssh-config returns error and be able to debug the
    83  	// virtual machines status.
    84  	debugVms := func() {
    85  		cmd := getCmd("vagrant status --machine-readable")
    86  		output, _ := cmd.CombinedOutput()
    87  		fmt.Fprintf(&config.TestLogWriter, "Vagrant status on failure:\n%s\n", output)
    88  	}
    89  
    90  	var stdout, stderr bytes.Buffer
    91  	cmd := getCmd(fmt.Sprintf("vagrant ssh-config %s", vmName))
    92  	if config.CiliumTestConfig.SSHConfig != "" {
    93  		cmd = getCmd(config.CiliumTestConfig.SSHConfig)
    94  		debugVms = func() {} // not apply the debug helper due is a dev env.
    95  	}
    96  	cmd.Stdout = &stdout
    97  	cmd.Stderr = &stderr
    98  
    99  	err := cmd.Run()
   100  	if err != nil {
   101  		fmt.Fprintf(&config.TestLogWriter, "cmd='%s %s'\noutput:\n%s\nstderr:\n%s\n",
   102  			cmd.Path, strings.Join(cmd.Args, " "), stdout.String(), stderr.String())
   103  		debugVms()
   104  		return nil, err
   105  	}
   106  	return stdout.Bytes(), nil
   107  }
   108  
   109  //DestroyVM destroys all running Vagrant VMs in the provided scope. It returns an
   110  //error if deletion of either the VMs fails
   111  func DestroyVM(scope string) error {
   112  	command := fmt.Sprintf("vagrant destroy -f %s ", scope)
   113  	cmd := getCmd(command)
   114  	_, err := cmd.CombinedOutput()
   115  	if err != nil {
   116  		return err
   117  	}
   118  	return nil
   119  }
   120  
   121  func getCmd(vmCommand string) *exec.Cmd {
   122  	log.Infof("Vagrant: running command '%v'", vmCommand)
   123  	cmd := exec.Command(getPath("bash"), "-c", vmCommand)
   124  	cmd.Dir = getDir()
   125  	return cmd
   126  }
   127  
   128  func getDir() string {
   129  	dir, err := os.Getwd()
   130  	if err != nil {
   131  		return "/tmp/"
   132  	}
   133  	return fmt.Sprintf("%s/", dir)
   134  }
   135  
   136  func getPath(prog string) string {
   137  	path, err := exec.LookPath(prog)
   138  	if err != nil {
   139  		return ""
   140  	}
   141  	return path
   142  }
   143  
   144  //Status returns a mapping of Vagrant VM name to its status
   145  func Status(key string) map[string]string {
   146  	result := map[string]string{}
   147  
   148  	cmd := getCmd(fmt.Sprintf("vagrant status %s --machine-readable", key))
   149  	data, err := cmd.CombinedOutput()
   150  	if err != nil {
   151  		return result
   152  	}
   153  	for _, line := range strings.Split(string(data), "\n") {
   154  		val := strings.Split(line, ",")
   155  		if len(val) > 2 && val[2] == "state" {
   156  			result[val[1]] = val[3]
   157  		}
   158  	}
   159  	return result
   160  }