blob: 30700c301d4c8e2d9db7dc371b1bcb0980dc91ef [file] [log] [blame]
Mohammed Naser8613c862023-04-24 17:26:51 -04001#!/usr/bin/env python3
2
3import argparse
4import functools
5
Mohammed Naser12207172024-02-05 18:49:35 -05006import requests
Mohammed Naser8613c862023-04-24 17:26:51 -04007from docker_image import reference
8from oslo_config import cfg
9from oslo_log import log as logging
10from ruyaml import YAML
Mohammed Naser8613c862023-04-24 17:26:51 -040011
12LOG = logging.getLogger(__name__)
13CONF = cfg.CONF
14
Oleksandr Kd4e5b232024-01-18 17:20:16 +010015SKIP_IMAGE_LIST = ["secretgen_controller"]
Mohammed Naser8613c862023-04-24 17:26:51 -040016
Mohammed Naser891a2902024-01-23 09:47:47 -050017
Mohammed Naser21066a12024-01-02 11:14:53 -050018def get_digest(image_ref, token=None):
Mohammed Nasere1ad8c82024-06-18 14:43:26 -040019 url = f"https://{image_ref.domain()}/v2/{image_ref.path()}/manifests/{image_ref['tag']}"
20
Mohammed Naser21066a12024-01-02 11:14:53 -050021 headers = {}
22 if token:
23 headers["Authorization"] = f"Bearer {token}"
Mohammed Nasere1ad8c82024-06-18 14:43:26 -040024 else:
Jason Hall54238122024-02-19 12:56:23 -060025 r = requests.get(url, timeout=5)
Mohammed Nasere1ad8c82024-06-18 14:43:26 -040026 auth_header = r.headers.get("Www-Authenticate")
27 if auth_header:
28 realm = auth_header.split(",")[0].split("=")[1].strip('"')
29
30 r = requests.get(
31 realm,
32 timeout=5,
33 params={"scope": f"repository:{image_ref.path()}:pull"},
Mohammed Nasere1ad8c82024-06-18 14:43:26 -040034 )
35 r.raise_for_status()
36
37 headers["Authorization"] = f"Bearer {r.json()['token']}"
Mohammed Naser21066a12024-01-02 11:14:53 -050038
39 try:
40 headers["Accept"] = "application/vnd.docker.distribution.manifest.v2+json"
41
42 r = requests.get(
43 f"https://{image_ref.domain()}/v2/{image_ref.path()}/manifests/{image_ref['tag']}",
44 timeout=5,
45 headers=headers,
46 )
47 r.raise_for_status()
48 return r.headers["Docker-Content-Digest"]
49 except requests.exceptions.HTTPError:
50 headers["Accept"] = "application/vnd.oci.image.index.v1+json"
51
52 r = requests.get(
53 f"https://{image_ref.domain()}/v2/{image_ref.path()}/manifests/{image_ref['tag']}",
54 timeout=5,
55 headers=headers,
56 )
57 r.raise_for_status()
58 return r.headers["Docker-Content-Digest"]
59
60
Mohammed Naser8613c862023-04-24 17:26:51 -040061@functools.cache
62def get_pinned_image(image_src):
63 image_ref = reference.Reference.parse(image_src)
Mohammed Naser1cfa0ce2025-01-19 00:04:34 -050064 if image_ref.domain() != "harbor.atmosphere.dev":
65 image_ref = reference.Reference.parse("harbor.atmosphere.dev/" + image_src)
Mohammed Naser8613c862023-04-24 17:26:51 -040066
Yaguang Tang1d50b802024-12-13 03:35:11 +080067 if (
68 image_ref.domain() == "registry.atmosphere.dev"
69 or image_ref.domain() == "harbor.atmosphere.dev"
70 ):
Mohammed Naser1dfea6b2024-02-09 01:04:26 -050071 # Get token for docker.io
72 r = requests.get(
Yaguang Tang1d50b802024-12-13 03:35:11 +080073 "https://harbor.atmosphere.dev/service/token",
Mohammed Naser1dfea6b2024-02-09 01:04:26 -050074 timeout=5,
75 params={
76 "service": "harbor-registry",
77 "scope": f"repository:{image_ref.path()}:pull",
78 },
79 )
80 r.raise_for_status()
81 token = r.json()["token"]
82
83 digest = get_digest(image_ref, token=token)
Mohammed Nasere1ad8c82024-06-18 14:43:26 -040084 elif image_ref.domain() == "quay.io":
Mohammed Naser8613c862023-04-24 17:26:51 -040085 r = requests.get(
86 f"https://quay.io/api/v1/repository/{image_ref.path()}/tag/",
Mohammed Naser21066a12024-01-02 11:14:53 -050087 timeout=5,
Mohammed Naser8613c862023-04-24 17:26:51 -040088 params={"specificTag": image_ref["tag"]},
89 )
90 r.raise_for_status()
91 digest = r.json()["tags"][0]["manifest_digest"]
Mohammed Nasere1ad8c82024-06-18 14:43:26 -040092 elif image_ref.domain() == "docker.io":
Mohammed Naser49e66372023-07-10 14:57:00 -040093 # Get token for docker.io
94 r = requests.get(
95 "https://auth.docker.io/token",
Mohammed Naser21066a12024-01-02 11:14:53 -050096 timeout=5,
Mohammed Naser16baaab2023-07-10 15:07:11 -040097 params={
98 "service": "registry.docker.io",
99 "scope": f"repository:{image_ref.path()}:pull",
100 },
Mohammed Naser49e66372023-07-10 14:57:00 -0400101 )
102 r.raise_for_status()
103 token = r.json()["token"]
104
105 r = requests.get(
106 f"https://registry-1.docker.io/v2/{image_ref.path()}/manifests/{image_ref['tag']}",
Mohammed Naser21066a12024-01-02 11:14:53 -0500107 timeout=5,
Mohammed Naser16baaab2023-07-10 15:07:11 -0400108 headers={
109 "Accept": "application/vnd.docker.distribution.manifest.v2+json",
110 "Authorization": f"Bearer {token}",
111 },
Mohammed Naser49e66372023-07-10 14:57:00 -0400112 )
113 r.raise_for_status()
114 digest = r.headers["Docker-Content-Digest"]
Mohammed Nasere1ad8c82024-06-18 14:43:26 -0400115 elif image_ref.domain() == "ghcr.io":
Mohammed Naser21066a12024-01-02 11:14:53 -0500116 # Get token for docker.io
117 r = requests.get(
118 "https://ghcr.io/token",
119 timeout=5,
120 params={
121 "service": "ghcr.io",
122 "scope": f"repository:{image_ref.path()}:pull",
123 },
124 )
125 r.raise_for_status()
126 token = r.json()["token"]
127
128 digest = get_digest(image_ref, token=token)
Mohammed Nasere1ad8c82024-06-18 14:43:26 -0400129 else:
130 digest = get_digest(image_ref)
Mohammed Naser21066a12024-01-02 11:14:53 -0500131
Mohammed Naser1cfa0ce2025-01-19 00:04:34 -0500132 original_ref = reference.Reference.parse(image_src)
133 return (
134 f"{original_ref.domain()}/{original_ref.path()}:{original_ref['tag']}@{digest}"
135 )
Mohammed Naser8613c862023-04-24 17:26:51 -0400136
137
138def main():
139 logging.register_options(CONF)
140 logging.setup(CONF, "atmosphere-bump-images")
141
142 parser = argparse.ArgumentParser("bump-images")
143 parser.add_argument(
144 "src", help="Path for default values file", type=argparse.FileType("r")
145 )
Mohammed Nasereb257cb2024-04-10 21:14:24 -0400146 parser.add_argument(
147 "dst", help="Path for output file", type=argparse.FileType("r+")
148 )
Mohammed Naser8613c862023-04-24 17:26:51 -0400149
150 args = parser.parse_args()
151
152 yaml = YAML(typ="rt")
153 data = yaml.load(args.src)
154
Mohammed Naser21066a12024-01-02 11:14:53 -0500155 for image in data["_atmosphere_images"]:
Oleksandr Kd4e5b232024-01-18 17:20:16 +0100156 if image in SKIP_IMAGE_LIST:
157 continue
Mohammed Naserfdd5cee2024-02-07 23:45:52 -0500158
Jason Hall54238122024-02-19 12:56:23 -0600159 image_src = (
160 data["_atmosphere_images"][image]
161 .replace("{{ atmosphere_release }}", data["atmosphere_release"])
162 .replace("{{ atmosphere_image_prefix }}", "")
Mohammed Naser12207172024-02-05 18:49:35 -0500163 )
Mohammed Naser1cfa0ce2025-01-19 00:04:34 -0500164 pinned_image = get_pinned_image(image_src).replace(
165 "harbor.atmosphere.dev", "registry.atmosphere.dev"
166 )
Mohammed Naser8613c862023-04-24 17:26:51 -0400167
168 LOG.info("Pinning image %s from %s to %s", image, image_src, pinned_image)
Jason Hall54238122024-02-19 12:56:23 -0600169 data["_atmosphere_images"][image] = "{{ atmosphere_image_prefix }}%s" % (
170 pinned_image,
171 )
Mohammed Naser8613c862023-04-24 17:26:51 -0400172
173 yaml.dump(data, args.dst)
174
175
176if __name__ == "__main__":
177 main()