From a502ce346f8c433bdbe7f44950cc574f1ffc12a8 Mon Sep 17 00:00:00 2001 From: Arnaud de Grandmaison Date: Mon, 23 Sep 2024 17:11:36 +0200 Subject: [PATCH] feature: add an optional 'image' field to config files. Some configurations require specific docker images to build (e.g. cca-4worl.yaml needs shrinkwraptool/base-full), and this requirement is exposed on the command line, as the user has to specify the image when invoking shrinkwrap. This seems unnecessary and error prone. This patch adds support for an 'image' keyword in the configuration file. If no image is specified on the command line, shrinkwrap will use the image from the recipe (or a default one if none is specified). When merging multiple configurations, conflicts (different image names) are detected and reported as we have no way of merging this requirement. At this time, in case of conflict there is no way to resolve it by forcing the image to use from the command line. Signed-off-by: Arnaud de Grandmaison --- config/cca-4world.yaml | 4 ++- documentation/userguide/configmodel.rst | 1 + .../userguide/configstore/cca-4world.rst | 2 +- shrinkwrap/commands/buildall.py | 2 +- shrinkwrap/commands/clean.py | 2 +- shrinkwrap/commands/inspect.py | 7 +++++ shrinkwrap/commands/run.py | 2 +- shrinkwrap/shrinkwrap.py | 6 ++-- shrinkwrap/utils/config.py | 28 ++++++++++++++++++- 9 files changed, 46 insertions(+), 8 deletions(-) diff --git a/config/cca-4world.yaml b/config/cca-4world.yaml index 5949c66..82851d8 100644 --- a/config/cca-4world.yaml +++ b/config/cca-4world.yaml @@ -9,7 +9,7 @@ description: >- Build with: .. code-block:: shell - $ shrinkwrap --image shrinkwraptool/base-full build cca-4world.yaml --overlay buildroot.yaml --btvar GUEST_ROOTFS='${artifact:BUILDROOT}' + $ shrinkwrap build cca-4world.yaml --overlay buildroot.yaml --btvar GUEST_ROOTFS='${artifact:BUILDROOT}' Then run the model with: @@ -31,6 +31,8 @@ description: >- See cca-3worlds.yaml config :ref:`userguide/configstore/cca-3world:description` if willing to launch a realm using kvmtool. +image: shrinkwraptool/base-full + concrete: true layers: diff --git a/documentation/userguide/configmodel.rst b/documentation/userguide/configmodel.rst index debcc6f..7785669 100644 --- a/documentation/userguide/configmodel.rst +++ b/documentation/userguide/configmodel.rst @@ -154,6 +154,7 @@ not documented. key type description =========== ========== =========== description string A human-readable description of what the config contains and does. Displayed by the ``inspect`` command. +image string An optional field to require a specific runtime image to build / run this config. If not defined, the default image used by ``shrinkwrap`` will be used. This can be overriden with the ``--image`` command line option to ``shrinkwrap``. concrete boolean true if the config is intended to be directly built and run, or false if it is intended as a fragment to be included in other configs. build dictionary Contains all the components to be built. The key is the component name and the value is a dictionary. run dictionary Contains all the information about how to run the built artifacts on the FVP. diff --git a/documentation/userguide/configstore/cca-4world.rst b/documentation/userguide/configstore/cca-4world.rst index c28e06a..f290cc8 100644 --- a/documentation/userguide/configstore/cca-4world.rst +++ b/documentation/userguide/configstore/cca-4world.rst @@ -14,7 +14,7 @@ Builds on cca-3world.yaml, and adds support for running Hafnium along with some .. code-block:: shell - $ shrinkwrap --image shrinkwraptool/base-full build cca-4world.yaml --overlay buildroot.yaml --btvar GUEST_ROOTFS='${artifact:BUILDROOT}' + $ shrinkwrap build cca-4world.yaml --overlay buildroot.yaml --btvar GUEST_ROOTFS='${artifact:BUILDROOT}' Then run the model with: diff --git a/shrinkwrap/commands/buildall.py b/shrinkwrap/commands/buildall.py index 012cf69..39104ab 100644 --- a/shrinkwrap/commands/buildall.py +++ b/shrinkwrap/commands/buildall.py @@ -130,7 +130,7 @@ def build(configs, btvarss, nosync, args): # Run under a runtime environment, which may just run commands # natively on the host or may execute commands in a container, # depending on what the user specified. - with runtime.Runtime(name=args.runtime, image=args.image, + with runtime.Runtime(name=args.runtime, image=config.get_image(configs, args), ssh_agent_keys=args.ssh_agent_keys) as rt: def add_volume(path, levels_up=0): while levels_up: diff --git a/shrinkwrap/commands/clean.py b/shrinkwrap/commands/clean.py index cc50b68..75d8af1 100644 --- a/shrinkwrap/commands/clean.py +++ b/shrinkwrap/commands/clean.py @@ -112,7 +112,7 @@ def dispatch(args): # Run under a runtime environment, which may just run commands # natively on the host or may execute commands in a container, # depending on what the user specified. - with runtime.Runtime(name=args.runtime, image=args.image, + with runtime.Runtime(name=args.runtime, image=config.get_image(configs, args), ssh_agent_keys=args.ssh_agent_keys) as rt: def add_volume(path, levels_up=0): while levels_up: diff --git a/shrinkwrap/commands/inspect.py b/shrinkwrap/commands/inspect.py index b6bae57..ec9e69b 100644 --- a/shrinkwrap/commands/inspect.py +++ b/shrinkwrap/commands/inspect.py @@ -66,6 +66,7 @@ def dispatch(args): cfgs.append({ 'name': c['fullname'], 'description': c['description'], + 'image': c['image'] if c['image'] is not None else '', 'concrete': c['concrete'], 'btvars': { k: _var_value(v['value']) @@ -101,6 +102,12 @@ def dispatch(args): indent=indent, paraspace=1)) buf.write('\n') + buf.write(_text_wrap('image', + c['image'], + width=width, + indent=indent, + paraspace=1)) + buf.write('\n') buf.write(_text_wrap('concrete', c['concrete'], width=width, diff --git a/shrinkwrap/commands/run.py b/shrinkwrap/commands/run.py index 9c566ec..3fa9d40 100644 --- a/shrinkwrap/commands/run.py +++ b/shrinkwrap/commands/run.py @@ -228,7 +228,7 @@ def dispatch(args): # Run under a runtime environment, which may just run commands natively # on the host or may execute commands in a container, depending on what # the user specified. - with runtime.Runtime(name=args.runtime, image=args.image, + with runtime.Runtime(name=args.runtime, image=config.get_image([resolveb], args), ssh_agent_keys=args.ssh_agent_keys) as rt: for rtvar in resolver['run']['rtvars'].values(): if rtvar['type'] == 'path': diff --git a/shrinkwrap/shrinkwrap.py b/shrinkwrap/shrinkwrap.py index dbfcf74..b532c8c 100755 --- a/shrinkwrap/shrinkwrap.py +++ b/shrinkwrap/shrinkwrap.py @@ -79,9 +79,11 @@ def main(): parser.add_argument('-I', '--image', metavar='name', required=False, - default='docker.io/shrinkwraptool/base-slim:latest', + type=str, + default=None, help="""If using a container runtime, specifies the name of the - image to use. Defaults to the official shrinkwrap image.""") + image to use. Defaults to the official shrinkwrap image, + unless a specific image is required in the config file.""") parser.add_argument('--ssh-agent', default=False, diff --git a/shrinkwrap/utils/config.py b/shrinkwrap/utils/config.py index a0ea126..239cf8e 100644 --- a/shrinkwrap/utils/config.py +++ b/shrinkwrap/utils/config.py @@ -10,6 +10,29 @@ import yaml import shrinkwrap.utils.clivars as uclivars import shrinkwrap.utils.workspace as workspace +default_image = 'docker.io/shrinkwraptool/base-slim:latest' + +def get_image(configs, args): + """ + Determine the image to use + """ + if args.image is not None: + # An image was specified on the command line, just use it ! + return args.image + else: + # No image forced on the command line, use one required by one of the configs, + # but make sure that if multiple config require an image, this is the same. + image = None + for c in configs: + if c['image'] is not None: + if image is None: + image = c['image'] + elif c['image'] == image: + pass + else: + raise Exception('Unsupported case of different images requested.') + # No image required in the configs or from the command line, use the default one + return image if image else default_image def _component_normalize(component, name): """ @@ -114,6 +137,9 @@ def _config_normalize(config): if 'description' not in config: config['description'] = None + if 'image' not in config: + config['image'] = None + if 'concrete' not in config: config['concrete'] = False @@ -190,7 +216,7 @@ def _config_sort(config): config['build'] = _build_sort(config['build']) config['run'] = _run_sort(config['run']) - lut = ['name', 'fullname', 'description', 'concrete', 'layers', + lut = ['name', 'fullname', 'description', 'image', 'concrete', 'layers', 'graph', 'build', 'buildex', 'artifacts', 'run'] lut = {k: i for i, k in enumerate(lut)} return dict(sorted(config.items(), key=lambda x: lut[x[0]])) -- GitLab