Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move container build infrastructure to Ansible #1009

Merged
merged 11 commits into from
Jul 18, 2024
16 changes: 13 additions & 3 deletions .github/workflows/runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,25 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3

- name: Install Ansible
run: |
sudo apt-get update
sudo apt-get -y install ansible

- name: Build container
run: |
docker buildx build -f ./share/containers/${{ matrix.os }}.dockerfile . --output build-out
Copy link
Collaborator

@alejandro-colomar alejandro-colomar Jun 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, what if we would map some volume to write the log files to it with docker(1)?

RUN bash -c "trap 'cat <tests/unit/test-suite.log | tee /some/mapped/volume/test-suite.log >&2' ERR; make check;"

And use https://docs.docker.com/storage/volumes/.

Copy link
Collaborator

@alejandro-colomar alejandro-colomar Jun 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would allow storing the artifacts without needing Ansible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't mount volumes while building a container image, and that's exactly what we are doing with dockerfile. I have tried to solve this problem in various ways and with the technology we use it is not possible 😓

Copy link
Collaborator

@alejandro-colomar alejandro-colomar Jun 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm; understood.

How about something like this?

docker build ... | tee >(sed -n '/BEGIN MARKER/,/END MARKER/p' >/host/log/file)

(probably needs redirecting stderr too (or only))

We would only need to find some consistent markers in the log.

So, the full docker logs would go to the output, and the specific error logs that we want, which would be delimited by those delimiters, would go to a specific file in the host (so the runner).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same answer as in #1009 (comment)

Copy link
Collaborator

@alejandro-colomar alejandro-colomar Jun 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If each log output has a different marker, you can parse each one separately:

docker build ... \
| tee \
    >(sed -n '/BEGIN A/,/END A/p' >/host/A.log) \
    >(sed -n '/BEGIN B/,/END B/p' >/host/B.log) \
    >(sed -n '/BEGIN C/,/END C/p' >/host/C.log);

pushd share/ansible/
ansible-playbook playbook.yml -i inventory.ini -e 'distribution=${{ matrix.os }}'
popd
ikerexxe marked this conversation as resolved.
Show resolved Hide resolved

- name: Store artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.os }}-build
path: |
./build-out/config.log
./build-out/config.h
./share/ansible/build-out/config.log
./share/ansible/build-out/config.h
./share/ansible/build-out/build.log
./share/ansible/build-out/test-suite.log
if-no-files-found: ignore
4 changes: 3 additions & 1 deletion doc/contributions/build_install.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,11 @@ You can either generate a single image by running the following command from
the root folder of the project (i.e. Alpine):

```
docker build -f share/containers/alpine.dockerfile . --output build-out/alpine
ansible-playbook share/ansible/playbook.yml -i share/ansible/inventory.ini -e 'distribution=alpine'
```

**Note**: you'll need to install ansible to run this automation.

Or generate all of the images with the `container-build.sh` script, as if you
were running some of the CI checks locally:

Expand Down
1 change: 1 addition & 0 deletions share/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ansible/build-out/
1 change: 1 addition & 0 deletions share/ansible/inventory.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
builder ansible_connection=containers.podman.podman
17 changes: 17 additions & 0 deletions share/ansible/playbook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
- name: Start build container
hosts: localhost
vars:
image:
fedora: registry.fedoraproject.org/fedora:latest
alpine: docker.io/library/alpine:latest
debian: docker.io/library/debian:latest

roles:
- role: build_container

- name: CI run
hosts: builder
connection: podman
gather_facts: false
roles:
- role: ci_run
28 changes: 28 additions & 0 deletions share/ansible/roles/build_container/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Role Name
=========

Build container images.

Role Variables
--------------

* `image[distribution]` defines the container image URL.

Example Playbook
----------------

Usage example:

- hosts: localhost
roles:
- role: build_container

License
-------

BSD

Author Information
------------------

Iker Pedrosa <[email protected]>
20 changes: 20 additions & 0 deletions share/ansible/roles/build_container/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
# tasks file for build_container
- name: Pull container image
containers.podman.podman_image:
name: '{{ image[distribution] }}'

- name: Create and start container
containers.podman.podman_container:
name: builder
state: started
image: '{{ image[distribution] }}'
command: "sleep 1d"

- name: Create repo
ansible.builtin.shell:
podman exec builder mkdir -p /usr/local/src

- name: Copy repo
ansible.builtin.shell:
podman cp ../../ builder:/usr/local/src/shadow
25 changes: 25 additions & 0 deletions share/ansible/roles/ci_run/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Role Name
=========

Build, install and test package.

Example Playbook
----------------

Usage example:

- hosts: builder
connection: podman
gather_facts: false
roles:
- role: ci_run

License
-------

BSD

Author Information
------------------

Iker Pedrosa <[email protected]>
66 changes: 66 additions & 0 deletions share/ansible/roles/ci_run/tasks/alpine.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
# tasks file for ci_run
- name: Ensure python is installed
ansible.builtin.raw: apk add python3

- name: Ensure dependencies are installed
community.general.apk:
name:
- autoconf
- automake
- bash
- build-base
- byacc
- cmocka-dev
- expect
- gettext-dev
- git
- libbsd-dev
- libeconf-dev
- libtool
- libxslt
- pkgconf
state: present

