This repository has been archived by the owner on Nov 7, 2020. It is now read-only.
forked from Praqma/helmsman
/
helm_helpers.go
233 lines (201 loc) · 7.38 KB
/
helm_helpers.go
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package main
import (
"log"
"strconv"
"strings"
"time"
)
var currentState map[string]releaseState
// releaseState represents the current state of a release
type releaseState struct {
Revision int
Updated time.Time
Status string
Chart string
Namespace string
TillerNamespace string
}
// getAllReleases fetches a list of all releases in a k8s cluster
func getAllReleases() string {
result := getTillerReleases("kube-system")
for ns, v := range s.Namespaces {
if v.InstallTiller && ns != "kube-system" {
result = result + getTillerReleases(ns)
}
}
return result
}
// getTillerReleases gets releases deployed with a given Tiller (in agiven namespace)
func getTillerReleases(tillerNS string) string {
cmd := command{
Cmd: "bash",
Args: []string{"-c", "helm list --all --tiller-namespace " + tillerNS + getNSTLSFlags(tillerNS)},
Description: "listing all existing releases in namespace [ " + tillerNS + " ]...",
}
exitCode, result := cmd.exec(debug, verbose)
if exitCode != 0 {
log.Fatal("ERROR: failed to list all releases in namespace [ " + tillerNS + " ]: " + result)
}
// appending tiller-namespace to each release found
lines := strings.Split(result, "\n")
for i, l := range lines {
if l != "" && !strings.HasPrefix(strings.TrimSpace(l), "NAME") && !strings.HasSuffix(strings.TrimSpace(l), "NAMESPACE") {
lines[i] = strings.TrimSuffix(l, "\n") + " " + tillerNS
}
}
return strings.Join(lines, "\n")
}
// buildState builds the currentState map contianing information about all releases existing in a k8s cluster
func buildState() {
log.Println("INFO: mapping the current helm state ...")
currentState = make(map[string]releaseState)
lines := strings.Split(getAllReleases(), "\n")
for i := 0; i < len(lines); i++ {
if lines[i] == "" || (strings.HasPrefix(strings.TrimSpace(lines[i]), "NAME") && strings.HasSuffix(strings.TrimSpace(lines[i]), "NAMESPACE")) {
continue
}
r, _ := strconv.Atoi(strings.Fields(lines[i])[1])
t := strings.Fields(lines[i])[2] + " " + strings.Fields(lines[i])[3] + " " + strings.Fields(lines[i])[4] + " " +
strings.Fields(lines[i])[5] + " " + strings.Fields(lines[i])[6]
time, err := time.Parse("Mon Jan _2 15:04:05 2006", t)
if err != nil {
log.Fatal("ERROR: while converting release time: " + err.Error())
}
currentState[strings.Fields(lines[i])[0]] = releaseState{
Revision: r,
Updated: time,
Status: strings.Fields(lines[i])[7],
Chart: strings.Fields(lines[i])[8],
Namespace: strings.Fields(lines[i])[9],
TillerNamespace: strings.Fields(lines[i])[10],
}
}
}
// Deprecated: listReleases lists releases in a given namespace and with a given status
func listReleases(namespace string, scope string) string {
var options string
if scope == "all" {
options = "--all -q"
} else if scope == "deleted" {
options = "--deleted -q"
} else if scope == "deployed" && namespace != "" {
options = "--deployed -q --namespace " + namespace
} else if scope == "deployed" && namespace == "" {
options = "--deployed -q "
} else if scope == "failed" {
options = "--failed -q"
} else {
options = "--all -q"
log.Println("INFO: scope " + scope + " is not valid, using [ all ] instead!")
}
ns := namespace
tls := ""
if namespace == "" {
ns = "all"
tls = getNSTLSFlags("kube-system")
} else {
tls = getNSTLSFlags(namespace)
}
cmd := command{
Cmd: "bash",
Args: []string{"-c", "helm list " + options + tls},
Description: "listing the existing releases in namespace [ " + ns + " ] with status [ " + scope + " ]",
}
exitCode, result := cmd.exec(debug, verbose)
if exitCode != 0 {
log.Fatal("ERROR: failed to list " + scope + " releases in " + ns + " namespace(s): " + result)
}
return result
}
// helmRealseExists checks if a Helm release is/was deployed in a k8s cluster.
// The search criteria is:
//
// -releaseName: the name of the release to look for. Helm releases have unique names within a k8s cluster.
// -scope: defines where to search for the release. Options are: [deleted, deployed, all, failed]
// -namespace: search in that namespace (only applicable if searching for currently deployed releases)
func helmReleaseExists(namespace string, releaseName string, status string) bool {
v, ok := currentState[releaseName]
if !ok {
return false
}
if namespace != "" && status != "" {
if v.Namespace == namespace && v.Status == strings.ToUpper(status) {
return true
}
return false
} else if namespace != "" {
if v.Namespace == namespace {
return true
}
return false
} else if status != "" {
if v.Status == strings.ToUpper(status) {
return true
}
return false
}
return true
}
// getReleaseNamespace returns the namespace in which a release is deployed.
// throws an error and exits the program if the release does not exist.
func getReleaseNamespace(releaseName string) string {
v, ok := currentState[releaseName]
if !ok {
log.Fatal("ERROR: seems release [ " + releaseName + " ] does not exist.")
}
return v.Namespace
}
// getReleaseChart returns the Helm chart which is used by a deployed release.
// throws an error and exits the program if the release does not exist.
func getReleaseChart(releaseName string) string {
v, ok := currentState[releaseName]
if !ok {
log.Fatal("ERROR: seems release [ " + releaseName + " ] does not exist.")
}
return v.Chart
}
// getReleaseRevision returns the revision number for a release (if it exists)
func getReleaseRevision(releaseName string, state string) string {
v, ok := currentState[releaseName]
if !ok {
log.Fatal("ERROR: seems release [ " + releaseName + " ] does not exist.")
}
return strconv.Itoa(v.Revision)
}
// getReleaseChartName extracts and returns the Helm chart name from the chart info retrieved by getReleaseChart().
// example: getReleaseChart() returns "jenkins-0.9.0" and this functions will extract "jenkins" from it.
func getReleaseChartName(releaseName string) string {
chart := getReleaseChart(releaseName)
runes := []rune(chart)
return string(runes[0:strings.LastIndexByte(chart[0:strings.IndexByte(chart, '.')], '-')])
}
// getReleaseChartVersion extracts and returns the Helm chart version from the chart info retrieved by getReleaseChart().
// example: getReleaseChart() returns "jenkins-0.9.0" and this functions will extract "0.9.0" from it.
func getReleaseChartVersion(releaseName string) string {
chart := getReleaseChart(releaseName)
runes := []rune(chart)
return string(runes[strings.LastIndexByte(chart, '-')+1 : len(chart)])
}
// getReleaseStatus returns the output of Helm status command for a release.
// if the release does not exist, it returns an empty string without breaking the program execution.
func getReleaseStatus(releaseName string) string {
cmd := command{
Cmd: "bash",
Args: []string{"-c", "helm status " + releaseName + getNSTLSFlags("kube-system")},
Description: "inspecting the status of release: " + releaseName,
}
exitCode, result := cmd.exec(debug, verbose)
if exitCode == 0 {
return result
}
log.Fatal("ERROR: while checking release [ " + releaseName + " ] status: " + result)
return ""
}
// getNSTLSFlags returns TLS flags for a given namespace if it's deployed with TLS
func getNSTLSFlags(ns string) string {
tls := ""
if tillerTLSEnabled(ns) {
tls = " --tls --tls-ca-cert " + ns + "-ca.cert --tls-cert " + ns + "-client.cert --tls-key " + ns + "-client.key "
}
return tls
}