debian-forge/inputs/org.osbuild.containers
Alexander Larsson 46a228df38 Add support for installing containers in images
This adds a stage called org.osbuild.skopeo that installs docker and
oci archive files into the container storage of the tree being
constructed.

The source can either be a file from another pipeline, for example one
created with the existing org.osbuild.oci-archive stage, or it can
be using the new org.osbuild.skopeo source and org.osbuild.containers
input, which will download an image from a registry and install that.

There is an optional option in the install stage that lets you
configure a custom storage location, which allows the use of the
additionalimagestores option in the container storage.conf
to use a read-only image stores (instead of /var/lib/container).

Note: skopeo fails to start if /etc/containers/policy.json is
not available, so we bind mount it from the build tree to the
buildroot if available.
2022-02-10 14:43:17 +01:00

180 lines
4.5 KiB
Python
Executable file

#!/usr/bin/python3
"""Inputs for container images
This reads images from the `org.osbuild.containers` directory in the
sources store.
The store is indexed by the "container image id", which is the digest
of the container configuration file (rather than the outer manifest)
and is what will be shown in the "podman images" output when the image
is installed. This digest is stable as opposed to the manifest digest
which can change during transfer and storage due to
e.g. recompression.
"""
import os
import sys
import pathlib
from osbuild import inputs
SCHEMA = r"""
"definitions": {
"source-options": {
"type": "object",
"required": ["name"],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "The name to use for the image"
}
}
},
"source-object-ref": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"patternProperties": {
".*": {
"$ref": "#/definitions/source-options"
}
}
},
"source-origin": {
"type": "string",
"description": "When the origin of the input is a source",
"enum": ["org.osbuild.source"]
},
"pipeline-options": {
"type": "object",
"additionalProperties": false,
"required": ["name", "file", "format"],
"properties": {
"name": {
"type": "string",
"description": "The name to use for the image"
},
"file": {
"description": "File to access with in a pipeline",
"type": "string"
},
"format": {
"description": "Container archive format",
"enum": ["oci-archive", "docker-archive"]
}
}
},
"pipeline-object-ref": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"patternProperties": {
".*": {
"$ref": "#/definitions/pipeline-options"
}
}
},
"pipeline-origin": {
"type": "string",
"description": "When the origin of the input is a pipeline",
"enum": ["org.osbuild.pipeline"]
}
},
"additionalProperties": true,
"oneOf": [
{
"additionalProperties": false,
"required": ["type", "origin", "references"],
"properties": {
"type": {
"enum": ["org.osbuild.containers"]
},
"origin": {
"description": "The org.osbuild.source origin case",
"$ref": "#/definitions/source-origin"
},
"references": {
"description": "Container image id",
"$ref": "#/definitions/source-object-ref"
}
}
},
{
"additionalProperties": false,
"required": ["type", "origin", "references"],
"properties": {
"type": {
"enum": ["org.osbuild.containers"]
},
"origin": {
"description": "The org.osbuild.source origin case",
"$ref": "#/definitions/pipeline-origin"
},
"references": {
"description": "References to pipelines",
"$ref": "#/definitions/pipeline-object-ref"
}
}
}
]
"""
class ContainersInput(inputs.InputService):
@staticmethod
def map_source_ref(source, ref, data, target):
cache_dir = os.path.join(source, ref)
os.link(os.path.join(cache_dir, "container-image.tar"), os.path.join(target, ref))
return ref, "docker-archive"
@staticmethod
def map_pipeline_ref(store, ref, data, target):
filepath = data["file"].lstrip("/")
container_format = data["format"]
# prepare the mount point
filename = pathlib.Path(target, filepath)
os.makedirs(filename.parent, exist_ok=True)
filename.touch()
store.read_tree_at(ref, filename, filepath)
return filepath, container_format
def map(self, store, origin, refs, target, _options):
source = store.source("org.osbuild.containers")
images = {}
for ref, data in refs.items():
if origin == "org.osbuild.source":
ref, container_format = self.map_source_ref(source, ref, data, target)
else:
ref, container_format = self.map_pipeline_ref(store, ref, data, target)
images[ref] = {
"format": container_format,
"name": data["name"]
}
images[ref]["name"] = data["name"]
reply = {
"path": target,
"data": {
"archives": images
}
}
return reply
def main():
service = ContainersInput.from_args(sys.argv[1:])
service.main()
if __name__ == '__main__':
main()