Add loop var policy to ansible-lint

This adds a custom ansible-lint rule at .rules/ZuulJobsNamespaceLoopVar.py
that enforces the loop var policy described at:
https://zuul-ci.org/docs/zuul-jobs/policy.html#ansible-loops-in-roles

It also updates existing roles to follow the policy.

Change-Id: I92b2ff56a1c2702542fc07b316f1809087a4c92f
This commit is contained in:
Albin Vass 2020-04-29 12:52:21 +02:00
parent d775467078
commit d0e2016592
22 changed files with 105 additions and 56 deletions

View File

@ -6,5 +6,7 @@ parseable: true
skip_list: skip_list:
- '204' - '204'
- '301' - '301'
rulesdir:
- ./.rules/
use_default_rules: true use_default_rules: true
verbosity: 1 verbosity: 1

View File

@ -0,0 +1,29 @@
from ansiblelint import AnsibleLintRule
class ZuulJobsNamespaceLoopVar(AnsibleLintRule):
id = 'ZUULJOBS0001'
shortdesc = 'Loop vars should have zj_ prefix'
description = """
Check for tasks that does not follow
the policy of namespacing loop variables with zj_ prefix.
See: \
https://zuul-ci.org/docs/zuul-jobs/policy.html\
#ansible-loops-in-roles
"""
tags = {'zuul-jobs-namespace-loop-var'}
def matchtask(self, file, task):
if file.get('type') != 'tasks':
return False
if 'loop' in set(task.keys()):
if 'loop_control' not in set(task.keys()):
return True
elif 'loop_var' not in task.get('loop_control'):
return True
elif not task.get('loop_control')\
.get('loop_var').startswith('zj_'):
return True
return False

View File

@ -3,6 +3,8 @@
- include_role: - include_role:
name: helm-template name: helm-template
vars: vars:
helm_release_name: "{{ item.key }}" helm_release_name: "{{ zj_item.key }}"
helm_chart: "{{ item.value }}" helm_chart: "{{ zj_item.value }}"
loop: "{{ helm_charts | dict2items }}" loop: "{{ helm_charts | dict2items }}"
loop_control:
loop_var: 'zj_item'

View File

@ -18,11 +18,11 @@
- name: Copy sibling source directories - name: Copy sibling source directories
command: command:
cmd: 'cp --parents -r {{ sibling }} {{ ansible_user_dir }}/{{ zuul_work_dir }}/{{ item.context }}/.zuul-siblings' cmd: 'cp --parents -r {{ zj_sibling }} {{ ansible_user_dir }}/{{ zuul_work_dir }}/{{ item.context }}/.zuul-siblings'
chdir: '~/src' chdir: '~/src'
loop: '{{ item.siblings }}' loop: '{{ item.siblings }}'
loop_control: loop_control:
loop_var: sibling loop_var: zj_sibling
when: item.siblings is defined when: item.siblings is defined
- name: Build a container image - name: Build a container image

View File

@ -1,12 +1,12 @@
- name: Tag image for buildset registry - name: Tag image for buildset registry
command: >- command: >-
{{ container_command }} tag {{ image.repository }}:{{ image_tag }} {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ image.repository }}:{{ image_tag }} {{ container_command }} tag {{ image.repository }}:{{ zj_image_tag }} {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ image.repository }}:{{ zj_image_tag }}
loop: "{{ image.tags | default(['latest']) }}" loop: "{{ image.tags | default(['latest']) }}"
loop_control: loop_control:
loop_var: image_tag loop_var: zj_image_tag
- name: Push tag to buildset registry - name: Push tag to buildset registry
command: >- command: >-
{{ container_command }} push {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ image.repository }}:{{ image_tag }} {{ container_command }} push {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ image.repository }}:{{ zj_image_tag }}
loop: "{{ image.tags | default(['latest']) }}" loop: "{{ image.tags | default(['latest']) }}"
loop_control: loop_control:
loop_var: image_tag loop_var: zj_image_tag

