| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- package resource_test
- import (
- "bytes"
- "encoding/json"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "syscall"
- "time"
- "github.com/google/go-containerregistry/pkg/name"
- "github.com/google/go-containerregistry/pkg/v1"
- "github.com/google/go-containerregistry/pkg/v1/tarball"
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
- "github.com/concourse/registry-image-resource"
- )
- var _ = Describe("In", func() {
- var destDir string
- var req struct {
- Source resource.Source
- Params resource.GetParams
- Version resource.Version
- }
- var res struct {
- Version resource.Version
- Metadata []resource.MetadataField
- }
- rootfsPath := func(path ...string) string {
- return filepath.Join(append([]string{destDir, "rootfs"}, path...)...)
- }
- BeforeEach(func() {
- var err error
- destDir, err = ioutil.TempDir("", "docker-image-in-dir")
- Expect(err).ToNot(HaveOccurred())
- })
- AfterEach(func() {
- Expect(os.RemoveAll(destDir)).To(Succeed())
- })
- JustBeforeEach(func() {
- cmd := exec.Command(bins.In, destDir)
- payload, err := json.Marshal(req)
- Expect(err).ToNot(HaveOccurred())
- outBuf := new(bytes.Buffer)
- cmd.Stdin = bytes.NewBuffer(payload)
- cmd.Stdout = outBuf
- cmd.Stderr = GinkgoWriter
- err = cmd.Run()
- Expect(err).ToNot(HaveOccurred())
- err = json.Unmarshal(outBuf.Bytes(), &res)
- Expect(err).ToNot(HaveOccurred())
- })
- Describe("image metadata", func() {
- BeforeEach(func() {
- req.Source.Repository = "concourse/test-image-metadata"
- req.Version.Digest = latestDigest(req.Source.Repository)
- })
- It("captures the env and user", func() {
- var meta struct {
- User string `json:"user"`
- Env []string `json:"env"`
- }
- md, err := os.Open(filepath.Join(destDir, "metadata.json"))
- Expect(err).ToNot(HaveOccurred())
- defer md.Close()
- json.NewDecoder(md).Decode(&meta)
- Expect(meta.User).To(Equal("someuser"))
- Expect(meta.Env).To(Equal([]string{
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
- "FOO=1",
- }))
- })
- })
- Describe("file attributes", func() {
- BeforeEach(func() {
- req.Source.Repository = "concourse/test-image-file-perms-mtime"
- req.Version.Digest = latestDigest(req.Source.Repository)
- })
- It("keeps file ownership, permissions, and modified times", func() {
- stat, err := os.Stat(rootfsPath("home", "alex", "birthday"))
- Expect(err).ToNot(HaveOccurred())
- Expect(stat.Mode()).To(Equal(os.FileMode(0603)))
- Expect(stat.ModTime()).To(BeTemporally("==", time.Date(1991, 06, 03, 05, 30, 30, 0, time.UTC)))
- sys, ok := stat.Sys().(*syscall.Stat_t)
- Expect(ok).To(BeTrue())
- Expect(sys.Uid).To(Equal(uint32(1000)))
- Expect(sys.Gid).To(Equal(uint32(1000)))
- })
- })
- Describe("removed files in layers", func() {
- BeforeEach(func() {
- req.Source.Repository = "concourse/test-image-whiteout"
- req.Version.Digest = latestDigest(req.Source.Repository)
- })
- It("does not restore files that were removed in later layers", func() {
- infos, err := ioutil.ReadDir(rootfsPath("top-dir-1"))
- Expect(err).ToNot(HaveOccurred())
- Expect(infos).To(HaveLen(2))
- stat, err := os.Stat(rootfsPath("top-dir-1", "nested-file"))
- Expect(err).ToNot(HaveOccurred())
- Expect(stat.IsDir()).To(BeFalse())
- stat, err = os.Stat(rootfsPath("top-dir-1", "nested-dir"))
- Expect(err).ToNot(HaveOccurred())
- Expect(stat.IsDir()).To(BeTrue())
- infos, err = ioutil.ReadDir(rootfsPath("top-dir-1", "nested-dir"))
- Expect(err).ToNot(HaveOccurred())
- Expect(infos).To(HaveLen(3))
- stat, err = os.Stat(rootfsPath("top-dir-1", "nested-dir", "file-gone"))
- Expect(err).To(HaveOccurred())
- stat, err = os.Stat(rootfsPath("top-dir-1", "nested-dir", "file-here"))
- Expect(err).ToNot(HaveOccurred())
- Expect(stat.IsDir()).To(BeFalse())
- stat, err = os.Stat(rootfsPath("top-dir-1", "nested-dir", "file-recreated"))
- Expect(err).ToNot(HaveOccurred())
- Expect(stat.IsDir()).To(BeFalse())
- stat, err = os.Stat(rootfsPath("top-dir-1", "nested-dir", "file-then-dir"))
- Expect(err).ToNot(HaveOccurred())
- Expect(stat.IsDir()).To(BeTrue())
- stat, err = os.Stat(rootfsPath("top-dir-2"))
- Expect(err).To(HaveOccurred())
- })
- })
- Describe("a hardlink that is later removed", func() {
- BeforeEach(func() {
- req.Source.Repository = "concourse/test-image-removed-hardlinks"
- req.Version.Digest = latestDigest(req.Source.Repository)
- })
- It("works", func() {
- lstat, err := os.Lstat(rootfsPath("usr", "libexec", "git-core", "git"))
- Expect(err).ToNot(HaveOccurred())
- Expect(lstat.Mode() & os.ModeSymlink).To(BeZero())
- stat, err := os.Stat(rootfsPath("usr", "libexec", "git-core", "git"))
- Expect(err).ToNot(HaveOccurred())
- Expect(stat.Mode() & os.ModeSymlink).To(BeZero())
- })
- })
- Describe("layers that replace symlinks with regular files", func() {
- BeforeEach(func() {
- req.Source.Repository = "concourse/test-image-symlinks"
- req.Version.Digest = latestDigest(req.Source.Repository)
- })
- It("removes the symlink and writes to a new file rather than trying to open and write to it (thereby overwriting its target)", func() {
- Expect(cat(rootfsPath("a"))).To(Equal("symlinked\n"))
- Expect(cat(rootfsPath("b"))).To(Equal("replaced\n"))
- })
- })
- Describe("fetching from a private repository with credentials", func() {
- BeforeEach(func() {
- req.Source = resource.Source{
- Repository: dockerPrivateRepo,
- Tag: "latest",
- Username: dockerUsername,
- Password: dockerPassword,
- }
- checkDockerUserConfigured()
- req.Version = resource.Version{
- Digest: PRIVATE_LATEST_STATIC_DIGEST,
- }
- })
- It("works", func() {
- Expect(cat(rootfsPath("Dockerfile"))).To(ContainSubstring("hello!"))
- })
- })
- Describe("fetching in OCI format", func() {
- var manifest *v1.Manifest
- BeforeEach(func() {
- req.Source.Repository = "concourse/test-image-static"
- req.Params.RawFormat = "oci"
- req.Version.Digest, manifest = latestManifest(req.Source.Repository)
- })
- It("saves the tagged image as image.tar instead of saving the rootfs", func() {
- _, err := os.Stat(filepath.Join(destDir, "rootfs"))
- Expect(os.IsNotExist(err)).To(BeTrue())
- _, err = os.Stat(filepath.Join(destDir, "manifest.json"))
- Expect(os.IsNotExist(err)).To(BeTrue())
- tag, err := name.NewTag("concourse/test-image-static:latest", name.WeakValidation)
- Expect(err).ToNot(HaveOccurred())
- img, err := tarball.ImageFromPath(filepath.Join(destDir, "image.tar"), &tag)
- Expect(err).ToNot(HaveOccurred())
- fetchedManifest, err := img.Manifest()
- Expect(err).ToNot(HaveOccurred())
- // cannot assert against digest because the saved image's manifest isn't
- // JSON-prettified, so it has a different sha256. so just assert against
- // digest within manifest, which is what ends up being the 'image id'
- // anyway.
- Expect(fetchedManifest.Config.Digest).To(Equal(manifest.Config.Digest))
- })
- })
- Describe("saving the digest", func() {
- BeforeEach(func() {
- req.Source.Repository = "alpine"
- req.Version.Digest = latestDigest(req.Source.Repository)
- })
- It("saves the digest to a file", func() {
- digest, err := ioutil.ReadFile(filepath.Join(destDir, "digest"))
- Expect(err).ToNot(HaveOccurred())
- Expect(string(digest)).To(Equal(req.Version.Digest))
- })
- })
- })
|