Skip to content

Commit 1135fc3

Browse files
committed
Add support to accept incomplete release files for S3 artifact upload
Signed-off-by: Tobias Wolf <wolf@b1-systems.de> On-behalf-of: SAP <tobias.wolf@sap.com>
1 parent 2d52501 commit 1135fc3

File tree

6 files changed

+85
-33
lines changed

6 files changed

+85
-33
lines changed

.github/actions/setup/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: Installs the given GardenLinux Python library
44
inputs:
55
version:
66
description: GardenLinux Python library version
7-
default: "0.10.13"
7+
default: "0.10.14"
88
python_version:
99
description: Python version to setup
1010
default: "3.13"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "gardenlinux"
3-
version = "0.10.13"
3+
version = "0.10.14"
44
description = "Contains tools to work with the features directory of gardenlinux, for example deducting dependencies from feature sets or validating cnames"
55
authors = ["Garden Linux Maintainers <contact@gardenlinux.io>"]
66
license = "Apache-2.0"

src/gardenlinux/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
GL_DEB_REPO_BASE_URL = "https://packages.gardenlinux.io/gardenlinux"
149149
GL_DISTRIBUTION_NAME = "Garden Linux"
150150
GL_HOME_URL = "https://gardenlinux.io"
151+
GL_PLATFORM_FRANKENSTEIN = "frankenstein"
151152
GL_RELEASE_ID = "gardenlinux"
152153
GL_REPOSITORY_URL = "https://github.com/gardenlinux/gardenlinux"
153154
GL_SUPPORT_URL = "https://github.com/gardenlinux/gardenlinux"

src/gardenlinux/features/cname.py

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
from configparser import UNNAMED_SECTION, ConfigParser
99
from os import PathLike, environ
1010
from pathlib import Path
11-
from typing import List, Optional, Self
11+
from typing import Any, Dict, List, Optional, Self
1212

