-
Notifications
You must be signed in to change notification settings - Fork 150
[reproducer] Replace all reproducer vars with zuul vars #3794
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
Changes from all commits
8aadd02
7c6e830
53b0e26
4de75d4
6bb3778
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| #!/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): | ||
| """YAML SafeDumper subclass; | ||
| emits strings with double quotes when needed for readability.""" | ||
|
|
||
| pass | ||
|
|
||
|
|
||
| def _str_representer(dumper, data): | ||
| """Take a SafeDumper and a str; | ||
| return a YAML scalar node (literal | or double-quoted when needed).""" | ||
| 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: | ||
| """Take two parsed values; | ||
| return merge where both are dicts (recursive), else override wins.""" | ||
| 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: | ||
| """Read CLI paths (two or three args), | ||
| merge YAML mappings, write stdout or MERGED_OUT; exit on error.""" | ||
| 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] | ||
|
danpawlik marked this conversation as resolved.
|
||
| 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_options = dict( | ||
| default_flow_style=False, | ||
| sort_keys=False, | ||
| allow_unicode=True, | ||
| ) | ||
| if out_path is None: | ||
| yaml.dump(merged, sys.stdout, Dumper=DoubleQuoteDumper, **dump_options) | ||
| else: | ||
| with open(out_path, "w", encoding="utf-8") as out_f: | ||
| yaml.dump(merged, out_f, Dumper=DoubleQuoteDumper, **dump_options) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| 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 }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| --- | ||
| # 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 | ||
| delegate_to: controller-0 | ||
| no_log: true | ||
|
|
||
| - name: Merge and update cluster pull-secret | ||
|
evallesp marked this conversation as resolved.
|
||
| 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 }}" | ||
| delegate_to: controller-0 | ||
| no_log: true | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,48 +13,60 @@ | |
| path: "{{ ansible_user_dir }}/configs/zuul_vars.yaml" | ||
| register: _current_zuul_vars | ||
|
|
||
| - name: Overwrite reproducer-variables.yml when ZIronic used | ||
| - name: Overwrite reproducer-variables.yml when PreMetal 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (non-blocking) question: Could we merge this task with the following one? I'm unsure if in this one the backup as true is necessary, or if we should move in the following the backup to true.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. backup does not have extension yaml, might be helpful to compare vars during bootstrap vs before test execution. IMHO would be good to keep it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean that we have two tasks almost equal, just the dest path changes. One has backup and the other has no: to: |
||
| 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 | ||
| 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('') }} |
Uh oh!
There was an error while loading. Please reload this page.