Merge ~cjdc/ubuntu-docker-images/+git/templates:add-skopeo-authfile into ~ubuntu-docker-images/ubuntu-docker-images/+git/templates:main
- Git
- lp:~cjdc/ubuntu-docker-images/+git/templates
- add-skopeo-authfile
- Merge into main
Proposed by
Cristovao Cordeiro
Status: | Merged |
---|---|
Merged at revision: | 8e77445de94d5427288239f37e965eb167d71298 |
Proposed branch: | ~cjdc/ubuntu-docker-images/+git/templates:add-skopeo-authfile |
Merge into: | ~ubuntu-docker-images/ubuntu-docker-images/+git/templates:main |
Diff against target: |
991 lines (+444/-451) 2 files modified
README.md (+2/-2) generate_ubuntu_yaml.py (+442/-449) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Samir Akarioh (community) | Approve | ||
Ubuntu Docker Images | Pending | ||
Review via email:
|
Commit message
fix: Skopeo needs an --authfile for private repos
Description of the change
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Cristovao Cordeiro (cjdc) wrote : | # |
> You need to change some signatures of some function
good catch. done, ty
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samir Akarioh (samiraka) wrote : | # |
Good for me, you can merge it
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/README.md b/README.md | |||
2 | index 29d4e38..6d86182 100644 | |||
3 | --- a/README.md | |||
4 | +++ b/README.md | |||
5 | @@ -15,12 +15,12 @@ The DevContainer will provide you with a working environment out of the box. **Y | |||
6 | 15 | ```bash | 15 | ```bash |
7 | 16 | git clone https://github.com/misterw97/RenderDown | 16 | git clone https://github.com/misterw97/RenderDown |
8 | 17 | sudo apt update && sudo apt install -y python3-mako python3-yaml | 17 | sudo apt update && sudo apt install -y python3-mako python3-yaml |
10 | 18 | pip install boto3 # if you want to run the generate_ubuntu_yaml file | 18 | pip install boto3 requests pyyaml && apt install -y distro-info # if you want to run the generate_ubuntu_yaml script |
11 | 19 | ``` | 19 | ``` |
12 | 20 | 20 | ||
13 | 21 | #### Generate_ubuntu_yaml | 21 | #### Generate_ubuntu_yaml |
14 | 22 | 22 | ||
16 | 23 | This script allows to generate the ubuntu.yaml file in order to use it by the RenderDown script. It uses the template ubuntu.yaml located in the template folder. | 23 | This script allows us to generate the ubuntu.yaml file in order to use it by the RenderDown script. It uses the template ubuntu.yaml located in the template folder. |
17 | 24 | 24 | ||
18 | 25 | Here are the available arguments and examples of commands: | 25 | Here are the available arguments and examples of commands: |
19 | 26 | 26 | ||
20 | diff --git a/generate_ubuntu_yaml.py b/generate_ubuntu_yaml.py | |||
21 | index 6c70e7c..dacd6e5 100755 | |||
22 | --- a/generate_ubuntu_yaml.py | |||
23 | +++ b/generate_ubuntu_yaml.py | |||
24 | @@ -1,525 +1,518 @@ | |||
25 | 1 | #!/usr/bin/env python3 | 1 | #!/usr/bin/env python3 |
26 | 2 | 2 | ||
27 | 3 | import argparse | 3 | import argparse |
28 | 4 | import base64 | ||
29 | 4 | import datetime | 5 | import datetime |
30 | 5 | import json | 6 | import json |
31 | 6 | import logging | 7 | import logging |
32 | 7 | import os | 8 | import os |
33 | 8 | import subprocess | 9 | import subprocess |
34 | 10 | import sys | ||
35 | 9 | from typing import Dict, List | 11 | from typing import Dict, List |
36 | 10 | 12 | ||
37 | 11 | import boto3 | 13 | import boto3 |
38 | 12 | import requests | 14 | import requests |
40 | 13 | import sys | 15 | import tempfile |
41 | 14 | import yaml | 16 | import yaml |
42 | 15 | 17 | ||
43 | 16 | logging.basicConfig(stream=sys.stdout, level=logging.INFO) | 18 | logging.basicConfig(stream=sys.stdout, level=logging.INFO) |
44 | 17 | NOW = datetime.datetime.now() | ||
45 | 18 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) | 19 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) |
46 | 19 | 20 | ||
47 | 20 | 21 | ||
147 | 21 | def cli_args() -> argparse.ArgumentParser: | 22 | class GenerateUbuntuYaml: |
148 | 22 | """Argument parser""" | 23 | def __init__(self): |
149 | 23 | parser = argparse.ArgumentParser( | 24 | self.now = datetime.datetime.now() |
150 | 24 | description="Generate documentation about Ubuntu for ECR and DockerHub" | 25 | self.validate_args() |
151 | 25 | ) | 26 | self.add_yaml_representer() |
152 | 26 | 27 | self.build_image_endpoint() | |
153 | 27 | parser.add_argument( | 28 | self._skopeo_auth_token = None |
55 | 28 | "--provider", | ||
56 | 29 | default="docker", | ||
57 | 30 | dest="provider", | ||
58 | 31 | help="aws or docker", | ||
59 | 32 | required=True, | ||
60 | 33 | ) | ||
61 | 34 | parser.add_argument( | ||
62 | 35 | "--username", | ||
63 | 36 | default="admin", | ||
64 | 37 | dest="username", | ||
65 | 38 | help="either the Docker Hub username, or the AWS access key ID", | ||
66 | 39 | required=True, | ||
67 | 40 | ) | ||
68 | 41 | parser.add_argument( | ||
69 | 42 | "--password", | ||
70 | 43 | default="admin", | ||
71 | 44 | dest="password", | ||
72 | 45 | help="either the Docker Hub password/token, or the AWS secret access key", | ||
73 | 46 | required=True, | ||
74 | 47 | ) | ||
75 | 48 | parser.add_argument( | ||
76 | 49 | "--token-docker", | ||
77 | 50 | dest="dockertoken", | ||
78 | 51 | default=None, | ||
79 | 52 | help="JWT token for Docker Hub authentication. \ | ||
80 | 53 | Only useful for the 'docker' provider.", | ||
81 | 54 | ) | ||
82 | 55 | parser.add_argument( | ||
83 | 56 | "--repository-basename", | ||
84 | 57 | dest="repository", | ||
85 | 58 | default=None, | ||
86 | 59 | help="repository basename of the ubuntu images. \ | ||
87 | 60 | Used to infer existing information.", | ||
88 | 61 | ) | ||
89 | 62 | parser.add_argument( | ||
90 | 63 | "--data-dir", | ||
91 | 64 | default="data", | ||
92 | 65 | dest="data_dir", | ||
93 | 66 | help="""The path of the folder | ||
94 | 67 | where the data file will be | ||
95 | 68 | saved ( if not exist, the script | ||
96 | 69 | will create the folder)""", | ||
97 | 70 | ) | ||
98 | 71 | parser.add_argument( | ||
99 | 72 | "--unpublished-suite", | ||
100 | 73 | dest="unpublished_suite", | ||
101 | 74 | help="""an Ubuntu Suite (e.g. jammy). | ||
102 | 75 | if given we will take the | ||
103 | 76 | tags pass on command lines (required) | ||
104 | 77 | and the arches for this section | ||
105 | 78 | of the yaml file. | ||
106 | 79 | """, | ||
107 | 80 | ) | ||
108 | 81 | parser.add_argument( | ||
109 | 82 | "--unpublished-tags", | ||
110 | 83 | dest="unpublished_tags", | ||
111 | 84 | help="""list of tags | ||
112 | 85 | (e.g. 'kinetic 22.10 22.10_edge kinetic)""", | ||
113 | 86 | ) | ||
114 | 87 | parser.add_argument( | ||
115 | 88 | "--unpublished-archs", | ||
116 | 89 | dest="unpublished_archs", | ||
117 | 90 | help="list of archs (e.g amd64 arm)", | ||
118 | 91 | ) | ||
119 | 92 | |||
120 | 93 | return parser | ||
121 | 94 | |||
122 | 95 | |||
123 | 96 | def validate_args( | ||
124 | 97 | parser: argparse.ArgumentParser, | ||
125 | 98 | ) -> argparse.ArgumentParser.parse_args: | ||
126 | 99 | """Parse and validate the CLI arguments""" | ||
127 | 100 | args = parser.parse_args() | ||
128 | 101 | if any( | ||
129 | 102 | [ | ||
130 | 103 | args.unpublished_suite is None, | ||
131 | 104 | args.unpublished_tags is None, | ||
132 | 105 | args.unpublished_archs is None, | ||
133 | 106 | ] | ||
134 | 107 | ) and not all( | ||
135 | 108 | [ | ||
136 | 109 | args.unpublished_suite is None, | ||
137 | 110 | args.unpublished_tags is None, | ||
138 | 111 | args.unpublished_archs is None, | ||
139 | 112 | ] | ||
140 | 113 | ): | ||
141 | 114 | parser.error( | ||
142 | 115 | """--unpublished-suite need | ||
143 | 116 | --unpublished-archs and --unpublished_tags""" | ||
144 | 117 | ) | ||
145 | 118 | |||
146 | 119 | return args | ||
154 | 120 | 29 | ||
155 | 30 | @staticmethod | ||
156 | 31 | def cli_args() -> argparse.ArgumentParser: | ||
157 | 32 | """Argument parser""" | ||
158 | 33 | parser = argparse.ArgumentParser( | ||
159 | 34 | description="Generate documentation about Ubuntu for ECR and DockerHub" | ||
160 | 35 | ) | ||
161 | 121 | 36 | ||
170 | 122 | def build_image_endpoint(provider: str, repo_base: str = None) -> (str, str): | 37 | parser.add_argument( |
171 | 123 | """Define the image's registry URL""" | 38 | "--provider", |
172 | 124 | if provider == "aws": | 39 | default="docker", |
173 | 125 | registry_url = "docker://public.ecr.aws/" | 40 | dest="provider", |
174 | 126 | staging_repo = "rocksdev" | 41 | help="aws or docker", |
175 | 127 | else: | 42 | required=True, |
176 | 128 | registry_url = "docker://docker.io/" | 43 | ) |
177 | 129 | staging_repo = "rocksdev4staging" | 44 | parser.add_argument( |
178 | 45 | "--username", | ||
179 | 46 | default="admin", | ||
180 | 47 | dest="username", | ||
181 | 48 | help="either the Docker Hub username, or the AWS access key ID", | ||
182 | 49 | required=True, | ||
183 | 50 | ) | ||
184 | 51 | parser.add_argument( | ||
185 | 52 | "--password", | ||
186 | 53 | default="admin", | ||
187 | 54 | dest="password", | ||
188 | 55 | help="either the Docker Hub password/token, or the AWS secret access key", | ||
189 | 56 | required=True, | ||
190 | 57 | ) | ||
191 | 58 | parser.add_argument( | ||
192 | 59 | "--jwt-token-docker", | ||
193 | 60 | dest="jwt_token_docker", | ||
194 | 61 | default=None, | ||
195 | 62 | help="JWT token for Docker Hub authentication. \ | ||
196 | 63 | Only useful for the 'docker' provider.", | ||
197 | 64 | ) | ||
198 | 65 | parser.add_argument( | ||
199 | 66 | "--repository-basename", | ||
200 | 67 | dest="repository", | ||
201 | 68 | default=None, | ||
202 | 69 | help="repository basename of the ubuntu images. \ | ||
203 | 70 | Used to infer existing information.", | ||
204 | 71 | ) | ||
205 | 72 | parser.add_argument( | ||
206 | 73 | "--data-dir", | ||
207 | 74 | default="data", | ||
208 | 75 | dest="data_dir", | ||
209 | 76 | help="""The path of the folder | ||
210 | 77 | where the data file will be | ||
211 | 78 | saved ( if not exist, the script | ||
212 | 79 | will create the folder)""", | ||
213 | 80 | ) | ||
214 | 81 | parser.add_argument( | ||
215 | 82 | "--unpublished-suite", | ||
216 | 83 | dest="unpublished_suite", | ||
217 | 84 | help="""an Ubuntu Suite (e.g. jammy). | ||
218 | 85 | if given we will take the | ||
219 | 86 | tags pass on command lines (required) | ||
220 | 87 | and the arches for this section | ||
221 | 88 | of the yaml file. | ||
222 | 89 | """, | ||
223 | 90 | ) | ||
224 | 91 | parser.add_argument( | ||
225 | 92 | "--unpublished-tags", | ||
226 | 93 | dest="unpublished_tags", | ||
227 | 94 | help="""list of tags | ||
228 | 95 | (e.g. 'kinetic 22.10 22.10_edge kinetic)""", | ||
229 | 96 | ) | ||
230 | 97 | parser.add_argument( | ||
231 | 98 | "--unpublished-archs", | ||
232 | 99 | dest="unpublished_archs", | ||
233 | 100 | help="list of archs (e.g amd64 arm)", | ||
234 | 101 | ) | ||
235 | 130 | 102 | ||
243 | 131 | if repo_base is None: | 103 | return parser |
244 | 132 | logging.warning("Using staging repository") | 104 | |
245 | 133 | url = f"{registry_url}{staging_repo}/ubuntu" | 105 | def validate_args(self) -> None: |
246 | 134 | namespace = staging_repo | 106 | """Parse and validate the CLI arguments""" |
247 | 135 | else: | 107 | parser = self.cli_args() |
248 | 136 | url = f"{registry_url}{repo_base}/ubuntu" | 108 | parser.parse_args(namespace=self) |
249 | 137 | namespace = repo_base | 109 | if any( |
250 | 110 | [ | ||
251 | 111 | self.unpublished_suite is None, | ||
252 | 112 | self.unpublished_tags is None, | ||
253 | 113 | self.unpublished_archs is None, | ||
254 | 114 | ] | ||
255 | 115 | ) and not all( | ||
256 | 116 | [ | ||
257 | 117 | self.unpublished_suite is None, | ||
258 | 118 | self.unpublished_tags is None, | ||
259 | 119 | self.unpublished_archs is None, | ||
260 | 120 | ] | ||
261 | 121 | ): | ||
262 | 122 | parser.error( | ||
263 | 123 | """--unpublished-suite need | ||
264 | 124 | --unpublished-archs and --unpublished_tags""" | ||
265 | 125 | ) | ||
266 | 138 | 126 | ||
268 | 139 | logging.info(f"Using {url} to collect information") | 127 | def build_image_endpoint(self) -> None: |
269 | 128 | """Define the image's registry URL""" | ||
270 | 129 | if self.provider == "aws": | ||
271 | 130 | registry_url = "public.ecr.aws/" | ||
272 | 131 | staging_repo = "rocksdev" | ||
273 | 132 | else: | ||
274 | 133 | registry_url = "docker.io/" | ||
275 | 134 | staging_repo = "rocksdev4staging" | ||
276 | 140 | 135 | ||
278 | 141 | return url, namespace | 136 | if self.repository is None: |
279 | 137 | logging.warning("Using staging repository") | ||
280 | 138 | self.url = f"{registry_url}{staging_repo}/ubuntu" | ||
281 | 139 | self.namespace = staging_repo | ||
282 | 140 | else: | ||
283 | 141 | self.url = f"{registry_url}{self.repository}/ubuntu" | ||
284 | 142 | self.namespace = self.repository | ||
285 | 143 | |||
286 | 144 | logging.info(f"Using {self.url} to collect information") | ||
287 | 145 | |||
288 | 146 | @staticmethod | ||
289 | 147 | def add_yaml_representer() -> None: | ||
290 | 148 | def str_presenter(dumper, data): | ||
291 | 149 | """ | ||
292 | 150 | Permit to format | ||
293 | 151 | multiline string into | ||
294 | 152 | yaml file | ||
295 | 153 | """ | ||
296 | 154 | |||
297 | 155 | c = "tag:yaml.org,2002:str" | ||
298 | 156 | if len(data.splitlines()) > 1: # check for multiline string | ||
299 | 157 | return dumper.represent_scalar(c, data, style="|") | ||
300 | 158 | return dumper.represent_scalar(c, data) | ||
301 | 159 | |||
302 | 160 | yaml.add_representer(str, str_presenter) | ||
303 | 161 | yaml.representer.SafeRepresenter.add_representer(str, str_presenter) | ||
304 | 162 | |||
305 | 163 | @staticmethod | ||
306 | 164 | def process_run(command: List[str], **kwargs) -> str: | ||
307 | 165 | """Run a command and handle its output.""" | ||
308 | 166 | logging.info(f"Execute process: {command!r}, kwargs={kwargs!r}") | ||
309 | 167 | try: | ||
310 | 168 | out = subprocess.run( | ||
311 | 169 | command, | ||
312 | 170 | **kwargs, | ||
313 | 171 | capture_output=True, | ||
314 | 172 | check=True, | ||
315 | 173 | universal_newlines=True, | ||
316 | 174 | ) | ||
317 | 175 | except subprocess.CalledProcessError as err: | ||
318 | 176 | msg = f"Failed to run command: {err!s}" | ||
319 | 177 | if err.stderr: | ||
320 | 178 | msg += f" ({err.stderr.strip()!s})" | ||
321 | 179 | raise Exception(msg) from err | ||
322 | 180 | |||
323 | 181 | return out.stdout.strip() | ||
324 | 182 | |||
325 | 183 | def run_skopeo_command(self, cmd: str, args: List[str]) -> Dict: | ||
326 | 184 | """Builds the Skopeo command and runs it""" | ||
327 | 185 | command = ["skopeo", cmd] | ||
328 | 186 | |||
329 | 187 | with tempfile.TemporaryDirectory() as tmp_dir: | ||
330 | 188 | if self._skopeo_auth_token: | ||
331 | 189 | auth_config = { | ||
332 | 190 | "auths": {self.url: {"auth": self._skopeo_auth_token}} | ||
333 | 191 | } | ||
334 | 192 | auth_file = os.path.join(tmp_dir, "auth.json") | ||
335 | 193 | with open(auth_file, "w") as f: | ||
336 | 194 | os.fchmod(f.fileno(), 0o600) | ||
337 | 195 | json.dump(auth_config, f) | ||
338 | 196 | command += ["--authfile", auth_file] | ||
339 | 197 | command += args | ||
340 | 198 | |||
341 | 199 | return json.loads(self.process_run(command)) | ||
342 | 200 | |||
343 | 201 | def get_arches(self, release: str) -> List[str]: | ||
344 | 202 | """ | ||
345 | 203 | Permit to get the arches associated to the release | ||
346 | 204 | """ | ||
347 | 205 | logging.info(f"Getting the arches for {release}") | ||
348 | 206 | manifest = self.run_skopeo_command( | ||
349 | 207 | "inspect", [f"docker://{self.url}:{release}", "--raw"] | ||
350 | 208 | )["manifests"] | ||
351 | 209 | arches = [] | ||
352 | 210 | for arch in manifest: | ||
353 | 211 | arches.append(arch["platform"]["architecture"]) | ||
354 | 212 | return arches | ||
355 | 213 | |||
356 | 214 | def get_dockerhub_jwt_token(self) -> str: | ||
357 | 215 | """ | ||
358 | 216 | Permit to get the token associated to the docker account | ||
359 | 217 | """ | ||
360 | 218 | logging.info("Getting the token form Docker") | ||
361 | 142 | 219 | ||
362 | 220 | url_token = "https://hub.docker.com/v2/users/login" | ||
363 | 221 | data = {"username": self.username, "password": self.password} | ||
364 | 222 | get_jwt_token = requests.post(url_token, json=data) | ||
365 | 223 | get_jwt_token.raise_for_status() | ||
366 | 224 | return get_jwt_token.json()["token"] | ||
367 | 143 | 225 | ||
370 | 144 | def add_yaml_representer(): | 226 | def get_tags_docker(self, release: str, token: str) -> List[str]: |
369 | 145 | def str_presenter(dumper, data): | ||
371 | 146 | """ | 227 | """ |
375 | 147 | Permit to format | 228 | Permit to get the tags associated to the release |
373 | 148 | multiline string into | ||
374 | 149 | yaml file | ||
376 | 150 | """ | 229 | """ |
397 | 151 | 230 | logging.info(f"Getting the tags from Docker for {release}") | |
398 | 152 | c = "tag:yaml.org,2002:str" | 231 | tags = [] |
399 | 153 | if len(data.splitlines()) > 1: # check for multiline string | 232 | result_json = self.run_skopeo_command( |
400 | 154 | return dumper.represent_scalar(c, data, style="|") | 233 | "inspect", [f"docker://{self.url}:{release}", "--raw"] |
381 | 155 | return dumper.represent_scalar(c, data) | ||
382 | 156 | |||
383 | 157 | yaml.add_representer(str, str_presenter) | ||
384 | 158 | yaml.representer.SafeRepresenter.add_representer(str, str_presenter) | ||
385 | 159 | |||
386 | 160 | |||
387 | 161 | def _process_run(command: List[str], **kwargs) -> str: | ||
388 | 162 | """Run a command and handle its output.""" | ||
389 | 163 | logging.info(f"Execute process: {command!r}, kwargs={kwargs!r}") | ||
390 | 164 | try: | ||
391 | 165 | out = subprocess.run( | ||
392 | 166 | command, | ||
393 | 167 | **kwargs, | ||
394 | 168 | capture_output=True, | ||
395 | 169 | check=True, | ||
396 | 170 | universal_newlines=True, | ||
401 | 171 | ) | 234 | ) |
490 | 172 | except subprocess.CalledProcessError as err: | 235 | digest = result_json["manifests"][0]["digest"] |
403 | 173 | msg = f"Failed to run command: {err!s}" | ||
404 | 174 | if err.stderr: | ||
405 | 175 | msg += f" ({err.stderr.strip()!s})" | ||
406 | 176 | raise Exception(msg) from err | ||
407 | 177 | |||
408 | 178 | return out.stdout.strip() | ||
409 | 179 | |||
410 | 180 | |||
411 | 181 | def get_arches(release: str, image_url: str) -> List[str]: | ||
412 | 182 | """ | ||
413 | 183 | Permit to get the arches associated to the release | ||
414 | 184 | """ | ||
415 | 185 | logging.info(f"Getting the arches for {release}") | ||
416 | 186 | command = ["skopeo", "inspect", f"{image_url}:{release}", "--raw"] | ||
417 | 187 | manifest = json.loads(_process_run(command))["manifests"] | ||
418 | 188 | arches = [] | ||
419 | 189 | for arch in manifest: | ||
420 | 190 | arches.append(arch["platform"]["architecture"]) | ||
421 | 191 | return arches | ||
422 | 192 | |||
423 | 193 | |||
424 | 194 | def get_dockerhub_token(username: str, password: str) -> str: | ||
425 | 195 | """ | ||
426 | 196 | Permit to get the token associated to the docker account | ||
427 | 197 | """ | ||
428 | 198 | logging.info("Getting the token form Docker") | ||
429 | 199 | |||
430 | 200 | url_token = "https://hub.docker.com/v2/users/login" | ||
431 | 201 | data = {"username": username, "password": password} | ||
432 | 202 | get_token = requests.post(url_token, json=data) | ||
433 | 203 | get_token.raise_for_status() | ||
434 | 204 | return get_token.json()["token"] | ||
435 | 205 | |||
436 | 206 | |||
437 | 207 | def get_tags_docker( | ||
438 | 208 | release: str, token: str, image_url: str, image_namespace: str | ||
439 | 209 | ) -> List[str]: | ||
440 | 210 | """ | ||
441 | 211 | Permit to get the tags associated to the release | ||
442 | 212 | """ | ||
443 | 213 | logging.info(f"Getting the tags from Docker for {release}") | ||
444 | 214 | tags = [] | ||
445 | 215 | command = [ | ||
446 | 216 | "skopeo", | ||
447 | 217 | "inspect", | ||
448 | 218 | f"{image_url}:{release}", | ||
449 | 219 | "--raw", | ||
450 | 220 | ] | ||
451 | 221 | result_json = _process_run(command) | ||
452 | 222 | digest = json.loads(result_json)["manifests"][0]["digest"] | ||
453 | 223 | |||
454 | 224 | url_dockerhub = "https://hub.docker.com/v2/repositories/" | ||
455 | 225 | url_dockerhub += f"{image_namespace}/ubuntu/tags/?page_size=999" | ||
456 | 226 | Headers = {"Authorization": f"JWT {token}"} | ||
457 | 227 | get_the_tags = requests.get(url_dockerhub, headers=Headers) | ||
458 | 228 | get_the_tags = get_the_tags.json()["results"] | ||
459 | 229 | for image in get_the_tags: | ||
460 | 230 | for info_image in image["images"]: | ||
461 | 231 | if info_image["digest"] == digest and image["name"] not in tags: | ||
462 | 232 | tags.append(image["name"]) | ||
463 | 233 | |||
464 | 234 | return tags | ||
465 | 235 | |||
466 | 236 | |||
467 | 237 | def get_tags_aws(release: str, client: boto3.Session, image_url: str) -> List[str]: | ||
468 | 238 | """ | ||
469 | 239 | Permit to get the tags associated to the release | ||
470 | 240 | """ | ||
471 | 241 | logging.info(f"Getting the tags from AWS for {release}") | ||
472 | 242 | |||
473 | 243 | tags = [] | ||
474 | 244 | command = [ | ||
475 | 245 | "skopeo", | ||
476 | 246 | "inspect", | ||
477 | 247 | f"{image_url}:{release}", | ||
478 | 248 | ] | ||
479 | 249 | result_json = _process_run(command) | ||
480 | 250 | digest = json.loads(result_json)["Digest"] | ||
481 | 251 | response = client.describe_image_tags(repositoryName="ubuntu") | ||
482 | 252 | |||
483 | 253 | for image in response["imageTagDetails"]: | ||
484 | 254 | if ( | ||
485 | 255 | image["imageDetail"]["imageDigest"] == digest | ||
486 | 256 | and image["imageTag"] not in tags | ||
487 | 257 | ): | ||
488 | 258 | tags.append(image["imageTag"]) | ||
489 | 259 | return tags | ||
491 | 260 | 236 | ||
492 | 237 | url_dockerhub = "https://hub.docker.com/v2/repositories/" | ||
493 | 238 | url_dockerhub += f"{self.namespace}/ubuntu/tags/?page_size=999" | ||
494 | 239 | Headers = {"Authorization": f"JWT {token}"} | ||
495 | 240 | get_the_tags = requests.get(url_dockerhub, headers=Headers) | ||
496 | 241 | get_the_tags = get_the_tags.json()["results"] | ||
497 | 242 | for image in get_the_tags: | ||
498 | 243 | for info_image in image["images"]: | ||
499 | 244 | if info_image["digest"] == digest and image["name"] not in tags: | ||
500 | 245 | tags.append(image["name"]) | ||
501 | 261 | 246 | ||
507 | 262 | def get_fullname(release: str) -> str: | 247 | return tags |
503 | 263 | """ | ||
504 | 264 | Permit to get the full name associated to the release | ||
505 | 265 | """ | ||
506 | 266 | logging.info(f"Getting full name of {release} ") | ||
508 | 267 | 248 | ||
512 | 268 | command = ["ubuntu-distro-info", f"--series={release}", "-f"] | 249 | def get_tags_aws(self, release: str, client: boto3.Session) -> List[str]: |
513 | 269 | result_json = _process_run(command) | 250 | """ |
514 | 270 | return result_json.replace("Ubuntu", "").strip() | 251 | Permit to get the tags associated to the release |
515 | 252 | """ | ||
516 | 253 | logging.info(f"Getting the tags from AWS for {release}") | ||
517 | 271 | 254 | ||
518 | 255 | tags = [] | ||
519 | 256 | result_json = self.run_skopeo_command( | ||
520 | 257 | "inspect", [f"docker://{self.url}:{release}"] | ||
521 | 258 | ) | ||
522 | 259 | digest = result_json["Digest"] | ||
523 | 260 | response = client.describe_image_tags(repositoryName="ubuntu") | ||
524 | 261 | |||
525 | 262 | for image in response["imageTagDetails"]: | ||
526 | 263 | if ( | ||
527 | 264 | image["imageDetail"]["imageDigest"] == digest | ||
528 | 265 | and image["imageTag"] not in tags | ||
529 | 266 | ): | ||
530 | 267 | tags.append(image["imageTag"]) | ||
531 | 268 | return tags | ||
532 | 269 | |||
533 | 270 | def get_fullname(self, release: str) -> str: | ||
534 | 271 | """ | ||
535 | 272 | Permit to get the full name associated to the release | ||
536 | 273 | """ | ||
537 | 274 | logging.info(f"Getting full name of {release} ") | ||
538 | 272 | 275 | ||
542 | 273 | def get_support(series: str, is_lts: bool) -> Dict[str, Dict[str, str]]: | 276 | command = ["ubuntu-distro-info", f"--series={release}", "-f"] |
543 | 274 | """Calculates the end of support dates for a given Ubuntu series""" | 277 | result_json = self.process_run(command) |
544 | 275 | logging.info(f"Getting support information for the {series}") | 278 | return result_json.replace("Ubuntu", "").strip() |
545 | 276 | 279 | ||
548 | 277 | base_cmd = ["ubuntu-distro-info", "--series", series] | 280 | def get_support(self, series: str, is_lts: bool) -> Dict[str, Dict[str, str]]: |
549 | 278 | eol_cmd = base_cmd + ["--day=eol"] | 281 | """Calculates the end of support dates for a given Ubuntu series""" |
550 | 282 | logging.info(f"Getting support information for the {series}") | ||
551 | 279 | 283 | ||
554 | 280 | eol = int(_process_run(eol_cmd)) | 284 | base_cmd = ["ubuntu-distro-info", "--series", series] |
555 | 281 | eol_date = NOW + datetime.timedelta(days=eol) | 285 | eol_cmd = base_cmd + ["--day=eol"] |
556 | 282 | 286 | ||
558 | 283 | support = {"support": {"until": f"{eol_date.month:02d}/{eol_date.year}"}} | 287 | eol = int(self.process_run(eol_cmd)) |
559 | 288 | eol_date = self.now + datetime.timedelta(days=eol) | ||
560 | 284 | 289 | ||
563 | 285 | if not is_lts: | 290 | support = {"support": {"until": f"{eol_date.month:02d}/{eol_date.year}"}} |
562 | 286 | return support | ||
564 | 287 | 291 | ||
567 | 288 | # The it is LTS, and lts_until=until | 292 | if not is_lts: |
568 | 289 | support["support"]["lts_until"] = support["support"]["until"] | 293 | return support |
569 | 290 | 294 | ||
571 | 291 | eol_esm_cmd = base_cmd + ["--day=eol-esm"] | 295 | # The it is LTS, and lts_until=until |
572 | 296 | support["support"]["lts_until"] = support["support"]["until"] | ||
573 | 292 | 297 | ||
578 | 293 | eol_esm = int(_process_run(eol_esm_cmd)) | 298 | eol_esm_cmd = base_cmd + ["--day=eol-esm"] |
575 | 294 | eol_esm_date = NOW + datetime.timedelta(days=eol_esm) | ||
576 | 295 | eol_esm_value = f"{eol_esm_date.month:02d}/{eol_esm_date.year}" | ||
577 | 296 | support["support"]["esm_until"] = eol_esm_value | ||
579 | 297 | 299 | ||
581 | 298 | return support | 300 | eol_esm = int(self.process_run(eol_esm_cmd)) |
582 | 301 | eol_esm_date = self.now + datetime.timedelta(days=eol_esm) | ||
583 | 302 | eol_esm_value = f"{eol_esm_date.month:02d}/{eol_esm_date.year}" | ||
584 | 303 | support["support"]["esm_until"] = eol_esm_value | ||
585 | 299 | 304 | ||
586 | 305 | return support | ||
587 | 300 | 306 | ||
594 | 301 | def get_deprecated(series: str) -> Dict[str, Dict[str, object]]: | 307 | def get_deprecated(self, series: str) -> Dict[str, Dict[str, object]]: |
595 | 302 | """ | 308 | """ |
596 | 303 | Calculated the deprecation date | 309 | Calculated the deprecation date |
597 | 304 | and upgrade path for a deprecated release | 310 | and upgrade path for a deprecated release |
598 | 305 | """ | 311 | """ |
599 | 306 | logging.info(f"Getting support information for the {series}") | 312 | logging.info(f"Getting support information for the {series}") |
600 | 307 | 313 | ||
602 | 308 | eol_cmd = ["ubuntu-distro-info", "--series", series, "--day=eol"] | 314 | eol_cmd = ["ubuntu-distro-info", "--series", series, "--day=eol"] |
603 | 309 | 315 | ||
607 | 310 | eol = int(_process_run(eol_cmd)) | 316 | eol = int(self.process_run(eol_cmd)) |
608 | 311 | eol_date = NOW + datetime.timedelta(days=eol) | 317 | eol_date = self.now + datetime.timedelta(days=eol) |
609 | 312 | # For now, the upgrade path is always the next release | 318 | # For now, the upgrade path is always the next release |
610 | 313 | 319 | ||
615 | 314 | this_release_cmd = ["ubuntu-distro-info", "--series", series, "--day=release"] | 320 | this_release_cmd = ["ubuntu-distro-info", "--series", series, "--day=release"] |
616 | 315 | this_release = int(_process_run(this_release_cmd)) | 321 | this_release = int(self.process_run(this_release_cmd)) |
617 | 316 | # add 60 days to the release date, to get the next development version | 322 | # add 60 days to the release date, to get the next development version |
618 | 317 | next_date = NOW + datetime.timedelta(days=this_release + 60) | 323 | next_date = self.now + datetime.timedelta(days=this_release + 60) |
619 | 318 | 324 | ||
626 | 319 | following_dev_series_cmd = [ | 325 | following_dev_series_cmd = [ |
627 | 320 | "ubuntu-distro-info", | 326 | "ubuntu-distro-info", |
628 | 321 | "-d", | 327 | "-d", |
629 | 322 | f"--date={next_date.year}-{next_date.month}-{next_date.day}", | 328 | f"--date={next_date.year}-{next_date.month}-{next_date.day}", |
630 | 323 | ] | 329 | ] |
631 | 324 | development_suite_at_eol = _process_run(following_dev_series_cmd) | 330 | development_suite_at_eol = self.process_run(following_dev_series_cmd) |
632 | 325 | 331 | ||
640 | 326 | upgrade_path_cmd = [ | 332 | upgrade_path_cmd = [ |
641 | 327 | "ubuntu-distro-info", | 333 | "ubuntu-distro-info", |
642 | 328 | "--series", | 334 | "--series", |
643 | 329 | development_suite_at_eol, | 335 | development_suite_at_eol, |
644 | 330 | "-r", | 336 | "-r", |
645 | 331 | ] | 337 | ] |
646 | 332 | upgrade_path = _process_run(upgrade_path_cmd).strip(" LTS") | 338 | upgrade_path = self.process_run(upgrade_path_cmd).strip(" LTS") |
647 | 333 | 339 | ||
652 | 334 | return { | 340 | return { |
653 | 335 | "deprecated": { | 341 | "deprecated": { |
654 | 336 | "date": f"{eol_date.month:02d}/{eol_date.year}", | 342 | "date": f"{eol_date.month:02d}/{eol_date.year}", |
655 | 337 | "path": {"track": upgrade_path}, | 343 | "path": {"track": upgrade_path}, |
656 | 344 | } | ||
657 | 338 | } | 345 | } |
658 | 339 | } | ||
659 | 340 | |||
660 | 341 | |||
661 | 342 | def is_deprecated(series: str) -> bool: | ||
662 | 343 | |||
663 | 344 | """Checks whether a series is completely deprecated (both LTS and ESM)""" | ||
664 | 345 | logging.info(f"Checking is {series} is deprecated") | ||
665 | 346 | supported_cmd = "ubuntu-distro-info --supported" | ||
666 | 347 | supported_esm_cmd = supported_cmd + "-esm" | ||
667 | 348 | all_supported = _process_run(supported_cmd.split(" ")) + _process_run( | ||
668 | 349 | supported_esm_cmd.split(" ") | ||
669 | 350 | ) | ||
670 | 351 | return series not in all_supported | ||
671 | 352 | |||
672 | 353 | 346 | ||
681 | 354 | def is_lts(series: str) -> bool: | 347 | def is_deprecated(self, series: str) -> bool: |
682 | 355 | 348 | """Checks whether a series is completely deprecated (both LTS and ESM)""" | |
683 | 356 | """Checks if a given series is LTS""" | 349 | logging.info(f"Checking is {series} is deprecated") |
684 | 357 | logging.info(f"Checking is {series} is lts") | 350 | supported_cmd = "ubuntu-distro-info --supported" |
685 | 358 | 351 | supported_esm_cmd = supported_cmd + "-esm" | |
686 | 359 | cmd = ["ubuntu-distro-info", "--series", series, "-f"] | 352 | all_supported = self.process_run(supported_cmd.split(" ")) + self.process_run( |
687 | 360 | 353 | supported_esm_cmd.split(" ") | |
688 | 361 | return "LTS" in _process_run(cmd) | 354 | ) |
689 | 355 | return series not in all_supported | ||
690 | 362 | 356 | ||
691 | 357 | def is_lts(self, series: str) -> bool: | ||
692 | 358 | """Checks if a given series is LTS""" | ||
693 | 359 | logging.info(f"Checking is {series} is lts") | ||
694 | 363 | 360 | ||
700 | 364 | def get_lowest_risk(tags: List[str]) -> str: | 361 | cmd = ["ubuntu-distro-info", "--series", series, "-f"] |
696 | 365 | """ | ||
697 | 366 | Get the lowest risk associated with the release | ||
698 | 367 | """ | ||
699 | 368 | risk_sorted = ["stable", "candidate", "beta", "edge"] | ||
701 | 369 | 362 | ||
706 | 370 | all_tags_str = " ".join(tags) | 363 | return "LTS" in self.process_run(cmd) |
703 | 371 | for risk in risk_sorted: | ||
704 | 372 | if risk in all_tags_str: | ||
705 | 373 | return risk | ||
707 | 374 | 364 | ||
709 | 375 | return "edge" | 365 | @staticmethod |
710 | 366 | def get_lowest_risk(tags: List[str]) -> str: | ||
711 | 367 | """ | ||
712 | 368 | Get the lowest risk associated with the release | ||
713 | 369 | """ | ||
714 | 370 | risk_sorted = ["stable", "candidate", "beta", "edge"] | ||
715 | 376 | 371 | ||
716 | 372 | all_tags_str = " ".join(tags) | ||
717 | 373 | for risk in risk_sorted: | ||
718 | 374 | if risk in all_tags_str: | ||
719 | 375 | return risk | ||
720 | 377 | 376 | ||
723 | 378 | def get_release(series: str) -> str: | 377 | return "edge" |
722 | 379 | command = ["ubuntu-distro-info", f"--series={series}", "-r"] | ||
724 | 380 | 378 | ||
726 | 381 | return _process_run(command) | 379 | def get_release(self, series: str) -> str: |
727 | 380 | command = ["ubuntu-distro-info", f"--series={series}", "-r"] | ||
728 | 382 | 381 | ||
729 | 382 | return self.process_run(command) | ||
730 | 383 | 383 | ||
767 | 384 | def infer_registry_user( | 384 | def infer_registry_user(self) -> object: |
768 | 385 | provider: str, username: str, password: str, dh_token: str = None | 385 | user = None |
769 | 386 | ) -> object: | 386 | if self.provider == "aws": |
770 | 387 | user = None | 387 | logging.info("Connecting to AWS") |
771 | 388 | if provider == "aws": | 388 | session = boto3.Session( |
772 | 389 | logging.info("Connecting to AWS") | 389 | region_name="us-east-1", |
773 | 390 | session = boto3.Session( | 390 | aws_access_key_id=self.username, |
774 | 391 | region_name="us-east-1", | 391 | aws_secret_access_key=self.password, |
739 | 392 | aws_access_key_id=username, | ||
740 | 393 | aws_secret_access_key=password, | ||
741 | 394 | ) | ||
742 | 395 | user = session.client("ecr-public") | ||
743 | 396 | else: | ||
744 | 397 | logging.info("Fetching Docker Hub token") | ||
745 | 398 | if dh_token: | ||
746 | 399 | user = dh_token | ||
747 | 400 | else: | ||
748 | 401 | user = get_dockerhub_token(username, password) | ||
749 | 402 | |||
750 | 403 | return user | ||
751 | 404 | |||
752 | 405 | |||
753 | 406 | def build_releases_data( | ||
754 | 407 | list_of_series: List[str], | ||
755 | 408 | all_tags: List[str], | ||
756 | 409 | image_url: str, | ||
757 | 410 | image_ns: str, | ||
758 | 411 | arguments: argparse.ArgumentParser.parse_args, | ||
759 | 412 | registry_user: object, | ||
760 | 413 | ) -> Dict: | ||
761 | 414 | """Build the releases info data structure""" | ||
762 | 415 | releases = [] | ||
763 | 416 | for count, series in enumerate(list_of_series): | ||
764 | 417 | if series not in all_tags and series != arguments.unpublished_suite: | ||
765 | 418 | logging.warning( | ||
766 | 419 | f"Series {series} does not exist in {image_url}. Skipping it..." | ||
775 | 420 | ) | 392 | ) |
791 | 421 | continue | 393 | user = session.client("ecr-public") |
792 | 422 | 394 | self._skopeo_auth_token = user.get_authorization_token()[ | |
793 | 423 | release_data = {} | 395 | "authorizationData" |
794 | 424 | 396 | ]["authorizationToken"] | |
780 | 425 | release = get_release(series) | ||
781 | 426 | if "LTS" in release: | ||
782 | 427 | release_data["type"] = "LTS" | ||
783 | 428 | |||
784 | 429 | release_data["track"] = release.rstrip(" LTS") | ||
785 | 430 | |||
786 | 431 | if arguments.unpublished_suite and arguments.unpublished_suite == series: | ||
787 | 432 | release_data["architectures"] = arguments.unpublished_archs.split() | ||
788 | 433 | release_data["version"] = get_fullname(arguments.unpublished_suite) | ||
789 | 434 | release_data["risk"] = get_lowest_risk(arguments.unpublished_tags.split()) | ||
790 | 435 | release_data["tags"] = arguments.unpublished_tags.split() | ||
795 | 436 | else: | 397 | else: |
802 | 437 | release_data["architectures"] = get_arches(series, image_url) | 398 | logging.info("Fetching Docker Hub token") |
803 | 438 | release_data["version"] = get_fullname(series) | 399 | if self.jwt_token_docker: |
804 | 439 | if arguments.provider == "docker": | 400 | user = self.jwt_token_docker |
799 | 440 | release_data["tags"] = get_tags_docker( | ||
800 | 441 | series, registry_user, image_url, image_ns | ||
801 | 442 | ) | ||
805 | 443 | else: | 401 | else: |
844 | 444 | release_data["tags"] = get_tags_aws(series, registry_user, image_url) | 402 | user = self.get_dockerhub_jwt_token() |
845 | 445 | release_data["risk"] = get_lowest_risk(release_data["tags"]) | 403 | |
846 | 446 | 404 | self._skopeo_auth_token = base64.b64encode( | |
847 | 447 | if is_deprecated(series): | 405 | f"{self.username}:{self.password}".encode() |
848 | 448 | release_data["deprecated"] = get_deprecated(series) | 406 | ).decode() |
849 | 449 | else: | 407 | |
850 | 450 | release_data["support"] = get_support(series, is_lts(series)) | 408 | return user |
851 | 451 | 409 | ||
852 | 452 | releases.append(release_data) | 410 | def build_releases_data( |
853 | 453 | 411 | self, | |
854 | 454 | return releases | 412 | list_of_series: List[str], |
855 | 455 | 413 | all_tags: List[str], | |
856 | 456 | 414 | registry_user: object, | |
857 | 457 | def read_ubuntu_data_template() -> Dict: | 415 | ) -> List[Dict]: |
858 | 458 | """Reads and parses the YAML contents of the data template""" | 416 | """Build the releases info data structure""" |
859 | 459 | template_file = f"{SCRIPT_DIR}/templates/ubuntu.yaml" | 417 | releases = [] |
860 | 460 | logging.info(f"Opening the template file {template_file}") | 418 | for count, series in enumerate(list_of_series): |
861 | 461 | with open(template_file) as file: | 419 | if series not in all_tags and series != self.unpublished_suite: |
862 | 462 | try: | 420 | logging.warning( |
863 | 463 | return yaml.safe_load(file) | 421 | f"Series {series} does not exist in {self.url}. Skipping it..." |
864 | 464 | except yaml.YAMLError as exc: | 422 | ) |
865 | 465 | logging.error("Error when loading the ubuntu template file") | 423 | continue |
828 | 466 | raise exc | ||
829 | 467 | |||
830 | 468 | |||
831 | 469 | def create_data_dir(path: str): | ||
832 | 470 | """Create data dir if it doesn't exist""" | ||
833 | 471 | if not os.path.exists(path): | ||
834 | 472 | logging.info(f"Creating the {path} folder") | ||
835 | 473 | |||
836 | 474 | os.makedirs(path) | ||
837 | 475 | |||
838 | 476 | |||
839 | 477 | def write_ubuntu_data_file(file_path: str, content: Dict): | ||
840 | 478 | """Write the YAML content into the ubuntu file path""" | ||
841 | 479 | with open(file_path, "w") as file: | ||
842 | 480 | logging.info(f"Create the yaml file {file_path}") | ||
843 | 481 | yaml.dump(content, file) | ||
866 | 482 | 424 | ||
867 | 425 | release_data = {} | ||
868 | 483 | 426 | ||
877 | 484 | def main(): | 427 | release = self.get_release(series) |
878 | 485 | arguments = validate_args(cli_args()) | 428 | if "LTS" in release: |
879 | 486 | registry_user = infer_registry_user( | 429 | release_data["type"] = "LTS" |
872 | 487 | arguments.provider, | ||
873 | 488 | arguments.username, | ||
874 | 489 | arguments.password, | ||
875 | 490 | arguments.dockertoken, | ||
876 | 491 | ) | ||
880 | 492 | 430 | ||
883 | 493 | add_yaml_representer() | 431 | release_data["track"] = release.rstrip(" LTS") |
882 | 494 | url, ns = build_image_endpoint(arguments.provider, repo_base=arguments.repository) | ||
884 | 495 | 432 | ||
888 | 496 | logging.info(f"Getting all tags from {url}") | 433 | if self.unpublished_suite and self.unpublished_suite == series: |
889 | 497 | command_tags = ["skopeo", "list-tags", url] | 434 | release_data["architectures"] = self.unpublished_archs.split() |
890 | 498 | existing_tags = json.loads(_process_run(command_tags))["Tags"] | 435 | release_data["version"] = self.get_fullname(self.unpublished_suite) |
891 | 436 | release_data["risk"] = self.get_lowest_risk( | ||
892 | 437 | self.unpublished_tags.split() | ||
893 | 438 | ) | ||
894 | 439 | release_data["tags"] = self.unpublished_tags.split() | ||
895 | 440 | else: | ||
896 | 441 | release_data["architectures"] = self.get_arches(series) | ||
897 | 442 | release_data["version"] = self.get_fullname(series) | ||
898 | 443 | if self.provider == "docker": | ||
899 | 444 | release_data["tags"] = self.get_tags_docker(series, registry_user) | ||
900 | 445 | else: | ||
901 | 446 | release_data["tags"] = self.get_tags_aws(series, registry_user) | ||
902 | 447 | release_data["risk"] = self.get_lowest_risk(release_data["tags"]) | ||
903 | 448 | |||
904 | 449 | if self.is_deprecated(series): | ||
905 | 450 | release_data["deprecated"] = self.get_deprecated(series) | ||
906 | 451 | else: | ||
907 | 452 | release_data["support"] = self.get_support(series, self.is_lts(series)) | ||
908 | 453 | |||
909 | 454 | releases.append(release_data) | ||
910 | 455 | |||
911 | 456 | return releases | ||
912 | 457 | |||
913 | 458 | @staticmethod | ||
914 | 459 | def read_ubuntu_data_template() -> Dict: | ||
915 | 460 | """Reads and parses the YAML contents of the data template""" | ||
916 | 461 | template_file = f"{SCRIPT_DIR}/templates/ubuntu.yaml" | ||
917 | 462 | logging.info(f"Opening the template file {template_file}") | ||
918 | 463 | with open(template_file) as file: | ||
919 | 464 | try: | ||
920 | 465 | return yaml.safe_load(file) | ||
921 | 466 | except yaml.YAMLError as exc: | ||
922 | 467 | logging.error("Error when loading the ubuntu template file") | ||
923 | 468 | raise exc | ||
924 | 469 | |||
925 | 470 | @staticmethod | ||
926 | 471 | def create_data_dir(path: str) -> None: | ||
927 | 472 | """Create data dir if it doesn't exist""" | ||
928 | 473 | if not os.path.exists(path): | ||
929 | 474 | logging.info(f"Creating the {path} folder") | ||
930 | 475 | |||
931 | 476 | os.makedirs(path) | ||
932 | 477 | |||
933 | 478 | @staticmethod | ||
934 | 479 | def write_ubuntu_data_file(file_path: str, content: Dict) -> None: | ||
935 | 480 | """Write the YAML content into the ubuntu file path""" | ||
936 | 481 | with open(file_path, "w") as file: | ||
937 | 482 | logging.info(f"Create the yaml file {file_path}") | ||
938 | 483 | yaml.dump(content, file) | ||
939 | 484 | |||
940 | 485 | def main(self) -> None: | ||
941 | 486 | registry_user = self.infer_registry_user() | ||
942 | 487 | |||
943 | 488 | logging.info(f"Getting all tags from {self.url}") | ||
944 | 489 | |||
945 | 490 | existing_tags = self.run_skopeo_command("list-tags", [f"docker://{self.url}"])[ | ||
946 | 491 | "Tags" | ||
947 | 492 | ] | ||
948 | 499 | 493 | ||
952 | 500 | logging.info("Getting all the series from ubuntu-distro-info") | 494 | logging.info("Getting all the series from ubuntu-distro-info") |
953 | 501 | command_suites = ["ubuntu-distro-info", "--all"] | 495 | command_suites = ["ubuntu-distro-info", "--all"] |
954 | 502 | series_names = _process_run(command_suites).split("\n") | 496 | series_names = self.process_run(command_suites).split("\n") |
955 | 503 | 497 | ||
961 | 504 | if arguments.unpublished_suite and arguments.unpublished_suite not in series_names: | 498 | if self.unpublished_suite and self.unpublished_suite not in series_names: |
962 | 505 | logging.error( | 499 | logging.error( |
963 | 506 | f"The provided unpublished suite {arguments.unpublished_suite}" | 500 | f"The provided unpublished suite {self.unpublished_suite}" |
964 | 507 | "is not recognized. Ignoring it" | 501 | "is not recognized. Ignoring it" |
965 | 508 | ) | 502 | ) |
966 | 509 | 503 | ||
971 | 510 | logging.info("Building releases info") | 504 | logging.info("Building releases info") |
972 | 511 | releases = build_releases_data( | 505 | releases = self.build_releases_data(series_names, existing_tags, registry_user) |
969 | 512 | series_names, existing_tags, url, ns, arguments, registry_user | ||
970 | 513 | ) | ||
973 | 514 | 506 | ||
976 | 515 | dict_file = read_ubuntu_data_template() | 507 | dict_file = self.read_ubuntu_data_template() |
977 | 516 | dict_file["releases"] = releases | 508 | dict_file["releases"] = releases |
978 | 517 | 509 | ||
980 | 518 | create_data_dir(arguments.data_dir) | 510 | self.create_data_dir(self.data_dir) |
981 | 519 | 511 | ||
984 | 520 | ubuntu_data_file = f"{arguments.data_dir}/ubuntu.yaml" | 512 | ubuntu_data_file = f"{self.data_dir}/ubuntu.yaml" |
985 | 521 | write_ubuntu_data_file(ubuntu_data_file, dict_file) | 513 | self.write_ubuntu_data_file(ubuntu_data_file, dict_file) |
986 | 522 | 514 | ||
987 | 523 | 515 | ||
988 | 524 | if __name__ == "__main__": | 516 | if __name__ == "__main__": |
990 | 525 | main() | 517 | runner = GenerateUbuntuYaml() |
991 | 518 | runner.main() |
You need to change some signatures of some function