Skip to content

Commit feddd13

Browse files
authored
Adds helm chart to app version matrix (#1714)
* Adds helm chart to app version matrix * Previously, there was not an easy way to correlate the app version to a helm chart version. Many people ask this question in public slack channel or must get data themselves. This adds a script to produce the desired matrix data as a table, json, or yaml output to help folks who are upgrading. Signed-off-by: Corey Osman <[email protected]> * Remove bin directory from gitignore * Previously the bin directory was being ignored despite this being a logical place to contain scripts. It has been re-enabled and the python script was added as a result. * Update script to handle edge releases better Signed-off-by: Corey Osman <[email protected]> * Add build release matrix to build target Signed-off-by: Corey Osman <[email protected]> * Rebase and regenerate matrix with 2.14.7 Signed-off-by: Corey Osman <[email protected]> --------- Signed-off-by: Corey Osman <[email protected]>
1 parent 94a29f5 commit feddd13

File tree

8 files changed

+796
-2
lines changed

8 files changed

+796
-2
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ node_modules
44
.DS_Store
55
tmp
66
.idea
7-
bin
7+
bin/htmltest

Makefile

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ serve-api.linkerd.io: build-api.linkerd.io
112112
&& python3 -m http.server 9999
113113

114114
.PHONY: build-linkerd.io
115-
build-linkerd.io: get-versions tmp/linkerd.io
115+
build-linkerd.io: build-release-matrix get-versions tmp/linkerd.io
116116
@# Build linkerd.io
117117
ifndef HAS_HUGO
118118
@printf "Install hugo first. For OSX: brew install hugo\n"; exit 1
@@ -140,10 +140,20 @@ replace-env-%: has-env-% tmp-sites
140140
sed 's/$*/$($*)/g' < $$fname > /tmp/__sed && mv /tmp/__sed $$fname; \
141141
done
142142

143+
.PHONY: build-release-matrix
144+
build-release-matrix:
145+
@# Build release matrix
146+
./bin/generate_release_matrix.py --release_type=stable --format=json > linkerd.io/data/releases/release_matrix.json
147+
./bin/generate_release_matrix.py --release_type=stable --format=yaml > linkerd.io/content/releases/release_matrix.yaml
148+
cp linkerd.io/data/releases/release_matrix.json linkerd.io/content/releases/release_matrix.json
149+
150+
143151
.PHONY: has-env-%
144152
has-env-%:
145153
@if [ ! $${$*:-} ]; then printf "You must define: $*\n" && exit 1; fi
146154

147155
.PHONY: clean
148156
clean:
149157
rm -rf tmp
158+
159+

bin/generate_release_matrix.py

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
#!/usr/bin/env python3
2+
# Author: Corey Osman <[email protected]>
3+
# Purpose: Geneate a list in specified format of all the linkerd charts by app version
4+
# Usage: ./generate_release_matrix.py [--format=json|table|yaml] [--update_repo]
5+
# Notes: Help determine which charts go with which app versions
6+
# Notes: This is primary aimed at the stable release only, although a slight modification
7+
# could yeild support for edge and Enterprise releases too. Right now edge versions
8+
# follow date schemes and the manual mappings do not work
9+
10+
11+
import subprocess
12+
import json
13+
import yaml
14+
import argparse
15+
16+
17+
charts = [
18+
# "linkerd2", # This makes the search return results for everything
19+
"linkerd2-cni",
20+
"linkerd-viz",
21+
"linkerd-control-plane",
22+
"linkerd-jaeger",
23+
"linkerd-multicluster",
24+
"linkerd-failover",
25+
]
26+
27+
# these versions are old and do not have the proper mappings anyways
28+
ignored_y_versions = [6, 7, 8, 9]
29+
30+
# Manually map in the old chart, otherwise we get duplicates mixed in
31+
linkerd2_map = {
32+
'2.11': {
33+
"linkerd2": {
34+
"chart_name": "linkerd2",
35+
"chart_version": "2.11.5",
36+
"chart_url": "https://artifacthub.io/packages/helm/linkerd2/linkerd2/2.11.5"
37+
}
38+
},
39+
'2.10': {
40+
"linkerd2": {
41+
"chart_name": "linkerd2",
42+
"chart_version": "2.10.2",
43+
"chart_url": "https://artifacthub.io/packages/helm/linkerd2/linkerd2/2.10.2"
44+
}
45+
}
46+
}
47+
# Manually map in the crds because there is no app version associated with them
48+
crds_map = {
49+
"2.12": {
50+
"linkerd-crds": {
51+
"chart_name": "linkerd-crds",
52+
"chart_version": "1.6.1",
53+
"chart_url": "https://artifacthub.io/packages/helm/linkerd2/linkerd-crds/1.6.1"
54+
}
55+
},
56+
"2.13": {
57+
"linkerd-crds": {
58+
"chart_name": "linkerd-crds",
59+
"chart_version": "1.6.1",
60+
"chart_url": "https://artifacthub.io/packages/helm/linkerd2/linkerd-crds/1.6.1"
61+
}
62+
},
63+
"2.14": {
64+
"linkerd-crds": {
65+
"chart_name": "linkerd-crds",
66+
"chart_version": "1.8.0",
67+
"chart_url": "https://artifacthub.io/packages/helm/linkerd2/linkerd-crds/1.8.0"
68+
}
69+
},
70+
}
71+
72+
73+
def find_newest_versions(versions):
74+
"""
75+
Finds the newest version with the highest X value for a given Y version
76+
77+
Parameters:
78+
versions (list): A list of version objects
79+
Example: [('linkerd2/linkerd2', '2.11.5', 'stable-2.11.5'), ('linkerd2/linkerd2', '2.11.4', 'stable-2.11.4')
80+
81+
Returns:
82+
list: A list of version objects
83+
Example: [('linkerd2/linkerd2', '2.11.5', 'stable-2.11.5'),
84+
('linkerd2/linkerd2', '2.10.2', 'stable-2.10.2'),
85+
('linkerd2/linkerd2', '30.12.1', 'stable-2.14.3'),
86+
('linkerd2/linkerd2', '30.8.5', 'stable-2.13.7'),
87+
('linkerd2/linkerd2', '30.3.8', 'stable-2.12.6'),
88+
('linkerd2/linkerd2', '2.11.5', 'stable-2.11.5'),
89+
('linkerd2/linkerd2', '2.10.2', 'stable-2.10.2')]
90+
91+
"""
92+
winners = {}
93+
for entry in versions:
94+
_, _, version, _ = entry
95+
try:
96+
x, y, z = map(int, version.split("-")[1].split("."))
97+
if not y in ignored_y_versions:
98+
current_winner = winners.get(
99+
f"{x}.{y}.Z", {"x": x, "y": y, "z": z, version: version}
100+
)
101+
if current_winner["y"] == y and z >= current_winner["z"]:
102+
# new winner
103+
winners[f"{x}.{y}.Z"] = {"x": x, "y": y, "z": z, "version": version}
104+
105+
except IndexError:
106+
next
107+
except UnboundLocalError:
108+
next
109+
110+
latest_versions = [v["version"] for v in winners.values()]
111+
common_versions = []
112+
113+
for version in versions:
114+
if version[2] in latest_versions:
115+
common_versions.append(version)
116+
117+
return common_versions
118+
119+
120+
def combine_charts_by_app_version(versions):
121+
"""
122+
Gathers all charts tuples under a single app version
123+
124+
Parameters:
125+
versions (list): Raw list of versions tuples
126+
[('linkerd2/linkerd-control-plane', '1.12.7', 'stable-2.13.7'),
127+
('linkerd2/linkerd-control-plane', '1.16.4', 'stable-2.14.3'),
128+
('linkerd2/linkerd-control-plane', '1.9.8', 'stable-2.12.6')]
129+
130+
Returns:
131+
dict: versions object after combing under an app version.
132+
{'stable-2.13.7':
133+
{'linkerd2-crds':
134+
{'chart_name': 'linkerd2/linkerd2-crds', 'chart_version': '1.6.1'},
135+
'linkerd-jaeger': {
136+
'chart_name': 'linkerd2/linkerd-jaeger', 'chart_version': '30.8.7'}
137+
}
138+
}
139+
140+
"""
141+
combined_charts = {}
142+
for chart, version, app_version, link in versions:
143+
name = chart # chart.split("/")[1]
144+
145+
if not app_version:
146+
app_version = version
147+
if not combined_charts.get(app_version):
148+
combined_charts[app_version] = {}
149+
150+
combined_charts[app_version][name] = {
151+
"chart_name": chart,
152+
"chart_version": version,
153+
"chart_url": link
154+
}
155+
# merge in the crds chart info if it exist for the version
156+
x, y, z = map(int, app_version.split("-")[1].split("."))
157+
try:
158+
if y > 11 and x == 2:
159+
combined_charts[app_version] = {**combined_charts[app_version], **crds_map[f"{x}.{y}"]}
160+
elif x == 2:
161+
combined_charts[app_version] = {**combined_charts[app_version], **linkerd2_map[f"{x}.{y}"]}
162+
except KeyError as e:
163+
print(e)
164+
165+
# sorted(combined_charts.items(), key=lambda x: x[0] != "stable-2.11.5")
166+
return combined_charts
167+
168+
def find_repo_name(release_type):
169+
if release_type == "stable":
170+
repo_name = "linkerd2"
171+
elif release_type == "edge":
172+
repo_name = "linkerd2-edge"
173+
else:
174+
repo_name = "linkerd2"
175+
176+
return repo_name
177+
178+
def add_repo(release_type, helm_url, repo_name):
179+
command = ["helm", "repo", "add", repo_name, f"{helm_url}/{release_type}"]
180+
181+
try:
182+
process = subprocess.Popen(
183+
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
184+
)
185+
stdout, stderr = process.communicate()
186+
187+
except OSError as e:
188+
print(f"Error: {e}")
189+
190+
return repo_name
191+
192+
193+
def update_repo(release_type, helm_url, repo_name):
194+
add_repo(release_type, helm_url, repo_name)
195+
196+
command = ["helm", "repo", "update", repo_name]
197+
198+
try:
199+
process = subprocess.Popen(
200+
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
201+
)
202+
stdout, stderr = process.communicate()
203+
204+
except OSError as e:
205+
print(f"Error: {e}")
206+
207+
return repo_name
208+
209+
def list_chart_versions(charts, repo_name, latest_only=True):
210+
"""
211+
Fetches all the linkerd charts
212+
213+
Parameters:
214+
charts (list): A list of chart names to fetch
215+
216+
Returns:
217+
dict: A list of chart version tuples
218+
Example: {('linkerd2/linkerd2', '2.11.5', 'stable-2.11.5')}
219+
220+
"""
221+
all_versions = set()
222+
223+
224+
for chart in charts:
225+
# helm repo update linkerd2
226+
search_term = f"{repo_name}/{chart}"
227+
command = ["helm", "search", "repo", search_term, "--versions", "--output", "json"]
228+
try:
229+
process = subprocess.Popen(
230+
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
231+
)
232+
stdout, stderr = process.communicate()
233+
234+
if process.returncode == 0:
235+
chart_data = json.loads(stdout)
236+
versions = [
237+
(chart, item["version"], item["app_version"], f"https://artifacthub.io/packages/helm/{repo_name}/{chart}/{item['version']}") for item in chart_data
238+
]
239+
if latest_only:
240+
latest_versions = find_newest_versions(versions)
241+
else:
242+
latest_versions = versions
243+
244+
all_versions.update(latest_versions)
245+
else:
246+
print(f"Error: Failed to list chart versions for {chart}")
247+
print(stderr)
248+
except OSError as e:
249+
print(f"Error: {e}")
250+
251+
return sorted(all_versions)
252+
253+
254+
def print_output(data, format):
255+
if format == "json":
256+
print(json.dumps(data, indent=4))
257+
elif format == "yaml":
258+
print(yaml.dump(data, indent=4))
259+
else:
260+
try:
261+
from tabulate import tabulate
262+
except ImportError as e:
263+
print("Please install tabulate: pip3 install tabulate")
264+
exit(1)
265+
headers = ["App Version", "Chart Name", "Chart Version"]
266+
table = []
267+
for app_version, charts in sorted(
268+
data.items(), reverse=True
269+
):
270+
for chart_name, chart_data in charts.items():
271+
table.append(
272+
[app_version, chart_data["chart_name"], chart_data["chart_version"]]
273+
)
274+
print(tabulate(table, headers=headers, tablefmt="github"))
275+
print("\n")
276+
table = []
277+
278+
279+
parser = argparse.ArgumentParser(description="List linkerd chart versions in various formats")
280+
parser.add_argument(
281+
"--format", default="table", choices=["table", "json", "yaml"], help="Desired Output format, defaults to table"
282+
)
283+
parser.add_argument('--release_type', choices=["stable", "edge"], default="stable", help="Use the specific release type, defaults to stable")
284+
parser.add_argument('--helm_url', choices=["https://helm.linkerd.io"], default="https://helm.linkerd.io", help="The helm url to use" )
285+
args = parser.parse_args()
286+
287+
repo_name = find_repo_name(args.release_type)
288+
update_repo(args.release_type, args.helm_url, repo_name)
289+
290+
all_versions = list_chart_versions(charts, repo_name)
291+
combined_charts_by_app_version = combine_charts_by_app_version(all_versions)
292+
print_output(combined_charts_by_app_version, args.format)

linkerd.io/content/releases/_index.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ bugfixes or enhancements.
3636
Commercial providers of Linkerd (e.g. [Buoyant](https://buoyant.io)) may
3737
provide stronger support guarantees.
3838

39+
## Helm Version Matrix
40+
41+
The following version matrices include only the latest versions of the stable
42+
releases along with corresponding app and helm versions for linkerd and
43+
extensions. Use these to guide you to the right helm chart version or to
44+
automate workflows you might have.
45+
46+
* [YAML matrix](./release_matrix.yaml)
47+
* [JSON matrix](./release_matrix.json)
48+
49+
{{< release-data-table />}}
50+
3951
## Edge (latest version: {{% latestedge %}})
4052

4153
Edge releases are frequent (usually, weekly) and can be used to work with the

0 commit comments

Comments
 (0)