Skip to content

Commit 4e56ee5

Browse files
Use oc adm release extract --tools instead of unreliable file-cache for OCP binaries
1 parent 4caf935 commit 4e56ee5

3 files changed

Lines changed: 167 additions & 30 deletions

File tree

collection/tools/roles/tools_get_openshift_release/defaults/main.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
# defaults file for tools_get_openshift_release
33
openshift_releasestream_url: "https://openshift-release.apps.ci.l2s4.p1.openshiftapps.com/api/v1/releasestream"
44
release_name: "{{ openshift_release_build_name | default('') }}"
5-
openshift_download_url: "{{ 'https://openshift-release-artifacts.apps.ci.l2s4.p1.openshiftapps.com' + '/' + release_name }}"
65
openshift_mirror_url: "https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp"
76
ocp_build_info_file: "{{ controller_home_dir }}/latest_build.json"
Lines changed: 142 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,153 @@
11
---
2+
# Extract OCP installer and/or client binaries directly from the release image
3+
# using `oc adm release extract --tools` instead of the release-controller's
4+
# file-cache (openshift-release-artifacts), which has no SLA and can get stuck
5+
# indefinitely during tool extraction.
6+
#
7+
# The pull secret is extracted from the host cluster via the Kubernetes API
8+
# using the kubeconfig's client certificate. If `oc` is not already present
9+
# in the pod (cold-start), a stable client is bootstrapped from
10+
# mirror.openshift.com before running `oc adm release extract --tools`.
211
- name: Get the OCP installer and/or client binaries
312
vars:
4-
installer_url: "{{ openshift_download_url }}/openshift-install-linux-{{ release_name }}.tar.gz"
5-
client_url: "{{ openshift_download_url }}/openshift-client-linux-{{ release_name }}.tar.gz"
13+
installer_tarball: "openshift-install-linux-{{ release_name }}.tar.gz"
14+
client_tarball: "openshift-client-linux-{{ release_name }}.tar.gz"
15+
pull_secret_file: "{{ home_dir }}/pull-secret.json"
16+
bootstrap_oc_dir: "{{ home_dir }}/bootstrap-oc"
17+
bootstrap_oc_url: "{{ openshift_mirror_url }}/stable/openshift-client-linux.tar.gz"
618
block:
719
- name: Fail if release_name var is not defined
820
ansible.builtin.fail:
921
msg: "'release_name' variable must be defined and cannot be empty"
1022
when: release_name == ''
1123

