Skip to content

Commit d97b116

Browse files
committed
Move CName parser to a separate class
Signed-off-by: Tobias Wolf <[email protected]>
1 parent 891d12d commit d97b116

File tree

6 files changed

+126
-138
lines changed

6 files changed

+126
-138
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf-8 -*-
22

3+
from .cname import CName
34
from .parser import Parser
45

5-
__all__ = ["Parser"]
6+
__all__ = ["CName", "Parser"]

src/gardenlinux/features/__main__.py

Lines changed: 29 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
33

4+
from .cname import CName
45
from .parser import Parser
56

67
from functools import reduce
@@ -54,61 +55,41 @@ def main():
5455
), "Please provide either `--features` or `--cname` argument"
5556

5657
arch = None
57-
cname_base = None
58+
flavor = None
5859
commit_id = None
5960
gardenlinux_root = path.dirname(args.feature_dir)
6061
version = None
6162

62-
if args.cname:
63-
re_match = re.match(
64-
"([a-zA-Z0-9]+([\\_\\-][a-zA-Z0-9]+)*?)(-([a-z0-9]+)(-([a-z0-9.]+)-([a-z0-9]+))*)?$",
65-
args.cname,
66-
)
63+
if args.arch is not None:
64+
arch = args.arch
6765

68-
assert re_match, f"Not a valid GardenLinux canonical name {args.cname}"
66+
if args.version is not None:
67+
version = args.version
68+
69+
if arch is None or arch == "":
70+
arch = args.default_arch
6971

70-
if re_match.lastindex == 1:
71-
data_splitted = re_match[1].split("-", 1)
72+
if version is None or version == "":
73+
version_data = get_version_and_commit_id_from_files(gardenlinux_root)
74+
version = f"{version_data[0]}-{version_data[1]}"
7275

73-
cname_base = data_splitted[0]
76+
if args.cname:
77+
cname = CName(args.cname, arch = arch, version = version)
7478

75-
if len(data_splitted) > 1:
76-
if args.arch is None:
77-
arch = data_splitted[1]
78-
else:
79-
cname_base += "-" + data_splitted[1]
80-
else:
81-
arch = re_match[4]
82-
cname_base = re_match[1]
83-
commit_id = re_match[7]
84-
version = re_match[6]
79+
arch = cname.arch
80+
flavor = cname.flavor
81+
commit_id = cname.commit_id
82+
version = cname.version
8583

86-
input_features = Parser.get_cname_as_feature_set(cname_base)
84+
input_features = Parser.get_cname_as_feature_set(flavor)
8785
else:
8886
input_features = args.features
8987

90-
if args.arch is not None:
91-
arch = args.arch
92-
93-
if args.version is not None:
94-
re_match = re.match("([a-z0-9.]+)(-([a-z0-9]+))?$", args.version)
95-
assert re_match, f"Not a valid version {args.version}"
96-
97-
commit_id = re_match[3]
98-
version = re_match[1]
99-
10088
if arch is None or arch == "" and (args.type in ("cname", "arch")):
101-
assert (
102-
args.default_arch
103-
), "Architecture could not be determined and no default architecture set"
104-
arch = args.default_arch
105-
106-
if not commit_id or not version:
107-
version, commit_id = get_version_and_commit_id_from_files(gardenlinux_root)
89+
raise RuntimeError("Architecture could not be determined and no default architecture set")
10890

109-
if not version and (args.type in ("cname", "version")):
110-
assert args.default_version, "version not specified and no default version set"
111-
version = args.default_version
91+
if version is None or version == "" and (args.type in ("cname", "version")):
92+
raise RuntimeError("Version not specified and no default version set")
11293

11394
feature_dir_name = path.basename(args.feature_dir)
11495

@@ -124,7 +105,7 @@ def main():
124105
print(arch)
125106
elif args.type in ("cname_base", "cname", "graph"):
126107
graph = Parser(gardenlinux_root, feature_dir_name).filter(
127-
cname_base, additional_filter_func=additional_filter_func
108+
flavor, additional_filter_func=additional_filter_func
128109
)
129110

130111
sorted_features = Parser.sort_graph_nodes(graph)
@@ -137,7 +118,7 @@ def main():
137118
if args.type == "cname_base":
138119
print(cname_base)
139120
elif args.type == "cname":
140-
cname = cname_base
121+
cname = flavor
141122

142123
if arch is not None:
143124
cname += f"-{arch}"
@@ -147,16 +128,16 @@ def main():
147128

