-
Notifications
You must be signed in to change notification settings - Fork 0
/
helm-export.py
executable file
·176 lines (144 loc) · 6.86 KB
/
helm-export.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/usr/bin/env python
import argparse
import json
import subprocess
import os
"""
export helm chart to current or specific directory from a running chart release with "helm get <name>"
command.
the exported helm chart can be re-deployed with "helm install --name <name> <chart>"
"""
def export_manifest(release, outDir, namespace="default", revision=None):
command = ["helm", "get", "manifest"]
if namespace == "default":
command.append(release)
else:
command.extend(["--namespace", namespace, release])
if revision is not None:
command.extend(['--revision', revision])
output = subprocess.check_output(command).decode()
SOURCE_INDICATOR = "# Source:"
sourceFile = None
content = ""
exported_files = []
dep_charts = []
for line in output.split("\n"):
if line.startswith(SOURCE_INDICATOR):
if len(content) > 0 and sourceFile is not None:
save_to_file(sourceFile, content, append=sourceFile in exported_files)
if sourceFile not in exported_files:
exported_files.append(sourceFile)
chart_name = find_chart(sourceFile)
if chart_name is not None and chart_name not in dep_charts:
dep_charts.append(chart_name)
sourceFile = line[len(SOURCE_INDICATOR):].strip()
sourceFile = os.path.join(outDir, sourceFile)
content = ""
else:
content = "{}\n{}".format(content, line)
# save the last content
if len(content) > 0 and sourceFile is not None:
save_to_file(sourceFile, content, append=sourceFile in exported_files)
for dep_chart in dep_charts:
export_dep_chart_yaml(release, namespace, dep_chart, outDir)
def get_dep_chart_filename(dep_chart, outDir):
fileName = outDir
for chart in dep_chart.split("/"):
fileName = os.path.join(fileName, 'charts')
fileName = os.path.join(fileName, chart)
return os.path.join(fileName, "Chart.yaml")
def find_chart(fileName):
path_array = split_path_to_array(fileName)
i = 1
chart_name = ""
while i < len(path_array):
if path_array[i] == 'charts' and i + 2 < len(path_array) and path_array[i + 2] == 'templates':
chart_name = "{}/{}".format(chart_name, path_array[i + 1]) if len(chart_name) > 0 else path_array[i + 1]
i += 1
return chart_name if len(chart_name) > 0 else None
def export_values(release, outDir, namespace, revision=None):
command = ["helm", "get", "values"]
if namespace == "default":
command.append(release)
else:
command.extend(["--namespace", namespace, release])
if revision is not None:
command.extend(['--revision', revision])
output = subprocess.check_output(command)
fileName = os.path.join(outDir, release)
fileName = os.path.join(fileName, "values.yaml")
save_to_file(fileName, output)
def export_chart_yaml(release, outDir, namespace="default", revision=None):
chart_version, app_version = get_app_version(release, namespace, revision)
if chart_version is not None:
fileName = os.path.join(outDir, release)
fileName = os.path.join(fileName, "Chart.yaml")
save_to_file(fileName,
"apiVersion: v1\ndescription: {}\nname: {}\nappVersion: {}\nversion: {}\nengine: gotpl".format(
release, release, app_version, chart_version))
def get_app_version(release, namespace, revision):
output = subprocess.check_output(['helm', 'list', "--namespace", namespace, "--output", "json"]).decode()
r = json.loads(output)
if isinstance(r, dict) and "Releases" in r:
r = r["Releases"]
for item in r:
app_name = item['Name'] if 'Name' in item else item['name']
app_revision = item['Revision'] if 'Revision' in item else item['revision']
if app_name == release and (revision is None or app_revision == revision):
chart_name = item['Chart'] if 'Chart' in item else item['chart']
app_version = item["AppVersion"] if "AppVersion" in item else item['app_version']
return chart_name.split('-')[-1], app_version
return None
def get_all_charts(namespace):
command = ['helm', 'list', "--namespace", namespace, "--output", "json"]
output = subprocess.check_output(command).decode()
r = json.loads(output)
if isinstance(r, dict) and "Releases" in r:
r = r["Releases"]
return [item['Name'] if 'Name' in item else item['name'] for item in r]
def export_dep_chart_yaml(release, namespace, dep_chart, outDir, revision=None):
chart_version, app_version = get_app_version(release, namespace, revision)
fileName = get_dep_chart_filename(dep_chart, os.path.join(outDir, release))
save_to_file(fileName,
"apiVersion: v1\ndescription: {}\nname: {}\nappVersion: {}\nversion: {}\nengine: gotpl".format(release,
release,
app_version,
chart_version))
def save_to_file(fileName, content, append=False):
dir_name = os.path.dirname(fileName)
if not os.path.exists(dir_name):
os.makedirs(dir_name)
mode = "ab" if append else "wb"
with open(fileName, mode) as fp:
fp.write(content)
def split_path_to_array(path):
"""
split a path "this/is/a/path" to array ["this","is","a","path"]
"""
result = []
while True:
basename = os.path.basename(path)
if len(basename) <= 0: break
result.append(basename)
path = os.path.dirname(path)
if len(path) <= 0: break
return list(reversed(result))
def parse_args():
parser = argparse.ArgumentParser(description="export a release of helm chart from k8s running environment")
parser.add_argument("--release", help="helm release name", required=False)
parser.add_argument("--revision", help="revision of helm release", required=False)
parser.add_argument("--namespace", "-n", help="the namespace", default="default")
parser.add_argument("--out-dir", help="output directory, default is current directory", required=False, default=".")
return parser.parse_args()
def main():
args = parse_args()
if args.release is None:
releases = get_all_charts(args.namespace)
else:
releases = [args.release]
for release in releases:
export_manifest(release, args.out_dir, namespace=args.namespace, revision=args.revision)
export_values(release, args.out_dir, namespace=args.namespace, revision=args.revision)
export_chart_yaml(release, args.out_dir, namespace=args.namespace, revision=args.revision)
if __name__ == "__main__":
main()