#4 PLEASE REVIEW - add docker and rhts roles
Merged 6 years ago by merlinm. Opened 6 years ago by merlinm.
Unknown source docker+rhts_roles  into  master

@@ -0,0 +1,11 @@

+ # Ansible role for docker image subjects

+ 

+ Put this role in your test_docker.yml playbook. You'll need

+ to have the following variables defined:

+ 

+  * subjects: A docker image name

+  * artifacts: An artifacts directory

+  * playbooks: A playbook to run inside of the container

+ 

+ Set the FEDORA_TEST_DIAGNOSE=1 environment variable to diagnose

+ any issues in the docker container.

@@ -0,0 +1,2 @@

+ ---

+ docker_python_version: 2

@@ -0,0 +1,1 @@

+ localhost ansible_ssh_port=2222 ansible_ssh_host=127.0.0.3 ansible_ssh_user=root ansible_ssh_pass=foobar ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'

@@ -0,0 +1,16 @@

+ #!/bin/sh

+ python_version=2

+ while [ $# -gt 0 ]; do

+     case "$1" in

+     --py3) python_version=3 ;;

+     esac

+     shift

+ done

+ dnf install -y openssh-server python${python_version}-dnf

+ ssh-keygen -q -t rsa -N '' -f /etc/ssh/ssh_host_rsa_key

+ echo 'root:foobar' | chpasswd

+ # start sshd in background

+ /usr/sbin/sshd

+ echo SSHD READY

+ # wait forever

+ tail -f /dev/null

@@ -0,0 +1,81 @@

+ ---

+ - name: Install the docker requirements

+   package: name="{{item}}" state=latest

+   with_items:

+     - docker

+ 

+ - name: Make artifacts directory

+   file: path={{ artifacts }} state=directory owner=root mode=755 recurse=yes

+ 

+ - name: Start the docker service

+   service:

+     name: docker

+     state: running

+ 

+ - name: Initialize docker and ansible playbook extra args variables

+   set_fact:

+     my_docker_extra_args: null

+     my_playbook_extra_args: null

+ 

+ - name: Configure python version-dependent argument settings

+   set_fact:

+     my_playbook_extra_args: "{{my_playbook_extra_args}} -e ansible_python_interpreter='/usr/bin/python{{docker_python_version}}'"

+     my_docker_extra_args: "{{my_docker_extra_args}} --py{{docker_python_version}}"

+   when: docker_python_version is defined and docker_python_version != 2

+ 

+ - name: Special case - pass along 'rpms' variable to playbook

+   set_fact:

+     # note the quoting here: ansible needs to see the single quotes

+     my_playbook_extra_args: "{{my_playbook_extra_args}} -e \"rpms='{{rpms}}'\""

+   when: rpms|default("") != ""

+ 

+ - name: Start ansible-ready docker container running SSHD

+   shell: >

+     docker run -d

+     -p '{{docker_ssh_port}}:22'

+     -v '{{artifacts}}:/artifacts:z,rw'

+     -v '{{role_path}}/files/docker-run-ssh:/run.sh:z'

+     '{{subjects}}'

+     /bin/sh -ex /run.sh {{my_docker_extra_args}}

+   register: docker_run_output

+ 

+ - name: Capture docker container ID

+   set_fact: container_id="{{docker_run_output.stdout}}"

+ 

+ - block:

+   - name: Wait for container to initialize

+     wait_for: port="{{docker_ssh_port}}" search_regex=OpenSSH

+ 

+ # ********************************

+ # HELP!

+ # There must be a better way to run the actual test playbook without running
ralph commented 6 years ago

Yes. Try "including" the playbook?

http://docs.ansible.com/ansible/playbooks_roles.html#task-versus-play-includes

It may be that you cannot include a playbook from a role -- and that you can only include a playbook from another playbook.

If this is the case, this may need to be refactored to bring this "outside" of the current role.

+ # another instance of ansible-playbook in an external shell command.

Option 1: You can have one call to run the first role then another
Option 2: Use meta to have dependencies of other roles
http://docs.ansible.com/ansible/playbooks_roles.html#role-dependencies

+ # The external run means we can't track the return status of the invidual

+ # tasks in the test playbook--which is the whole point.

+ # ********************************

Thanks for the suggestions, @ralph and @alivigni. Perhaps I'm still misunderstanding something, but they don't seem to apply. This is the point where the designated playbook needs to be run inside the docker container that was just spun up by the previous tasks in this role.

+   - name: Run the playbook in the container

