Skip to content

Commit c6a2753

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

File tree

6 files changed

+130
-139
lines changed

6 files changed

+130
-139
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: 31 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,43 @@ 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
6968

70-
if re_match.lastindex == 1:
71-
data_splitted = re_match[1].split("-", 1)
69+
if arch is None or arch == "":
70+
arch = args.default_arch
71+
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(
90+
"Architecture could not be determined and no default architecture set"
91+
)
10892

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
93+
if version is None or version == "" and (args.type in ("cname", "version")):
94+
raise RuntimeError("Version not specified and no default version set")
11295

11396
feature_dir_name = path.basename(args.feature_dir)
11497

@@ -124,7 +107,7 @@ def main():
124107
print(arch)
125108
elif args.type in ("cname_base", "cname", "graph"):
126109
graph = Parser(gardenlinux_root, feature_dir_name).filter(
127-
cname_base, additional_filter_func=additional_filter_func
110+
flavor, additional_filter_func=additional_filter_func
128111
)
129112

130113
sorted_features = Parser.sort_graph_nodes(graph)
@@ -137,7 +120,7 @@ def main():
137120
if args.type == "cname_base":
138121
print(cname_base)
139122
elif args.type == "cname":
140-
cname = cname_base
123+
cname = flavor
141124

142125
if arch is not None:
143126
cname += f"-{arch}"
@@ -147,16 +130,16 @@ def main():
147130

148131
print(cname)
149132
elif args.type == "graph":
150-
print(graph_as_mermaid_markup(cname_base, graph))
133+
print(graph_as_mermaid_markup(flavor, graph))
151134
elif args.type == "features":
152135
print(
153136
Parser(gardenlinux_root, feature_dir_name).filter_as_string(
154-
cname_base, additional_filter_func=additional_filter_func
137+
flavor, additional_filter_func=additional_filter_func
155138
)
156139
)
157140
elif args.type in ("flags", "elements", "platforms"):
158141
features_by_type = Parser(gardenlinux_root, feature_dir_name).filter_as_dict(
159-
cname_base, additional_filter_func=additional_filter_func
142+
flavor, additional_filter_func=additional_filter_func
160143
)
161144

162145
if args.type == "platforms":
@@ -194,15 +177,15 @@ def get_minimal_feature_set(graph):
194177
return set([node for (node, degree) in graph.in_degree() if degree == 0])
195178

196179

197-
def graph_as_mermaid_markup(cname_base, graph):
180+
def graph_as_mermaid_markup(flavor, graph):
198181
"""
199182
Generates a mermaid.js representation of the graph.
200183
This is helpful to identify dependencies between features.
201184
202185
Syntax docs:
203186
https://mermaid.js.org/syntax/flowchart.html?id=flowcharts-basic-syntax
204187
"""
205-
markup = f"---\ntitle: Dependency Graph for Feature {cname_base}\n---\ngraph TD;\n"
188+
markup = f"---\ntitle: Dependency Graph for Feature {flavor}\n---\ngraph TD;\n"
206189
for u, v in graph.edges:
207190
markup += f" {u}-->{v};\n"
208191
return markup

src/gardenlinux/features/cname.py

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