Open ikorolev93 opened 5 years ago
Thanks for reporting, @deadNightTiger !
I noticed that this is also true (for the same reason) for anyone leveraging buildkit
directly with --output type=oci,dest=image.tar
.
(for anyone wanting to try that out, check https://github.com/cirocosta/buildkit-task)
@cirocosta Broken link - maybe the repo's private?
Also re: the original issue, this resource is pretty thin and relies on google/go-containerregistry
for all the heavy lifting. It looks like it has OCI support in some form or another (https://github.com/google/go-containerregistry/pull/261, https://github.com/google/go-containerregistry/pull/406), but I haven't had time to look into it too deeply.
whoops, it was indeed private - just made it public :grin:
I was looking at the code, and yeah, it makes sense - the tarball
package
doesn't consider that within a tarball, an image layout might exist.
With recent versions though, we can leverage that (with few modifications).
For instance, something like the following:
var img v1.Image
switch {
case req.Params.Layout != "":
imageIndex, err := layout.ImageIndexFromPath(req.Params.Layout)
if err != nil {
logrus.Errorf("could not find layout valid oci layout in %s: %s", req.Params.Layout, err)
os.Exit(1)
return
}
indexManifest, err := imageIndex.IndexManifest()
if err != nil {
logrus.Errorf("failed to retrieve index's manifest obj: %s", err)
os.Exit(1)
return
}
firstDigest := indexManifest.Manifests[0].Digest
img, err = imageIndex.Image(firstDigest)
if err != nil {
logrus.Errorf("failed to retrieve image from first digest %s: %s", firstDigest, err)
os.Exit(1)
return
}
case req.Params.Image != "":
imagePath := filepath.Join(src, req.Params.Image)
img, err = tarball.ImageFromPath(imagePath, nil)
if err != nil {
logrus.Errorf("could not load image from path '%s': %s", req.Params.Image, err)
os.Exit(1)
return
}
default:
logrus.Errorf("neither image nor layout specified")
os.Exit(1)
return
}
(ps.: the above depends on bumping our deps - https://github.com/concourse/registry-image-resource/pull/52)
Note that differently from the tarball
, one can have multiple images (as the
idea of the image layout is that in the index
you can specify as many images
as you'd like), thus, we end up having to pick one.
Also, layout
in go-containerregistry
assumes that you have a directory, not
a tarball (thus, we'd end up having to perform the unarchive
'ing ourselves in
here if we'd like to accept a tarball there)
It seems to me that we could either:
WriteIndex
// WriteIndex pushes the provided ImageIndex to the specified image reference.
// WriteIndex will attempt to push all of the referenced manifests before
// attempting to push the ImageIndex, to retain referential integrity.
func WriteIndex(ref name.Reference, ii v1.ImageIndex, options ...Option) error {
Wdyt?
thx!
@cirocosta option 2 sounds more correct; we should probably just mimic the behaviour of already-established tools for image pushing, and I'm assuming that's what they do
@cirocosta I suppose an alternative would be to push whichever manifest matches the name (+ tag) configured in the resource? I'm not really familiar enough with the use case of having multiple images in an OCI layout to judge whether that makes sense though. :thinking:
Eh, actually that's not as easy as I thought. I was thinking we could look at the manifest annotation org.opencontainers.image.ref.name
but that only contains the tag value, and doesn't include the repository.
I suppose that may explain the use case for multiple images in one layout: each are different tags associated to the same repository?
Ahh, looks like it's also for supporting multiple platforms. In that case we might just want to push all of them.
yeahh, in that case, they all have the same name + tag 😬
I was even wondering about the name oci
that we give to the current format. Should we rename it to docker
, instead? As layout
is the actual OCI 🤔
@cirocosta Hmm would it be accurate to say the current oci
format (supported by in
), while having Docker-specific manifest.json
in it, is still valid OCI at least? i.e. it's a superset?
We could and probably should rename it, but it'd be backwards-incompatible, so I'm wondering if we should just leave it as-is.
On the other hand, we haven't gone official with this resource yet, so now would be the time to change it. We could add docker
and oci
, and have oci
be "proper" OCI.
I am using concourse/concourse:7.9.1
and my pipeline looks like this trying to build and push multi-arch container images:
resources:
- name: repo
type: git
source:
uri: ....git
branch: ((branch))
private_key: ((private-key))
paths:
- src/golang/**
- name: golang-image
type: registry-image
icon: docker
source:
repository: ((registry))/((golang-builder-image-name))
tag: ((golang-builder-image-tag))
insecure: ((registry-insecure))
- name: alpine-image
type: registry-image
icon: docker
source:
repository: ((registry))/((golang-runner-image-name))
tag: ((golang-runner-image-tag))
insecure: ((registry-insecure))
- name: core-service-image
type: registry-image
icon: docker
source:
repository: ((registry))/core-service
insecure: ((registry-insecure))
jobs:
- name: build-container-image
plan:
- in_parallel:
- get: repo
trigger: true
- get: golang-image
params:
format: oci
- get: alpine-image
params:
format: oci
- task: detect-version
config:
platform: linux
image_resource:
type: registry-image
source:
repository: ((registry))/busybox
insecure: ((registry-insecure))
inputs:
- name: repo
outputs:
- name: version
run:
path: sh
args:
- -cx
- |
cat repo/src/golang/cmd/core-service/VERSION
cp -v repo/src/golang/cmd/core-service/VERSION version/
- load_var: version
file: version/VERSION
- task: build-image
privileged: true # oci-build-task must run in a privileged container
config:
platform: linux
image_resource:
type: registry-image
source:
repository: ((registry))/concourse/oci-build-task
insecure: ((registry-insecure))
inputs:
- name: repo
- name: golang-image
- name: alpine-image
outputs:
- name: image
params:
# ref: https://github.com/concourse/oci-build-task
CONTEXT: repo/src/golang
DOCKERFILE: repo/src/golang/cmd/core-service/Dockerfile
IMAGE_PLATFORM: ((platform))
IMAGE_ARG_BUILDER_IMAGE: golang-image/image.tar
IMAGE_ARG_RUNNER_IMAGE: alpine-image/image.tar
OUTPUT_OCI: true
BUILD_ARG_VERSION: ((.:version))
run:
path: build
- put: core-service-image
params:
image: image/image.tar
version: ((.:version))
oci-build-task
seem to produce OCI image, but registry-image
fails to push it to my registry with the following error:
ERRO[0000] could not load image from path 'image/image.tar': loading /tmp/build/put/image/image.tar as tarball: file manifest.json not found in tar
ok, after a not so short research the following change fixed my problem:
- put: core-service-image
params:
image: image/image
version: ((.:version))
get_params:
format: oci
Now all layers of OCI image can be successfully pushed to registry.
ok, after a not so short research the following change fixed my problem:
Finally!
Reproduction:
Output:
Indeed, there is no
manifest.json
in the OCI Image Layout Specification.As a workaround, I changed my task to do
buildah push whatever docker-archive:image.tar
, but it would be nice ifregistry-image-resource
supported OCI images.