Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | import argparse |
| 4 | import functools |
| 5 | |
Mohammed Naser | 1220717 | 2024-02-05 18:49:35 -0500 | [diff] [blame] | 6 | import requests |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 7 | from docker_image import reference |
| 8 | from oslo_config import cfg |
| 9 | from oslo_log import log as logging |
| 10 | from ruyaml import YAML |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 11 | |
| 12 | LOG = logging.getLogger(__name__) |
| 13 | CONF = cfg.CONF |
| 14 | |
Oleksandr K | d4e5b23 | 2024-01-18 17:20:16 +0100 | [diff] [blame] | 15 | SKIP_IMAGE_LIST = ["secretgen_controller"] |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 16 | |
Mohammed Naser | 891a290 | 2024-01-23 09:47:47 -0500 | [diff] [blame] | 17 | |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 18 | def get_digest(image_ref, token=None): |
vexxhost-bot | f5ee799 | 2024-06-19 00:13:44 +0200 | [diff] [blame] | 19 | url = f"https://{image_ref.domain()}/v2/{image_ref.path()}/manifests/{image_ref['tag']}" |
| 20 | |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 21 | headers = {} |
| 22 | if token: |
| 23 | headers["Authorization"] = f"Bearer {token}" |
vexxhost-bot | f5ee799 | 2024-06-19 00:13:44 +0200 | [diff] [blame] | 24 | else: |
| 25 | r = requests.get(url, timeout=5, verify=False) |
| 26 | 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"}, |
| 34 | verify=False, |
| 35 | ) |
| 36 | r.raise_for_status() |
| 37 | |
| 38 | headers["Authorization"] = f"Bearer {r.json()['token']}" |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 39 | |
| 40 | try: |
| 41 | headers["Accept"] = "application/vnd.docker.distribution.manifest.v2+json" |
| 42 | |
| 43 | r = requests.get( |
| 44 | f"https://{image_ref.domain()}/v2/{image_ref.path()}/manifests/{image_ref['tag']}", |
| 45 | timeout=5, |
| 46 | headers=headers, |
vexxhost-bot | f5ee799 | 2024-06-19 00:13:44 +0200 | [diff] [blame] | 47 | verify=False, |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 48 | ) |
| 49 | r.raise_for_status() |
| 50 | return r.headers["Docker-Content-Digest"] |
| 51 | except requests.exceptions.HTTPError: |
| 52 | headers["Accept"] = "application/vnd.oci.image.index.v1+json" |
| 53 | |
| 54 | r = requests.get( |
| 55 | f"https://{image_ref.domain()}/v2/{image_ref.path()}/manifests/{image_ref['tag']}", |
| 56 | timeout=5, |
| 57 | headers=headers, |
vexxhost-bot | f5ee799 | 2024-06-19 00:13:44 +0200 | [diff] [blame] | 58 | verify=False, |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 59 | ) |
| 60 | r.raise_for_status() |
| 61 | return r.headers["Docker-Content-Digest"] |
| 62 | |
| 63 | |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 64 | @functools.cache |
| 65 | def get_pinned_image(image_src): |
| 66 | image_ref = reference.Reference.parse(image_src) |
Mohammed Naser | 859988e | 2025-01-19 00:04:34 -0500 | [diff] [blame^] | 67 | if image_ref.domain() != "harbor.atmosphere.dev": |
| 68 | image_ref = reference.Reference.parse("harbor.atmosphere.dev/" + image_src) |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 69 | |
Yaguang Tang | 0953b61 | 2024-12-13 04:14:34 +0800 | [diff] [blame] | 70 | if ( |
| 71 | image_ref.domain() == "registry.atmosphere.dev" |
| 72 | or image_ref.domain() == "harbor.atmosphere.dev" |
| 73 | ): |
Mohammed Naser | 1dfea6b | 2024-02-09 01:04:26 -0500 | [diff] [blame] | 74 | # Get token for docker.io |
| 75 | r = requests.get( |
Yaguang Tang | 0953b61 | 2024-12-13 04:14:34 +0800 | [diff] [blame] | 76 | "https://harbor.atmosphere.dev/service/token", |
Mohammed Naser | 1dfea6b | 2024-02-09 01:04:26 -0500 | [diff] [blame] | 77 | timeout=5, |
| 78 | params={ |
| 79 | "service": "harbor-registry", |
| 80 | "scope": f"repository:{image_ref.path()}:pull", |
| 81 | }, |
| 82 | ) |
| 83 | r.raise_for_status() |
| 84 | token = r.json()["token"] |
| 85 | |
| 86 | digest = get_digest(image_ref, token=token) |
vexxhost-bot | f5ee799 | 2024-06-19 00:13:44 +0200 | [diff] [blame] | 87 | elif image_ref.domain() == "quay.io": |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 88 | r = requests.get( |
| 89 | f"https://quay.io/api/v1/repository/{image_ref.path()}/tag/", |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 90 | timeout=5, |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 91 | params={"specificTag": image_ref["tag"]}, |
| 92 | ) |
| 93 | r.raise_for_status() |
| 94 | digest = r.json()["tags"][0]["manifest_digest"] |
vexxhost-bot | f5ee799 | 2024-06-19 00:13:44 +0200 | [diff] [blame] | 95 | elif image_ref.domain() == "docker.io": |
Mohammed Naser | 49e6637 | 2023-07-10 14:57:00 -0400 | [diff] [blame] | 96 | # Get token for docker.io |
| 97 | r = requests.get( |
| 98 | "https://auth.docker.io/token", |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 99 | timeout=5, |
Mohammed Naser | 16baaab | 2023-07-10 15:07:11 -0400 | [diff] [blame] | 100 | params={ |
| 101 | "service": "registry.docker.io", |
| 102 | "scope": f"repository:{image_ref.path()}:pull", |
| 103 | }, |
Mohammed Naser | 49e6637 | 2023-07-10 14:57:00 -0400 | [diff] [blame] | 104 | ) |
| 105 | r.raise_for_status() |
| 106 | token = r.json()["token"] |
| 107 | |
| 108 | r = requests.get( |
| 109 | f"https://registry-1.docker.io/v2/{image_ref.path()}/manifests/{image_ref['tag']}", |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 110 | timeout=5, |
Mohammed Naser | 16baaab | 2023-07-10 15:07:11 -0400 | [diff] [blame] | 111 | headers={ |
| 112 | "Accept": "application/vnd.docker.distribution.manifest.v2+json", |
| 113 | "Authorization": f"Bearer {token}", |
| 114 | }, |
Mohammed Naser | 49e6637 | 2023-07-10 14:57:00 -0400 | [diff] [blame] | 115 | ) |
| 116 | r.raise_for_status() |
| 117 | digest = r.headers["Docker-Content-Digest"] |
vexxhost-bot | f5ee799 | 2024-06-19 00:13:44 +0200 | [diff] [blame] | 118 | elif image_ref.domain() == "ghcr.io": |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 119 | # Get token for docker.io |
| 120 | r = requests.get( |
| 121 | "https://ghcr.io/token", |
| 122 | timeout=5, |
| 123 | params={ |
| 124 | "service": "ghcr.io", |
| 125 | "scope": f"repository:{image_ref.path()}:pull", |
| 126 | }, |
| 127 | ) |
| 128 | r.raise_for_status() |
| 129 | token = r.json()["token"] |
| 130 | |
| 131 | digest = get_digest(image_ref, token=token) |
vexxhost-bot | f5ee799 | 2024-06-19 00:13:44 +0200 | [diff] [blame] | 132 | else: |
| 133 | digest = get_digest(image_ref) |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 134 | |
Mohammed Naser | 859988e | 2025-01-19 00:04:34 -0500 | [diff] [blame^] | 135 | original_ref = reference.Reference.parse(image_src) |
| 136 | return ( |
| 137 | f"{original_ref.domain()}/{original_ref.path()}:{original_ref['tag']}@{digest}" |
| 138 | ) |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 139 | |
| 140 | |
| 141 | def main(): |
| 142 | logging.register_options(CONF) |
| 143 | logging.setup(CONF, "atmosphere-bump-images") |
| 144 | |
| 145 | parser = argparse.ArgumentParser("bump-images") |
| 146 | parser.add_argument( |
| 147 | "src", help="Path for default values file", type=argparse.FileType("r") |
| 148 | ) |
Mohammed Naser | eb257cb | 2024-04-10 21:14:24 -0400 | [diff] [blame] | 149 | parser.add_argument( |
| 150 | "dst", help="Path for output file", type=argparse.FileType("r+") |
| 151 | ) |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 152 | |
| 153 | args = parser.parse_args() |
| 154 | |
| 155 | yaml = YAML(typ="rt") |
| 156 | data = yaml.load(args.src) |
| 157 | |
Mohammed Naser | 21066a1 | 2024-01-02 11:14:53 -0500 | [diff] [blame] | 158 | for image in data["_atmosphere_images"]: |
Oleksandr K | d4e5b23 | 2024-01-18 17:20:16 +0100 | [diff] [blame] | 159 | if image in SKIP_IMAGE_LIST: |
| 160 | continue |
Mohammed Naser | fdd5cee | 2024-02-07 23:45:52 -0500 | [diff] [blame] | 161 | |
Mohammed Naser | 1220717 | 2024-02-05 18:49:35 -0500 | [diff] [blame] | 162 | image_src = data["_atmosphere_images"][image].replace( |
Mohammed Naser | d30f18d | 2024-04-17 01:20:43 -0400 | [diff] [blame] | 163 | "{{ atmosphere_release }}", data["atmosphere_release"] |
Mohammed Naser | 1220717 | 2024-02-05 18:49:35 -0500 | [diff] [blame] | 164 | ) |
Mohammed Naser | 859988e | 2025-01-19 00:04:34 -0500 | [diff] [blame^] | 165 | pinned_image = get_pinned_image(image_src).replace( |
| 166 | "harbor.atmosphere.dev", "registry.atmosphere.dev" |
| 167 | ) |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 168 | |
| 169 | LOG.info("Pinning image %s from %s to %s", image, image_src, pinned_image) |
ricolin | b8ab017 | 2023-06-01 15:41:02 +0800 | [diff] [blame] | 170 | data["_atmosphere_images"][image] = pinned_image |
Mohammed Naser | 8613c86 | 2023-04-24 17:26:51 -0400 | [diff] [blame] | 171 | |
| 172 | yaml.dump(data, args.dst) |
| 173 | |
| 174 | |
| 175 | if __name__ == "__main__": |
| 176 | main() |