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 }