Skip to content

image/save: Fix untagged images not present in index.json#47232

Merged
thaJeztah merged 3 commits intomoby:masterfrom
vvoland:fix-save-manifests
Feb 5, 2024
Merged

image/save: Fix untagged images not present in index.json#47232
thaJeztah merged 3 commits intomoby:masterfrom
vvoland:fix-save-manifests

Conversation

@vvoland
Copy link
Contributor

@vvoland vvoland commented Jan 26, 2024

Saving an image via digested reference, ID or truncated ID doesn't store the image reference in the archive. This also causes the save code to not add the image's manifest to the index.json.
This PR explicitly adds the untagged manifests to the index.json if no tagged manifests were added.

- How to verify it

  • TestSaveOCI integration test

By digest

$ docker save hello-world@sha256:4bd78111b6914a99dbc560e6a20eab57ff6655aea4a80c50b0c5491968cbc2e6 | tar xO index.json | jq
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:411caf340c828657e915a83ed561a79d2b8150dabad4dc079d881cbfe6f86afe",
      "size": 447,
      "platform": {
        "architecture": "arm64",
        "os": "linux"
      }
    }
  ]
}

By tag

$ docker save hello-world | tar xO index.json | jq
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:411caf340c828657e915a83ed561a79d2b8150dabad4dc079d881cbfe6f86afe",
      "size": 447,
      "annotations": {
        "io.containerd.image.name": "docker.io/library/hello-world:latest",
        "org.opencontainers.image.ref.name": "latest"
      },
      "platform": {
        "architecture": "arm64",
        "os": "linux"
      }
    }
  ]
}

Tag and digest

$ docker tag hello-world my:hello
$ docker save hello-world my:hello \
   hello-world@sha256:4bd78111b6914a99dbc560e6a20eab57ff6655aea4a80c50b0c5491968cbc2e6 | \
   tar xO index.json | jq
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:411caf340c828657e915a83ed561a79d2b8150dabad4dc079d881cbfe6f86afe",
      "size": 447,
      "annotations": {
        "io.containerd.image.name": "docker.io/library/hello-world:latest",
        "org.opencontainers.image.ref.name": "latest"
      },
      "platform": {
        "architecture": "arm64",
        "os": "linux"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:411caf340c828657e915a83ed561a79d2b8150dabad4dc079d881cbfe6f86afe",
      "size": 447,
      "annotations": {
        "io.containerd.image.name": "docker.io/library/my:hello",
        "org.opencontainers.image.ref.name": "hello"
      },
      "platform": {
        "architecture": "arm64",
        "os": "linux"
      }
    }
  ]
}

- Description for the changelog

- Fix `docker save <image>@<digest>` producing an OCI archive with index without manifests

- A picture of a cute animal (not mandatory but encouraged)

Saving an image via digested reference, ID or truncated ID doesn't store
the image reference in the archive. This also causes the save code to
not add the image's manifest to the index.json.
This commit explicitly adds the untagged manifests to the index.json if
no tagged manifests were added.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
When saving an image treat `image@sha256:abcdef...` the same as
`abcdef...`, this makes it:

- Not export the digested tag as the image name
- Not try to export all tags from the image repository

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
@vvoland vvoland force-pushed the fix-save-manifests branch 3 times, most recently from da0e203 to fbe269f Compare January 29, 2024 19:00
@vvoland vvoland marked this pull request as ready for review January 29, 2024 19:39
@vvoland vvoland requested a review from cpuguy83 January 29, 2024 19:39
@vvoland vvoland force-pushed the fix-save-manifests branch from fbe269f to 364316c Compare February 1, 2024 13:39
@vvoland vvoland added the containerd-integration Issues and PRs related to containerd integration label Feb 1, 2024
}

manifestDesc.Annotations = map[string]string{
"io.containerd.image.name": "docker.io/library/multilayer:latest",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really like to see these hard-coded values moved up the stack. With it like this it makes the test more confusing, for me, to work through.
Likewise with the layer contents.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted another function and moved the hardcoded values up. Let me know if that works for you!

@thaJeztah
Copy link
Member

@vvoland could you have a peek at the review comment above?

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
@vvoland vvoland force-pushed the fix-save-manifests branch from 364316c to 2ef0b53 Compare February 5, 2024 10:18
@thaJeztah
Copy link
Member

Failures are Windows + containerd snapshotter, and are known to still fail, so can be ignored

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quick blurb, as I started to look (so far nothing blocking) but had to drop off, so let me post what I had

Comment on lines +277 to +282
taggedManifest := untaggedMfstDesc
taggedManifest.Annotations = map[string]string{
images.AnnotationImageName: ref.String(),
ocispec.AnnotationRefName: ref.Tag(),
}
manifestDescriptors = append(manifestDescriptors, taggedManifest)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly somewhat more liked the struct-literal here, to prevent any possible confusion that things / fields could be used by-reference 🤔

It would result in some small amount of duplication (constructing the struct twice), just have been bit by maps or slices not being properly de-referenced on some occasions.

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@thaJeztah thaJeztah merged commit 2156635 into moby:master Feb 5, 2024
taggedManifest := untaggedMfstDesc
taggedManifest.Annotations = map[string]string{
images.AnnotationImageName: ref.String(),
ocispec.AnnotationRefName: ref.Tag(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@thaJeztah thaJeztah Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm.. interesting; so that would mean duplicating the same information (and implicitly; requiring the reader of that information having to parse the format to get the tag only?).

One thing I notice in that section is that it looks like separator as part of components seems to have been conflated with separator between name and tag (or digest);

  • org.opencontainers.image.ref.name Name of the reference for a target (string).
    • SHOULD only be considered valid when on descriptors on index.json within image layout.

    • Character set of the value SHOULD conform to alphanum of A-Za-z0-9 and separator set of -._:@/+

    • A valid reference matches the following grammar:

      ref       ::= component ("/" component)*
      component ::= alphanum (separator alphanum)*
      alphanum  ::= [A-Za-z0-9]+
      separator ::= [-._:@+] | "--"

The distribution/reference grammar uses a different definition;

separator := /[_.]|__|[-]*/
  • it only allows -._ as separators
  • 2 consecutive underscores (__)
  • zero or more hypens (-)

The :, @ would be to separate tag or digest, and

If I read the OCI spec there correctly, something like docker.io/github.com/he:llo@sha256+fo--b_ar would be a valid value, but not parseable with the distribution/reference package 😬

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm.. for org.opencontainers.image.base.name, there's a mention that it should be in the format described in distribution/reference; should that also be done for the above?

  • org.opencontainers.image.base.name Image reference of the image this image is based on (string)
  • This SHOULD be image references in the format defined by [distribution/distribution][distribution-reference].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/images Image Distribution containerd-integration Issues and PRs related to containerd integration kind/bugfix PR's that fix bugs process/cherry-picked status/2-code-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[v25] docker save <IMAGE>@<DIGEST> produces index.json with "manifest": null integration test: Check OCI Manifest layers order

4 participants