Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 15 additions & 20 deletions reproducer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,31 +108,23 @@
name: firewalld
state: restarted

# NOTE(dpawlik): Keep support for legacy workflow where
# controller host is created and actions are done not on
# hypervisor directly, but on controller.
# NOTE(dpawlik): Synchronize current Zuul repositories stored in $HOME/src
# to controller-0 to make sure all projects are in state as they should.
- name: Synchronize src dir with controller-0
when:
- not cifmw_deploy_reproducer_env | default(true) | bool
vars:
sync_dir: "{{ ansible_user_dir }}/src"
ansible.posix.synchronize:
src: "{{ sync_dir }}/"
dest: "zuul@controller-0:{{ sync_dir }}"
archive: true
recursive: true

# NOTE(dpawlik): After calling reproducer role using ZIronic tool,
# when the bootstrap phase has been completed, it generates a file
# "reproducer-variables.yml" that contains all variables done in the
# CI job. The problem here is, that "testing" phase might have other
# variables than what was in the bootstrap phase. It means, that
# we need to overwrite the variables with current CI job vars.
- name: Overwrite reproducer-variables.yml when ZIronic bootstrap
- name: Set hypervisor name if not defined
when: hypervisor is not defined
ansible.builtin.set_fact:
hypervisor: "{{ ansible_fqdn }}"

- name: Prepare job after earlier bootstrap by PreMetal
when:
- not cifmw_deploy_reproducer_env | default(true) | bool
ansible.builtin.include_role:
name: reproducer
tasks_from: overwrite_zuul_vars.yml
tasks_from: premetal.yml

- name: Run deployment if instructed to
when:
- cifmw_deploy_architecture | default(false) | bool
Expand All @@ -141,7 +133,10 @@
poll: 20
delegate_to: controller-0
ansible.builtin.command:
cmd: "$HOME/deploy-architecture.sh {{ cifmw_deploy_architecture_args | default('') }}"
cmd: >
$HOME/deploy-architecture.sh
-e hypervisor="{{ hypervisor }}"
{{ cifmw_deploy_architecture_args | default('') }}

- name: Run post deployment if instructed to
when:
Expand Down
118 changes: 118 additions & 0 deletions roles/reproducer/files/merge_yaml_override.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#!/usr/bin/env python3
#
# Copyright Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Merge two YAML files whose root is a mapping: keys from OVERRIDE replace BASE
# recursively for nested mappings; non-dict values (scalars, lists) are
# replaced entirely by OVERRIDE. Requires PyYAML (python3-pyyaml / python3-yaml
# on RPM systems).

from __future__ import annotations

import sys

try:
import yaml
except ImportError:
sys.stderr.write(
"merge_yaml_override.py: PyYAML is required "
"(e.g. dnf install python3-pyyaml or pip install pyyaml)\n"
)
sys.exit(1)


class DoubleQuoteDumper(yaml.SafeDumper):
"""SafeDumper that uses double quotes instead of single quotes."""

pass


def _str_representer(dumper, data):
if "\n" in data:
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
if any(c in data for c in "{}[]%*&!@#"):
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style='"')
return dumper.represent_scalar("tag:yaml.org,2002:str", data)


DoubleQuoteDumper.add_representer(str, _str_representer)


def deep_merge(base: object, override: object) -> object:
if base is None:
return override
if override is None:
return base
if isinstance(base, dict) and isinstance(override, dict):
out = dict(base)
for key, val in override.items():
if key in out:
out[key] = deep_merge(out[key], val)
else:
out[key] = val
return out
return override


def main() -> None:
argc = len(sys.argv)
if argc not in (3, 4):
sys.stderr.write(
f"usage: {sys.argv[0]} FILE1_BASE.yml FILE2_OVERRIDE.yml [MERGED_OUT.yml]\n"
" Deep-merge two YAML mappings: FILE2 wins on duplicate keys (nested dicts merged).\n"
" Scalars and lists from FILE2 replace FILE1. If MERGED_OUT is omitted, print to stdout.\n"
)
sys.exit(2)

