github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/tests/rkt_tests.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 main 16 17 import ( 18 "crypto/sha512" 19 "encoding/hex" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "os" 24 "os/exec" 25 "path/filepath" 26 "strings" 27 "syscall" 28 "testing" 29 "time" 30 31 "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types" 32 "github.com/coreos/rkt/Godeps/_workspace/src/github.com/steveeJ/gexpect" 33 "github.com/coreos/rkt/tests/testutils" 34 ) 35 36 const ( 37 nobodyUid = uint32(65534) 38 ) 39 40 func expectCommon(p *gexpect.ExpectSubprocess, searchString string, timeout time.Duration) error { 41 var err error 42 43 p.Capture() 44 if timeout == 0 { 45 err = p.Expect(searchString) 46 } else { 47 err = p.ExpectTimeout(searchString, timeout) 48 } 49 if err != nil { 50 return fmt.Errorf(string(p.Collect())) 51 } 52 53 return nil 54 } 55 56 func expectWithOutput(p *gexpect.ExpectSubprocess, searchString string) error { 57 return expectCommon(p, searchString, 0) 58 } 59 60 func expectRegexWithOutput(p *gexpect.ExpectSubprocess, searchPattern string) ([]string, string, error) { 61 return p.ExpectRegexFindWithOutput(searchPattern) 62 } 63 64 func expectRegexTimeoutWithOutput(p *gexpect.ExpectSubprocess, searchPattern string, timeout time.Duration) ([]string, string, error) { 65 return p.ExpectTimeoutRegexFindWithOutput(searchPattern, timeout) 66 } 67 68 func expectTimeoutWithOutput(p *gexpect.ExpectSubprocess, searchString string, timeout time.Duration) error { 69 return expectCommon(p, searchString, timeout) 70 } 71 72 func patchACI(inputFileName, newFileName string, args ...string) string { 73 var allArgs []string 74 75 actool := testutils.GetValueFromEnvOrPanic("ACTOOL") 76 tmpDir := testutils.GetValueFromEnvOrPanic("FUNCTIONAL_TMP") 77 78 imagePath, err := filepath.Abs(filepath.Join(tmpDir, newFileName)) 79 if err != nil { 80 panic(fmt.Sprintf("Cannot create ACI: %v\n", err)) 81 } 82 allArgs = append(allArgs, "patch-manifest") 83 allArgs = append(allArgs, "--no-compression") 84 allArgs = append(allArgs, "--overwrite") 85 allArgs = append(allArgs, args...) 86 allArgs = append(allArgs, inputFileName) 87 allArgs = append(allArgs, imagePath) 88 89 output, err := exec.Command(actool, allArgs...).CombinedOutput() 90 if err != nil { 91 panic(fmt.Sprintf("Cannot create ACI: %v: %s\n", err, output)) 92 } 93 return imagePath 94 } 95 96 func patchTestACI(newFileName string, args ...string) string { 97 image := getInspectImagePath() 98 return patchACI(image, newFileName, args...) 99 } 100 101 func spawnOrFail(t *testing.T, cmd string) *gexpect.ExpectSubprocess { 102 t.Logf("Running command: %v", cmd) 103 child, err := gexpect.Spawn(cmd) 104 if err != nil { 105 t.Fatalf("Cannot exec rkt: %v", err) 106 } 107 return child 108 } 109 110 func waitOrFail(t *testing.T, child *gexpect.ExpectSubprocess, shouldSucceed bool) { 111 err := child.Wait() 112 switch { 113 case !shouldSucceed && err == nil: 114 t.Fatalf("Expected test to fail but it didn't") 115 case shouldSucceed && err != nil: 116 t.Fatalf("rkt didn't terminate correctly: %v", err) 117 case err != nil && err.Error() != "exit status 1": 118 t.Fatalf("rkt terminated with unexpected error: %v", err) 119 } 120 } 121 122 func spawnAndWaitOrFail(t *testing.T, cmd string, shouldSucceed bool) { 123 child := spawnOrFail(t, cmd) 124 waitOrFail(t, child, shouldSucceed) 125 } 126 127 func getEmptyImagePath() string { 128 return testutils.GetValueFromEnvOrPanic("RKT_EMPTY_IMAGE") 129 } 130 131 func getInspectImagePath() string { 132 return testutils.GetValueFromEnvOrPanic("RKT_INSPECT_IMAGE") 133 } 134 135 func getHashOrPanic(path string) string { 136 hash, err := getHash(path) 137 if err != nil { 138 panic(fmt.Sprintf("Cannot get hash from file located at %v", path)) 139 } 140 return hash 141 } 142 143 func getHash(filePath string) (string, error) { 144 f, err := os.Open(filePath) 145 if err != nil { 146 return "", fmt.Errorf("error opening file: %v", err) 147 } 148 149 hash := sha512.New() 150 r := io.TeeReader(f, hash) 151 152 if _, err := io.Copy(ioutil.Discard, r); err != nil { 153 return "", fmt.Errorf("error reading file: %v", err) 154 } 155 156 return hex.EncodeToString(hash.Sum(nil)), nil 157 } 158 159 func createTempDirOrPanic(dirName string) string { 160 tmpDir, err := ioutil.TempDir("", dirName) 161 if err != nil { 162 panic(fmt.Sprintf("Cannot create temp dir: %v", err)) 163 } 164 return tmpDir 165 } 166 167 func importImageAndFetchHashAsGid(t *testing.T, ctx *testutils.RktRunCtx, img string, gid int) string { 168 // Import the test image into store manually. 169 cmd := fmt.Sprintf("%s --insecure-skip-verify fetch %s", ctx.Cmd(), img) 170 171 // TODO(jonboulle): non-root user breaks trying to read root-written 172 // config directories. Should be a better way to approach this. Should 173 // config directories be readable by the rkt group too? 174 if gid != 0 { 175 cmd = fmt.Sprintf("%s --insecure-skip-verify fetch %s", ctx.CmdNoConfig(), img) 176 } 177 child, err := gexpect.Command(cmd) 178 if err != nil { 179 t.Fatalf("cannot create rkt command: %v", err) 180 } 181 if gid != 0 { 182 child.Cmd.SysProcAttr = &syscall.SysProcAttr{} 183 child.Cmd.SysProcAttr.Credential = &syscall.Credential{Uid: nobodyUid, Gid: uint32(gid)} 184 } 185 186 err = child.Start() 187 if err != nil { 188 t.Fatalf("cannot exec rkt: %v", err) 189 } 190 191 // Read out the image hash. 192 result, out, err := expectRegexWithOutput(child, "sha512-[0-9a-f]{32}") 193 if err != nil || len(result) != 1 { 194 t.Fatalf("Error: %v\nOutput: %v", err, out) 195 } 196 197 waitOrFail(t, child, true) 198 199 return result[0] 200 } 201 202 func importImageAndFetchHash(t *testing.T, ctx *testutils.RktRunCtx, img string) string { 203 return importImageAndFetchHashAsGid(t, ctx, img, 0) 204 } 205 206 func patchImportAndFetchHash(image string, patches []string, t *testing.T, ctx *testutils.RktRunCtx) string { 207 imagePath := patchTestACI(image, patches...) 208 defer os.Remove(imagePath) 209 210 return importImageAndFetchHash(t, ctx, imagePath) 211 } 212 213 func runGC(t *testing.T, ctx *testutils.RktRunCtx) { 214 cmd := fmt.Sprintf("%s gc --grace-period=0s", ctx.Cmd()) 215 spawnAndWaitOrFail(t, cmd, true) 216 } 217 218 func runImageGC(t *testing.T, ctx *testutils.RktRunCtx) { 219 cmd := fmt.Sprintf("%s image gc", ctx.Cmd()) 220 spawnAndWaitOrFail(t, cmd, true) 221 } 222 223 func removeFromCas(t *testing.T, ctx *testutils.RktRunCtx, hash string) { 224 cmd := fmt.Sprintf("%s image rm %s", ctx.Cmd(), hash) 225 spawnAndWaitOrFail(t, cmd, true) 226 } 227 228 func runRktAndGetUUID(t *testing.T, rktCmd string) string { 229 child := spawnOrFail(t, rktCmd) 230 defer waitOrFail(t, child, true) 231 232 result, out, err := expectRegexWithOutput(child, "\n[0-9a-f-]{36}") 233 if err != nil || len(result) != 1 { 234 t.Fatalf("Error: %v\nOutput: %v", err, out) 235 } 236 237 podIDStr := strings.TrimSpace(result[0]) 238 podID, err := types.NewUUID(podIDStr) 239 if err != nil { 240 t.Fatalf("%q is not a valid UUID: %v", podIDStr, err) 241 } 242 243 return podID.String() 244 } 245 246 func runRktAsGidAndCheckOutput(t *testing.T, rktCmd, expectedLine string, expectError bool, gid int) { 247 child, err := gexpect.Command(rktCmd) 248 if err != nil { 249 t.Fatalf("cannot exec rkt: %v", err) 250 } 251 if gid != 0 { 252 child.Cmd.SysProcAttr = &syscall.SysProcAttr{} 253 child.Cmd.SysProcAttr.Credential = &syscall.Credential{Uid: nobodyUid, Gid: uint32(gid)} 254 } 255 256 err = child.Start() 257 if err != nil { 258 t.Fatalf("cannot exec rkt: %v", err) 259 } 260 defer waitOrFail(t, child, !expectError) 261 262 if expectedLine != "" { 263 if err := expectWithOutput(child, expectedLine); err != nil { 264 t.Fatalf("didn't receive expected output %q: %v", expectedLine, err) 265 } 266 } 267 } 268 269 func runRktAndCheckRegexOutput(t *testing.T, rktCmd, match string) { 270 child := spawnOrFail(t, rktCmd) 271 defer child.Wait() 272 273 result, out, err := expectRegexWithOutput(child, match) 274 if err != nil || len(result) != 1 { 275 t.Fatalf("%q regex must be found one time, Error: %v\nOutput: %v", match, err, out) 276 } 277 } 278 279 func runRktAndCheckOutput(t *testing.T, rktCmd, expectedLine string, expectError bool) { 280 runRktAsGidAndCheckOutput(t, rktCmd, expectedLine, expectError, 0) 281 } 282 283 func checkAppStatus(t *testing.T, ctx *testutils.RktRunCtx, multiApps bool, appName, expected string) { 284 cmd := fmt.Sprintf(`/bin/sh -c "`+ 285 `UUID=$(%s list --full|grep '%s'|awk '{print $1}') ;`+ 286 `echo -n 'status=' ;`+ 287 `%s status $UUID|grep '^app-%s.*=[0-9]*$'|cut -d= -f2"`, 288 ctx.Cmd(), appName, ctx.Cmd(), appName) 289 290 if multiApps { 291 cmd = fmt.Sprintf(`/bin/sh -c "`+ 292 `UUID=$(%s list --full|grep '^[a-f0-9]'|awk '{print $1}') ;`+ 293 `echo -n 'status=' ;`+ 294 `%s status $UUID|grep '^app-%s.*=[0-9]*$'|cut -d= -f2"`, 295 ctx.Cmd(), ctx.Cmd(), appName) 296 } 297 298 t.Logf("Get status for app %s\n", appName) 299 child := spawnOrFail(t, cmd) 300 defer waitOrFail(t, child, true) 301 302 if err := expectWithOutput(child, expected); err != nil { 303 // For debugging purposes, print the full output of 304 // "rkt list" and "rkt status" 305 cmd := fmt.Sprintf(`%s list --full ;`+ 306 `UUID=$(%s list --full|grep '^[a-f0-9]'|awk '{print $1}') ;`+ 307 `%s status $UUID`, 308 ctx.Cmd(), ctx.Cmd(), ctx.Cmd()) 309 out, err2 := exec.Command("/bin/sh", "-c", cmd).CombinedOutput() 310 if err2 != nil { 311 t.Logf("Could not run rkt status: %v. %s", err2, out) 312 } else { 313 t.Logf("%s\n", out) 314 } 315 316 t.Fatalf("Failed to get the status for app %s: expected: %s. %v", 317 appName, expected, err) 318 } 319 }