project-stacker/stacker

Bug: top level directories are removed if there is only one

Opened this issue · 5 comments

stacker version

v0.30.1-15-gdc0ada0

Describe the bug

If you attempt to build the stacker.yaml below it will fail.
The reason is that the /files directory is stripped away in the 'mybuild' layer. The files 'subuid' and 'subgid' are directly in /.

$ stacker build --layer-type=squashfs --layer-type=tar
...
error: lstat /files/subuid: no such file or directory
error: execute failed: exit status 1
error: exit status 1
$ cat stacker.yaml 
mybuild:
  from:
    type: scratch
  import:
    - path: /etc/subuid
      dest: /files/
    - path: /etc/subgid
      dest: /files

mytest:
  from: 
    type: docker
    url: docker://ubuntu:latest
  import:
    - stacker://mybuild/files/subuid
    - stacker://mybuild/files/subgid
  run: |
    ls -l /stacker

To reproduce

No response

Expected behavior

No response

Screenshots

No response

Additional context

No response

Part of the fix is this:

--- a/pkg/squashfs/squashfs.go
+++ b/pkg/squashfs/squashfs.go
@@ -135,7 +135,7 @@ func MakeSquashfs(tempdir string, rootfs string, eps *ExcludePaths, verity Verit
        tmpSquashfs.Close()
        os.Remove(tmpSquashfs.Name())
        defer os.Remove(tmpSquashfs.Name())
-       args := []string{rootfs, tmpSquashfs.Name()}
+       args := []string{rootfs, tmpSquashfs.Name(), "-keep-as-directory"}
        compression := GzipCompression
        if mksquashfsSupportsZstd() {
                args = append(args, "-comp", "zstd")

but I think the problem is also happening with tar layers. (fails to build with '--layer-type=tar' also).

When I was poking around trying to fix this, I noticed that each 'import' gets creates its own layer in the created oci.
i think it might be better to have stacker start populating the same overlay that the 'run' would be handed before 'run' (if present) occurs.

Given this stcker.lyaml:

mybuild:
  from:
    type: docker
    url: ubuntu:latest
  import:
    - my-file
    - path: /etc/passwd
      dest: /etc/passwd-host
    - path: /static/some-binary-here
      dest: /usr/bin/
  run: |
    ls -l /usr/bin/some-binary-here
    ls -l /etc/passwd /etc/passwd-host
    ls -l /stacker/my-file

When 'run' occurred:

  • there would be read-only bind-mount on /stacker (current behavior, i think)
  • / would be a single overlay as if imports[1] and imports[2] were not present.
  • /usr/bin/some-binary-here would be present.

I know that @rchincha was interested in keeping the import logic as simple as possible, and for sure we have some corner cases to work out with the above. What I suggest to handle those edge cases (new file over existing directory, symlink following ...) is just to mimic what gnu 'tar' would do. And one path we could follow for implementation would be to have all the 'import' statements ultimately result in a tar archive, and then to just enter the filesystem view (including /overlay) and extract that tar archive .

overlay-dirs, possibly modified after import: [{/tmp/a/.stacker/imports-copy/mybuild/2106930494 "/files/"} {/tmp/a/.stacker/imports-copy/mybuild/918196220 "/files"}]
lxc rootfs overlay arg overlayfs:/tmp/a/roots/sha256_f4447d034d1e036e1d4c281f25e1a32aa7a252ac6216c50f59c9e4f6e70a4272/overlay:/tmp/a/roots/sha256_0d9b1e118cb2b6462cffe390ab4becc2313f5bd898c0caee8fb372526c98bbe1/overlay:/tmp/a/roots/mybuild/overlay
stacker version v1.0.0-rc1-c00511a
stacker subcommand: [/tmp/a/stacker --oci-dir /tmp/a/oci --roots-dir /tmp/a/roots --stacker-dir /tmp/a/.stacker --storage-type overlay --internal-userns --debug internal-go check-aa-profile lxc-container-default-cgns]
bind mounting /tmp/a/.stacker/imports/mybuild into container
Generating overlay_dirs layers
filesystem mybuild built successfully

quotes :\

rootfs/"/files/"
$ ls
subuid

smoser commented

quotes :\

rootfs/"/files/" $ ls subuid

The scourge of shell will never end.