View File

@ -20,11 +20,11 @@
# Ansible 2.8 only. take the simple approach. # Ansible 2.8 only. take the simple approach.
- name: Copy sibling source directories - name: Copy sibling source directories
command: command:
cmd: 'cp --parents -r {{ sibling }} {{ ansible_user_dir }}/{{ zuul_work_dir }}/{{ item.context }}/.zuul-siblings' cmd: 'cp --parents -r {{ zj_sibling }} {{ ansible_user_dir }}/{{ zuul_work_dir }}/{{ item.context }}/.zuul-siblings'
chdir: '~/src' chdir: '~/src'
loop: '{{ item.siblings }}' loop: '{{ item.siblings }}'
loop_control: loop_control:
loop_var: sibling loop_var: zj_sibling
when: item.siblings is defined when: item.siblings is defined
- name: Build a docker image - name: Build a docker image

View File

@ -1,12 +1,12 @@
- name: Tag image for buildset registry - name: Tag image for buildset registry
command: >- command: >-
docker tag {{ image.repository }}:{{ image_tag }} {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ image.repository }}:{{ image_tag }} docker tag {{ image.repository }}:{{ zj_image_tag }} {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ image.repository }}:{{ zj_image_tag }}
loop: "{{ image.tags | default(['latest']) }}" loop: "{{ image.tags | default(['latest']) }}"
loop_control: loop_control:
loop_var: image_tag loop_var: zj_image_tag
- name: Push tag to buildset registry - name: Push tag to buildset registry
command: >- command: >-
docker push {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ image.repository }}:{{ image_tag }} docker push {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ image.repository }}:{{ zj_image_tag }}
loop: "{{ image.tags | default(['latest']) }}" loop: "{{ image.tags | default(['latest']) }}"
loop_control: loop_control:
loop_var: image_tag loop_var: zj_image_tag

View File

@ -11,8 +11,8 @@
- name: Save pod descriptions - name: Save pod descriptions
loop: "{{ podlist.stdout_lines | default([]) }}" loop: "{{ podlist.stdout_lines | default([]) }}"
loop_control: loop_control:
loop_var: pod_name loop_var: zj_pod_name
shell: "kubectl describe po {{ pod_name }} &> {{ ansible_user_dir }}/zuul-output/logs/pods/{{ pod_name }}.txt" shell: "kubectl describe po {{ zj_pod_name }} &> {{ ansible_user_dir }}/zuul-output/logs/pods/{{ zj_pod_name }}.txt"
args: args:
executable: /bin/bash executable: /bin/bash
failed_when: false failed_when: false

View File

@ -7,9 +7,9 @@
build: "{{ build.json[0] }}" build: "{{ build.json[0] }}"
- name: Download archive by type - name: Download archive by type
uri: uri:
url: "{{ artifact.url }}" url: "{{ zj_artifact.url }}"
dest: "{{ download_artifact_directory }}" dest: "{{ download_artifact_directory }}"
loop: "{{ build.artifacts }}" loop: "{{ build.artifacts }}"
loop_control: loop_control:
loop_var: artifact loop_var: zj_artifact
when: "'metadata' in artifact and 'type' in artifact.metadata and (artifact.metadata.type == download_artifact_type or ((download_artifact_type | type_debug) == 'list' and artifact.metadata.type in download_artifact_type))" when: "'metadata' in zj_artifact and 'type' in zj_artifact.metadata and (zj_artifact.metadata.type == download_artifact_type or ((download_artifact_type | type_debug) == 'list' and zj_artifact.metadata.type in download_artifact_type))"

View File

