-
Notifications
You must be signed in to change notification settings - Fork 148
/
client.go
187 lines (158 loc) · 5.43 KB
/
client.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
// Copyright 2019 The Kanister Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package helm
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"time"
"github.com/kanisterio/kanister/pkg/field"
"github.com/kanisterio/kanister/pkg/log"
)
type HelmVersion string
const (
DefaultCommandTimeout = 5 * time.Minute
// Add elastic charts url
ElasticRepoName = "elastic"
ElasticRepoURL = "https://helm.elastic.co"
// Add incubator charts url
IncubatorRepoName = "incubator"
IncubatorRepoURL = "https://kubernetes-charts-incubator.storage.googleapis.com"
// Add stable charts url
StableRepoName = "stable"
StableRepoURL = "https://kubernetes-charts.storage.googleapis.com"
// Add couchbase chart
CouchbaseRepoName = "couchbase"
CouchbaseRepoURL = "https://couchbase-partners.github.io/helm-charts"
// HelmVersion to differentiate between helm2 and helm3 commands
V2 HelmVersion = "helmv2"
V3 HelmVersion = "helmv3"
// HelmBinNameEnvVar env var to pass a helm bin name into kanister test apps
HelmBinNameEnvVar = "KANISTER_HELM_BIN"
)
type CliClient struct {
version HelmVersion
helmBin string
}
// FindVersion returns HelmVersion based on helm binary present in the path
func FindVersion() (HelmVersion, error) {
out, err := RunCmdWithTimeout(context.TODO(), GetHelmBinName(), []string{"version", "--client", "--short"})
if err != nil {
return "", err
}
// Trim prefix if output from helm v2
out = strings.TrimPrefix(out, "Client: ")
if strings.HasPrefix(out, "v2") {
return V2, nil
}
if strings.HasPrefix(out, "v3") {
return V3, nil
}
return "", fmt.Errorf("Unsupported helm version %s", out)
}
// GetHelmBinName returns a helm bin name from env var
func GetHelmBinName() string {
if helmBin, ok := os.LookupEnv(HelmBinNameEnvVar); ok {
return helmBin
}
return "helm"
}
func NewCliClient() (Client, error) {
version, err := FindVersion()
if err != nil {
return nil, err
}
return &CliClient{
version: version,
helmBin: GetHelmBinName(),
}, nil
}
// AddRepo adds new helm repo and fetches latest charts
func (h CliClient) AddRepo(ctx context.Context, name, url string) error {
log.Debug().Print("Adding helm repo", field.M{"name": name, "url": url})
out, err := RunCmdWithTimeout(ctx, h.helmBin, []string{"repo", "add", name, url})
if err != nil {
return err
}
log.Debug().Print("Result", field.M{"output": out})
// Update all repos to fetch the latest charts
_ = h.UpdateRepo(ctx)
return nil
}
// UpdateRepo fetches latest helm charts from the repo
func (h CliClient) UpdateRepo(ctx context.Context) error {
log.Debug().Print("Fetching latest helm charts from the helm repos")
out, err := RunCmdWithTimeout(ctx, h.helmBin, []string{"repo", "update"})
if err != nil {
return err
}
log.Debug().Print("Result", field.M{"output": out})
return nil
}
// Install installs helm chart with given release name
func (h CliClient) Install(ctx context.Context, chart, version, release, namespace string, values map[string]string) error {
log.Debug().Print("Installing helm chart", field.M{"chart": chart, "version": version, "release": release, "namespace": namespace})
var setVals string
for k, v := range values {
setVals += fmt.Sprintf("%s=%s,", k, v)
}
var cmd []string
if h.version == V3 {
cmd = []string{"install", release, "--version", version, "--namespace", namespace, chart, "--set", setVals, "--wait"}
} else {
cmd = []string{"install", "--name", release, "--version", version, "--namespace", namespace, chart, "--set", setVals, "--wait"}
}
out, err := RunCmdWithTimeout(ctx, h.helmBin, cmd)
if err != nil {
return err
}
log.Debug().Print("Result", field.M{"output": out})
return nil
}
// Uninstall deletes helm release
func (h CliClient) Uninstall(ctx context.Context, release, namespace string) error {
log.Debug().Print("Uninstalling helm chart", field.M{"release": release, "namespace": namespace})
var cmd []string
if h.version == V3 {
cmd = []string{"delete", release, "--namespace", namespace}
} else {
cmd = []string{"delete", "--purge", release}
}
out, err := RunCmdWithTimeout(ctx, h.helmBin, cmd)
if err != nil {
return err
}
log.Debug().Print("Result", field.M{"output": out})
return nil
}
// RemoveRepo removes helm repo
func (h CliClient) RemoveRepo(ctx context.Context, name string) error {
log.Debug().Print("Removing helm repo", field.M{"name": name})
out, err := RunCmdWithTimeout(ctx, h.helmBin, []string{"repo", "remove", name})
if err != nil {
return err
}
log.Debug().Print("Result", field.M{"output": out})
return nil
}
// RunCmdWithTimeout executes command on host with DefaultCommandTimeout timeout
func RunCmdWithTimeout(ctx context.Context, command string, args []string) (string, error) {
log.Debug().Print("Executing command", field.M{"command": command, "args": args})
ctx, cancel := context.WithTimeout(ctx, DefaultCommandTimeout)
defer cancel()
out, err := exec.CommandContext(ctx, command, args...).CombinedOutput()
return strings.TrimSpace(string(out)), err
}