project-stacker/stacker

Feat: allow creation of layers via stacking (maybe bring back `apply`?)

Opened this issue · 3 comments

smoser commented

Is your feature request related to a problem? Please describe.

I would like to be able to publish a set of individual layers and a "combined" layer, where the combined layer is joined layer of the individual layers.

An example, I have a stacker file like this:

base:
  build_only: true
  from:
    type: docker
    url: docker://docker.io/library/ubuntu:jammy
  run:
    apt-get update -q

kernel-build:
  build_only: true
  from:
    type: built
    tag: base
  run: |
      mkdir -p /tmp/stage/kernel /export
      trap "rm -Rf /tmp/stage" EXIT

      apt-get install --no-install-recommends --assume-yes linux-image-virtual
      mv /boot /lib/modules /tmp/stage/kernel
      tar -C /tmp/stage -cf /export/kernel.tar kernel/

# the only top level dir this layer is 'kernel/'
kernel:
  from:
    type: scratch
    url: stacker://kernel-build/export/kernel.tar

ovmf-build:
  build_only: true
  from:
    type: built
    tag: base
  run: |
      mkdir -p /tmp/stage/ovmf /export
      trap "rm -Rf /tmp/stage" EXIT

      apt-get install --no-install-recommends --assume-yes ovmf
      cp /usr/share/OVMF/OVMF_CODE.secboot.fd /tmp/stage/ovmf/ovmf-code.fd
      cp /usr/share/OVMF/OVMF_VARS.fd /tmp/stage/ovmf/ovmf-vars.fd
      tar -C /tmp/stage -cf /export/ovmf.tar ovmf/

# the only top level dir this layer is 'ovmf/'
ovmf:
  from:
    type: scratch
    url: stacker://ovmf-build/export/ovmf.tar

The 'kernel' and 'ovmf' layers are built and have only top level directories 'kernel/' and 'ovmf/' respectively.

I'd like to add another layer build to this file that has:

combined:
  from:
    type: stack
  layers:
    - type: built
      tag: kernel
    - type: built
      tag: ovmf

The goal would be to have 3 layers published:

  • ovmf
  • kernel
  • combined

combined would simply be 'ovmf' on top of 'kernel', such that index.json had:

{
    "schemaVersion": 2,
    "mediaType": "application/vnd.oci.image.index.v1+json",
    "manifests": [
        {
            "mediaType": "application/vnd.oci.image.manifest.v1+json",
            "digest": "sha256:d3743cb7f9799554f6188ce1936259465b06ef181c27ef0ceaf3ce77413ee759",
            "size": 2001,
            "annotations": {
                "org.opencontainers.image.ref.name": "kernel-squashfs"
            }
        },
        {
            "mediaType": "application/vnd.oci.image.manifest.v1+json",
            "digest": "sha256:37f2a392a570e035ef2887918661eca14a984ff2f84d53e5e0d2aaa920900fdf",
            "size": 2000,
            "annotations": {
                "org.opencontainers.image.ref.name": "ovmf-squashfs"
            }
        },
        {
            "mediaType": "application/vnd.oci.image.manifest.v1+json",
            "digest": "sha256:639a0e5badf32904dd4178e6c203fb68884a3b1aa450ce38a44c840321dfbd32",
            "size": 1252,
            "annotations": {
                "org.opencontainers.image.ref.name": "combined-squashfs"
            }
        }
    ]
}

And oci/blobs/sha256/639a0e5badf32904dd4178e6c203fb68884a3b1aa450ce38a44c840321dfbd32 had:

{
    "schemaVersion": 2,
    "mediaType": "application/vnd.oci.image.manifest.v1+json",
    "config": {
        "mediaType": "application/vnd.oci.image.config.v1+json",
        "digest": "sha256:44759f13b3436e793280e2489b53ce8c65c780b81c99763082cdbc3646ab1a3f",
        "size": 376
    },
    "layers": [
      {
          "mediaType": "application/vnd.stacker.image.layer.squashfs+zstd+verity",
          "digest": "sha256:65b132eb797f441c164540aa66e81e7c1d27e45008e82c27487530940335fef4",
          "size": 42680320,
          "annotations": {
              "io.stackeroci.stacker.squashfs_verity_root_hash": "56c9936673423ac92275a9112b6bf0d0f1bbe3fa92767bc519d74484f2ee1936"
          }
      },
      {
          "mediaType": "application/vnd.stacker.image.layer.squashfs+zstd+verity",
          "digest": "sha256:72db3a90a010ee3e954473fabde73f2d05ee25b16615819eef741f7045c3849d",
          "size": 1568768,
          "annotations": {
              "io.stackeroci.stacker.squashfs_verity_root_hash": "3460bb576d5d6568152820b0c0f6f67f2836f46763272717fbc453d6cd245e29"
          }
      }
    ],
    "annotations": {
        "io.stackeroci.stacker.git_version": "v1.0.0-rc4-5-g479fca8",
        "io.stackeroci.stacker.stacker_yaml": "myob",
    }
}

where kernel-squashfs had a single layer in layers with sha of 65b1 and ovmf had a single laye in its layers with sha 72db3

Describe the solution you'd like

No response

Describe alternatives you've considered

No response

Additional context

I'm not set on a particular solution or syntax.

I think this functionality makes sense and can be useful in many contexts. It doesn't work well for traditional package based distributions (with package files spread all over and a single-file package database), but it would work well for non-distro things and potentially even distros like nixOS.

My specific desire is to publish N different layers so that each layer can be pulled individually and a 'combined' layer could be pulled as well without any duplication.

smoser commented

I had originally suggested to @hallyn that maybe this could be implemented as multiple `from' so overloading that (which would require having the yaml parser accept dict or array) we'd have:

combined:
  from:
    - type: built
      tag: kernel
    - type: built
      tag: ovmf

Either solution has to deal with (or ignore) duplicate 'under layers' as discussed in doc/layer-merging.md.

The following should give us separate layers right?

combined:
  from:
    type: docker
    url: docker://ubuntu:latest
  import:
    - path: docker://...
       dest: /
    - path: stacker://built/...
       dest: /
...
smoser commented

The following should give us separate layers right?

combined:
  from:
    type: docker
    url: docker://ubuntu:latest
  import:
    - path: docker://.../
       dest: /
    - path: stacker://built/.../
       dest: /
...

I edited to add a '/' on the end fof the 'path' elements or clarity based on your comment about trailing / in #453 .

I would expect the above to create a single 'combined' layer that would sit on top of ubuntu:latest. And I think it does, based on existing import behavior, each import does not create its own new layer, does it?