@ -27,12 +27,14 @@
debug: debug:
msg: | msg: |
# Node Information # Node Information
Hostname: {{ hostvars[item]['ansible_hostname']|default('unknown') }} Hostname: {{ hostvars[zj_item]['ansible_hostname']|default('unknown') }}
Distro: {{ hostvars[item]['ansible_distribution'] | default('unknown') }} {{ hostvars[item]['ansible_distribution_version'] | default('unknown') }} Distro: {{ hostvars[zj_item]['ansible_distribution'] | default('unknown') }} {{ hostvars[zj_item]['ansible_distribution_version'] | default('unknown') }}
Provider: {{ hostvars[item]['nodepool']['provider'] }} Provider: {{ hostvars[zj_item]['nodepool']['provider'] }}
Label: {{ hostvars[item]['nodepool']['label'] }} Label: {{ hostvars[zj_item]['nodepool']['label'] }}
{% if hostvars[item]['nodepool']['interface_ip'] is defined %} {% if hostvars[zj_item]['nodepool']['interface_ip'] is defined %}
Interface IP: {{ hostvars[item]['nodepool']['interface_ip'] }} Interface IP: {{ hostvars[zj_item]['nodepool']['interface_ip'] }}
{% endif %} {% endif %}
loop: "{{ query('inventory_hostnames', 'all,!localhost') }}" loop: "{{ query('inventory_hostnames', 'all,!localhost') }}"
loop_control:
loop_var: zj_item
ignore_errors: yes ignore_errors: yes

View File

@ -22,8 +22,10 @@
- name: Install configuration files - name: Install configuration files
become: true become: true
get_url: get_url:
url: "https://raw.githubusercontent.com/helm/chart-testing/v{{ chart_testing_version }}/etc/{{ item }}" url: "https://raw.githubusercontent.com/helm/chart-testing/v{{ chart_testing_version }}/etc/{{ zj_item }}"
dest: "/etc/ct/{{ item }}" dest: "/etc/ct/{{ zj_item }}"
loop: loop:
- chart_schema.yaml - chart_schema.yaml
- lintconf.yaml - lintconf.yaml
loop_control:
loop_var: zj_item

View File

@ -76,9 +76,9 @@
become: yes become: yes
loop: "{{ kube_config['users'] }}" loop: "{{ kube_config['users'] }}"
loop_control: loop_control:
loop_var: item loop_var: zj_item
file: file:
path: "{{ item['user']['client-key'] }}" path: "{{ zj_item['user']['client-key'] }}"
owner: "{{ ansible_user }}" owner: "{{ ansible_user }}"
- name: Get cluster info - name: Get cluster info

View File

@ -9,10 +9,12 @@
url: '{{ ensure_pip_from_upstream_url }}' url: '{{ ensure_pip_from_upstream_url }}'
dest: '{{ _install_dir.path }}/get-pip.py' dest: '{{ _install_dir.path }}/get-pip.py'
- name: 'Run get-pip.py for {{ item }}' - name: 'Run get-pip.py for {{ zj_item }}'
command: '{{ item }} {{ _install_dir.path }}/get-pip.py' command: '{{ zj_item }} {{ _install_dir.path }}/get-pip.py'
become: yes become: yes
loop: '{{ ensure_pip_from_upstream_interpreters }}' loop: '{{ ensure_pip_from_upstream_interpreters }}'
loop_control:
loop_var: zj_item
- name: Remove temporary install dir - name: Remove temporary install dir
file: file:

View File

@ -46,11 +46,13 @@
- name: Return artifacts to Zuul - name: Return artifacts to Zuul
loop: "{{ result.files }}" loop: "{{ result.files }}"
loop_control:
loop_var: zj_item
zuul_return: zuul_return:
data: data:
zuul: zuul:
artifacts: artifacts:
- name: Javascript content archive - name: Javascript content archive
url: "artifacts/{{ item.path | basename }}" url: "artifacts/{{ zj_item.path | basename }}"
metadata: metadata:
type: javascript_content type: javascript_content

View File