base_path, override_path = sys.argv[1], sys.argv[2]
out_path = sys.argv[3] if argc == 4 else None

with open(base_path, encoding="utf-8") as f:
base = yaml.safe_load(f)
with open(override_path, encoding="utf-8") as f:
override = yaml.safe_load(f)

if base is None:
base = {}
if override is None:
override = {}

if not isinstance(base, dict):
sys.stderr.write(
f"{base_path}: root must be a mapping, got {type(base).__name__}\n"
)
sys.exit(3)
if not isinstance(override, dict):
sys.stderr.write(
f"{override_path}: root must be a mapping, got {type(override).__name__}\n"
)
sys.exit(3)

merged = deep_merge(base, override)
dump_kw = dict(
default_flow_style=False,
sort_keys=False,
allow_unicode=True,
)
if out_path is None:
yaml.dump(merged, sys.stdout, Dumper=DoubleQuoteDumper, **dump_kw)
else:
with open(out_path, "w", encoding="utf-8") as out_f:
yaml.dump(merged, out_f, Dumper=DoubleQuoteDumper, **dump_kw)


if __name__ == "__main__":
main()
12 changes: 12 additions & 0 deletions roles/reproducer/tasks/compute_additional_args.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
- name: "Checking if file exists {{ secret_file }}"
ansible.builtin.stat:
path: "{{ secret_file }}"
register: _current_file

- name: Add secret file into cifmw_deploy_architecture_args when exists
when: _current_file.stat.exists
ansible.builtin.set_fact:
cifmw_deploy_architecture_args: >
{{ cifmw_deploy_architecture_args | default('') }}
-e @{{ secret_file }}
36 changes: 36 additions & 0 deletions roles/reproducer/tasks/overwrite_pull_secret.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
# No log in that file are necessary and it should not use: cifmw_nolog var.
- name: Slurp full pull-secret from secrets dir
ansible.builtin.slurp:
src: "{{ ansible_user_dir }}/secrets/pull_secret.json"
register: _full_pull_secret
no_log: true

- name: Get current cluster pull-secret
kubernetes.core.k8s_info:
kubeconfig: "{{ cifmw_openshift_kubeconfig | default(ansible_user_dir ~ '/.kube/config') }}"
kind: Secret
name: pull-secret
namespace: openshift-config
register: _cluster_pull_secret
no_log: true

- name: Merge and update cluster pull-secret
vars:
_full_auths: "{{ (_full_pull_secret.content | b64decode | from_json).auths }}"
_cluster_auths: "{{ (_cluster_pull_secret.resources[0].data['.dockerconfigjson'] | b64decode | from_json).auths }}"
_merged:
auths: "{{ _cluster_auths | combine(_full_auths, recursive=true) }}"
kubernetes.core.k8s:
kubeconfig: "{{ cifmw_openshift_kubeconfig | default(ansible_user_dir ~ '/.kube/config') }}"
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: pull-secret
namespace: openshift-config
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: "{{ _merged | to_json | b64encode }}"
no_log: true
58 changes: 35 additions & 23 deletions roles/reproducer/tasks/overwrite_zuul_vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,58 @@

- name: Overwrite reproducer-variables.yml when ZIronic used
when: _current_zuul_vars.stat.exists
vars:
temp_reproducer_var_path: /tmp/reproducer-variables.yml
temp_merged_reproducer_var_path: /tmp/merged-reproducer-variables.yml
block:
- name: Dump reproducer-variables.yml to var
- name: Slurp reproducer-variables.yml to hypervisor
ansible.builtin.slurp:
src: "{{ cifmw_basedir }}/parameters/reproducer-variables.yml"
register: reproducer_original
delegate_to: controller-0
no_log: "{{ cifmw_nolog | default(true) | bool }}"

- name: Dump zuul_vars.yaml to var
ansible.builtin.slurp:
src: "{{ ansible_user_dir }}/configs/zuul_vars.yaml"
register: zuul_job_vars
- name: Create temp file reproducer-variables.yml on hypervisor
ansible.builtin.copy:
content: "{{ reproducer_original.content | b64decode }}"
dest: "{{ temp_reproducer_var_path }}"
mode: "0664"
no_log: "{{ cifmw_nolog | default(true) | bool }}"

