github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/integration/runner/runner.go (about)

     1  package runner
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"path"
     9  	"path/filepath"
    10  	"strconv"
    11  	"syscall"
    12  	"time"
    13  
    14  	"github.com/cloudfoundry-incubator/garden-shed/pkg/retrier"
    15  	"github.com/cloudfoundry-incubator/garden/client"
    16  	"github.com/cloudfoundry-incubator/garden/client/connection"
    17  	"github.com/onsi/ginkgo"
    18  	. "github.com/onsi/ginkgo"
    19  	. "github.com/onsi/gomega"
    20  	"github.com/onsi/gomega/gbytes"
    21  	"github.com/pivotal-golang/clock"
    22  	"github.com/pivotal-golang/lager"
    23  	"github.com/pivotal-golang/lager/lagertest"
    24  	"github.com/tedsuo/ifrit"
    25  	"github.com/tedsuo/ifrit/ginkgomon"
    26  )
    27  
    28  var RootFSPath = os.Getenv("GARDEN_TEST_ROOTFS")
    29  var GraphRoot = os.Getenv("GARDEN_TEST_GRAPHPATH")
    30  var BinPath = "../../linux_backend/bin"
    31  var GardenBin = "../../out/garden-linux"
    32  
    33  type RunningGarden struct {
    34  	client.Client
    35  	process ifrit.Process
    36  	runner  *ginkgomon.Runner
    37  
    38  	Pid int
    39  
    40  	tmpdir        string
    41  	GraphRoot     string
    42  	GraphPath     string
    43  	StateDirPath  string
    44  	DepotPath     string
    45  	SnapshotsPath string
    46  
    47  	logger lager.Logger
    48  }
    49  
    50  func Start(argv ...string) *RunningGarden {
    51  	gardenAddr := fmt.Sprintf("/tmp/garden_%d.sock", GinkgoParallelNode())
    52  	return start("unix", gardenAddr, argv...)
    53  }
    54  
    55  func start(network, addr string, argv ...string) *RunningGarden {
    56  	tmpDir := filepath.Join(
    57  		os.TempDir(),
    58  		fmt.Sprintf("test-garden-%d", ginkgo.GinkgoParallelNode()),
    59  	)
    60  	Expect(os.MkdirAll(tmpDir, 0755)).To(Succeed())
    61  
    62  	if GraphRoot == "" {
    63  		GraphRoot = filepath.Join(tmpDir, "graph")
    64  	}
    65  
    66  	graphPath := filepath.Join(GraphRoot, fmt.Sprintf("node-%d", ginkgo.GinkgoParallelNode()))
    67  	stateDirPath := filepath.Join(tmpDir, "state")
    68  	depotPath := filepath.Join(tmpDir, "containers")
    69  	snapshotsPath := filepath.Join(tmpDir, "snapshots")
    70  
    71  	if err := os.MkdirAll(stateDirPath, 0755); err != nil {
    72  		Expect(err).ToNot(HaveOccurred())
    73  	}
    74  
    75  	if err := os.MkdirAll(depotPath, 0755); err != nil {
    76  		Expect(err).ToNot(HaveOccurred())
    77  	}
    78  
    79  	if err := os.MkdirAll(snapshotsPath, 0755); err != nil {
    80  		Expect(err).ToNot(HaveOccurred())
    81  	}
    82  
    83  	MustMountTmpfs(graphPath)
    84  
    85  	r := &RunningGarden{
    86  		GraphRoot:     GraphRoot,
    87  		GraphPath:     graphPath,
    88  		StateDirPath:  stateDirPath,
    89  		DepotPath:     depotPath,
    90  		SnapshotsPath: snapshotsPath,
    91  		tmpdir:        tmpDir,
    92  		logger:        lagertest.NewTestLogger("garden-runner"),
    93  
    94  		Client: client.New(connection.New(network, addr)),
    95  	}
    96  
    97  	c := cmd(stateDirPath, depotPath, snapshotsPath, graphPath, network, addr, GardenBin, BinPath, RootFSPath, argv...)
    98  	r.runner = ginkgomon.New(ginkgomon.Config{
    99  		Name:              "garden-linux",
   100  		Command:           c,
   101  		AnsiColorCode:     "31m",
   102  		StartCheck:        "garden-linux.started",
   103  		StartCheckTimeout: 30 * time.Second,
   104  	})
   105  
   106  	r.process = ifrit.Invoke(r.runner)
   107  	r.Pid = c.Process.Pid
   108  
   109  	return r
   110  }
   111  
   112  func (r *RunningGarden) Buffer() *gbytes.Buffer {
   113  	return r.runner.Buffer()
   114  }
   115  
   116  func (r *RunningGarden) Kill() error {
   117  	r.process.Signal(syscall.SIGKILL)
   118  	select {
   119  	case err := <-r.process.Wait():
   120  		return err
   121  	case <-time.After(time.Second * 10):
   122  		r.process.Signal(syscall.SIGKILL)
   123  		return errors.New("timed out waiting for garden to shutdown after 10 seconds")
   124  	}
   125  }
   126  
   127  func (r *RunningGarden) DestroyAndStop() error {
   128  	if err := r.DestroyContainers(); err != nil {
   129  		return err
   130  	}
   131  
   132  	if err := r.Stop(); err != nil {
   133  		return err
   134  	}
   135  
   136  	return nil
   137  }
   138  
   139  func (r *RunningGarden) Stop() error {
   140  	r.process.Signal(syscall.SIGTERM)
   141  	select {
   142  	case err := <-r.process.Wait():
   143  		return err
   144  	case <-time.After(time.Second * 10):
   145  		r.process.Signal(syscall.SIGKILL)
   146  		return errors.New("timed out waiting for garden to shutdown after 10 seconds")
   147  	}
   148  }
   149  
   150  func cmd(stateDirPath, depotPath, snapshotsPath, graphPath, network, addr, bin, binPath, RootFSPath string, argv ...string) *exec.Cmd {
   151  	appendDefaultFlag := func(ar []string, key, value string) []string {
   152  		for _, a := range argv {
   153  			if a == key {
   154  				return ar
   155  			}
   156  		}
   157  
   158  		if value != "" {
   159  			return append(ar, key, value)
   160  		} else {
   161  			return append(ar, key)
   162  		}
   163  	}
   164  
   165  	hasFlag := func(ar []string, key string) bool {
   166  		for _, a := range ar {
   167  			if a == key {
   168  				return true
   169  			}
   170  		}
   171  
   172  		return false
   173  	}
   174  
   175  	gardenArgs := make([]string, len(argv))
   176  	copy(gardenArgs, argv)
   177  
   178  	gardenArgs = appendDefaultFlag(gardenArgs, "--listenNetwork", network)
   179  	gardenArgs = appendDefaultFlag(gardenArgs, "--listenAddr", addr)
   180  	gardenArgs = appendDefaultFlag(gardenArgs, "--bin", binPath)
   181  	if RootFSPath != "" { //rootfs is an optional parameter
   182  		gardenArgs = appendDefaultFlag(gardenArgs, "--rootfs", RootFSPath)
   183  	}
   184  	gardenArgs = appendDefaultFlag(gardenArgs, "--stateDir", stateDirPath)
   185  	gardenArgs = appendDefaultFlag(gardenArgs, "--depot", depotPath)
   186  	gardenArgs = appendDefaultFlag(gardenArgs, "--snapshots", snapshotsPath)
   187  	gardenArgs = appendDefaultFlag(gardenArgs, "--graph", graphPath)
   188  	gardenArgs = appendDefaultFlag(gardenArgs, "--logLevel", "debug")
   189  	gardenArgs = appendDefaultFlag(gardenArgs, "--networkPool", fmt.Sprintf("10.250.%d.0/24", ginkgo.GinkgoParallelNode()))
   190  	gardenArgs = appendDefaultFlag(gardenArgs, "--portPoolStart", strconv.Itoa(51000+(1000*ginkgo.GinkgoParallelNode())))
   191  	gardenArgs = appendDefaultFlag(gardenArgs, "--portPoolSize", "1000")
   192  	gardenArgs = appendDefaultFlag(gardenArgs, "--tag", strconv.Itoa(ginkgo.GinkgoParallelNode()))
   193  
   194  	if !hasFlag(gardenArgs, "-enableGraphCleanup=false") {
   195  		gardenArgs = appendDefaultFlag(gardenArgs, "--enableGraphCleanup", "")
   196  	}
   197  
   198  	gardenArgs = appendDefaultFlag(gardenArgs, "--debugAddr", fmt.Sprintf(":808%d", ginkgo.GinkgoParallelNode()))
   199  	return exec.Command(bin, gardenArgs...)
   200  }
   201  
   202  func (r *RunningGarden) Cleanup() error {
   203  	// unmount aufs since the docker graph driver leaves this around,
   204  	// otherwise the following commands might fail
   205  	retry := &retrier.Retrier{
   206  		Timeout:         100 * time.Second,
   207  		PollingInterval: 500 * time.Millisecond,
   208  		Clock:           clock.NewClock(),
   209  	}
   210  
   211  	err := retry.Retry(func() error {
   212  		if err := os.RemoveAll(path.Join(r.GraphPath, "aufs")); err == nil {
   213  			return nil // if we can remove it, it's already unmounted
   214  		}
   215  
   216  		err := syscall.Unmount(path.Join(r.GraphPath, "aufs"), 0)
   217  		r.logger.Error("failed-unmount-attempt", err)
   218  		return err
   219  	})
   220  
   221  	if err != nil {
   222  		r.logger.Error("failed-to-unmount", err)
   223  		return err
   224  	}
   225  
   226  	MustUnmountTmpfs(r.GraphPath)
   227  
   228  	// In the kernel version 3.19.0-33-generic the code bellow results in
   229  	// hanging the running VM. We are not deleting the node-X directories. They
   230  	// are empty and the next test will re-use them. We will stick with that
   231  	// workaround until we can test on a newer kernel that will hopefully not
   232  	// have this bug.
   233  	//
   234  	// if err := os.RemoveAll(r.logger, r.GraphPath); err != nil {
   235  	// 	r.logger.Error("remove-graph", err)
   236  	// 	return err
   237  	// }
   238  
   239  	r.logger.Info("cleanup-tempdirs")
   240  	if err := os.RemoveAll(r.tmpdir); err != nil {
   241  		r.logger.Error("cleanup-tempdirs-failed", err, lager.Data{"tmpdir": r.tmpdir})
   242  		return err
   243  	}
   244  	r.logger.Info("tempdirs-removed")
   245  
   246  	return nil
   247  }
   248  
   249  func (r *RunningGarden) DestroyContainers() error {
   250  	containers, err := r.Containers(nil)
   251  	if err != nil {
   252  		return err
   253  	}
   254  
   255  	for _, container := range containers {
   256  		err := r.Destroy(container.Handle())
   257  		if err != nil {
   258  			return err
   259  		}
   260  	}
   261  
   262  	return nil
   263  }