@ -4,8 +4,8 @@
oc --context "{{ item.1.context }}" oc --context "{{ item.1.context }}"
--namespace "{{ item.1.namespace }}" --namespace "{{ item.1.namespace }}"
rsync -q --progress=false rsync -q --progress=false
{{ item.1.pod }}:{{ output.src }}/ {{ item.1.pod }}:{{ zj_output.src }}/
{{ output.dst }}/ {{ zj_output.dst }}/
no_log: "{{ not zuul_log_verbose }}" no_log: "{{ not zuul_log_verbose }}"
delegate_to: localhost delegate_to: localhost
loop: loop:
@ -16,4 +16,4 @@
- src: "{{ zuul_output_dir }}/docs" - src: "{{ zuul_output_dir }}/docs"
dst: "{{ zuul.executor.work_root }}/docs" dst: "{{ zuul.executor.work_root }}/docs"
loop_control: loop_control:
loop_var: output loop_var: zj_output

View File

@ -26,8 +26,10 @@
- name: Generate subunit file - name: Generate subunit file
shell: shell:
cmd: "{{ testr_command.stdout_lines[0] }} last --subunit >>{{ temp_subunit_file.path }}" cmd: "{{ testr_command.stdout_lines[0] }} last --subunit >>{{ temp_subunit_file.path }}"
chdir: "{{ item }}" chdir: "{{ zj_item }}"
loop: "{{ all_subunit_dirs }}" loop: "{{ all_subunit_dirs }}"
loop_control:
loop_var: zj_item
- name: Copy the combined subunit file to the zuul work directory - name: Copy the combined subunit file to the zuul work directory
copy: copy:

View File

@ -9,7 +9,9 @@
- name: HTMLify text files - name: HTMLify text files
htmlify: htmlify:
input: "{{ item.path }}" input: "{{ zj_item.path }}"
output: "{{ item.path | regex_replace('\\.txt', '.html') }}" output: "{{ zj_item.path | regex_replace('\\.txt', '.html') }}"
loop: "{{ htmlify_files.files }}" loop: "{{ htmlify_files.files }}"
loop_control:
loop_var: zj_item
no_log: true no_log: true

View File

