.. _adding-and-modifying-container-images: Adding and Modifying Container Images ===================================== All container images are defined in the :py:mod:`bci_build.package` package as instances of the :py:class:`~bci_build.package.DevelopmentContainer`, :py:class:`~bci_build.package.ApplicationStackContainer` or :py:class:`~bci_build.package.OsContainer` classes. The different classes result in slightly different images, mostly with respect to the way versions, tags and labels are handled. For example instances of :py:class:`~bci_build.package.ApplicationStackContainer` will receive the label ``com.suse.image-type="application"``, whereas all others will have ``com.suse.image-type="sle-bci"``. To create a new container image, pick the most fitting class for it and create an instance of it supplying the following values: - :py:attr:`~bci_build.package.BaseContainerImage.name`: This is a short identifier of your image and is used to generate the name of this image on the registry as well as the prefixlabel. Pick something short and descriptive, e.g. ``node`` for a Node.js container image. - :py:attr:`~bci_build.package.BaseContainerImage.pretty_name` - :py:attr:`~bci_build.package.BaseContainerImage.package_name`: The name of the package on build.opensuse.org in ``devel:BCI:SLE-15-SP${os_version}`` that is used to build this container image. This value is not autogenerated, as we were inconsistent in the past, but this value should default to :py:attr:`~bci_build.package.BaseContainerImage.uid` ``-image``. - :py:attr:`~bci_build.package.BaseContainerImage.os_version` - :py:attr:`~bci_build.package.BaseContainerImage.release_stage` - :py:attr:`~bci_build.package.BaseContainerImage.package_list`: The list of packages to be installed into the container image. You can either provide a list of strings here or provide a list of :py:attr:`~bci_build.package.Package` class instances. The latter is only necessary if you want to build your container image with kiwi and wish to customize the package installation (or deletion) process. See `Installing Packages into the Container Image`_ for further details. Adding files to the Image ------------------------- The package on build.opensuse.org will by default only consist of the following files: - :file:`Dockerfile` or :file:`${pkg_name}.kiwi` and optionally a :file:`config.sh` - :file:`${pkg_name}.changes` - :file:`_service` It is sometimes necessary to include additional files in a Container Image, e.g. a longer script or configuration file. This can be achieved by adding the file to the :py:attr:`~bci_build.package.BaseContainerImage.extra_files` dictionary. The key should be the file name and the value are the file contents. Please only include very short files directly in :py:mod:`~bci_build.package`. Longer files should go into a subdirectory of :file:`src/bci_build/package/` and be read in on construction. In case you are taking the additional file from an upstream source, then consider adding it to the script :file:`update-files.sh` as well. A Github Action runs this script every day and ensures that your external file stays up to date. Automatic Package Version Substitution -------------------------------------- Some Container Images ship with environment variables that include the version of a component in the container image. For example the PostgreSQL Container Image sets the environment variable ``PG_VERSION`` to the ``major.minor`` version of PostgreSQL installed in the container image. Setting this environment variable manually is rather brittle and would require to constantly update the sources. Instead, we can leverage the service `obs-service-replace_using_package_version `_. The :py:class:`~bci_build.package.BaseContainerImage` has builtin support for this service via the attribute :py:attr:`~bci_build.package.BaseContainerImage.replacements_via_service`. To use it in your container image, pick a replacement string that is unique for your whole build description. For the PostgreSQL version we could for instance pick ``%%pg_version%%``. Then an instance of :py:class:`~bci_build.package.Replacement` needs to be added to the list :py:attr:`~bci_build.package.BaseContainerImage.replacements_via_service`, where the attribute :py:attr:`~bci_build.package.Replacement.regex_in_build_description` is set to the replacement string. Additionally the attribute :py:attr:`~bci_build.package.Replacement.package_name` has to be set to the **exact** name of the package which version we wish to extract. If only a part of the version is required, e.g. as with ``PG_VERSION`` where we only care about the major and minor version, but not the patch level, we can instruct the service to only extract the version up to a certain point via the attribute :py:attr:`~bci_build.package.Replacement.parse_version`. Our PostgreSQL example would result in the following code: .. code-block:: python ApplicationStackContainer( additional_versions=[f"%%pg_version%%"], env={ "PG_VERSION": f"%%pg_version%%", }, replacements_via_service=[ Replacement( regex_in_build_description="%%pg_version%%", package_name=f"postgresql14-server", parse_version="minor", ) ], # rest follows here ) Note that this process is **not** limited to environment variable, it can be used to replace **anything** inside the container build description. This can be seen in the above code block, where we also set the :py:attr:`~bci_build.package.DevelopmentContainer.additional_versions` attribute via this mechanism. .. Caution:: The current setup **cannot** replace versions in READMEs. If you rely on versions getting replaced in a README (usually via the :py:attr:`~bci_build.package.BaseContainerImage.pretty_reference` property), then you **must** hardcode the version using the mechanism outlined in the :py:mod:`~bci_build.package.versions` module. Installing Packages into the Container Image -------------------------------------------- In most cases it sufficient to just set the :py:attr:`~bci_build.package.BaseContainerImage.package_list` attribute to a list of package names as strings. This will yield a :command:`RUN zypper -n in --no-recommends $list_of_packages` line in the :file:`Dockerfile` or the following XML in the kiwi build description: .. code-block:: xml Kiwi supports additional package types to e.g. explicitly delete packages or add them to the bootstrap image. Please see ``_ for further details. To add packages of a different type than ``image`` requires to use instances of :py:class:`~bci_build.package.Package` with the :py:attr:`~bci_build.package.Package.pkg_type` set to the appropriate value. For example: .. code-block:: python package_list=[ Package(name, pkg_type=PackageType.BOOTSTRAP) for name in ( "bash", "ca-certificates-mozilla-prebuilt", "distribution-release", ) ] Applying additional changes to your Image ----------------------------------------- Container Images can be tweaked extensively via a plethora of different keywords in :file:`Dockerfile`. To stay compatible with kiwi build descriptions and to avoid some common pitfalls when creating a :file:`Dockerfile`. For the following :file:`Dockerfile` settings, use the respective properties of :py:class:`~bci_build.package.BaseContainerImage`: - ``ENTRYPOINT``: :py:attr:`~bci_build.package.BaseContainerImage.entrypoint` - ``CMD``: :py:attr:`~bci_build.package.BaseContainerImage.cmd` - ``VOLUME``: :py:attr:`~bci_build.package.BaseContainerImage.volumes` - ``EXPOSE``: :py:attr:`~bci_build.package.BaseContainerImage.exposes_ports` - ``ENV``: :py:attr:`~bci_build.package.BaseContainerImage.env` - ``LABEL``: :py:attr:`~bci_build.package.BaseContainerImage.extra_labels` - ``MAINTAINER``: :py:attr:`~bci_build.package.BaseContainerImage.maintainer` - ``USER``: :py:attr:`~bci_build.package.BaseContainerImage.entrypoint_user` For additional settings that do not fit the existing attributes, either create an abstraction (if feasible and meaningful) or use :py:attr:`~bci_build.package.BaseContainerImage.custom_end` to write a raw :file:`Dockerfile` yourself. New Package Checklist --------------------- - [ ] Merge the pull request creating the container into ``main`` - [ ] Review the automatically created pull request against the deployment branches and add a changelog entry - [ ] Create the package on ``devel:BCI:`` with a ```` entry so that it is pulled from git, this can be done automatically via the :command:`scratch_build_bot setup_obs_package` command. - [ ] Set up branch protection rules for the deployment branches including the newly added package for the OBS SCM builds