Skip to content

Commit 29e3a46

Browse files
author
OpenShift Bot
authored
Merge pull request openshift#13059 from miminar/image-manifest-migration-script
Merged by openshift-bot
2 parents 50496d3 + 7b4c62b commit 29e3a46

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
#!/bin/bash
2+
3+
set -iuo pipefail
4+
IFS=$'\n\t'
5+
6+
readonly AUDITOR_ROLE='system:image-auditor'
7+
8+
readonly USAGE="Usage: $(basename ${BASH_SOURCE[0]}) [OPTIONS]
9+
10+
It fetches manifests of images from OpenShift integrated registry forcing it to
11+
store the manifests on the local storage and remove them from etcd. This will
12+
work as long as PR https://github.com/openshift/origin/pull/11925 is compiled
13+
in the regisry binary.
14+
15+
The script doesn't make any changes unless -a flag is given.
16+
17+
See bug https://bugzilla.redhat.com/show_bug.cgi?id=1378180 for more details.
18+
19+
Options:
20+
-h Print this message and exit.
21+
-a Apply. Do the changes.
22+
-r <registry_url> Specify registry url to connect to.
23+
If not given, script will try to determine from
24+
cluster status.
25+
-s Registry is secured.
26+
-c <cacert> CA certificate for use with registry if the registry is
27+
secured.
28+
-t <token> Token to use to query the registry.
29+
Its user or service account must be able to
30+
get imagestreams/layers in all the namespaces.
31+
If not given, token of current user will be used.
32+
Run this command as cluster admin to give particular
33+
user enough rights to query the images:
34+
35+
\$ oadm policy add-cluster-role-to-user registry-viewer <user>
36+
37+
-f Force migration of externally managed images (those imported
38+
from remote registries). A migration attempt is done by
39+
default. If it fails (manifest cannot be stored on
40+
registry's storage because of missing dependencies), the
41+
manifest will be kept in image object (and in etcd). This
42+
option causes a removal of the manifest in any way. It
43+
will still be available only on the remote registry. If
44+
pullthrough feature is enabled, registry will serve such
45+
manifests from the external locations.
46+
47+
For this to work, the user must be an image auditor:
48+
49+
\$ oadm policy add-cluster-role-to-user ${AUDITOR_ROLE} <user>
50+
"
51+
52+
registry_address=""
53+
secured=0
54+
cacert=""
55+
token=""
56+
force_removal_of_manifests=0
57+
dry_run=1
58+
59+
function get_docker_registry_url() {
60+
local service_ip ports
61+
local tmpl=$'{{.spec.clusterIP}}%{{range $i, $port := .spec.ports}}{{$port.targetPort}}@{{$port.port}},{{end}}'
62+
IFS=% read -r service_ip ports <<<"$(oc get -o go-template="${tmpl}" -n default svc/docker-registry)"
63+
if [[ -z "${service_ip:-}" ]]; then
64+
echo "failed to get service ip of svc/docker-registry" >&2
65+
return 1
66+
fi
67+
68+
# first, try to get a port with targetPort == 5000
69+
local port="$(echo "${ports}" | tr ',' '\n' | sed -n 's/^5000@\([0-9]\+\)/\1/p')"
70+
if [[ -z "${port:-}" ]]; then
71+
# if no such port, get the first port
72+
port=$(echo "${ports}" | sed 's/^[^@]\+@\([^,]\+\).*/\1/')
73+
fi
74+
if [[ -z "${port:-}" ]]; then
75+
echo "failed to get port of svc/docker-registry" >&2
76+
return 1
77+
fi
78+
echo "${service_ip}:${port}"
79+
}
80+
81+
function check_permissions() {
82+
local authorized=1
83+
local verb
84+
for verb in get list update; do
85+
if [[ "$(oc policy can-i "${verb}" images)" != yes ]]; then
86+
echo "The user isn't authorized to ${verb} images!" >&2
87+
authorized=0
88+
fi
89+
done
90+
if [[ "${authorized}" == 0 ]]; then
91+
echo "Ask your admin to give you permissions to work with images, e.g.:" >&2
92+
echo " oadm policy add-cluster-role-to-user ${AUDITOR_ROLE} $(oc whoami)" >&2
93+
return 1
94+
fi
95+
96+
if [[ "$(oc policy can-i get imagestreams/layers --token=${token})" != yes ]]; then
97+
echo "The registry user isn't authorized to get imagestreams/layers!" >&2
98+
echo " oadm policy add-cluster-role-to-user registry-viewer <user>" >&2
99+
return 1
100+
fi
101+
}
102+
103+
function manifest_removed() {
104+
local ref="$1"
105+
local tmpl_manifest_removed=$'{{if .dockerImageManifest}}present{{else}}removed{{end}},'
106+
tmpl_manifest_removed+=$'{{if .metadata.annotations}}'
107+
tmpl_manifest_removed+=$'{{if index .metadata.annotations "openshift.io/image.managed"}}'
108+
tmpl_manifest_removed+=$'managed{{else}}external{{end}}{{else}}external{{end}}\n'
109+
110+
oc get image -o go-template="${tmpl_manifest_removed}" "${ref}"
111+
}
112+
113+
function force_manifest_removal() {
114+
local reference="$1"
115+
116+
echo "Forcibly removing manifest from external image ${reference}".
117+
if ! oc patch image "${reference}" -p 'dockerImageManifest: ""'; then
118+
echo "Failed to remove manifest from image ${reference}!" >&2
119+
fi
120+
# config can be safely removed as well
121+
oc patch image "${reference}" -p 'dockerImageConfig: ""' >/dev/null 2>&1 || :
122+
}
123+
124+
function migrate() {
125+
local tmpl_istags=$'{{range $isi, $is := .items}}{{range $tagname, $tag := $is.status.tags}}'
126+
tmpl_istags+=$'{{range $i, $item := $tag.items}}{{$is.metadata.name}}@{{$item.image}}\n'
127+
tmpl_istags+=$'{{end}}{{end}}{{end}}'
128+
129+
declare -A processed_images
130+
local total=0
131+
local to_migrate=0
132+
local proj isimage isname reference removed managed
133+
134+
for proj in $(oc get -o jsonpath=$'{range .items[*]}{.metadata.name}\n{end}' project | sort -u); do
135+
echo "Processing project '${proj}'"
136+
for isimage in $(oc get -n "${proj}" -o go-template="${tmpl_istags}" is 2>/dev/null); do
137+
IFS='@' read -r isname reference <<<"${isimage}"
138+
[[ -n "${processed_images[${reference}]+set}" ]] && continue
139+
processed_images[${reference}]=1
140+
total="$((${total} + 1))"
141+
142+
IFS=',' read -r removed managed <<<"$(manifest_removed "${reference}")"
143+
[[ "${removed}" == 'removed' || -z "${removed}" ]] && continue
144+
to_migrate="$((${to_migrate} + 1))"
145+
146+
if [[ "${dry_run}" = 1 ]]; then
147+
echo "Would migrate ${managed} isimage '${proj}/${isname}@${reference}'"
148+
continue
149+
fi
150+
151+
echo "Migrating ${managed} isimage '${proj}/${isname}@${reference}'"
152+
if ! curl --fail -k ${curlargs[@]-} -u "unused:${token}" -i -s \
153+
"${url}/v2/${proj}/${isname}/manifests/${reference}" | \
154+
sed -e '/^[[:space:]]*$/,$d' | grep '^HTTP'; then
155+
echo "Failed to fetch '${url}/v2/${proj}/${isname}/manifests/${reference}'!"
156+
fi
157+
158+
if [[ "${force_removal_of_manifests}" == 1 && "${managed}" == 'external' ]]; then
159+
force_manifest_removal "${reference}"
160+
fi
161+
done
162+
done
163+
164+
if [[ "${dry_run}" = 1 ]]; then
165+
printf '\nWould migrate %d out of %d images.\n' "${to_migrate}" "${total}"
166+
fi
167+
}
168+
169+
while getopts 'hr:sc:t:fa' opt; do
170+
case "${opt}" in
171+
h)
172+
echo "${USAGE}";
173+
exit 0
174+
;;
175+
a)
176+
dry_run=0
177+
;;
178+
s)
179+
secured=1
180+
;;
181+
c)
182+
cacert="${OPTARG}"
183+
;;
184+
r)
185+
registry_address="${OPTARG}"
186+
;;
187+
t)
188+
token="${OPTARG}"
189+
;;
190+
f)
191+
force_removal_of_manifests=1
192+
;;
193+
*)
194+
echo "${USAGE}" >&2
195+
exit 1
196+
;;
197+
esac
198+
done
199+
200+
if [[ -z "${registry_address:-}" ]]; then
201+
registry_address="$(get_docker_registry_url)"
202+
fi
203+
204+
case "${registry_address},${secured}" in
205+
https://*)
206+
secured=1
207+
url="${registry_address}"
208+
;;
209+
http://*)
210+
secured=0
211+
url="${registry_address}"
212+
;;
213+
*,1)
214+
url="https://${registry_address}"
215+
;;
216+
*,0)
217+
url="http://${registry_address}"
218+
;;
219+
esac
220+
221+
curlargs=()
222+
if [[ -n "${cacert}" ]]; then
223+
curlargs+=( "--cacert" "${cacert}" )
224+
fi
225+
226+
if [[ -z "${token:-}" ]]; then
227+
token="$(oc whoami -t)"
228+
fi
229+
230+
if [[ -z "${token:-}" ]]; then
231+
echo "Please, provide a token of user authorized to get imagestreams/layers in all namespaces." >&2
232+
exit 1
233+
fi
234+
235+
if ! curl --fail ${curlargs[@]-} --max-time 15 "${url}/healthz"; then
236+
echo "Please, provide endpoint of integrated docker registry." >&2
237+
exit 1
238+
fi
239+
240+
check_permissions || exit 1
241+
migrate