148129
print(cname)
149130
elif args.type == "graph":
150-
print(graph_as_mermaid_markup(cname_base, graph))
131+
print(graph_as_mermaid_markup(flavor, graph))
151132
elif args.type == "features":
152133
print(
153134
Parser(gardenlinux_root, feature_dir_name).filter_as_string(
154-
cname_base, additional_filter_func=additional_filter_func
135+
flavor, additional_filter_func=additional_filter_func
155136
)
156137
)
157138
elif args.type in ("flags", "elements", "platforms"):
158139
features_by_type = Parser(gardenlinux_root, feature_dir_name).filter_as_dict(
159-
cname_base, additional_filter_func=additional_filter_func
140+
flavor, additional_filter_func=additional_filter_func
160141
)
161142

162143
if args.type == "platforms":
@@ -194,15 +175,15 @@ def get_minimal_feature_set(graph):
194175
return set([node for (node, degree) in graph.in_degree() if degree == 0])
195176

196177

197-
def graph_as_mermaid_markup(cname_base, graph):
178+
def graph_as_mermaid_markup(flavor, graph):
198179
"""
199180
Generates a mermaid.js representation of the graph.
200181
This is helpful to identify dependencies between features.
201182
202183
Syntax docs:
203184
https://mermaid.js.org/syntax/flowchart.html?id=flowcharts-basic-syntax
204185
"""
205-
markup = f"---\ntitle: Dependency Graph for Feature {cname_base}\n---\ngraph TD;\n"
186+
markup = f"---\ntitle: Dependency Graph for Feature {flavor}\n---\ngraph TD;\n"
206187
for u, v in graph.edges:
207188
markup += f" {u}-->{v};\n"
208189
return markup

src/gardenlinux/features/cname.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from typing import Optional
4+
import re
5+
6+
class CName(object):
7+
def __init__(self, cname, arch = None, version = None):
8+
self._arch = None
9+
self._flavor = None
10+
self._commit_id = None
11+
self._version = None
12+
13+
re_match = re.match(
14+
"([a-zA-Z0-9]+([\\_\\-][a-zA-Z0-9]+)*?)(-([a-z0-9]+)(-([a-z0-9.]+)-([a-z0-9]+))*)?$",
15+
cname,
16+
)
17+
18+
assert re_match, f"Not a valid GardenLinux canonical name {cname}"
19+
20+
if re_match.lastindex == 1:
21+
self._flavor = re_match[1]
22+
else:
23+
self._commit_id = re_match[7]
24+
self._flavor = re_match[1]
25+
self._version = re_match[6]
26+
27+
if re_match[2] is None:
28+
self._flavor += re_match[3]
29+
else:
30+
self._arch = re_match[4]
31+
32+
if self._arch is None and arch is not None:
33+
self._arch = arch
34+
35+
if self._version is None and version is not None:
36+
re_match = re.match("([a-z0-9.]+)(-([a-z0-9]+))?$", version)
37+
assert re_match, f"Not a valid version {version}"
38+
39+
self._commit_id = re_match[3]
40+
self._version = re_match[1]
41+
42+
@property
43+
def arch(self) -> Optional[str]:
44+
return self._arch
45+
46+
@property
47+
def cname(self) -> str:
48+
cname = self._flavor
49+
50+
if self._arch is not None:
51+
cname += f"-{self._arch}"
52+
53+
if self._commit_id is not None:
54+
cname += f"-{self.version_and_commit_id}"
55+
56+
return cname
57+
58+
@property
59+
def commit_id(self) -> Optional[str]:
60+
return self._commit_id
61+
62+
@property
63+
def flavor(self) -> str:
64+
return self._flavor
65+
66+
@property
67+
def version(self) -> Optional[str]:
68+
return self._version
69+
70+
@property
71+
def version_and_commit_id(self) -> Optional[str]:
72+
if self._commit_id is None:
73+
return None
74+
75+
return f"{self._version}-{self._commit_id}"

src/gardenlinux/features/cname_main.py

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
get_version_and_commit_id_from_files,
1313
sort_subset,
1414
)
15+
from .cname import CName
1516
from .parser import Parser
1617

1718

@@ -33,62 +34,44 @@ def main():
3334
assert re_match, f"Not a valid GardenLinux canonical name {args.cname}"
3435

3536
arch = None
36-
commit_id = None
3737
gardenlinux_root = dirname(args.feature_dir)
38-
version = None
39-
40-
if re_match.lastindex == 1:
41-
data_splitted = re_match[1].split("-", 1)
42-
43-
cname_base = data_splitted[0]
44-
45-
if len(data_splitted) > 1:
46-
if args.arch is None:
47-
arch = data_splitted[1]
48-
else:
49-
cname_base += "-" + data_splitted[1]
50-
else:
51-
arch = re_match[4]
52-
cname_base = re_match[1]
53-
commit_id = re_match[7]
54-
version = re_match[6]
38+
version = args.version
5539