12-
- name: Wait for content to come up on {{ openshift_download_url }}
13-
ansible.builtin.uri:
14-
url: "{{ openshift_download_url }}"
15-
method: GET
16-
return_content: yes
17-
status_code: 200
18-
body_format: json
19-
register: result
20-
until: result.content.find("openshift-install-linux") != -1
21-
retries: 20
22-
delay: 60
24+
- name: Fail if openshift_release_pull_spec is not defined
25+
ansible.builtin.fail:
26+
msg: "'openshift_release_pull_spec' must be set by get_openshift_release_build_name.yml"
27+
when: openshift_release_pull_spec is not defined or openshift_release_pull_spec == ''
28+
29+
- name: Extract pull secret from host cluster via Kubernetes API
30+
ansible.builtin.shell: |
31+
python3 << 'PYEOF'
32+
import yaml, json, base64, subprocess, os, sys, tempfile
33+
34+
kubeconfig_path = "{{ rhoso_kubeconfig }}"
35+
output_path = "{{ pull_secret_file }}"
36+
37+
with open(kubeconfig_path) as f:
38+
kc = yaml.safe_load(f)
39+
40+
server = kc['clusters'][0]['cluster']['server']
41+
user = kc['users'][0]['user']
42+
43+
try:
44+
cert_data = user['client-certificate-data']
45+
key_data = user['client-key-data']
46+
except KeyError:
47+
print(f"rhoso_kubeconfig must use client-certificate auth, "
48+
f"found auth keys: {list(user.keys())}", file=sys.stderr)
49+
sys.exit(1)
50+
51+
with tempfile.TemporaryDirectory() as tmpdir:
52+
ca_path = os.path.join(tmpdir, 'ca.crt')
53+
cert_path = os.path.join(tmpdir, 'client.crt')
54+
key_path = os.path.join(tmpdir, 'client.key')
55+
56+
with open(ca_path, 'wb') as f:
57+
f.write(base64.b64decode(kc['clusters'][0]['cluster']['certificate-authority-data']))
58+
with open(cert_path, 'wb') as f:
59+
f.write(base64.b64decode(cert_data))
60+
with open(key_path, 'wb') as f:
61+
f.write(base64.b64decode(key_data))
62+
63+
result = subprocess.run([
64+
'curl', '-s', '--fail',
65+
'--cacert', ca_path,
66+
'--cert', cert_path,
67+
'--key', key_path,
68+
f'{server}/api/v1/namespaces/openshift-config/secrets/pull-secret'
69+
], capture_output=True, text=True)
70+
71+
if result.returncode != 0:
72+
print(f"Failed to fetch pull secret from {server}: {result.stderr}", file=sys.stderr)
73+
sys.exit(1)
74+
75+
data = json.loads(result.stdout)
76+
decoded = base64.b64decode(data['data']['.dockerconfigjson']).decode()
77+
auths = json.loads(decoded)
78+
79+
with open(output_path, 'w') as f:
80+
f.write(decoded)
81+
82+
print(f"Pull secret extracted: {len(auths.get('auths', {}))} registries")
83+
PYEOF
84+
register: _pull_secret_result
85+
no_log: true
86+
87+
- name: Verify pull secret file is valid
88+
ansible.builtin.shell: >-
89+
python3 -c "import json; d=json.load(open('{{ pull_secret_file }}'));
90+
print(len(d.get('auths',{})), 'registries found')"
91+
register: _pull_secret_verify
92+
changed_when: false
93+
94+
- name: Check if oc is already available
95+
ansible.builtin.command: which oc
96+
register: _oc_available
97+
ignore_errors: true
98+
changed_when: false
99+
100+
- name: Bootstrap oc client from {{ bootstrap_oc_url }}
101+
when: _oc_available is failed
102+
block:
103+
- name: Create bootstrap directory
104+
ansible.builtin.file:
105+
path: "{{ bootstrap_oc_dir }}"
106+
state: directory
107+
mode: u=rwx,g=rw,o=r
108+
109+
- name: Download stable oc client from mirror
110+
ansible.builtin.unarchive:
111+
src: "{{ bootstrap_oc_url }}"
112+
dest: "{{ bootstrap_oc_dir }}"
113+
remote_src: yes
114+
register: _bootstrap_download
115+
until: _bootstrap_download is not failed
116+
retries: 3
117+
delay: 10
118+
119+
- name: Set oc binary path
120+
ansible.builtin.set_fact:
121+
_oc_bin: "{{ (bootstrap_oc_dir + '/oc') if _oc_available is failed else 'oc' }}"
23122

24123
- name: Create the installer directory
25124
ansible.builtin.file:
26125
path: "{{ home_dir }}/{{ release_name }}"
27126
state: directory
28127
mode: u=rwx,g=rw,o=r
29128

129+
- name: Extract OCP tools from release image {{ openshift_release_pull_spec }}
130+
ansible.builtin.command:
131+
cmd: >-
132+
timeout 900
133+
{{ _oc_bin }} adm release extract
134+
--tools
135+
--registry-config={{ pull_secret_file }}
136+
--to={{ home_dir }}/{{ release_name }}
137+
{{ openshift_release_pull_spec }}
138+
register: extract_result
139+
until: extract_result is not failed
140+
retries: 3
141+
delay: 30
142+
30143
- name: Get the installer binary and create a symlink
31144
when: "'installer' in binaries"
32145
block:
33-
- name: Download and unarchive the installer from {{ installer_url }}
146+
- name: Unarchive the installer from {{ installer_tarball }}
34147
ansible.builtin.unarchive:
35-
src: "{{ installer_url }}"
148+
src: "{{ home_dir }}/{{ release_name }}/{{ installer_tarball }}"
36149
dest: "{{ home_dir }}/{{ release_name }}"
37150
remote_src: yes
38-
register: result
39-
until: result is not failed
40-
retries: 3
41-
delay: 10
42151