origin.spec

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,9 @@ sed "s|@@CONF_FILE-VARIABLE@@|${OS_CONF_FILE}|" contrib/excluder/excluder-templa
359359
sed -i "s|@@PACKAGE_LIST-VARIABLE@@|docker*1.13* docker*1.14* docker*1.15* docker*1.16* docker*1.17* docker*1.18* docker*1.19* docker*1.20*|" $RPM_BUILD_ROOT/usr/sbin/%{name}-docker-excluder
360360
chmod 0744 $RPM_BUILD_ROOT/usr/sbin/%{name}-docker-excluder
361361

362+
# Install migration scripts
363+
install -d %{buildroot}%{_datadir}/%{name}/migration
364+
install -p -m 755 contrib/migration/* %{buildroot}%{_datadir}/%{name}/migration/
362365

363366
%files
364367
%doc README.md
@@ -408,6 +411,8 @@ fi
408411
%files master
409412
%{_unitdir}/%{name}-master.service
410413
%config(noreplace) %{_sysconfdir}/sysconfig/%{name}-master
414+
%dir %{_datadir}/%{name}/migration/
415+
%{_datadir}/%{name}/migration/*
411416
%defattr(-,root,root,0700)
412417
%config(noreplace) %{_sysconfdir}/origin/master
413418
%ghost %config(noreplace) %{_sysconfdir}/origin/admin.crt

0 commit comments

Comments
 (0)