PageRenderTime 94ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/github.com/coreos/rkt/tests/rkt_tests.go

https://gitlab.com/unofficial-mirrors/openshift-origin
Go | 1039 lines | 820 code | 155 blank | 64 comment | 234 complexity | 3d31569ab6745350cd034060bb1d224b MD5 | raw file
  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. package main
  15. import (
  16. "bytes"
  17. "crypto/sha512"
  18. "encoding/hex"
  19. "encoding/json"
  20. "fmt"
  21. "io"
  22. "io/ioutil"
  23. "os"
  24. "os/exec"
  25. "path"
  26. "path/filepath"
  27. "regexp"
  28. "strconv"
  29. "strings"
  30. "syscall"
  31. "testing"
  32. "time"
  33. "golang.org/x/crypto/openpgp"
  34. "github.com/appc/spec/schema"
  35. "github.com/appc/spec/schema/types"
  36. "github.com/coreos/gexpect"
  37. "github.com/coreos/rkt/api/v1alpha"
  38. "github.com/coreos/rkt/common"
  39. "github.com/coreos/rkt/tests/testutils"
  40. taas "github.com/coreos/rkt/tests/testutils/aci-server"
  41. shellquote "github.com/kballard/go-shellquote"
  42. "google.golang.org/grpc"
  43. )
  44. const (
  45. defaultTimeLayout = "2006-01-02 15:04:05.999 -0700 MST"
  46. baseAppName = "rkt-inspect"
  47. )
  48. func expectCommon(p *gexpect.ExpectSubprocess, searchString string, timeout time.Duration) error {
  49. var err error
  50. p.Capture()
  51. if timeout == 0 {
  52. err = p.Expect(searchString)
  53. } else {
  54. err = p.ExpectTimeout(searchString, timeout)
  55. }
  56. if err != nil {
  57. return fmt.Errorf(string(p.Collect()))
  58. }
  59. return nil
  60. }
  61. func expectWithOutput(p *gexpect.ExpectSubprocess, searchString string) error {
  62. return expectCommon(p, searchString, 0)
  63. }
  64. func expectRegexWithOutput(p *gexpect.ExpectSubprocess, searchPattern string) ([]string, string, error) {
  65. return p.ExpectRegexFindWithOutput(searchPattern)
  66. }
  67. func expectRegexTimeoutWithOutput(p *gexpect.ExpectSubprocess, searchPattern string, timeout time.Duration) ([]string, string, error) {
  68. return p.ExpectTimeoutRegexFindWithOutput(searchPattern, timeout)
  69. }
  70. func expectTimeoutWithOutput(p *gexpect.ExpectSubprocess, searchString string, timeout time.Duration) error {
  71. return expectCommon(p, searchString, timeout)
  72. }
  73. func patchACI(inputFileName, newFileName string, args ...string) string {
  74. var allArgs []string
  75. actool := testutils.GetValueFromEnvOrPanic("ACTOOL")
  76. tmpDir := testutils.GetValueFromEnvOrPanic("FUNCTIONAL_TMP")
  77. imagePath, err := filepath.Abs(filepath.Join(tmpDir, newFileName))
  78. if err != nil {
  79. panic(fmt.Sprintf("Cannot create ACI: %v\n", err))
  80. }
  81. allArgs = append(allArgs, "patch-manifest")
  82. allArgs = append(allArgs, "--no-compression")
  83. allArgs = append(allArgs, "--overwrite")
  84. allArgs = append(allArgs, args...)
  85. allArgs = append(allArgs, inputFileName)
  86. allArgs = append(allArgs, imagePath)
  87. output, err := exec.Command(actool, allArgs...).CombinedOutput()
  88. if err != nil {
  89. panic(fmt.Sprintf("Cannot create ACI: %v: %s\n", err, output))
  90. }
  91. return imagePath
  92. }
  93. func patchTestACI(newFileName string, args ...string) string {
  94. image := getInspectImagePath()
  95. return patchACI(image, newFileName, args...)
  96. }
  97. func spawnOrFail(t *testing.T, cmd string) *gexpect.ExpectSubprocess {
  98. t.Logf("Spawning command: %v\n", cmd)
  99. child, err := gexpect.Spawn(cmd)
  100. if err != nil {
  101. t.Fatalf("Cannot exec rkt: %v", err)
  102. }
  103. return child
  104. }
  105. // waitOrFail waits for the child to exit, draining all its output.
  106. // If a non-negative return value is provided, child exit status must match.
  107. func waitOrFail(t *testing.T, child *gexpect.ExpectSubprocess, expectedStatus int) {
  108. bufOut := []string{}
  109. // TODO(lucab): gexpect should accept those channels from the caller
  110. ttyIn, ttyOut := child.AsyncInteractChannels()
  111. close(ttyIn)
  112. // drain output till gexpect closes the channel (on EOF or error)
  113. for line := range ttyOut {
  114. bufOut = append(bufOut, line)
  115. }
  116. err := child.Wait()
  117. status, _ := common.GetExitStatus(err)
  118. if expectedStatus >= 0 && status != expectedStatus {
  119. t.Fatalf("rkt terminated with unexpected status %d, expected %d\nOutput:\n%s", status, expectedStatus, bufOut)
  120. }
  121. }
  122. // waitPodReady waits for the pod supervisor to get ready, busy-looping until `timeout`
  123. // while waiting for it. It returns the pod UUID or an error on failure.
  124. func waitPodReady(ctx *testutils.RktRunCtx, t *testing.T, uuidFile string, timeout time.Duration) (string, error) {
  125. var podUUID []byte
  126. var err error
  127. interval := 500 * time.Millisecond
  128. elapsed := time.Duration(0)
  129. for elapsed < timeout {
  130. time.Sleep(interval)
  131. elapsed += interval
  132. podUUID, err = ioutil.ReadFile(uuidFile)
  133. if err == nil {
  134. break
  135. }
  136. }
  137. if err != nil {
  138. return "", fmt.Errorf("Can't read pod UUID: %v", err)
  139. }
  140. // wait up to one minute for the pod supervisor to be ready
  141. cmd := strings.Fields(fmt.Sprintf("%s status --wait-ready=%s %s", ctx.Cmd(), timeout, podUUID))
  142. statusCmd := exec.Command(cmd[0], cmd[1:]...)
  143. t.Logf("Running command: %v\n", cmd)
  144. output, err := statusCmd.CombinedOutput()
  145. if err != nil {
  146. return "", fmt.Errorf("Failed to wait for pod readiness, error %v output %v", err, string(output))
  147. }
  148. return string(podUUID), nil
  149. }
  150. // waitAppAttachable waits for an attachable application to get ready, busy-looping until `timeout`
  151. // while waiting for it. It returns an error on failure.
  152. func waitAppAttachable(ctx *testutils.RktRunCtx, t *testing.T, podUUID, appName string, timeout time.Duration) error {
  153. var (
  154. err error
  155. output []byte
  156. appNameFlag string
  157. )
  158. if appName != "" {
  159. appNameFlag = "--app=" + appName
  160. }
  161. cmd := strings.Fields(fmt.Sprintf("%s attach --mode=list %s %s", ctx.Cmd(), appNameFlag, podUUID))
  162. interval := 500 * time.Millisecond
  163. elapsed := time.Duration(0)
  164. for elapsed < timeout {
  165. time.Sleep(interval)
  166. elapsed += interval
  167. statusCmd := exec.Command(cmd[0], cmd[1:]...)
  168. output, err = statusCmd.CombinedOutput()
  169. if err == nil {
  170. break
  171. }
  172. }
  173. if err != nil {
  174. return fmt.Errorf("%s", output)
  175. }
  176. return nil
  177. }
  178. func spawnAndWaitOrFail(t *testing.T, cmd string, expectedStatus int) {
  179. child := spawnOrFail(t, cmd)
  180. waitOrFail(t, child, expectedStatus)
  181. }
  182. func getEmptyImagePath() string {
  183. return testutils.GetValueFromEnvOrPanic("RKT_EMPTY_IMAGE")
  184. }
  185. func getInspectImagePath() string {
  186. return testutils.GetValueFromEnvOrPanic("RKT_INSPECT_IMAGE")
  187. }
  188. func getHashOrPanic(path string) string {
  189. hash, err := getHash(path)
  190. if err != nil {
  191. panic(fmt.Sprintf("Cannot get hash from file located at %v", path))
  192. }
  193. return hash
  194. }
  195. func getHash(filePath string) (string, error) {
  196. f, err := os.Open(filePath)
  197. if err != nil {
  198. return "", fmt.Errorf("error opening file: %v", err)
  199. }
  200. hash := sha512.New()
  201. r := io.TeeReader(f, hash)
  202. if _, err := io.Copy(ioutil.Discard, r); err != nil {
  203. return "", fmt.Errorf("error reading file: %v", err)
  204. }
  205. return hex.EncodeToString(hash.Sum(nil)), nil
  206. }
  207. func mustTempDir(dirName string) string {
  208. tmpDir, err := ioutil.TempDir("", dirName)
  209. if err != nil {
  210. panic(fmt.Sprintf("Cannot create temp dir: %v", err))
  211. }
  212. return tmpDir
  213. }
  214. // createFileOrPanic creates an empty file within the given directory
  215. // with a specified name. Panics if file creation fails for any reason.
  216. func createFileOrPanic(dirName, fileName string) string {
  217. name := filepath.Join(dirName, fileName)
  218. file, err := os.Create(name)
  219. if err != nil {
  220. panic(err)
  221. }
  222. defer file.Close()
  223. return name
  224. }
  225. func importImageAndFetchHashAsUidGid(t *testing.T, ctx *testutils.RktRunCtx, img string, fetchArgs string, uid int, gid int) (string, error) {
  226. // Import the test image into store manually.
  227. cmd := fmt.Sprintf("%s --insecure-options=image,tls fetch --pull-policy=new %s %s", ctx.Cmd(), fetchArgs, img)
  228. // TODO(jonboulle): non-root user breaks trying to read root-written
  229. // config directories. Should be a better way to approach this. Should
  230. // config directories be readable by the rkt group too?
  231. if gid != 0 {
  232. cmd = fmt.Sprintf("%s --insecure-options=image,tls fetch --pull-policy=new %s %s", ctx.CmdNoConfig(), fetchArgs, img)
  233. }
  234. child, err := gexpect.Command(cmd)
  235. if err != nil {
  236. t.Fatalf("cannot create rkt command: %v", err)
  237. }
  238. if gid != 0 {
  239. child.Cmd.SysProcAttr = &syscall.SysProcAttr{}
  240. child.Cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
  241. }
  242. err = child.Start()
  243. if err != nil {
  244. t.Fatalf("cannot exec rkt: %v", err)
  245. }
  246. // Read out the image hash.
  247. result, out, err := expectRegexWithOutput(child, "sha512-[0-9a-f]{32,64}")
  248. if exitErr := checkExitStatus(child); exitErr != nil {
  249. t.Logf("%v", exitErr)
  250. return "", fmt.Errorf("fetching of %q failed", img)
  251. }
  252. if err != nil || len(result) != 1 {
  253. t.Fatalf("Error: %v\nOutput: %v", err, out)
  254. }
  255. return result[0], nil
  256. }
  257. func importImageAndFetchHash(t *testing.T, ctx *testutils.RktRunCtx, fetchArgs string, img string) (string, error) {
  258. return importImageAndFetchHashAsUidGid(t, ctx, fetchArgs, img, 0, 0)
  259. }
  260. func importImageAndRun(imagePath string, t *testing.T, ctx *testutils.RktRunCtx) {
  261. cmd := fmt.Sprintf("%s --insecure-options=image run %s", ctx.Cmd(), imagePath)
  262. spawnAndWaitOrFail(t, cmd, 0)
  263. }
  264. func importImageAndPrepare(imagePath string, t *testing.T, ctx *testutils.RktRunCtx) {
  265. cmd := fmt.Sprintf("%s --insecure-options=image prepare %s", ctx.Cmd(), imagePath)
  266. spawnAndWaitOrFail(t, cmd, 0)
  267. }
  268. func patchImportAndFetchHash(image string, patches []string, t *testing.T, ctx *testutils.RktRunCtx) (string, error) {
  269. imagePath := patchTestACI(image, patches...)
  270. defer os.Remove(imagePath)
  271. return importImageAndFetchHash(t, ctx, "", imagePath)
  272. }
  273. func patchImportAndRun(image string, patches []string, t *testing.T, ctx *testutils.RktRunCtx) {
  274. imagePath := patchTestACI(image, patches...)
  275. defer os.Remove(imagePath)
  276. cmd := fmt.Sprintf("%s --insecure-options=image run %s", ctx.Cmd(), imagePath)
  277. spawnAndWaitOrFail(t, cmd, 0)
  278. }
  279. func patchImportAndPrepare(image string, patches []string, t *testing.T, ctx *testutils.RktRunCtx) {
  280. imagePath := patchTestACI(image, patches...)
  281. defer os.Remove(imagePath)
  282. cmd := fmt.Sprintf("%s --insecure-options=image prepare %s", ctx.Cmd(), imagePath)
  283. spawnAndWaitOrFail(t, cmd, 0)
  284. }
  285. func runGC(t *testing.T, ctx *testutils.RktRunCtx) {
  286. cmd := fmt.Sprintf("%s gc --grace-period=0s", ctx.Cmd())
  287. spawnAndWaitOrFail(t, cmd, 0)
  288. }
  289. func runImageGC(t *testing.T, ctx *testutils.RktRunCtx) {
  290. cmd := fmt.Sprintf("%s image gc", ctx.Cmd())
  291. spawnAndWaitOrFail(t, cmd, 0)
  292. }
  293. func removeFromCas(t *testing.T, ctx *testutils.RktRunCtx, hash string) {
  294. cmd := fmt.Sprintf("%s image rm %s", ctx.Cmd(), hash)
  295. spawnAndWaitOrFail(t, cmd, 0)
  296. }
  297. func runRktAndGetUUID(t *testing.T, rktCmd string) string {
  298. child := spawnOrFail(t, rktCmd)
  299. defer waitOrFail(t, child, 0)
  300. result, out, err := expectRegexWithOutput(child, "[0-9a-f-]{36}")
  301. if err != nil || len(result) != 1 {
  302. t.Fatalf("Error: %v\nOutput: %v", err, out)
  303. }
  304. podIDStr := strings.TrimSpace(result[0])
  305. podID, err := types.NewUUID(podIDStr)
  306. if err != nil {
  307. t.Fatalf("%q is not a valid UUID: %v", podIDStr, err)
  308. }
  309. return podID.String()
  310. }
  311. func runRktAsGidAndCheckOutput(t *testing.T, rktCmd, expectedLine string, expectError bool, gid int) {
  312. nobodyUid, _ := testutils.GetUnprivilegedUidGid()
  313. runRktAsUidGidAndCheckOutput(t, rktCmd, expectedLine, false, expectError, nobodyUid, gid)
  314. }
  315. func runRktAsGidAndCheckREOutput(t *testing.T, rktCmd, expectedLine string, expectError bool, gid int) {
  316. nobodyUid, _ := testutils.GetUnprivilegedUidGid()
  317. runRktAsUidGidAndCheckOutput(t, rktCmd, expectedLine, true, expectError, nobodyUid, gid)
  318. }
  319. func runRktAsUidGidAndCheckOutput(t *testing.T, rktCmd, expectedLine string, lineIsRegex, expectError bool, uid, gid int) {
  320. child, err := gexpect.Command(rktCmd)
  321. if err != nil {
  322. t.Fatalf("cannot exec rkt: %v", err)
  323. }
  324. if gid != 0 {
  325. child.Cmd.SysProcAttr = &syscall.SysProcAttr{}
  326. child.Cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
  327. }
  328. err = child.Start()
  329. if err != nil {
  330. t.Fatalf("cannot start rkt: %v", err)
  331. }
  332. expectedStatus := 0
  333. if expectError {
  334. expectedStatus = 254
  335. }
  336. defer waitOrFail(t, child, expectedStatus)
  337. if expectedLine != "" {
  338. if lineIsRegex == true {
  339. _, _, err := expectRegexWithOutput(child, expectedLine)
  340. if err != nil {
  341. t.Fatalf("didn't receive expected regex %q in output: %v", expectedLine, err)
  342. }
  343. } else {
  344. err = expectWithOutput(child, expectedLine)
  345. if err != nil {
  346. t.Fatalf("didn't receive expected output %q: %v", expectedLine, err)
  347. }
  348. }
  349. }
  350. }
  351. func runRkt(t *testing.T, rktCmd string, uid, gid int) (string, int) {
  352. child, err := gexpect.Command(rktCmd)
  353. if err != nil {
  354. t.Fatalf("cannot exec rkt: %v", err)
  355. }
  356. if gid != 0 {
  357. child.Cmd.SysProcAttr = &syscall.SysProcAttr{}
  358. child.Cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
  359. }
  360. err = child.Start()
  361. if err != nil {
  362. t.Fatalf("cannot start rkt: %v", err)
  363. }
  364. _, linesChan := child.AsyncInteractChannels()
  365. var buf bytes.Buffer
  366. for line := range linesChan {
  367. buf.WriteString(line + "\n") // reappend newline
  368. }
  369. status, _ := common.GetExitStatus(child.Wait())
  370. return buf.String(), status
  371. }
  372. func startRktAsGidAndCheckOutput(t *testing.T, rktCmd, expectedLine string, gid int) *gexpect.ExpectSubprocess {
  373. child, err := gexpect.Command(rktCmd)
  374. if err != nil {
  375. t.Fatalf("cannot exec rkt: %v", err)
  376. }
  377. if gid != 0 {
  378. child.Cmd.SysProcAttr = &syscall.SysProcAttr{}
  379. nobodyUid, _ := testutils.GetUnprivilegedUidGid()
  380. child.Cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(nobodyUid), Gid: uint32(gid)}
  381. }
  382. if err := child.Start(); err != nil {
  383. t.Fatalf("cannot exec rkt: %v", err)
  384. }
  385. if expectedLine != "" {
  386. if err := expectWithOutput(child, expectedLine); err != nil {
  387. t.Fatalf("didn't receive expected output %q: %v", expectedLine, err)
  388. }
  389. }
  390. return child
  391. }
  392. func startRktAsUidGidAndCheckOutput(t *testing.T, rktCmd, expectedLine string, expectError bool, uid, gid int) *gexpect.ExpectSubprocess {
  393. child, err := gexpect.Command(rktCmd)
  394. if err != nil {
  395. t.Fatalf("cannot exec rkt: %v", err)
  396. }
  397. if gid != 0 {
  398. child.Cmd.SysProcAttr = &syscall.SysProcAttr{}
  399. child.Cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
  400. }
  401. err = child.Start()
  402. if err != nil {
  403. t.Fatalf("cannot start rkt: %v", err)
  404. }
  405. if expectedLine != "" {
  406. if err := expectWithOutput(child, expectedLine); err != nil {
  407. t.Fatalf("didn't receive expected output %q: %v", expectedLine, err)
  408. }
  409. }
  410. return child
  411. }
  412. func runRktAndCheckRegexOutput(t *testing.T, rktCmd, match string) error {
  413. re, err := regexp.Compile(match)
  414. if err != nil {
  415. t.Fatalf("error compiling regex %q: %v", match, err)
  416. }
  417. args, err := shellquote.Split(rktCmd)
  418. if err != nil {
  419. t.Fatalf("error splitting cmd %q: %v", rktCmd, err)
  420. }
  421. path, err := exec.LookPath(args[0])
  422. cmd := exec.Command(path, args[1:]...)
  423. out, err := cmd.CombinedOutput()
  424. result := re.MatchString(string(out))
  425. if !result {
  426. t.Fatalf("%q regex must be found\nOutput: %q", match, string(out))
  427. }
  428. return err
  429. }
  430. func runRktAndCheckOutput(t *testing.T, rktCmd, expectedLine string, expectError bool) {
  431. runRktAsGidAndCheckOutput(t, rktCmd, expectedLine, expectError, 0)
  432. }
  433. func runRktAndCheckREOutput(t *testing.T, rktCmd, expectedLine string, expectError bool) {
  434. runRktAsGidAndCheckREOutput(t, rktCmd, expectedLine, expectError, 0)
  435. }
  436. func startRktAndCheckOutput(t *testing.T, rktCmd, expectedLine string) *gexpect.ExpectSubprocess {
  437. return startRktAsGidAndCheckOutput(t, rktCmd, expectedLine, 0)
  438. }
  439. func checkAppStatus(t *testing.T, ctx *testutils.RktRunCtx, multiApps bool, appName, expected string) {
  440. cmd := fmt.Sprintf(`/bin/sh -c "`+
  441. `UUID=$(%s list --full|grep '%s'|awk '{print $1}') ;`+
  442. `echo -n 'status=' ;`+
  443. `%s status $UUID|grep '^app-%s.*=[0-9]*$'|cut -d= -f2"`,
  444. ctx.Cmd(), appName, ctx.Cmd(), appName)
  445. if multiApps {
  446. cmd = fmt.Sprintf(`/bin/sh -c "`+
  447. `UUID=$(%s list --full|grep '^[a-f0-9]'|awk '{print $1}') ;`+
  448. `echo -n 'status=' ;`+
  449. `%s status $UUID|grep '^app-%s.*=[0-9]*$'|cut -d= -f2"`,
  450. ctx.Cmd(), ctx.Cmd(), appName)
  451. }
  452. t.Logf("Get status for app %s\n", appName)
  453. child := spawnOrFail(t, cmd)
  454. defer waitOrFail(t, child, 0)
  455. if err := expectWithOutput(child, expected); err != nil {
  456. // For debugging purposes, print the full output of
  457. // "rkt list" and "rkt status"
  458. cmd := fmt.Sprintf(`%s list --full ;`+
  459. `UUID=$(%s list --full|grep '^[a-f0-9]'|awk '{print $1}') ;`+
  460. `%s status $UUID`,
  461. ctx.Cmd(), ctx.Cmd(), ctx.Cmd())
  462. out, err2 := exec.Command("/bin/sh", "-c", cmd).CombinedOutput()
  463. if err2 != nil {
  464. t.Logf("Could not run rkt status: %v. %s", err2, out)
  465. } else {
  466. t.Logf("%s\n", out)
  467. }
  468. t.Fatalf("Failed to get the status for app %s: expected: %s. %v",
  469. appName, expected, err)
  470. }
  471. }
  472. type imageInfo struct {
  473. id string
  474. name string
  475. version string
  476. importTime int64
  477. size int64
  478. manifest []byte
  479. }
  480. type appInfo struct {
  481. name string
  482. exitCode int
  483. image *imageInfo
  484. // TODO(yifan): Add app state.
  485. }
  486. type networkInfo struct {
  487. name string
  488. ipv4 string
  489. }
  490. type podInfo struct {
  491. id string
  492. pid int
  493. state string
  494. apps map[string]*appInfo
  495. networks map[string]*networkInfo
  496. manifest *schema.PodManifest
  497. createdAt int64
  498. startedAt int64
  499. }
  500. type imagePatch struct {
  501. name string
  502. patches []string
  503. }
  504. // parsePodInfo parses the 'rkt status $UUID' result into podInfo struct.
  505. // For example, the 'result' can be:
  506. // state=running
  507. // networks=default:ip4=172.16.28.103
  508. // pid=14352
  509. // exited=false
  510. // created=2016-04-01 19:12:03.447 -0700 PDT
  511. // started=2016-04-01 19:12:04.279 -0700 PDT
  512. func parsePodInfoOutput(t *testing.T, result string, p *podInfo) {
  513. lines := strings.Split(strings.TrimSuffix(result, "\n"), "\n")
  514. for _, line := range lines {
  515. tuples := strings.SplitN(line, "=", 2)
  516. if len(tuples) != 2 {
  517. t.Logf("Unexpected line: %v", line)
  518. continue
  519. }
  520. switch tuples[0] {
  521. case "state":
  522. p.state = tuples[1]
  523. case "networks":
  524. if tuples[1] == "" {
  525. break
  526. }
  527. networks := strings.Split(tuples[1], ",")
  528. for _, n := range networks {
  529. fields := strings.Split(n, ":")
  530. if len(fields) != 2 {
  531. t.Fatalf("Unexpected network info format: %v", n)
  532. }
  533. ip4 := strings.Split(fields[1], "=")
  534. if len(ip4) != 2 {
  535. t.Fatalf("Unexpected network info format: %v", n)
  536. }
  537. networkName := fields[0]
  538. p.networks[networkName] = &networkInfo{
  539. name: networkName,
  540. ipv4: ip4[1],
  541. }
  542. }
  543. case "pid":
  544. pid, err := strconv.Atoi(tuples[1])
  545. if err != nil {
  546. t.Fatalf("Cannot parse the pod's pid %q: %v", tuples[1], err)
  547. }
  548. p.pid = pid
  549. case "created":
  550. createdAt, err := time.Parse(defaultTimeLayout, tuples[1])
  551. if err != nil {
  552. t.Fatalf("Cannot parse the pod's creation time %q: %v", tuples[1], err)
  553. }
  554. p.createdAt = createdAt.UnixNano()
  555. case "started":
  556. startedAt, err := time.Parse(defaultTimeLayout, tuples[1])
  557. if err != nil {
  558. t.Fatalf("Cannot parse the pod's start time %q: %v", tuples[1], err)
  559. }
  560. p.startedAt = startedAt.UnixNano()
  561. }
  562. if strings.HasPrefix(tuples[0], "app-") {
  563. exitCode, err := strconv.Atoi(tuples[1])
  564. if err != nil {
  565. t.Fatalf("cannot parse exit code from %q : %v", tuples[1], err)
  566. }
  567. appName := strings.TrimPrefix(tuples[0], "app-")
  568. for _, app := range p.apps {
  569. if app.name == appName {
  570. app.exitCode = exitCode
  571. break
  572. }
  573. }
  574. }
  575. }
  576. }
  577. func getPodDir(t *testing.T, ctx *testutils.RktRunCtx, podID string) string {
  578. podsDir := path.Join(ctx.DataDir(), "pods")
  579. dirs, err := ioutil.ReadDir(podsDir)
  580. if err != nil {
  581. t.Fatalf("Unexpected error: %v", err)
  582. }
  583. for _, dir := range dirs {
  584. podDir := path.Join(podsDir, dir.Name(), podID)
  585. if _, err := os.Stat(podDir); err == nil {
  586. return podDir
  587. }
  588. }
  589. t.Fatalf("Failed to find pod directory for pod %q", podID)
  590. return ""
  591. }
  592. // getPodInfo returns the pod info for the given pod ID.
  593. func getPodInfo(t *testing.T, ctx *testutils.RktRunCtx, podID string) *podInfo {
  594. p := &podInfo{
  595. id: podID,
  596. pid: -1,
  597. apps: make(map[string]*appInfo),
  598. networks: make(map[string]*networkInfo),
  599. }
  600. // Read pod manifest.
  601. output, err := exec.Command("/bin/bash", "-c", fmt.Sprintf("%s cat-manifest %s", ctx.Cmd(), podID)).CombinedOutput()
  602. if err != nil {
  603. t.Fatalf("Unexpected error: %v", err)
  604. }
  605. // Trim the last '\n' character.
  606. mfst := bytes.TrimSpace(output)
  607. // Fill app infos.
  608. if err := json.Unmarshal(mfst, &p.manifest); err != nil {
  609. t.Fatalf("Unexpected error: %v", err)
  610. }
  611. for _, app := range p.manifest.Apps {
  612. appName := app.Name.String()
  613. p.apps[appName] = &appInfo{
  614. name: appName,
  615. // TODO(yifan): Get the image's name.
  616. image: &imageInfo{id: app.Image.ID.String()},
  617. }
  618. }
  619. // Fill other infos.
  620. output, _ = exec.Command("/bin/bash", "-c", fmt.Sprintf("%s status %s", ctx.Cmd(), podID)).CombinedOutput()
  621. parsePodInfoOutput(t, string(output), p)
  622. return p
  623. }
  624. // parseImageInfoOutput parses the 'rkt image list' result into imageInfo struct.
  625. // For example, the 'result' can be:
  626. // 'sha512-e9b77714dbbfda12cb9e136318b103a6f0ce082004d09d0224a620d2bbf38133 nginx:latest 2015-10-16 17:42:57.741 -0700 PDT true'
  627. func parseImageInfoOutput(t *testing.T, result string) *imageInfo {
  628. fields := regexp.MustCompile("\t+").Split(result, -1)
  629. nameVersion := strings.Split(fields[1], ":")
  630. if len(nameVersion) != 2 {
  631. t.Fatalf("Failed to parse name version string: %q", fields[1])
  632. }
  633. importTime, err := time.Parse(defaultTimeLayout, fields[3])
  634. if err != nil {
  635. t.Fatalf("Failed to parse time string: %q", fields[3])
  636. }
  637. size, err := strconv.Atoi(fields[2])
  638. if err != nil {
  639. t.Fatalf("Failed to parse image size string: %q", fields[2])
  640. }
  641. return &imageInfo{
  642. id: fields[0],
  643. name: nameVersion[0],
  644. version: nameVersion[1],
  645. importTime: importTime.Unix(),
  646. size: int64(size),
  647. }
  648. }
  649. // getImageInfo returns the image info for the given image ID.
  650. func getImageInfo(t *testing.T, ctx *testutils.RktRunCtx, imageID string) *imageInfo {
  651. output, err := exec.Command("/bin/bash", "-c", fmt.Sprintf("%s image list --full | grep %s", ctx.Cmd(), imageID)).CombinedOutput()
  652. if err != nil {
  653. t.Fatalf("Unexpected error: %v", err)
  654. }
  655. imgInfo := parseImageInfoOutput(t, string(output))
  656. // Get manifest
  657. output, err = exec.Command("/bin/bash", "-c",
  658. fmt.Sprintf("%s image cat-manifest --pretty-print=false %s", ctx.Cmd(), imageID)).CombinedOutput()
  659. if err != nil {
  660. t.Fatalf("Unexpected error: %v", err)
  661. }
  662. imgInfo.manifest = bytes.TrimSuffix(output, []byte{'\n'})
  663. return imgInfo
  664. }
  665. func newAPIClientOrFail(t *testing.T, address string) (v1alpha.PublicAPIClient, *grpc.ClientConn) {
  666. conn, err := grpc.Dial(address, grpc.WithInsecure())
  667. if err != nil {
  668. t.Fatalf("Unexpected error: %v", err)
  669. }
  670. c := v1alpha.NewPublicAPIClient(conn)
  671. return c, conn
  672. }
  673. func runServer(t *testing.T, setup *taas.ServerSetup) *taas.Server {
  674. server := taas.NewServer(setup)
  675. go serverHandler(t, server)
  676. return server
  677. }
  678. func serverHandler(t *testing.T, server *taas.Server) {
  679. for {
  680. select {
  681. case msg, ok := <-server.Msg:
  682. if ok {
  683. t.Logf("server: %v", msg)
  684. } else {
  685. return
  686. }
  687. }
  688. }
  689. }
  690. func runSignImage(t *testing.T, imagePath string, keyIndex int) string {
  691. // keys stored in tests/secring.gpg.
  692. keyFingerprint := ""
  693. switch keyIndex {
  694. case 1:
  695. keyFingerprint = "D9DCEF41"
  696. case 2:
  697. keyFingerprint = "585091E3"
  698. default:
  699. panic("unknown key")
  700. }
  701. secringFile, err := os.Open("./secring.gpg")
  702. if err != nil {
  703. t.Fatalf("Cannot open secring.gpg file: %v", err)
  704. }
  705. defer secringFile.Close()
  706. entityList, err := openpgp.ReadKeyRing(secringFile)
  707. if err != nil {
  708. t.Fatalf("Failed to read secring.gpg file: %v", err)
  709. }
  710. var signingEntity *openpgp.Entity
  711. for _, entity := range entityList {
  712. if entity.PrivateKey.KeyIdShortString() == keyFingerprint {
  713. signingEntity = entity
  714. }
  715. }
  716. imageFile, err := os.Open(imagePath)
  717. if err != nil {
  718. t.Fatalf("Cannot open image file %s: %v", imagePath, err)
  719. }
  720. defer imageFile.Close()
  721. ascPath := fmt.Sprintf("%s.asc", imagePath)
  722. ascFile, err := os.Create(ascPath)
  723. if err != nil {
  724. t.Fatalf("Cannot create asc file %s: %v", ascPath, err)
  725. }
  726. defer ascFile.Close()
  727. err = openpgp.ArmoredDetachSign(ascFile, signingEntity, imageFile, nil)
  728. if err != nil {
  729. t.Fatalf("Cannot create armored detached signature: %v", err)
  730. }
  731. return ascPath
  732. }
  733. func runRktTrust(t *testing.T, ctx *testutils.RktRunCtx, prefix string, keyIndex int) {
  734. var cmd string
  735. keyFile := fmt.Sprintf("key%d.gpg", keyIndex)
  736. if prefix == "" {
  737. cmd = fmt.Sprintf(`%s trust --root %s`, ctx.Cmd(), keyFile)
  738. } else {
  739. cmd = fmt.Sprintf(`%s trust --prefix %s %s`, ctx.Cmd(), prefix, keyFile)
  740. }
  741. child := spawnOrFail(t, cmd)
  742. defer waitOrFail(t, child, 0)
  743. expected := "Are you sure you want to trust this key"
  744. if err := expectWithOutput(child, expected); err != nil {
  745. t.Fatalf("Expected but didn't find %q in %v", expected, err)
  746. }
  747. if err := child.SendLine("yes"); err != nil {
  748. t.Fatalf("Cannot confirm rkt trust: %s", err)
  749. }
  750. if prefix == "" {
  751. expected = "Added root key at"
  752. } else {
  753. expected = fmt.Sprintf(`Added key for prefix "%s" at`, prefix)
  754. }
  755. if err := expectWithOutput(child, expected); err != nil {
  756. t.Fatalf("Expected but didn't find %q in %v", expected, err)
  757. }
  758. }
  759. func generatePodManifestFile(t *testing.T, manifest *schema.PodManifest) string {
  760. tmpDir := testutils.GetValueFromEnvOrPanic("FUNCTIONAL_TMP")
  761. f, err := ioutil.TempFile(tmpDir, "rkt-test-manifest-")
  762. if err != nil {
  763. t.Fatalf("Cannot create tmp pod manifest: %v", err)
  764. }
  765. data, err := json.Marshal(manifest)
  766. if err != nil {
  767. t.Fatalf("Cannot marshal pod manifest: %v", err)
  768. }
  769. if err := ioutil.WriteFile(f.Name(), data, 0600); err != nil {
  770. t.Fatalf("Cannot write pod manifest file: %v", err)
  771. }
  772. return f.Name()
  773. }
  774. func checkUserNS() error {
  775. // CentOS 7 pretends to support user namespaces, but does not.
  776. // See https://bugzilla.redhat.com/show_bug.cgi?id=1168776#c5
  777. // Check if it really works
  778. return exec.Command("/bin/bash", "-c", "unshare -U true").Run()
  779. }
  780. func authDir(confDir string) string {
  781. return filepath.Join(confDir, "auth.d")
  782. }
  783. func pathsDir(confDir string) string {
  784. return filepath.Join(confDir, "paths.d")
  785. }
  786. func stage1Dir(confDir string) string {
  787. return filepath.Join(confDir, "stage1.d")
  788. }
  789. func writeConfig(t *testing.T, dir, filename, contents string) {
  790. if err := os.MkdirAll(dir, 0755); err != nil {
  791. t.Fatalf("Failed to create config directory %q: %v", dir, err)
  792. }
  793. path := filepath.Join(dir, filename)
  794. os.Remove(path)
  795. if err := ioutil.WriteFile(path, []byte(contents), 0644); err != nil {
  796. t.Fatalf("Failed to write file %q: %v", path, err)
  797. }
  798. }
  799. func verifyHostFile(t *testing.T, tmpdir, filename string, i int, expectedResult string) {
  800. filePath := path.Join(tmpdir, filename)
  801. defer os.Remove(filePath)
  802. // Verify the file is written to host.
  803. if strings.Contains(expectedResult, "host:") {
  804. data, err := ioutil.ReadFile(filePath)
  805. if err != nil {
  806. t.Fatalf("%d: Cannot read the host file: %v", i, err)
  807. }
  808. if string(data) != expectedResult {
  809. t.Fatalf("%d: Expecting %q in the host file, but saw %q", i, expectedResult, data)
  810. }
  811. }
  812. }
  813. func executeFuncsReverse(funcs []func()) {
  814. n := len(funcs)
  815. for i := n - 1; i >= 0; i-- {
  816. funcs[i]()
  817. }
  818. }
  819. func unmountPod(t *testing.T, ctx *testutils.RktRunCtx, uuid string, rmNetns bool) {
  820. podDir := filepath.Join(ctx.DataDir(), "pods", "run", uuid)
  821. stage1MntPath := filepath.Join(podDir, "stage1", "rootfs")
  822. stage2MntPath := filepath.Join(stage1MntPath, "opt", "stage2", "rkt-inspect", "rootfs")
  823. netnsPath := filepath.Join(podDir, "netns")
  824. podNetNSPathBytes, err := ioutil.ReadFile(netnsPath)
  825. // There may be no netns, e.g. kvm or --net=host
  826. if err != nil {
  827. if !os.IsNotExist(err) {
  828. t.Fatalf(`cannot read "netns" stage1: %v`, err)
  829. } else {
  830. rmNetns = false
  831. }
  832. }
  833. if err := syscall.Unmount(stage2MntPath, 0); err != nil {
  834. t.Fatalf("cannot umount stage2: %v", err)
  835. }
  836. if err := syscall.Unmount(stage1MntPath, 0); err != nil {
  837. t.Fatalf("cannot umount stage1: %v", err)
  838. }
  839. if rmNetns {
  840. podNetNSPath := string(podNetNSPathBytes)
  841. if err := syscall.Unmount(podNetNSPath, 0); err != nil {
  842. t.Fatalf("cannot umount pod netns: %v", err)
  843. }
  844. _ = os.RemoveAll(podNetNSPath)
  845. }
  846. }
  847. func checkExitStatus(child *gexpect.ExpectSubprocess) error {
  848. err := child.Wait()
  849. status, _ := common.GetExitStatus(err)
  850. if status != 0 {
  851. return fmt.Errorf("rkt terminated with unexpected status %d, expected %d\nOutput:\n%s", status, 0, child.Collect())
  852. }
  853. return nil
  854. }
  855. // combinedOutput executes the given command c for the given test context t
  856. // and fails test t if command execution failed.
  857. // It returns the command output.
  858. func combinedOutput(t *testing.T, c *exec.Cmd) string {
  859. t.Log("Running", c.Args)
  860. out, err := c.CombinedOutput()
  861. if err != nil {
  862. t.Fatal(err, "output", string(out))
  863. }
  864. return string(out)
  865. }
  866. // retry is the struct that represents retrying function calls.
  867. type retry struct {
  868. n int
  869. t time.Duration
  870. }
  871. // Retry retries the given function f n times with a delay t between invocations
  872. // until no error is returned from f or n is exceeded.
  873. // The last occured error is returned.
  874. func (r retry) Retry(f func() error) error {
  875. var err error
  876. for i := 0; i < r.n; i++ {
  877. err = f()
  878. if err == nil {
  879. return nil
  880. }
  881. time.Sleep(r.t)
  882. }
  883. return err
  884. }