+     shell: >

+       ANSIBLE_LOG_PATH='{{artifacts}}/container-playbook.log'

+       ansible-playbook '{{playbooks}}'

+       -i '{{role_path}}/files/docker-inventory'

+       -e artifacts='/artifacts'

+       {{my_playbook_extra_args}}

+     register: playbook_output

+ 

+   - debug: var=playbook_output

+ 

+   always:

+   - name: Pause if diagnosing container

+     pause:

+       prompt: |-

+        To diagnose the container, you can use one of the following commands:

+            ssh -p {{docker_ssh_port}} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@127.0.0.3 # password: foobar

+            docker exec -it {{ container_id }} /bin/bash

+        Continue when ready

+     when: lookup('env','FEDORA_TEST_DIAGNOSE')|bool

+ 

+   - name: Save the container log as an artifact

+     shell: docker logs "{{ container_id }}" >"{{artifacts}}/docker.log" 2>&1

+ 

+   - name: Clean up the docker container

+     shell: docker rm -f "{{ container_id }}"

@@ -0,0 +1,5 @@

+ ---

+ artifacts: ./artifacts

+ log: "{{artifacts}}/docker.log"

+ subjects: []

+ docker_ssh_port: 2222

@@ -0,0 +1,9 @@

+ # Ansible role for RHTS (Red Hat Test Suite) tests

+ 

+ Put this role in your test_local.yml playbook. You'll need

+ to have the following variables defined:

+ 

+  * tests: A list of RHTS test directories

+  * artifacts: An artifacts directory

+  * required_packages: A list of prerequisite packages required by RHTS tests

+  * rpms: Space separated list of RPMs to install (optional, may include SRPMs)

@@ -0,0 +1,2 @@

+ ---

+ rpms: []

@@ -0,0 +1,14 @@

+ <?xml version="1.0" encoding="ISO-8859-1"?>

+ 

+ <xsl:stylesheet version="1.0"

+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

+   xmlns="http://www.w3.org/TR/REC-html40">

+ 

+     <xsl:output method="text" omit-xml-declaration="yes" indent="no"/>

+     <xsl:strip-space elements="*"/>

+ 

+     <xsl:template match="job/recipeSet/recipe/task">

+         <xsl:value-of select="concat(@result, ' ', @name, '&#xA;')"/>

+     </xsl:template>

+ 

+ </xsl:stylesheet>

@@ -0,0 +1,182 @@

+ ---

+ - name: Install the RHTS pre-requirements

+   package: name={{item}} state=latest

+   with_items:

+   - dnf-plugins-core            # COPR plugin needed

+   - beakerlib

+   - make

+   - createrepo

+ 

+ - name: Enable COPR repo for restraint

+   shell: dnf copr -y enable bpeck/restraint

+ 

+ - name: Install restraint from COPR repo

+   package: name={{item}} state=latest

+   with_items:

+   - restraint

+   - restraint-rhts

+   - restraint-client

+ 

+ - name: Install any test-specific package requirements

+   package: name={{item}} state=latest

+   with_items:

+     - "{{ required_packages }}"

+ 

+ - name: Create legacy beakerlib directories

+   file:

+     dest: "{{ item }}"

+     state: directory

+   with_items:

+     - /usr/lib/beakerlib

+     - /usr/share/rhts-library

+ 

+ - name: Create legacy beakerlib links

+   file:

+     src: /usr/share/beakerlib/beakerlib.sh

+     dest: "{{ item }}"

+     state: link

+   with_items:

+     - /usr/lib/beakerlib/beakerlib.sh

+     - /usr/share/rhts-library/rhtslib.sh

+ 

+ - name: Create RPM repo directory

+   file:

+     dest: "{{ repo_dir }}"

+     state: directory

+ 

+ - name: Initialize RPM list

+   set_fact:

+     my_rpm_list: []

+ 

+ - name: Split 'rpms' argument into a list

+   set_fact:

+     my_rpm_list: "{{ rpms.split(' ') | unique }}"

+   when: rpms is defined and rpms|length > 0

+ 

+ - name: Copy RPMs to target

+   copy:

+     src: "{{ item }}"

+     dest: "{{ repo_dir }}/"

+   with_items:

+     - "{{ my_rpm_list }}"

+ 

+ - name: Generate RPM repo metadata

+   shell: createrepo "{{ repo_dir }}"

+ 

+ - name: Add RPM repo

+   yum_repository:

+     name: local

+     description: local repo

