github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/tests/testutils/ctx.go (about) 1 // Copyright 2015 The rkt Authors 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 testutils 16 17 import ( 18 "fmt" 19 "io/ioutil" 20 "os" 21 "os/exec" 22 "path/filepath" 23 "strings" 24 25 "github.com/coreos/rkt/Godeps/_workspace/src/github.com/steveeJ/gexpect" 26 "github.com/coreos/rkt/tests/testutils/logger" 27 ) 28 29 // dirDesc structure manages one directory and provides an option for 30 // rkt invocations 31 type dirDesc struct { 32 dir string // directory path 33 desc string // directory description, mostly for failure cases 34 prefix string // temporary directory prefix 35 option string // rkt option for given directory 36 } 37 38 // newDirDesc creates dirDesc instance managing a temporary directory. 39 func newDirDesc(prefix, desc, option string) *dirDesc { 40 dir := &dirDesc{ 41 dir: "", 42 desc: desc, 43 prefix: prefix, 44 option: option, 45 } 46 dir.reset() 47 return dir 48 } 49 50 // reset removes the managed directory and recreates it 51 func (d *dirDesc) reset() { 52 d.cleanup() 53 dir, err := ioutil.TempDir("", d.prefix) 54 if err != nil { 55 panic(fmt.Sprintf("Failed to create temporary %s directory: %v", d.desc, err)) 56 } 57 d.dir = dir 58 } 59 60 // cleanup removes the managed directory. After cleanup this instance 61 // cannot be used for anything, until it is reset. 62 func (d *dirDesc) cleanup() { 63 if d.dir == "" { 64 return 65 } 66 if err := os.RemoveAll(d.dir); err != nil && !os.IsNotExist(err) { 67 panic(fmt.Sprintf("Failed to remove temporary %s directory %q: %s", d.desc, d.dir, err)) 68 } 69 d.dir = "" 70 } 71 72 // rktOption returns option for rkt invocation 73 func (d *dirDesc) rktOption() string { 74 d.ensureValid() 75 return fmt.Sprintf("--%s=%s", d.option, d.dir) 76 } 77 78 func (d *dirDesc) ensureValid() { 79 if d.dir == "" { 80 panic(fmt.Sprintf("A temporary %s directory is not set up", d.desc)) 81 } 82 } 83 84 // TODO(emil): Document exported. 85 86 type RktRunCtx struct { 87 directories []*dirDesc 88 useDefaults bool 89 mds *exec.Cmd 90 children []*gexpect.ExpectSubprocess 91 } 92 93 func NewRktRunCtx() *RktRunCtx { 94 return &RktRunCtx{ 95 directories: []*dirDesc{ 96 newDirDesc("datadir-", "data", "dir"), 97 newDirDesc("localdir-", "local configuration", "local-config"), 98 newDirDesc("systemdir-", "system configuration", "system-config"), 99 }, 100 } 101 } 102 103 func (ctx *RktRunCtx) LaunchMDS() error { 104 ctx.mds = exec.Command(ctx.rktBin(), "metadata-service") 105 return ctx.mds.Start() 106 } 107 108 func (ctx *RktRunCtx) DataDir() string { 109 return ctx.dir(0) 110 } 111 112 func (ctx *RktRunCtx) LocalDir() string { 113 return ctx.dir(1) 114 } 115 116 func (ctx *RktRunCtx) SystemDir() string { 117 return ctx.dir(2) 118 } 119 120 func (ctx *RktRunCtx) dir(idx int) string { 121 ctx.ensureValid() 122 if idx < len(ctx.directories) { 123 return ctx.directories[idx].dir 124 } 125 panic("Directory index out of bounds") 126 } 127 128 func (ctx *RktRunCtx) Reset() { 129 ctx.cleanupChildren() 130 ctx.RunGC() 131 for _, d := range ctx.directories { 132 d.reset() 133 } 134 } 135 136 func (ctx *RktRunCtx) cleanupChildren() error { 137 for _, child := range ctx.children { 138 if child.Cmd.ProcessState.Exited() { 139 logger.Logf("Child %q already exited", child.Cmd.Path) 140 continue 141 } 142 logger.Logf("Shutting down child %q", child.Cmd.Path) 143 if err := child.Cmd.Process.Kill(); err != nil { 144 return err 145 } 146 if _, err := child.Cmd.Process.Wait(); err != nil { 147 return err 148 } 149 } 150 return nil 151 } 152 153 func (ctx *RktRunCtx) Cleanup() { 154 if ctx.mds != nil { 155 ctx.mds.Process.Kill() 156 ctx.mds.Wait() 157 os.Remove("/run/rkt/metadata-svc.sock") 158 } 159 if err := ctx.cleanupChildren(); err != nil { 160 logger.Logf("Error during child cleanup: %v", err) 161 } 162 ctx.RunGC() 163 for _, d := range ctx.directories { 164 d.cleanup() 165 } 166 } 167 168 func (ctx *RktRunCtx) RunGC() { 169 rktArgs := append(ctx.rktOptions(), 170 "gc", 171 "--grace-period=0s", 172 ) 173 if err := exec.Command(ctx.rktBin(), rktArgs...).Run(); err != nil { 174 panic(fmt.Sprintf("Failed to run gc: %v", err)) 175 } 176 } 177 178 func (ctx *RktRunCtx) Cmd() string { 179 return fmt.Sprintf("%s %s", 180 ctx.rktBin(), 181 strings.Join(ctx.rktOptions(), " "), 182 ) 183 } 184 185 // TODO(jonboulle): clean this up 186 func (ctx *RktRunCtx) CmdNoConfig() string { 187 return fmt.Sprintf("%s %s", 188 ctx.rktBin(), 189 ctx.directories[0].rktOption(), 190 ) 191 } 192 193 func (ctx *RktRunCtx) rktBin() string { 194 rkt := GetValueFromEnvOrPanic("RKT") 195 abs, err := filepath.Abs(rkt) 196 if err != nil { 197 abs = rkt 198 } 199 return abs 200 } 201 202 func (ctx *RktRunCtx) rktOptions() []string { 203 ctx.ensureValid() 204 opts := make([]string, 0, len(ctx.directories)) 205 for _, d := range ctx.directories { 206 opts = append(opts, d.rktOption()) 207 } 208 return opts 209 } 210 211 func (ctx *RktRunCtx) ensureValid() { 212 for _, d := range ctx.directories { 213 d.ensureValid() 214 } 215 } 216 217 func (ctx *RktRunCtx) RegisterChild(child *gexpect.ExpectSubprocess) { 218 ctx.children = append(ctx.children, child) 219 }