diff --git a/pkg/compiler/artifact.go b/pkg/compiler/artifact.go index dd288a81..5a087ea1 100644 --- a/pkg/compiler/artifact.go +++ b/pkg/compiler/artifact.go @@ -290,15 +290,27 @@ func (a *PackageArtifact) GenerateFinalImage(imageName string, b CompilerBackend return builderOpts, errors.Wrap(err, "error met while creating tempdir for "+a.Path) } + if err := a.Unpack(uncompressedFiles, keepPerms); err != nil { + return builderOpts, errors.Wrap(err, "error met while uncompressing artifact "+a.Path) + } + + empty, err := helpers.DirectoryIsEmpty(uncompressedFiles) + if err != nil { + return builderOpts, errors.Wrap(err, "error met while checking if directory is empty "+uncompressedFiles) + } + + // See https://github.com/moby/moby/issues/38039. + // We can't generate FROM scratch empty images. Docker will refuse to export them + // workaround: Inject a .virtual empty file + if empty { + helpers.Touch(filepath.Join(uncompressedFiles, ".virtual")) + } + data := a.genDockerfile() if err := ioutil.WriteFile(dockerFile, []byte(data), 0644); err != nil { return builderOpts, errors.Wrap(err, "error met while rendering artifact dockerfile "+a.Path) } - if err := a.Unpack(uncompressedFiles, keepPerms); err != nil { - return builderOpts, errors.Wrap(err, "error met while uncompressing artifact "+a.Path) - } - builderOpts = CompilerBackendOptions{ ImageName: imageName, SourcePath: archive, diff --git a/pkg/compiler/artifact_test.go b/pkg/compiler/artifact_test.go index 66b9513a..3aad23b4 100644 --- a/pkg/compiler/artifact_test.go +++ b/pkg/compiler/artifact_test.go @@ -203,6 +203,44 @@ RUN echo bar > /test2`)) Expect(content).To(Equal(testString)) }) + It("Generates empty packages images", func() { + b := NewSimpleDockerBackend() + imageprefix := "foo/" + + tmpdir, err := ioutil.TempDir(os.TempDir(), "artifact") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(tmpdir) // clean up + + tmpWork, err := ioutil.TempDir(os.TempDir(), "artifact2") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(tmpWork) // clean up + + artifact := NewPackageArtifact(filepath.Join(tmpWork, "fake.tar")) + artifact.SetCompileSpec(&LuetCompilationSpec{Package: &pkg.DefaultPackage{Name: "foo", Version: "1.0"}}) + + err = artifact.Compress(tmpdir, 1) + Expect(err).ToNot(HaveOccurred()) + resultingImage := imageprefix + "foo--1.0" + opts, err := artifact.GenerateFinalImage(resultingImage, b, false) + Expect(err).ToNot(HaveOccurred()) + Expect(opts.ImageName).To(Equal(resultingImage)) + + Expect(b.ImageExists(resultingImage)).To(BeTrue()) + + result, err := ioutil.TempDir(os.TempDir(), "result") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(result) // clean up + + err = b.ExtractRootfs(CompilerBackendOptions{ImageName: resultingImage, Destination: result}, false) + Expect(err).ToNot(HaveOccurred()) + + Expect(helpers.DirectoryIsEmpty(result)).To(BeFalse()) + content, err := ioutil.ReadFile(filepath.Join(result, ".virtual")) + Expect(err).ToNot(HaveOccurred()) + + Expect(string(content)).To(Equal("")) + }) + It("Retrieves uncompressed name", func() { a := NewPackageArtifact("foo.tar.gz") a.SetCompressionType(compiler.GZip) diff --git a/pkg/installer/repository_test.go b/pkg/installer/repository_test.go index eaa98d99..18568e0e 100644 --- a/pkg/installer/repository_test.go +++ b/pkg/installer/repository_test.go @@ -315,5 +315,82 @@ urls: Expect(a.Unpack(extracted, false)).ToNot(HaveOccurred()) Expect(helpers.Read(filepath.Join(extracted, "test6"))).To(Equal("artifact6\n")) }) + + It("generates images of virtual packages", func() { + b := backend.NewSimpleDockerBackend() + tmpdir, err := ioutil.TempDir("", "tree") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(tmpdir) // clean up + + generalRecipe := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false)) + + err = generalRecipe.Load("../../tests/fixtures/virtuals") + Expect(err).ToNot(HaveOccurred()) + + Expect(len(generalRecipe.GetDatabase().GetPackages())).To(Equal(5)) + + localcompiler := compiler.NewLuetCompiler(backend.NewSimpleDockerBackend(), generalRecipe.GetDatabase(), compiler.NewDefaultCompilerOptions(), solver.Options{Type: solver.SingleCoreSimple}) + + spec, err := localcompiler.FromPackage(&pkg.DefaultPackage{Name: "a", Category: "test", Version: "1.99"}) + Expect(err).ToNot(HaveOccurred()) + + Expect(spec.GetPackage().GetPath()).ToNot(Equal("")) + + tmpdir, err = ioutil.TempDir("", "tree") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(tmpdir) // clean up + + spec.SetOutputPath(tmpdir) + localcompiler.SetConcurrency(1) + + artifact, err := localcompiler.Compile(false, spec) + Expect(err).ToNot(HaveOccurred()) + Expect(helpers.Exists(artifact.GetPath())).To(BeTrue()) + Expect(helpers.Untar(artifact.GetPath(), tmpdir, false)).ToNot(HaveOccurred()) + + repo, err := dockerStubRepo(tmpdir, "../../tests/fixtures/virtuals", repoImage, true, true) + Expect(err).ToNot(HaveOccurred()) + Expect(repo.GetName()).To(Equal("test")) + Expect(helpers.Exists(spec.Rel(REPOSITORY_SPECFILE))).ToNot(BeTrue()) + Expect(helpers.Exists(spec.Rel(TREE_TARBALL + ".gz"))).ToNot(BeTrue()) + Expect(helpers.Exists(spec.Rel(REPOSITORY_METAFILE + ".tar"))).ToNot(BeTrue()) + err = repo.Write(repoImage, false, true) + Expect(err).ToNot(HaveOccurred()) + + Expect(b.ImageAvailable(fmt.Sprintf("%s:%s", repoImage, "tree.tar.gz"))).To(BeTrue()) + Expect(b.ImageAvailable(fmt.Sprintf("%s:%s", repoImage, "repository.meta.yaml.tar"))).To(BeTrue()) + Expect(b.ImageAvailable(fmt.Sprintf("%s:%s", repoImage, "repository.yaml"))).To(BeTrue()) + Expect(b.ImageAvailable(fmt.Sprintf("%s:%s", repoImage, "a-test-1.99"))).To(BeTrue()) + + extracted, err := ioutil.TempDir("", "extracted") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(extracted) // clean up + + c := repo.Client() + + f, err := c.DownloadFile("repository.yaml") + Expect(err).ToNot(HaveOccurred()) + Expect(helpers.Read(f)).To(ContainSubstring("name: test")) + + a, err := c.DownloadArtifact(&compiler.PackageArtifact{ + Path: "test.tar", + CompileSpec: &compiler.LuetCompilationSpec{ + Package: &pkg.DefaultPackage{ + Name: "a", + Category: "test", + Version: "1.99", + }, + }, + }) + Expect(err).ToNot(HaveOccurred()) + + Expect(a.Unpack(extracted, false)).ToNot(HaveOccurred()) + + Expect(helpers.DirectoryIsEmpty(extracted)).To(BeFalse()) + content, err := ioutil.ReadFile(filepath.Join(extracted, ".virtual")) + Expect(err).ToNot(HaveOccurred()) + + Expect(string(content)).To(Equal("")) + }) }) })