in_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package resource_test
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io/ioutil"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "syscall"
  10. "time"
  11. . "github.com/onsi/ginkgo"
  12. . "github.com/onsi/gomega"
  13. resource "github.com/concourse/registry-image-resource"
  14. )
  15. var _ = Describe("In", func() {
  16. var destDir string
  17. var req struct {
  18. Source resource.Source
  19. Version resource.Version
  20. }
  21. var res struct {
  22. Version resource.Version
  23. Metadata []resource.MetadataField
  24. }
  25. rootfsPath := func(path ...string) string {
  26. return filepath.Join(append([]string{destDir, "rootfs"}, path...)...)
  27. }
  28. BeforeEach(func() {
  29. var err error
  30. destDir, err = ioutil.TempDir("", "docker-image-in-dir")
  31. Expect(err).ToNot(HaveOccurred())
  32. })
  33. AfterEach(func() {
  34. Expect(os.RemoveAll(destDir)).To(Succeed())
  35. })
  36. JustBeforeEach(func() {
  37. cmd := exec.Command(bins.In, destDir)
  38. payload, err := json.Marshal(req)
  39. Expect(err).ToNot(HaveOccurred())
  40. outBuf := new(bytes.Buffer)
  41. cmd.Stdin = bytes.NewBuffer(payload)
  42. cmd.Stdout = outBuf
  43. cmd.Stderr = GinkgoWriter
  44. err = cmd.Run()
  45. Expect(err).ToNot(HaveOccurred())
  46. err = json.Unmarshal(outBuf.Bytes(), &res)
  47. Expect(err).ToNot(HaveOccurred())
  48. })
  49. Describe("image metadata", func() {
  50. BeforeEach(func() {
  51. req.Source.Repository = "concourse/test-image-metadata"
  52. req.Version.Digest = latestDigest(req.Source.Repository)
  53. })
  54. It("captures the env and user", func() {
  55. var meta struct {
  56. User string `json:"user"`
  57. Env []string `json:"env"`
  58. }
  59. md, err := os.Open(filepath.Join(destDir, "metadata.json"))
  60. Expect(err).ToNot(HaveOccurred())
  61. defer md.Close()
  62. json.NewDecoder(md).Decode(&meta)
  63. Expect(meta.User).To(Equal("someuser"))
  64. Expect(meta.Env).To(Equal([]string{
  65. "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  66. "FOO=1",
  67. }))
  68. })
  69. })
  70. Describe("file attributes", func() {
  71. BeforeEach(func() {
  72. req.Source.Repository = "concourse/test-image-file-perms-mtime"
  73. req.Version.Digest = latestDigest(req.Source.Repository)
  74. })
  75. It("keeps file ownership, permissions, and modified times", func() {
  76. stat, err := os.Stat(rootfsPath("home", "alex", "birthday"))
  77. Expect(err).ToNot(HaveOccurred())
  78. Expect(stat.Mode()).To(Equal(os.FileMode(0603)))
  79. Expect(stat.ModTime()).To(BeTemporally("==", time.Date(1991, 06, 03, 05, 30, 30, 0, time.UTC)))
  80. sys, ok := stat.Sys().(*syscall.Stat_t)
  81. Expect(ok).To(BeTrue())
  82. Expect(sys.Uid).To(Equal(uint32(1000)))
  83. Expect(sys.Gid).To(Equal(uint32(1000)))
  84. })
  85. })
  86. Describe("removed files in layers", func() {
  87. BeforeEach(func() {
  88. req.Source.Repository = "concourse/test-image-whiteout"
  89. req.Version.Digest = latestDigest(req.Source.Repository)
  90. })
  91. It("does not restore files that were removed in later layers", func() {
  92. infos, err := ioutil.ReadDir(rootfsPath("top-dir-1"))
  93. Expect(err).ToNot(HaveOccurred())
  94. Expect(infos).To(HaveLen(2))
  95. stat, err := os.Stat(rootfsPath("top-dir-1", "nested-file"))
  96. Expect(err).ToNot(HaveOccurred())
  97. Expect(stat.IsDir()).To(BeFalse())
  98. stat, err = os.Stat(rootfsPath("top-dir-1", "nested-dir"))
  99. Expect(err).ToNot(HaveOccurred())
  100. Expect(stat.IsDir()).To(BeTrue())
  101. infos, err = ioutil.ReadDir(rootfsPath("top-dir-1", "nested-dir"))
  102. Expect(err).ToNot(HaveOccurred())
  103. Expect(infos).To(HaveLen(3))
  104. stat, err = os.Stat(rootfsPath("top-dir-1", "nested-dir", "file-gone"))
  105. Expect(err).To(HaveOccurred())
  106. stat, err = os.Stat(rootfsPath("top-dir-1", "nested-dir", "file-here"))
  107. Expect(err).ToNot(HaveOccurred())
  108. Expect(stat.IsDir()).To(BeFalse())
  109. stat, err = os.Stat(rootfsPath("top-dir-1", "nested-dir", "file-recreated"))
  110. Expect(err).ToNot(HaveOccurred())
  111. Expect(stat.IsDir()).To(BeFalse())
  112. stat, err = os.Stat(rootfsPath("top-dir-1", "nested-dir", "file-then-dir"))
  113. Expect(err).ToNot(HaveOccurred())
  114. Expect(stat.IsDir()).To(BeTrue())
  115. stat, err = os.Stat(rootfsPath("top-dir-2"))
  116. Expect(err).To(HaveOccurred())
  117. })
  118. })
  119. Describe("a hardlink that is later removed", func() {
  120. BeforeEach(func() {
  121. req.Source.Repository = "concourse/test-image-removed-hardlinks"
  122. req.Version.Digest = latestDigest(req.Source.Repository)
  123. })
  124. It("works", func() {
  125. lstat, err := os.Lstat(rootfsPath("usr", "libexec", "git-core", "git"))
  126. Expect(err).ToNot(HaveOccurred())
  127. Expect(lstat.Mode() & os.ModeSymlink).To(BeZero())
  128. stat, err := os.Stat(rootfsPath("usr", "libexec", "git-core", "git"))
  129. Expect(err).ToNot(HaveOccurred())
  130. Expect(stat.Mode() & os.ModeSymlink).To(BeZero())
  131. })
  132. })
  133. Describe("layers that replace symlinks with regular files", func() {
  134. BeforeEach(func() {
  135. req.Source.Repository = "concourse/test-image-symlinks"
  136. req.Version.Digest = latestDigest(req.Source.Repository)
  137. })
  138. It("removes the symlink and writes to a new file rather than trying to open and write to it (thereby overwriting its target)", func() {
  139. Expect(cat(rootfsPath("a"))).To(Equal("symlinked\n"))
  140. Expect(cat(rootfsPath("b"))).To(Equal("replaced\n"))
  141. })
  142. })
  143. })
  144. func cat(path string) string {
  145. bytes, err := ioutil.ReadFile(path)
  146. Expect(err).ToNot(HaveOccurred())
  147. return string(bytes)
  148. }