- name: Load current job zuul_vars.yaml
ansible.builtin.include_vars:
file: "{{ ansible_user_dir }}/configs/zuul_vars.yaml"
name: _current_zuul_vars_include
- name: Copy merge yamls script
become: true
ansible.builtin.copy:
src: merge_yaml_override.py
dest: /usr/local/bin/merge_yaml_override
mode: "0755"

- name: Decode reproducer-variables.yml content
ansible.builtin.set_fact:
reproducer_vars_content: "{{ reproducer_original.content | b64decode | from_yaml }}"
- name: Execute merge script
ansible.builtin.shell: >
python3 /usr/local/bin/merge_yaml_override
{{ temp_reproducer_var_path }}
{{ ansible_user_dir }}/configs/zuul_vars.yaml >
{{ temp_merged_reproducer_var_path }}
no_log: "{{ cifmw_nolog | default(true) | bool }}"

- name: Filter zuul vars to only keys present in reproducer-variables
ansible.builtin.set_fact:
_zuul_vars_filtered: >-
{{
_current_zuul_vars_include | dict2items
| selectattr('key', 'in', reproducer_vars_content.keys())
| items2dict
}}
- name: Slurp merged reproducer-variables.yml from hypervisor
ansible.builtin.slurp:
src: "{{ temp_merged_reproducer_var_path }}"
register: merged_reproducer_slurp
no_log: "{{ cifmw_nolog | default(true) | bool }}"

- name: Write back merged reproducer-variables.yml
ansible.builtin.copy:
content: >-
{{ reproducer_vars_content | combine(_zuul_vars_filtered, recursive=true) | to_nice_yaml }}
content: "{{ merged_reproducer_slurp.content | b64decode }}"
dest: "{{ cifmw_basedir }}/parameters/reproducer-variables.yml"
mode: '0664'
mode: "0664"
backup: true
no_log: "{{ cifmw_nolog | default(true) | bool }}"
delegate_to: controller-0

- name: Overwrite custom-params.yml
ansible.builtin.copy:
content: "{{ merged_reproducer_slurp.content | b64decode }}"
dest: "{{ cifmw_basedir }}/artifacts/parameters/custom-params.yml"
mode: "0664"
no_log: "{{ cifmw_nolog | default(true) | bool }}"
delegate_to: controller-0
36 changes: 36 additions & 0 deletions roles/reproducer/tasks/premetal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
- name: Synchronize src dir
ansible.builtin.include_tasks:
file: sync_src_dir.yml

# NOTE(dpawlik): After calling reproducer role using PreMetal tool,
# when the bootstrap phase has been completed, it generates a file
# "reproducer-variables.yml" that contains all variables done in the
# CI job. The problem here is, that "testing" phase might have other
# variables than what was in the bootstrap phase. It means, that
# we need to overwrite the variables with current CI job vars.
- name: Overwrite reproducer-variables.yml when PreMetal bootstrap
ansible.builtin.include_tasks:
file: overwrite_zuul_vars.yml

- name: Overwrite pull-secret.json and add missing registries
ansible.builtin.include_tasks:
file: overwrite_pull_secret.yml

# NOTE(dpawlik): Since we use PreMetal, some variables are not
# redirected to nested ansible execution - they needs to be
# included on executing host - controller-0.
- name: Compute additional deploy_architecture_args with secrets
vars:
basic_secret_files:
- "{{ ansible_user_dir }}/secrets/registry_token_creds.yaml"
ansible.builtin.include_tasks:
file: compute_additional_args.yml
loop: "{{ cifmw_deploy_architecture_secret_files | default(basic_secret_files) }}"
loop_control:
loop_var: secret_file

- name: Print final cifmw_deploy_architecture_args
ansible.builtin.debug:
msg: >
Current cifmw_deploy_architecture_args {{ cifmw_deploy_architecture_args | default('') }}
Loading
Loading