+     baseurl: "file://{{ repo_dir }}"

+     gpgcheck: no

+ 

+ - name: Set list of SRPMs

+   set_fact:

+     my_srpm_list: "{{ my_rpm_list

+                     | map('regex_search', '^.*\\.src\\.rpm$')

+                     | select('string') | list }}"

+ 

+ - name: Set list of non-SRPMs

+   set_fact:

+     my_nonsrpm_list: "{{ my_rpm_list | difference(my_srpm_list) }}"

+ 

+ - name: Map non-SRPMs to target paths to be installed

+   set_fact:

+     my_install_rpm_list: "{{ my_nonsrpm_list | map('basename')

+                            | map('regex_replace', '^', repo_dir ~ '/')

+                            | list }}"

+ 

+ - name: Install RPMs

+   shell: dnf install -y {{ my_install_rpm_list | join(' ') }}

+   when: my_install_rpm_list|length > 0

+ 

+ - name: Create tests directory

+   file:

+     dest: "{{ test_dir }}"

+     state: directory

+ 

+ - name: Copy tests to target

+   copy:

+     src: "{{ playbook_dir }}/{{ item }}"

+     dest: "{{ test_dir }}/"

+   with_items:

+     - "{{ tests }}"

+ 

+ - name: Generate test archives for restraint

+   shell: tar czf "{{ test_dir }}/{{ item | basename }}.tgz" -C "{{ test_dir }}" "{{ item | basename }}"

+   with_items:

+     - "{{ tests }}"

+ 

+ - name: Generate restraint job XML file

+   template:

+     # template references variables 'tests' and 'local_www_port'

+     src: job.xml.j2

+     dest: "{{ job_xml_file }}"

+ 

+ - name: Copy restraint results conversion stylesheet to target

+   copy:

+     src: job2text.xsl

+     dest: /usr/local/share/

+ 

+ - name: Create root SSH directory

+   file:

+     dest: /root/.ssh

+     state: directory

+ 

+ - name: Create root SSH key pair

+   shell: ssh-keygen -q -t rsa -N '' -f /root/.ssh/id_rsa

+   args:

+     creates: /root/.ssh/id_rsa

+ 

+ - name: Configure password-less local SSH for restraint

+   shell: |

+     cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys

+     chmod 644 /root/.ssh/authorized_keys

+ 

+ - name: Make artifacts directory

+   file: path={{ artifacts }} state=directory owner=root mode=755 recurse=yes

+ 

+ - name: Start restraintd

+   # instead of using service module, daemon is started directly since we want the

+   # output as an artifact (and this could be running in a container)

+   shell: nohup /usr/bin/restraintd >"{{ artifacts }}/restraintd.log" 2>&1 &

+ 

+ - name: Start local web server for restraint

+   shell: nohup /usr/bin/python -m SimpleHTTPServer "{{ local_www_port }}" >"{{ artifacts }}/httpd.log" 2>&1 &

+   args:

+     chdir: "{{ local_www_dir }}/"

+ 

+ - block:

+   - name: Execute RHTS tests using restraint

+     shell: /usr/bin/restraint --host localhost --job "{{ job_xml_file }}" >"{{ artifacts }}/restraint.log" 2>&1

+     args:

+       chdir: "{{ artifacts }}/"

+     ignore_errors: True

+ 

+   - name: Extract job output directory from restraint logfile

+     shell: sed -n 's/^Using \([^ ]*\).*$/\1/p' "{{ artifacts }}/restraint.log"

+     register: restraint_job_dir

+ 

+   - name: Make job ouput directory tree readable by all

+     file:

+       path: "{{ artifacts }}/{{restraint_job_dir.stdout}}"

+       mode: u=rwX,g=rX,o=rX

+       recurse: yes

+ 

+   - name: Set name of restraint XML job results file

+     set_fact: results_xml="{{ artifacts }}/{{restraint_job_dir.stdout}}/job.xml"

+ 

+   - name: Convert restraint XML job results to text as main output artifact

+     shell: xsltproc /usr/local/share/job2text.xsl "{{ results_xml }}" >"{{ artifacts }}/test.log"

+ 

+   - name: Check the results for failures

+     shell: grep '^FAIL ' "{{ artifacts }}/test.log"

+     register: test_fails

+     failed_when: test_fails.stdout or test_fails.stderr

+ 

+ - always:

+   - name: Pull out the logs

+     fetch:

+       dest: "{{artifacts}}/"

+       src: "{{artifacts}}/"

+       flat: yes

