github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/integration/runner/runner.go (about) 1 package runner 2 3 import ( 4 "fmt" 5 "net" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strconv" 10 "syscall" 11 "time" 12 13 "github.com/cloudfoundry-incubator/garden/client" 14 "github.com/cloudfoundry-incubator/garden/client/connection" 15 "github.com/onsi/ginkgo" 16 "github.com/pivotal-golang/lager" 17 "github.com/pivotal-golang/lager/lagertest" 18 "github.com/tedsuo/ifrit" 19 "github.com/tedsuo/ifrit/ginkgomon" 20 ) 21 22 type Runner struct { 23 Command *exec.Cmd 24 25 network string 26 addr string 27 28 bin string 29 argv []string 30 31 binPath string 32 rootFSPath string 33 34 tmpdir string 35 graphPath string 36 } 37 38 func New(network, addr string, bin, binPath, rootFSPath, graphPath string, argv ...string) *Runner { 39 tmpDir := filepath.Join( 40 os.TempDir(), 41 fmt.Sprintf("test-garden-%d", ginkgo.GinkgoParallelNode()), 42 ) 43 44 if graphPath == "" { 45 graphPath = filepath.Join(tmpDir, "graph") 46 } 47 48 return &Runner{ 49 network: network, 50 addr: addr, 51 52 bin: bin, 53 argv: argv, 54 55 binPath: binPath, 56 rootFSPath: rootFSPath, 57 graphPath: graphPath, 58 tmpdir: tmpDir, 59 } 60 } 61 62 func (r *Runner) Run(signals <-chan os.Signal, ready chan<- struct{}) error { 63 logger := lagertest.NewTestLogger("garden-runner") 64 65 err := os.MkdirAll(r.tmpdir, 0755) 66 if err != nil { 67 return err 68 } 69 70 depotPath := filepath.Join(r.tmpdir, "containers") 71 overlaysPath := filepath.Join(r.tmpdir, "overlays") 72 snapshotsPath := filepath.Join(r.tmpdir, "snapshots") 73 74 if err := os.MkdirAll(depotPath, 0755); err != nil { 75 return err 76 } 77 78 if err := os.MkdirAll(snapshotsPath, 0755); err != nil { 79 return err 80 } 81 82 MustMountTmpfs(overlaysPath) 83 MustMountTmpfs(r.graphPath) 84 85 var appendDefaultFlag = func(ar []string, key, value string) []string { 86 for _, a := range r.argv { 87 if a == key { 88 return ar 89 } 90 } 91 92 if value != "" { 93 return append(ar, key, value) 94 } else { 95 return append(ar, key) 96 } 97 } 98 99 gardenArgs := make([]string, len(r.argv)) 100 copy(gardenArgs, r.argv) 101 102 gardenArgs = appendDefaultFlag(gardenArgs, "--listenNetwork", r.network) 103 gardenArgs = appendDefaultFlag(gardenArgs, "--listenAddr", r.addr) 104 gardenArgs = appendDefaultFlag(gardenArgs, "--bin", r.binPath) 105 if r.rootFSPath != "" { //rootfs is an optional parameter 106 gardenArgs = appendDefaultFlag(gardenArgs, "--rootfs", r.rootFSPath) 107 } 108 gardenArgs = appendDefaultFlag(gardenArgs, "--depot", depotPath) 109 gardenArgs = appendDefaultFlag(gardenArgs, "--overlays", overlaysPath) 110 gardenArgs = appendDefaultFlag(gardenArgs, "--snapshots", snapshotsPath) 111 gardenArgs = appendDefaultFlag(gardenArgs, "--graph", r.graphPath) 112 gardenArgs = appendDefaultFlag(gardenArgs, "--logLevel", "debug") 113 gardenArgs = appendDefaultFlag(gardenArgs, "--disableQuotas", "") 114 gardenArgs = appendDefaultFlag(gardenArgs, "--networkPool", fmt.Sprintf("10.250.%d.0/24", ginkgo.GinkgoParallelNode())) 115 gardenArgs = appendDefaultFlag(gardenArgs, "--portPoolStart", strconv.Itoa(51000+(1000*ginkgo.GinkgoParallelNode()))) 116 gardenArgs = appendDefaultFlag(gardenArgs, "--portPoolSize", "1000") 117 gardenArgs = appendDefaultFlag(gardenArgs, "--tag", strconv.Itoa(ginkgo.GinkgoParallelNode())) 118 119 var signal os.Signal 120 121 r.Command = exec.Command(r.bin, gardenArgs...) 122 123 process := ifrit.Invoke(&ginkgomon.Runner{ 124 Name: "garden-linux", 125 Command: r.Command, 126 AnsiColorCode: "31m", 127 StartCheck: "garden-linux.started", 128 StartCheckTimeout: 10 * time.Second, 129 Cleanup: func() { 130 if signal == syscall.SIGQUIT { 131 logger.Info("cleanup-tempdirs") 132 if err := os.RemoveAll(r.tmpdir); err != nil { 133 logger.Error("cleanup-tempdirs-failed", err, lager.Data{"tmpdir": r.tmpdir}) 134 } else { 135 logger.Info("tempdirs-removed") 136 } 137 } 138 }, 139 }) 140 141 close(ready) 142 143 for { 144 select { 145 case signal = <-signals: 146 // SIGQUIT means clean up the containers, the garden process (SIGTERM) and the temporary directories 147 // SIGKILL, SIGTERM and SIGINT are passed through to the garden process 148 if signal == syscall.SIGQUIT { 149 logger.Info("received-signal SIGQUIT") 150 if err := r.destroyContainers(); err != nil { 151 logger.Error("destroy-containers-failed", err) 152 return err 153 } 154 logger.Info("destroyed-containers") 155 process.Signal(syscall.SIGTERM) 156 } else { 157 logger.Info("received-signal", lager.Data{"signal": signal}) 158 process.Signal(signal) 159 } 160 161 case waitErr := <-process.Wait(): 162 logger.Info("process-exited") 163 return waitErr 164 } 165 } 166 } 167 168 func (r *Runner) TryDial() error { 169 conn, dialErr := net.DialTimeout(r.network, r.addr, 100*time.Millisecond) 170 171 if dialErr == nil { 172 conn.Close() 173 return nil 174 } 175 176 return dialErr 177 } 178 179 func (r *Runner) NewClient() client.Client { 180 return client.New(connection.New(r.network, r.addr)) 181 } 182 183 func (r *Runner) destroyContainers() error { 184 client := r.NewClient() 185 186 containers, err := client.Containers(nil) 187 if err != nil { 188 return err 189 } 190 191 for _, container := range containers { 192 err := client.Destroy(container.Handle()) 193 if err != nil { 194 return err 195 } 196 } 197 198 return nil 199 }