1313
from ..constants import (
1414
ARCHS,
1515
GL_BUG_REPORT_URL,
1616
GL_DISTRIBUTION_NAME,
1717
GL_HOME_URL,
18+
GL_PLATFORM_FRANKENSTEIN,
1819
GL_RELEASE_ID,
1920
GL_SUPPORT_URL,
2021
)
@@ -59,14 +60,20 @@ def __init__(
5960
self._feature_flags_cached: Optional[List[str]] = None
6061
self._feature_platforms_cached: Optional[List[str]] = None
6162
self._feature_set_cached: Optional[str] = None
63+
self._features_cached: Optional[Dict[str, Any]] = None
6264
self._platform_variant_cached: Optional[str] = None
6365
self._flavor = ""
6466
self._version = None
6567

68+
self._flag_frankenstein = bool(environ.get("GL_ALLOW_FRANKENSTEIN", False))
69+
6670
self._flag_multiple_platforms = bool(
67-
environ.get("GL_ALLOW_FRANKENSTEIN", False)
71+
environ.get("GL_ALLOW_MULTIPLE_PLATFORMS", False)
6872
)
6973

74+
if self._flag_frankenstein:
75+
self._flag_multiple_platforms = True
76+
7077
commit_id_or_hash = None
7178

7279
if version is not None:
@@ -213,6 +220,20 @@ def flavor(self) -> str:
213220

214221
return self._flavor
215222

223+
@property
224+
def features(self) -> Dict[str, Any]:
225+
"""
226+
Returns the features for the cname parsed.
227+
228+
:return: (dict) Features of the cname
229+
:since: 0.10.14
230+
"""
231+
232+
if self._features_cached is None:
233+
self._features_cached = Parser().filter_as_dict(self.flavor)
234+
235+
return self._features_cached
236+
216237
@property
217238
def feature_set(self) -> str:
218239
"""
@@ -239,7 +260,7 @@ def feature_set_element(self) -> str:
239260
if self._feature_elements_cached is not None:
240261
return ",".join(self._feature_elements_cached)
241262

242-
return ",".join(Parser().filter_as_dict(self.flavor)["element"])
263+
return ",".join(self.features["element"])
243264

244265
@property
245266
def feature_set_flag(self) -> str:
@@ -253,7 +274,7 @@ def feature_set_flag(self) -> str:
253274
if self._feature_flags_cached is not None:
254275
return ",".join(self._feature_flags_cached)
255276

256-
return ",".join(Parser().filter_as_dict(self.flavor)["flag"])
277+
return ",".join(self.features["flag"])
257278

258279
@property
259280
def feature_set_platform(self) -> str:
@@ -265,7 +286,7 @@ def feature_set_platform(self) -> str:
265286
"""
266287

267288
if self._feature_platforms_cached is None:
268-
platforms = Parser().filter_as_dict(self.flavor)["platform"]
289+
platforms = self.features["platform"]
269290
else:
270291
platforms = self._feature_platforms_cached
271292

@@ -274,7 +295,7 @@ def feature_set_platform(self) -> str:
274295

275296
assert len(platforms) < 2
276297
"Only one platform is supported"
277-
return platforms[0]
298+
return platforms[0] # type: ignore[no-any-return]
278299

279300
@property
280301
def feature_set_list(self) -> List[str]:
@@ -293,21 +314,20 @@ def feature_set_list(self) -> List[str]:
293314
@property
294315
def platform(self) -> str:
295316
"""
296-
Returns the feature set of type "platform" for the cname parsed.
317+
Returns the platform for the cname parsed.
297318
298-
:return: (str) Feature set platforms
319+
:return: (str) Platform
299320
:since: 0.7.0
300321
"""
301322

302-
if self._feature_platforms_cached is None:
303-
platforms = Parser().filter_as_dict(self.flavor)["platform"]
304-
else:
305-
platforms = self._feature_platforms_cached
306-
307-
if not self._flag_multiple_platforms:
308-
assert len(platforms) < 2
323+
if (
324+
self._flag_frankenstein
325+
and self._feature_platforms_cached is not None
326+
and len(self._feature_platforms_cached) > 1
327+
):
328+
return GL_PLATFORM_FRANKENSTEIN
309329

310-
return platforms[0]
330+
return self.feature_set_platform
311331

312332
@property
313333
def platform_variant(self) -> Optional[str]:
@@ -345,18 +365,8 @@ def release_metadata_string(self) -> str:
345365
:since: 1.0.0
346366
"""
347367

348-
features = Parser().filter_as_dict(self.flavor)
349-
350-
if not self._flag_multiple_platforms:
351-
assert len(features["platform"]) < 2
352-
"Only one platform is supported"
353-
354368
commit_hash = self.commit_hash
355369
commit_id = self.commit_id
356-
elements = ",".join(features["element"])
357-
flags = ",".join(features["flag"])
358-
platform = features["platform"][0]
359-
platforms = ",".join(features["platform"])
360370
platform_variant = self.platform_variant
361371
version = self.version
362372

@@ -387,10 +397,10 @@ def release_metadata_string(self) -> str:
387397
BUG_REPORT_URL="{GL_BUG_REPORT_URL}"
388398
GARDENLINUX_CNAME="{self.cname}"
389399
GARDENLINUX_FEATURES="{self.feature_set}"
390-
GARDENLINUX_FEATURES_PLATFORMS="{platforms}"
391-
GARDENLINUX_FEATURES_ELEMENTS="{elements}"
392-
GARDENLINUX_FEATURES_FLAGS="{flags}"
393-
GARDENLINUX_PLATFORM="{platform}"
400+
GARDENLINUX_FEATURES_PLATFORMS="{self.feature_set_platform}"
401+
GARDENLINUX_FEATURES_ELEMENTS="{self.feature_set_element}"
402+
GARDENLINUX_FEATURES_FLAGS="{self.feature_set_flag}"
403+
GARDENLINUX_PLATFORM="{self.platform}"
394404
GARDENLINUX_PLATFORM_VARIANT="{platform_variant}"
395405
GARDENLINUX_VERSION="{version}"
396406
GARDENLINUX_COMMIT_ID="{commit_id}"

src/gardenlinux/s3/s3_artifacts.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,24 @@ def upload_from_directory(
120120

121121
release_file = artifacts_dir.joinpath(f"{base_name}.release")
122122

123-
cname_object = CName.new_from_release_file(release_file)
123+
try:
124+
cname_object = CName.new_from_release_file(release_file)
125+
except RuntimeError:
126+
if not release_file.exists():
127+
raise RuntimeError(
128+
f"Release metadata file given is invalid: {release_file}"
129+
)
130+
131+
release_config = ConfigParser(allow_unnamed_section=True)
132+
release_config.read(release_file)
133+
134+
cname_object = CName(
135+
release_config.get(UNNAMED_SECTION, "GARDENLINUX_CNAME"),
136+
commit_hash=release_config.get(
137+
UNNAMED_SECTION, "GARDENLINUX_COMMIT_ID_LONG"
138+
),
139+
version=release_config.get(UNNAMED_SECTION, "GARDENLINUX_VERSION"),
140+
)
124141

125142
if cname_object.version_and_commit_id is None:
126143
raise RuntimeError(

tests/s3/test_s3_artifacts.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,30 @@ def test_upload_from_directory_invalid_artifact_name(s3_setup: S3Env) -> None:
215215
assert len(list(bucket.objects.filter(Prefix=f"meta/singles/{env.cname}"))) == 1
216216

217217

218+
def test_upload_from_directory_invalid_release_file_with_valid_cname(
219+
s3_setup: S3Env,
220+
) -> None:
221+
"""
222+
Raise RuntimeError if artifact release file is invalid but contains a valid cname.
223+
"""
224+
# Arrange
225+
env = s3_setup
226+
release_path = env.tmp_path / f"{env.cname}.release"
227+
bad_data = RELEASE_DATA.replace(
228+
"GARDENLINUX_FEATURES_PLATFORMS=", "GARDENLINUX_FEATURES_PLATFORMS_UNDEFINED="
229+
)
230+
release_path.write_text(bad_data)
231+
232+
artifacts = S3Artifacts(env.bucket_name)
233+
234+
# Act
235+
artifacts.upload_from_directory(env.cname, env.tmp_path)
236+
237+
# Assert
238+
bucket = env.s3.Bucket(env.bucket_name)
239+
assert len(list(bucket.objects.filter(Prefix=f"meta/singles/{env.cname}"))) == 1
240+
241+
218242
def test_upload_from_directory_commit_mismatch(s3_setup: S3Env) -> None:
219243
"""
220244
Validate that the release file may contain a different commit hash not matching the artifact name.

0 commit comments

Comments
 (0)