@ -2,15 +2,17 @@
when: zuul.change is defined when: zuul.change is defined
delegate_to: localhost delegate_to: localhost
shell: | shell: |
if [ -n "$(find {{ zuul.executor.work_root }}/{{ item }} -mindepth 1)" ] ; then if [ -n "$(find {{ zuul.executor.work_root }}/{{ zj_item }} -mindepth 1)" ] ; then
# Only create target directory if it is needed. # Only create target directory if it is needed.
# Do not fail if it is already there. # Do not fail if it is already there.
mkdir -p {{ zuul.executor.log_root }}/{{ item }} mkdir -p {{ zuul.executor.log_root }}/{{ zj_item }}
# Leave the original directory behind so that other roles # Leave the original directory behind so that other roles
# operating on the interface directories can simply no-op. # operating on the interface directories can simply no-op.
mv -f {{ zuul.executor.work_root }}/{{ item }}/* {{ zuul.executor.log_root }}/{{ item }} mv -f {{ zuul.executor.work_root }}/{{ zj_item }}/* {{ zuul.executor.log_root }}/{{ zj_item }}
fi fi
loop: loop:
- artifacts - artifacts
- docs - docs
loop_control:
loop_var: zj_item
run_once: true run_once: true

View File

@ -23,10 +23,10 @@
- name: Promote image - name: Promote image
loop: "{{ docker_images }}" loop: "{{ docker_images }}"
loop_control: loop_control:
loop_var: image loop_var: zj_image
include_tasks: promote-retag.yaml include_tasks: promote-retag.yaml
- name: Delete obsolete tags - name: Delete obsolete tags
loop: "{{ docker_images }}" loop: "{{ docker_images }}"
loop_control: loop_control:
loop_var: image loop_var: zj_image
include_tasks: promote-cleanup.yaml include_tasks: promote-cleanup.yaml

View File

@ -10,10 +10,10 @@
no_log: true no_log: true
loop: "{{ tags.json.results }}" loop: "{{ tags.json.results }}"
loop_control: loop_control:
loop_var: docker_tag loop_var: zj_docker_tag
when: docker_tag.last_updated < cutoff.stdout and docker_tag.name.startswith('change_') when: zj_docker_tag.last_updated < cutoff.stdout and zj_docker_tag.name.startswith('change_')
uri: uri:
url: "https://hub.docker.com/v2/repositories/{{ image.repository }}/tags/{{ docker_tag.name }}/" url: "https://hub.docker.com/v2/repositories/{{ image.repository }}/tags/{{ zj_docker_tag.name }}/"
method: DELETE method: DELETE
status_code: [200,204] status_code: [200,204]
headers: headers:

View File

@ -5,26 +5,26 @@
- name: Push tag to intermediate registry - name: Push tag to intermediate registry
command: >- command: >-
skopeo --insecure-policy copy skopeo --insecure-policy copy
docker://127.0.0.1:{{ socat_port }}/{{ image.repository | regex_replace('^docker\.io/(.*)', '\1') }}:{{ image_tag }} docker://127.0.0.1:{{ socat_port }}/{{ image.repository | regex_replace('^docker\.io/(.*)', '\1') }}:{{ zj_image_tag }}
docker://{{ intermediate_registry.host | ipwrap }}:{{ intermediate_registry.port }}/{{ image.repository }}:{{ zuul.build }}_{{ image_tag }} docker://{{ intermediate_registry.host | ipwrap }}:{{ intermediate_registry.port }}/{{ image.repository }}:{{ zuul.build }}_{{ zj_image_tag }}
retries: 3 retries: 3
register: result register: result
until: result is success until: result is success
loop: "{{ image.tags | default(['latest']) }}" loop: "{{ image.tags | default(['latest']) }}"
loop_control: loop_control:
loop_var: image_tag loop_var: zj_image_tag
- name: Return artifact to Zuul - name: Return artifact to Zuul
zuul_return: zuul_return:
data: data:
zuul: zuul:
artifacts: artifacts:
- name: "{{ image.repository }}:{{ image_tag }}" - name: "{{ image.repository }}:{{ zj_image_tag }}"
url: "docker://{{ intermediate_registry.host | ipwrap }}:{{ intermediate_registry.port }}/{{ image.repository }}:{{ zuul.build }}_{{ image_tag }}" url: "docker://{{ intermediate_registry.host | ipwrap }}:{{ intermediate_registry.port }}/{{ image.repository }}:{{ zuul.build }}_{{ zj_image_tag }}"
metadata: metadata:
type: container_image type: container_image
repository: "{{ image.repository }}" repository: "{{ image.repository }}"
tag: "{{ image_tag }}" tag: "{{ zj_image_tag }}"
loop: "{{ image.tags | default(['latest']) }}" loop: "{{ image.tags | default(['latest']) }}"
loop_control: loop_control:
loop_var: image_tag loop_var: zj_image_tag

View File

@ -10,12 +10,14 @@
register: _statefulsets register: _statefulsets
- name: Ensure the number of ready replicas matches the replicas - name: Ensure the number of ready replicas matches the replicas
shell: kubectl get {{ item }} -ogo-template='{{ '{{' }}eq .status.replicas .status.readyReplicas{{ '}}' }}' shell: kubectl get {{ zj_item }} -ogo-template='{{ '{{' }}eq .status.replicas .status.readyReplicas{{ '}}' }}'
register: _is_ready register: _is_ready
until: _is_ready.stdout == 'true' until: _is_ready.stdout == 'true'
retries: 60 retries: 60
delay: 5 delay: 5
loop: "{{ _statefulsets.stdout_lines }}" loop: "{{ _statefulsets.stdout_lines }}"
loop_control:
loop_var: zj_item
- name: Wait for all pods to become ready - name: Wait for all pods to become ready
command: kubectl wait --for=condition=Ready --timeout=120s pod --all command: kubectl wait --for=condition=Ready --timeout=120s pod --all