Skip to content

Commit 283aaf8

Browse files
committed
Add support to read and write metadata for a GL canonical name
Signed-off-by: Tobias Wolf <[email protected]>
1 parent 58af704 commit 283aaf8

File tree

2 files changed

+144
-4
lines changed

2 files changed

+144
-4
lines changed

src/gardenlinux/features/cname.py

Lines changed: 143 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
Canonical name (cname)
55
"""
66

7+
from configparser import ConfigParser, UNNAMED_SECTION
8+
from pathlib import Path
9+
from typing import List, Optional
10+
from os import PathLike
711
import re
8-
from typing import Optional
912

1013
from ..constants import ARCHS
1114
from .parser import Parser
@@ -37,9 +40,11 @@ def __init__(self, cname, arch=None, commit_id=None, version=None):
3740
"""
3841

3942
self._arch = None
40-
self._flavor = None
4143
self._commit_id = None
44+
self._feature_set_cached = None
45+
self._flavor = None
4246
self._version = None
47+
self._platforms_cached = None
4348

4449
re_match = re.match(
4550
"([a-zA-Z0-9]+([\\_\\-][a-zA-Z0-9]+)*?)(-([a-z0-9]+)(-([a-z0-9.]+)-([a-z0-9]+))*)?$",
@@ -81,6 +86,7 @@ def arch(self) -> Optional[str]:
8186
Returns the architecture for the cname parsed.
8287
8388
:return: (str) CName architecture
89+
:since: 0.7.0
8490
"""
8591

8692
return self._arch
@@ -91,6 +97,7 @@ def cname(self) -> str:
9197
Returns the cname parsed.
9298
9399
:return: (str) CName
100+
:since: 0.7.0
94101
"""
95102
assert self._flavor is not None, "CName flavor is not set!"
96103
cname = self._flavor
@@ -109,6 +116,7 @@ def commit_id(self) -> Optional[str]:
109116
Returns the commit ID if part of the cname parsed.
110117
111118
:return: (str) Commit ID
119+
:since: 0.7.0
112120
"""
113121

114122
return self._commit_id
@@ -119,6 +127,7 @@ def flavor(self) -> str | None:
119127
Returns the flavor for the cname parsed.
120128
121129
:return: (str) Flavor
130+
:since: 0.7.0
122131
"""
123132

124133
return self._flavor
@@ -129,8 +138,12 @@ def feature_set(self) -> str:
129138
Returns the feature set for the cname parsed.
130139
131140
:return: (str) Feature set of the cname
141+
:since: 0.7.0
132142
"""
133143

144+
if self._feature_set_cached is not None:
145+
return self._feature_set_cached
146+
134147
return Parser().filter_as_string(self.flavor)
135148

136149
@property
@@ -139,17 +152,36 @@ def platform(self) -> str:
139152
Returns the platform for the cname parsed.
140153
141154
:return: (str) Flavor
155+
:since: 0.7.0
142156
"""
143157
assert self._flavor is not None, "Flavor not set!"
144158

145-
return re.split("[_-]", self._flavor, maxsplit=1)[0]
159+
if self._platforms_cached is not None:
160+
return ",".join(self._platforms_cached)
161+
162+
return ",".join(Parser().filter_as_dict(self.flavor)["platform"])
163+
164+
@property
165+
def platforms(self) -> List[str]:
166+
"""
167+
Returns the platforms for the cname parsed.
168+
169+
:return: (str) Flavor
170+
:since: 0.9.2
171+
"""
172+
173+
if self._platforms_cached is not None:
174+
return self._platforms_cached
175+
176+
return Parser().filter_as_dict(self.flavor)["platform"]
146177

147178
@property
148179
def version(self) -> Optional[str]:
149180
"""
150181
Returns the version if part of the cname parsed.
151182
152183
:return: (str) Version
184+
:since: 0.7.0
153185
"""
154186

155187
return self._version
@@ -160,9 +192,117 @@ def version_and_commit_id(self) -> Optional[str]:
160192
Returns the version and commit ID if part of the cname parsed.
161193
162194
:return: (str) Version and commit ID
195+
:since: 0.7.0
163196
"""
164197

165198
if self._commit_id is None:
166199
return None
167200

168201
return f"{self._version}-{self._commit_id}"
202+
203+
def load_from_metadata_file(self, metadata_file: PathLike | str) -> None:
204+
"""
205+
Loads and parses a metadata file.
206+
207+
:param metadata_file: Metadata file containing information about the CName instance.
208+
209+
:since: 0.9.2
210+
"""
211+
212+
if not isinstance(metadata_file, PathLike):
213+
metadata_file = Path(metadata_file)
214+
215+
if not metadata_file.exists():
216+
raise RuntimeError(f"Metadata file given is invalid: {metadata_file}")
217+
218+
metadata_config = ConfigParser(allow_unnamed_section=True)
219+
metadata_config.read(metadata_file)
220+
221+
for metadata_field in (
222+
"GARDENLINUX_CNAME",
223+
"GARDENLINUX_FEATURES",
224+
"GARDENLINUX_FEATURES_PLATFORMS",
225+
"GARDENLINUX_VERSION",
226+
):
227+
if not metadata_config.has_option(UNNAMED_SECTION, metadata_field):
228+
raise RuntimeError(
229+
f"Metadata file given is invalid: {metadata_file} misses {metadata_field}"
230+
)
231+
232+
loaded_cname_instance = CName(
233+
metadata_config.get(UNNAMED_SECTION, "GARDENLINUX_CNAME")
234+
)
235+
236+
commit_id = metadata_config.get(UNNAMED_SECTION, "GARDENLINUX_COMMIT_ID")
237+
version = metadata_config.get(UNNAMED_SECTION, "GARDENLINUX_VERSION")
238+
239+
if (
240+
loaded_cname_instance.flavor != self.flavor
241+
or loaded_cname_instance.commit_id != commit_id
242+
or (self._commit_id is not None and self._commit_id != commit_id)
243+
or loaded_cname_instance.version != version
244+
or (self._version is not None and self._version != version)
245+
):
246+
raise RuntimeError(
247+
f"Metadata file given is invalid: {metadata_file} failed consistency check - {self.cname} != {loaded_cname_instance.cname}"
248+
)
249+
250+
self._arch = loaded_cname_instance.arch
251+
self._flavor = loaded_cname_instance.flavor
252+
self._commit_id = commit_id
253+
self._version = version
254+
255+
self._feature_set_cached = metadata_config.get(
256+
UNNAMED_SECTION, "GARDENLINUX_FEATURES"
257+
)
258+
259+
self._platforms_cached = metadata_config.get(
260+
UNNAMED_SECTION, "GARDENLINUX_FEATURES_PLATFORMS"
261+
).split(",")
262+
263+
def save_to_metadata_file(
264+
self, metadata_file: PathLike | str, overwrite: Optional[bool] = False
265+
) -> None:
266+
"""
267+
Saves the metadata file.
268+
269+
:param metadata_file: Metadata file containing information about the CName instance.
270+
271+
:since: 0.9.2
272+
"""
273+
274+
if not isinstance(metadata_file, PathLike):
275+
metadata_file = Path(metadata_file)
276+
277+
if not overwrite and metadata_file.exists():
278+
raise RuntimeError(
279+
f"Refused to overwrite existing metadata file: {metadata_file}"
280+
)
281+
282+
features = Parser().filter_as_dict(self.flavor)
283+
284+
elements = ",".join(features["element"])
285+
flags = ",".join(features["flag"])
286+
platforms = ",".join(features["platform"])
287+
288+
metadata = f"""
289+
ID=gardenlinux
290+
NAME="Garden Linux"
291+
PRETTY_NAME="Garden Linux {self.version}"
292+
IMAGE_VERSION={self.version}
293+
VARIANT_ID="{self.flavor}-{self.arch}"
294+
HOME_URL="https://gardenlinux.io"
295+
SUPPORT_URL="https://github.com/gardenlinux/gardenlinux"
296+
BUG_REPORT_URL="https://github.com/gardenlinux/gardenlinux/issues"
297+
GARDENLINUX_CNAME="{self.cname}"
298+
GARDENLINUX_FEATURES="{self.feature_set}"
299+
GARDENLINUX_FEATURES_PLATFORMS="{platforms}"
300+
GARDENLINUX_FEATURES_ELEMENTS="{elements}"
301+
GARDENLINUX_FEATURES_FLAGS="{flags}"
302+
GARDENLINUX_VERSION="{self.version}"
303+
GARDENLINUX_COMMIT_ID="{self.commit_id}"
304+
GARDENLINUX_COMMIT_ID_LONG=$BUILDER_COMMIT
305+
""".strip()
306+
307+
with metadata_file.open("w") as fp:
308+
fp.write(metadata)

tests/s3/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class S3Env:
2222

2323

2424
def make_cname(
25-
flavor: str = "testcname",
25+
flavor: str = "container",
2626
arch: str = "amd64",
2727
version: str = "1234.1",
2828
commit: str = "abc123",

0 commit comments

Comments
 (0)