5640
if args.arch is not None:
5741
arch = args.arch
5842

59-
assert arch is not None and arch != "", "Architecture could not be determined"
43+
if args.version is not None:
44+
version = args.version
6045

61-
if not commit_id or not version:
62-
version, commit_id = get_version_and_commit_id_from_files(gardenlinux_root)
46+
if not version:
47+
version_data = get_version_and_commit_id_from_files(gardenlinux_root)
48+
version = f"{version_data[0]}-{version_data[1]}"
6349

64-
if args.version is not None:
65-
re_match = re.match("([a-z0-9.]+)(-([a-z0-9]+))?$", args.version)
66-
assert re_match, f"Not a valid version {args.version}"
50+
cname = CName(args.cname, arch = arch, version = version)
6751

68-
commit_id = re_match[3]
69-
version = re_match[1]
52+
assert cname.arch, "Architecture could not be determined"
7053

7154
feature_dir_name = basename(args.feature_dir)
7255

7356
if gardenlinux_root == "":
7457
gardenlinux_root = "."
7558

76-
graph = Parser(gardenlinux_root, feature_dir_name).filter(cname_base)
59+
graph = Parser(gardenlinux_root, feature_dir_name).filter(cname.flavor)
7760

7861
sorted_features = Parser.sort_graph_nodes(graph)
7962
minimal_feature_set = get_minimal_feature_set(graph)
8063

8164
sorted_minimal_features = sort_subset(minimal_feature_set, sorted_features)
8265

83-
cname = get_cname_base(sorted_minimal_features)
66+
generated_cname = get_cname_base(sorted_minimal_features)
8467

85-
if arch is not None:
86-
cname += f"-{arch}"
68+
if cname.arch is not None:
69+
generated_cname += f"-{cname.arch}"
8770

88-
if commit_id is not None:
89-
cname += f"-{version}-{commit_id}"
71+
if cname.version_and_commit_id is not None:
72+
generated_cname += f"-{cname.version_and_commit_id}"
9073

91-
print(cname)
74+
print(generated_cname)
9275

9376

9477
if __name__ == "__main__":

src/gardenlinux/features/parser.py

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -221,57 +221,6 @@ def get_cname_as_feature_set(cname):
221221
cname = cname.replace("_", "-_")
222222
return set(cname.split("-"))
223223

224-
@staticmethod
225-
def get_flavor_from_cname(cname: str, get_arch: bool = True) -> str:
226-
"""
227-
Extracts the flavor from a canonical name.
228-
229-
This method parses a Garden Linux canonical name (cname) and extracts
230-
the flavor component, with or without the architecture suffix.
231-
232-
Example canonical names:
233-
- "aws-gardener_prod-amd64"
234-
- "azure-gardener_prod_tpm2_trustedboot-amd64-1312.2-80ffcc87"
235-
236-
The flavor is the platform plus feature string (e.g., "aws-gardener_prod")
237-
238-
Args:
239-
cname (str): Canonical name of an image
240-
get_arch (bool): Whether to include the architecture in the returned flavor
241-
If True: returns "aws-gardener_prod-amd64"
242-
If False: returns "aws-gardener_prod"
243-
244-
Returns:
245-
str: The extracted flavor string, with or without architecture
246-
"""
247-
# Use regex to extract components from the canonical name
248-
# This handles complex cnames with version and commit hash
249-
re_match = re.match(
250-
"([a-zA-Z0-9]+([\\_\\-][a-zA-Z0-9]+)*?)(-([a-z0-9]+)(-([a-z0-9.]+)-([a-z0-9]+))*)?$",
251-
cname,
252-
)
253-
254-
assert re_match, f"Not a valid GardenLinux canonical name {cname}"
255-
256-
if re_match.lastindex == 1:
257-
data_splitted = re_match[1].split("-", 1)
258-
259-
flavor = data_splitted[0]
260-
261-
if len(data_splitted) > 1:
262-
if get_arch is True:
263-
arch = data_splitted[1]
264-
else:
265-
flavor += "-" + data_splitted[1]
266-
else:
267-
arch = re_match[4]
268-
flavor = re_match[1]
269-
# Add architecture if requested
270-
if get_arch and arch:
271-
return f"{flavor}-{arch}"
272-
else:
273-
return flavor
274-
275224
@staticmethod
276225
def _get_filter_set_callable(filter_set, additional_filter_func):
277226
def filter_func(node):

0 commit comments

Comments
 (0)