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