- name: Build configuration
ansible.builtin.command: >
./autogen.sh
--disable-man
--disable-nls
--with-yescrypt
--without-selinux
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Build
ansible.builtin.shell:
make -Orecurse -j4 > build.log
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Run unit-tests
ansible.builtin.command:
make check
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Install
ansible.builtin.command:
make install
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Copy logs
ansible.builtin.fetch:
src: '{{ item }}'
dest: ./build-out/
flat: yes
with_items:
- "/usr/local/src/shadow/config.log"
- "/usr/local/src/shadow/config.h"
- "/usr/local/src/shadow/build.log"
- "/usr/local/src/shadow/tests/unit/test-suite.log"
73 changes: 73 additions & 0 deletions share/ansible/roles/ci_run/tasks/debian.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
# tasks file for ci_run
- name: Ensure repos are updated
ansible.builtin.raw: apt update

- name: Ensure python is installed
ansible.builtin.raw: apt install python3 -y

- name: Ensure dependencies are installed
ansible.builtin.apt:
name:
- libbsd-dev
- libcmocka-dev
- libltdl-dev
- pkgconf
state: present

- name: Gather selected facts
ansible.builtin.setup:
filter:
- 'ansible_distribution_release'

- name: Add specified repository into sources list
ansible.builtin.apt_repository:
repo: deb-src http://deb.debian.org/debian {{ ansible_distribution_release }} main
state: present

- name: Ensure build dependencies are installed
ansible.builtin.apt:
pkg: shadow
state: build-dep

- name: Build configuration
ansible.builtin.command: >
./autogen.sh
--enable-man
--with-yescrypt
--without-selinux
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Build
ansible.builtin.shell:
make -Orecurse -j4 > build.log
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Run unit-tests
ansible.builtin.command:
make check
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Install
ansible.builtin.command:
make install
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Copy logs
ansible.builtin.fetch:
src: '{{ item }}'
dest: ./build-out/
flat: yes
with_items:
- "/usr/local/src/shadow/config.log"
- "/usr/local/src/shadow/config.h"
- "/usr/local/src/shadow/build.log"
- "/usr/local/src/shadow/tests/unit/test-suite.log"
70 changes: 70 additions & 0 deletions share/ansible/roles/ci_run/tasks/fedora.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
# tasks file for ci_run
- name: Ensure dependencies are installed
ansible.builtin.dnf:
name:
- dnf-plugins-core
- libcmocka-devel
- systemd-devel
state: present

- name: Ensure build dependencies are installed
ansible.builtin.command:
dnf builddep -y shadow-utils
register: dnf_result
changed_when: '"Nothing to do" not in dnf_result.stdout'

- name: Build configuration
ansible.builtin.command: >
./autogen.sh
--disable-account-tools-setuid
--enable-lastlog
--enable-logind=no
--enable-man
--enable-shadowgrp
--enable-shared
--with-audit
--with-bcrypt
--with-group-name-max-length=32
--with-libpam
--with-selinux
--with-sha-crypt
--with-yescrypt
--without-libbsd
--without-libcrack
--without-sssd
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Build
ansible.builtin.shell:
make -Orecurse -j4 > build.log
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Run unit-tests
ansible.builtin.command:
make check
args:
chdir: /usr/local/src/shadow/
ignore_errors: true
Copy link
Collaborator

@alejandro-colomar alejandro-colomar May 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this exactly mean? Why do we want to ignore errors?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default if an ansible action fails, then the ansible execution is stopped. I want to ignore the errors and continue the execution to run the last action where the logs are copied from the container to the host system. This way we can gather them for inspection.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But does it report the error later? How will we know if ansible failed, if we ignore the errors? Sorry if this is obvious; I never used Ansible before.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error is reported, but the execution continues. At the end of the ansible execution there's the PLAY RECAP where we'll be able to check if anything failed. I created #1014 with an intentional failure to show how it works.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, I forgot to set if: always() in the Github Action. It's fine now.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! It seems to work now. BTW, dumb question: how do I find and read the logs (artifacts)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to open the action that failed, then click on summary, and finally scroll down to find all the artifacts

Copy link
Collaborator

@alejandro-colomar alejandro-colomar Jun 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ughhh, and then download a .zip and extract it to find the logs. Can we (also) have a copy on stderr? I very much prefer scrolling. :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will work until we start running the new tests and have several files to review


- name: Install
ansible.builtin.command:
make install
args:
chdir: /usr/local/src/shadow/
ignore_errors: true

- name: Copy logs
ansible.builtin.fetch:
src: '{{ item }}'
dest: ./build-out/
flat: yes
with_items:
- "/usr/local/src/shadow/config.log"
- "/usr/local/src/shadow/config.h"
- "/usr/local/src/shadow/build.log"
- "/usr/local/src/shadow/tests/unit/test-suite.log"
4 changes: 4 additions & 0 deletions share/ansible/roles/ci_run/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
# tasks file for ci_run
- name: 'Include distribution specific ci_run tasks fedora'
include_tasks: '{{ distribution }}.yml'
16 changes: 6 additions & 10 deletions share/container-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@

#
# SPDX-FileCopyrightText: 2023, Iker Pedrosa <[email protected]>
# SPDX-FileCopyrightText: 2024, Iker Pedrosa <[email protected]>
#
# SPDX-License-Identifier: BSD-3-Clause
#

for FILE in share/containers/*; do
IFS='/'
read -ra ADDR <<< "$FILE"
IFS='.'
read -ra ADDR <<< "${ADDR[2]}"
IFS=''
if ! docker build -f $FILE . --output build-out/${ADDR[0]}; then
exit
fi
done
set -eE
ikerexxe marked this conversation as resolved.
Show resolved Hide resolved
cd ansible/
ansible-playbook playbook.yml -i inventory.ini -e 'distribution=alpine'
ansible-playbook playbook.yml -i inventory.ini -e 'distribution=debian'
ansible-playbook playbook.yml -i inventory.ini -e 'distribution=fedora'
Loading
Loading