blob: 644d0a211833f63d281847a038dc194d1b5caf36 [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):
19 headers = {}
20 if token:
21 headers["Authorization"] = f"Bearer {token}"
22
23 try:
24 headers["Accept"] = "application/vnd.docker.distribution.manifest.v2+json"
25
26 r = requests.get(
27 f"https://{image_ref.domain()}/v2/{image_ref.path()}/manifests/{image_ref['tag']}",
28 timeout=5,
29 headers=headers,
30 )
31 r.raise_for_status()
32 return r.headers["Docker-Content-Digest"]
33 except requests.exceptions.HTTPError:
34 headers["Accept"] = "application/vnd.oci.image.index.v1+json"
35
36 r = requests.get(
37 f"https://{image_ref.domain()}/v2/{image_ref.path()}/manifests/{image_ref['tag']}",
38 timeout=5,
39 headers=headers,
40 )
41 r.raise_for_status()
42 return r.headers["Docker-Content-Digest"]
43
44
Mohammed Naser8613c862023-04-24 17:26:51 -040045@functools.cache
46def get_pinned_image(image_src):
47 image_ref = reference.Reference.parse(image_src)
48
Mohammed Naser12207172024-02-05 18:49:35 -050049 if image_ref.domain() in (
50 "registry.k8s.io",
51 "us-docker.pkg.dev",
Mohammed Naser12207172024-02-05 18:49:35 -050052 ):
Mohammed Naser21066a12024-01-02 11:14:53 -050053 digest = get_digest(image_ref)
54
Mohammed Naser1dfea6b2024-02-09 01:04:26 -050055 if image_ref.domain() == "registry.atmosphere.dev":
56 # Get token for docker.io
57 r = requests.get(
58 "https://registry.atmosphere.dev/service/token",
59 timeout=5,
60 params={
61 "service": "harbor-registry",
62 "scope": f"repository:{image_ref.path()}:pull",
63 },
64 )
65 r.raise_for_status()
66 token = r.json()["token"]
67
68 digest = get_digest(image_ref, token=token)
69
Mohammed Naser8613c862023-04-24 17:26:51 -040070 if image_ref.domain() == "quay.io":
71 r = requests.get(
72 f"https://quay.io/api/v1/repository/{image_ref.path()}/tag/",
Mohammed Naser21066a12024-01-02 11:14:53 -050073 timeout=5,
Mohammed Naser8613c862023-04-24 17:26:51 -040074 params={"specificTag": image_ref["tag"]},
75 )
76 r.raise_for_status()
77 digest = r.json()["tags"][0]["manifest_digest"]
78
Mohammed Naser49e66372023-07-10 14:57:00 -040079 if image_ref.domain() == "docker.io":
80 # Get token for docker.io
81 r = requests.get(
82 "https://auth.docker.io/token",
Mohammed Naser21066a12024-01-02 11:14:53 -050083 timeout=5,
Mohammed Naser16baaab2023-07-10 15:07:11 -040084 params={
85 "service": "registry.docker.io",
86 "scope": f"repository:{image_ref.path()}:pull",
87 },
Mohammed Naser49e66372023-07-10 14:57:00 -040088 )
89 r.raise_for_status()
90 token = r.json()["token"]
91
92 r = requests.get(
93 f"https://registry-1.docker.io/v2/{image_ref.path()}/manifests/{image_ref['tag']}",
Mohammed Naser21066a12024-01-02 11:14:53 -050094 timeout=5,
Mohammed Naser16baaab2023-07-10 15:07:11 -040095 headers={
96 "Accept": "application/vnd.docker.distribution.manifest.v2+json",
97 "Authorization": f"Bearer {token}",
98 },
Mohammed Naser49e66372023-07-10 14:57:00 -040099 )
100 r.raise_for_status()
101 digest = r.headers["Docker-Content-Digest"]
102
Mohammed Naser21066a12024-01-02 11:14:53 -0500103 if image_ref.domain() == "ghcr.io":
104 # Get token for docker.io
105 r = requests.get(
106 "https://ghcr.io/token",
107 timeout=5,
108 params={
109 "service": "ghcr.io",
110 "scope": f"repository:{image_ref.path()}:pull",
111 },
112 )
113 r.raise_for_status()
114 token = r.json()["token"]
115
116 digest = get_digest(image_ref, token=token)
117
118 return f"{image_ref.domain()}/{image_ref.path()}:{image_ref['tag']}@{digest}"
Mohammed Naser8613c862023-04-24 17:26:51 -0400119
120
121def main():
122 logging.register_options(CONF)
123 logging.setup(CONF, "atmosphere-bump-images")
124
125 parser = argparse.ArgumentParser("bump-images")
126 parser.add_argument(
127 "src", help="Path for default values file", type=argparse.FileType("r")
128 )
129 parser.add_argument("dst", help="Path for output file", type=argparse.FileType("w"))
Mohammed Naser12207172024-02-05 18:49:35 -0500130 parser.add_argument(
131 "-r",
132 "--registry",
133 default="ghcr.io/vexxhost/atmosphere",
134 help="Registry containing Atmosphere images",
135 )
Mohammed Naser8613c862023-04-24 17:26:51 -0400136
137 args = parser.parse_args()
138
139 yaml = YAML(typ="rt")
140 data = yaml.load(args.src)
141
Mohammed Naser21066a12024-01-02 11:14:53 -0500142 for image in data["_atmosphere_images"]:
Oleksandr Kd4e5b232024-01-18 17:20:16 +0100143 if image in SKIP_IMAGE_LIST:
144 continue
Mohammed Naserfdd5cee2024-02-07 23:45:52 -0500145
146 # NOTE(mnaser): If we're in CI, only pin the Atmosphere images
147 if (
Mohammed Naser1dfea6b2024-02-09 01:04:26 -0500148 "registry.atmosphere.dev" in args.registry
Mohammed Naserfdd5cee2024-02-07 23:45:52 -0500149 and "ghcr.io/vexxhost/atmosphere" not in data["_atmosphere_images"][image]
150 ):
151 continue
152
Mohammed Naser12207172024-02-05 18:49:35 -0500153 image_src = data["_atmosphere_images"][image].replace(
Mohammed Naser1dfea6b2024-02-09 01:04:26 -0500154 "ghcr.io/vexxhost/atmosphere", args.registry
Mohammed Naser12207172024-02-05 18:49:35 -0500155 )
Mohammed Naser8613c862023-04-24 17:26:51 -0400156 pinned_image = get_pinned_image(image_src)
157
158 LOG.info("Pinning image %s from %s to %s", image, image_src, pinned_image)
ricolinb8ab0172023-06-01 15:41:02 +0800159 data["_atmosphere_images"][image] = pinned_image
Mohammed Naser8613c862023-04-24 17:26:51 -0400160
161 yaml.dump(data, args.dst)
162
163
164if __name__ == "__main__":
165 main()