diff --git a/doc/source/container-roles.rst b/doc/source/container-roles.rst index 442eee430..3a329a55f 100644 --- a/doc/source/container-roles.rst +++ b/doc/source/container-roles.rst @@ -1,6 +1,7 @@ Container Roles =============== +.. zuul:autorole:: build-container-image .. zuul:autorole:: build-docker-image .. zuul:autorole:: deploy-openshift .. zuul:autorole:: install-docker diff --git a/roles/build-container-image/README.rst b/roles/build-container-image/README.rst new file mode 100644 index 000000000..1cc166a8c --- /dev/null +++ b/roles/build-container-image/README.rst @@ -0,0 +1,3 @@ +Build one or more container images. + +.. include:: ../../roles/build-container-image/common.rst diff --git a/roles/build-container-image/common.rst b/roles/build-container-image/common.rst new file mode 100644 index 000000000..3bbfedaaa --- /dev/null +++ b/roles/build-container-image/common.rst @@ -0,0 +1,142 @@ +This is one of a collection of roles which are designed to work +together to build, upload, and promote container images in a gating +context: + +* :zuul:role:`build-container-image`: Build the images. + +.. note:: Build and upload roles are forthcoming. + +The :zuul:role:`build-container-image` role is designed to be used in +`check` and `gate` pipelines and simply builds the images. It can be +used to verify that the build functions, or it can be followed by the +use of subsequent roles to upload the images to a registry. + +They all accept the same input data, principally a list of +dictionaries representing the images to build. YAML anchors_ can be +used to supply the same data to all three jobs. + +Use the :zuul:role:`install-docker` or :zuul:role:`install-podman` +role to install Docker or Podman before using these roles. + +**Role Variables** + +.. zuul:rolevar:: zuul_work_dir + :default: {{ zuul.project.src_dir }} + + The project directory. Serves as the base for + :zuul:rolevar:`build-container-image.container_images.context`. + +.. zuul:rolevar:: container_filename + + The default container filename name to use. Serves as the base for + :zuul:rolevar:`build-container-image.container_images.container_filename`. + This allows a global overriding of the container filename name, for + example when building all images from different folders with + similarily named containerfiles. + + If omitted, the default depends on the container command used. + Typically, this is ``Dockerfile`` for ``docker`` and + ``Containerfile`` (with a fallback on ``Dockerfile``) for + ``podman``. + +.. zuul:rolevar:: container_command + :default: podman + + The command to use when building the image (E.g., ``docker``). + +.. zuul:rolevar:: container_registry_credentials + :type: dict + + This is only required for the upload and promote roles. This is + expected to be a Zuul Secret in dictionary form. Each key is the + name of a registry, and its value a dictionary with information + about that registry. + + Example: + + .. code-block:: yaml + + container_registry_credentials: + quay.io: + username: foo + password: bar + + .. zuul:rolevar:: [registry name] + :type: dict + + Information about a registry. The key is the registry name, and + its value a dict as follows: + + .. zuul:rolevar:: username + + The registry username. + + .. zuul:rolevar:: password + + The registry password. + + .. zuul:rolevar:: repository + + Optional; if supplied this is a regular expression which + restricts to what repositories the image may be uploaded. The + following example allows projects to upload images to + repositories within an organization based on their own names:: + + repository: "^myorgname/{{ zuul.project.short_name }}.*" + +.. zuul:rolevar:: container_images + :type: list + + A list of images to build. Each item in the list should have: + + .. zuul:rolevar:: context + + The build context; this should be a directory underneath + :zuul:rolevar:`build-container-image.zuul_work_dir`. + + .. zuul:rolevar:: container_filename + + The filename of the container file, present in the context + folder, used for building the image. Provide this if you are + using a non-standard filename for a specific image. + + .. zuul:rolevar:: registry + + The name of the target registry (E.g., ``quay.io``). Used by + the upload and promote roles. + + .. zuul:rolevar:: repository + + The name of the target repository in the registry for the image. + Supply this even if the image is not going to be uploaded (it + will be tagged with this in the local registry). + + .. zuul:rolevar:: path + + Optional: the directory that should be passed to the build + command. Useful for building images with a container file in + the context directory but a source repository elsewhere. + + .. zuul:rolevar:: build_args + :type: list + + Optional: a list of values to pass to the ``--build-arg`` + parameter. + + .. zuul:rolevar:: target + + Optional: the target for a multi-stage build. + + .. zuul:rolevar:: tags + :type: list + :default: ['latest'] + + A list of tags to be added to the image when promoted. + + .. zuul:rolevar:: tags + :type: list + :default: ['latest'] + + A list of tags to be added to the image when promoted. + +.. _anchors: https://yaml.org/spec/1.2/spec.html#&%20anchor// diff --git a/roles/build-container-image/defaults/main.yaml b/roles/build-container-image/defaults/main.yaml new file mode 100644 index 000000000..916550c08 --- /dev/null +++ b/roles/build-container-image/defaults/main.yaml @@ -0,0 +1,2 @@ +zuul_work_dir: "{{ zuul.project.src_dir }}" +container_command: podman diff --git a/roles/build-container-image/tasks/main.yaml b/roles/build-container-image/tasks/main.yaml new file mode 100644 index 000000000..c9296c447 --- /dev/null +++ b/roles/build-container-image/tasks/main.yaml @@ -0,0 +1,52 @@ +# This can be removed if we add this functionality to Zuul directly +- name: Load information from zuul_return + when: buildset_registry is not defined + set_fact: + buildset_registry: "{{ (lookup('file', zuul.executor.work_root + '/results.json') | from_json)['buildset_registry'] }}" + ignore_errors: true +- name: Set container filename arg + set_fact: + containerfile: "{{ item.container_filename|default(container_filename|default('')) }}" +- name: Build a container image + command: >- + {{ container_command }} build {{ item.path | default('.') }} {% if containerfile %}-f {{ containerfile }}{% endif %} + {% if item.target | default(false) -%} + --target {{ item.target }} + {% endif -%} + {% for build_arg in item.build_args | default([]) -%} + --build-arg {{ build_arg }} + {% endfor -%} + {% for tag in item.tags | default(['latest']) -%} + --tag {{ item.repository }}:change_{{ zuul.change }}_{{ tag }} + --tag {{ item.repository }}:{{ tag }} + {% endfor -%} + args: + chdir: "{{ zuul_work_dir }}/{{ item.context }}" + loop: "{{ container_images }}" +# Docker, and therefore skopeo and podman, don't understand docker +# push [1234:5678::]:5000/image/path:tag so we set up /etc/hosts with +# a registry alias name to support ipv6 and 4. +- name: Configure /etc/hosts for buildset_registry to workaround not understanding ipv6 addresses + become: yes + lineinfile: + path: /etc/hosts + state: present + regex: "^{{ buildset_registry.host }}\tzuul-jobs.buildset-registry$" + line: "{{ buildset_registry.host }}\tzuul-jobs.buildset-registry" + insertafter: EOF + when: buildset_registry is defined and buildset_registry.host | ipaddr +- name: Set buildset_registry alias variable when using ip + set_fact: + buildset_registry_alias: zuul-jobs.buildset-registry + when: buildset_registry is defined and buildset_registry.host | ipaddr +- name: Set buildset_registry alias variable when using name + set_fact: + buildset_registry_alias: "{{ buildset_registry.host }}" + when: buildset_registry is defined and not ( buildset_registry.host | ipaddr ) +# Push each image. +- name: Push image to buildset registry + when: buildset_registry is defined + include_tasks: push.yaml + loop: "{{ container_images }}" + loop_control: + loop_var: image diff --git a/roles/build-container-image/tasks/push.yaml b/roles/build-container-image/tasks/push.yaml new file mode 100644 index 000000000..226c43703 --- /dev/null +++ b/roles/build-container-image/tasks/push.yaml @@ -0,0 +1,12 @@ +- name: Tag image for buildset registry + command: >- + {{ container_command }} tag {{ image.repository }}:{{ image_tag }} {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ image.repository }}:{{ image_tag }} + loop: "{{ image.tags | default(['latest']) }}" + loop_control: + loop_var: image_tag +- name: Push tag to buildset registry + command: >- + {{ container_command }} push {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ image.repository }}:{{ image_tag }} + loop: "{{ image.tags | default(['latest']) }}" + loop_control: + loop_var: image_tag