@@ -0,0 +1,11 @@

+ <job>

+  <recipeSet>

+   <recipe>

+ {% for item in tests %}

+     <task name='{{ item | basename }}'>

+      <fetch url='http://localhost:{{ local_www_port }}/{{ item | basename }}.tgz' />

+     </task>

+ {% endfor %}

+   </recipe>

+  </recipeSet>

+ </job>

@@ -0,0 +1,9 @@

+ ---

+ artifacts: ./artifacts

+ tests: []

+ required_packages: []

+ local_www_dir: "/var/www-test"

+ local_www_port: 8888

+ test_dir: "{{local_www_dir}}"

+ repo_dir: "/var/tmp/local_repo"

+ job_xml_file: "{{local_www_dir}}/job.xml"

This PR is for work I've done to implement two new Standard Test Interface ansible roles for docker and RHTS.

Since this is my first foray into using ansible and my docker experience is still limited, I would appreciate a thorough review and constructive feedback from some ansible experts before this PR gets merged!

The standard-test-docker role is for invoking tests against a docker image test subject in a similar manner to that described for other test subjects as described at https://fedoraproject.org/wiki/Changes/InvokingTestsAnsible#Invocation.

The standard-test-rhts role is for running RHTS (Red Hat Test System) style tests. (For those unfamiliar with RHTS, it is basically an enhanced version of beakerlib. Many of the tests being open sourced as part of the "upstream first" effort are based on RHTS.) The role uses restraint to run the tests as tasks, since the restraint-rhts package specifically includes support for running RHTS tests.

You can do a trial run that uses both of these new roles by running the following commands on a Fedora development machine:

Note: the standard-test-roles package in merlinm's COPR repo contains the latest copy of the roles referenced by this PR.

sudo dnf install ansible python2-dnf libselinux-python
sudo dnf copr enable merlinm/standard-test-roles
sudo dnf install standard-test-roles
mkdir scratch
cd scratch
git clone https://upstreamfirst.fedorainfracloud.org/diffutils.git
cd diffutils
sudo ansible-playbook test_docker.yml -e artifacts=$PWD/artifacts -e subjects=docker.io/fedora:latest
ls -l $PWD/artifacts
cat $PWD/artifacts/test.log

The playbook will take a while to run--especially the first time, if the fedora image needs to be downloaded. If you wish to examine the docker container that is used to run the tests before it gets cleaned up, set the environment variable FEDORA_TEST_DIAGNOSE=1.

You can also choose to run the tests directly on a local system by running the test_local.yml playbook, but you would be wise to do so on a disposable VM:

sudo systemctl start sshd
sudo ansible-playbook test_local.yml -e artifacts=$PWD/artifacts

Numerous additional examples of RHTS tests with test_local.yml and test_docker.yml playbooks can be found in many of the repos available at https://upstreamfirst.fedorainfracloud.org/browse/projects/.

Additional background information regarding the above commands for the quick trail run can be found in the "How to manually run migrated tests" section of the following document: https://docs.google.com/document/d/1nP_p1MwzCdMkcfz7Esl6A7ADFHnaCNkWq9lHxWqd2ig/edit#heading=h.z8o7qmgjtqpt

Yes. Try "including" the playbook?

http://docs.ansible.com/ansible/playbooks_roles.html#task-versus-play-includes

It may be that you cannot include a playbook from a role -- and that you can only include a playbook from another playbook.

If this is the case, this may need to be refactored to bring this "outside" of the current role.

Instead of all this file redirection, it might be better/simpler to use ansible copy or template modules.

No ability to use the service ansible module here? May be worth an inline comment explaining why.

Option 1: You can have one call to run the first role then another
Option 2: Use meta to have dependencies of other roles
http://docs.ansible.com/ansible/playbooks_roles.html#role-dependencies

Templates! Templates! Templates! are perfect for this in ansible

Thanks for the suggestions, @ralph and @alivigni. Perhaps I'm still misunderstanding something, but they don't seem to apply. This is the point where the designated playbook needs to be run inside the docker container that was just spun up by the previous tasks in this role.

This role could be running inside a docker container, so no service functionality AFAIK. I also want to capture the output from restraintd as an artifact. Thanks for the suggestion, @ralph.

1 new commit added

  • Use template module to generate restraint job.xml file.
6 years ago

Pull-Request has been merged by merlinm

6 years ago

I pushed ahead and merged this PR so I can get an updated package into the pipeline.

However, additional feedback and comments are still welcome!