43152
- name: Create a symlink to the openshift-install binary from /usr/local/bin
44153
ansible.builtin.file:
@@ -47,18 +156,14 @@
47156
state: link
48157
become: true
49158

50-
- name: Get the installer binary and create symlinks
159+
- name: Get the client binary and create symlinks
51160
when: "'client' in binaries"
52161
block:
53-
- name: Download and unarchive the client from {{ client_url }}
162+
- name: Unarchive the client from {{ client_tarball }}
54163
ansible.builtin.unarchive:
55-
src: "{{ client_url }}"
164+
src: "{{ home_dir }}/{{ release_name }}/{{ client_tarball }}"
56165
dest: "{{ home_dir }}/{{ release_name }}"
57166
remote_src: yes
58-
register: result
59-
until: result is not failed
60-
retries: 3
61-
delay: 10
62167

63168
- name: Create a symlink to the oc binary from /usr/local/bin
64169
ansible.builtin.file:
@@ -73,3 +178,14 @@
73178
dest: /usr/bin/kubectl
74179
state: link
75180
become: true
181+
182+
always:
183+
- name: Remove pull secret file
184+
ansible.builtin.file:
185+
path: "{{ pull_secret_file }}"
186+
state: absent
187+
188+
- name: Remove bootstrap oc directory
189+
ansible.builtin.file:
190+
path: "{{ bootstrap_oc_dir }}"
191+
state: absent

collection/tools/roles/tools_get_openshift_release/tasks/get_openshift_release_build_name.yml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,25 @@
3939
ansible.builtin.set_fact:
4040
openshift_release_build_name: "{{ latest_build_info.name }}"
4141

42-
- name: Set openshift_release_build_name when a specific build is given
43-
ansible.builtin.set_fact:
44-
openshift_release_build_name: "{{ build_name }}"
42+
- name: Set openshift_release_pull_spec from release stream API response
43+
ansible.builtin.set_fact:
44+
openshift_release_pull_spec: "{{ latest_build_info.pullSpec }}"
45+
46+
- name: Set build name and pull spec when a specific build is given
4547
when:
4648
- release is not match("4-stable")
4749
- build_name not in ['','candidate','fast','stable','eus']
50+
block:
51+
- name: Set openshift_release_build_name for specific build
52+
ansible.builtin.set_fact:
53+
openshift_release_build_name: "{{ build_name }}"
54+
55+
- name: Construct openshift_release_pull_spec for specific build
56+
ansible.builtin.set_fact:
57+
openshift_release_pull_spec: >-
58+
{{ 'registry.ci.openshift.org/ocp/release:' + build_name
59+
if build_name is search('nightly')
60+
else 'quay.io/openshift-release-dev/ocp-release:' + build_name + '-x86_64' }}
4861
4962
- name: Discover the release build name for the z-stream promoted to upgrade channel on {{ release }}
5063
# Ref: https://docs.openshift.com/container-platform/4.9/updating/understanding-upgrade-channels-release.html
@@ -68,3 +81,12 @@
6881
- name: Set openshift_release_build_name when openshift.build is set to a channel
6982
ansible.builtin.set_fact:
7083
openshift_release_build_name: "{{ result.stdout }}"
84+
85+
- name: Parse openshift_release_pull_spec from Pull From field in release.txt
86+
ansible.builtin.shell: set -o pipefail && grep '^Pull From:' {{ home_dir }}/release.txt | awk '{print $3}'
87+
changed_when: false
88+
register: pull_from_result
89+
90+
- name: Set openshift_release_pull_spec from channel release.txt
91+
ansible.builtin.set_fact:
92+
openshift_release_pull_spec: "{{ pull_from_result.stdout }}"

0 commit comments

Comments
 (0)