-
Notifications
You must be signed in to change notification settings - Fork 7
/
env.go
219 lines (179 loc) · 6.39 KB
/
env.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
package stim
import (
"fmt"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/PremiereGlobal/stim/pkg/downloader"
"github.com/PremiereGlobal/stim/pkg/env"
"github.com/PremiereGlobal/stim/pkg/kubernetes"
"github.com/PremiereGlobal/vault-to-envs/pkg/vaulttoenvs"
)
// EnvConfig represets a environment configuration
type EnvConfig struct {
// EnvVars slice of environment variables to be set
// Values look like "FOO=BAR"
EnvVars []string
// Kubernetes env config
Kubernetes *EnvConfigKubernetes
// Kubernetes env config
Vault *EnvConfigVault
// WorkDir sets the working directory where any shell commands will be executed
WorkDir string
// Tools should contains a list of supported binary tools to install and link
Tools map[string]EnvTool
}
// EnvConfig represets a environment's Kubernetes configuration
type EnvConfigKubernetes struct {
// Cluster is the name of the Kubernetes cluster to set up
Cluster string
// ServiceAccount to use when connecting to Kubernetes
ServiceAccount string
// DefaultNamespace to use when setting up Kubernetes
DefaultNamespace string
}
// EnvConfig represets a environment's Vault configuration
type EnvConfigVault struct {
// SecretItems to load into the environment
SecretItems []*vaulttoenvs.SecretItem
}
// EnvTool contains the configuration for a CLI tool
type EnvTool struct {
Version string `yaml:"version"`
Unset bool `yaml:"unset"`
}
// Env sets up an environment based on the given config
// Shell commands can be executed against the environment
func (stim *Stim) Env(config *EnvConfig) *env.Env {
e, err := env.New(env.Config{})
if err != nil {
stim.log.Fatal("Stim: Error creating new environment.", err)
}
e.SetWorkDir(config.WorkDir)
e.AddEnvVars(config.EnvVars...)
// If requiring Kubernetes, set things up
var kc *kubernetes.Config
if config.Kubernetes != nil {
// This is the path where the kubeconfig will be written
kubeConfigFilePath := filepath.Join(e.GetPath(), "kubeconfig")
vault := stim.Vault()
// Get the Kubernetes creds from Vault
secretValues, err := vault.GetSecretKeys("secret/kubernetes/" + config.Kubernetes.Cluster + "/" + config.Kubernetes.ServiceAccount + "/kube-config")
if err != nil {
stim.log.Fatal("Stim: Error getting kubeconfig secrets for environment. {}", err)
}
// If namespace not set use the default from Vault
defaultNamespace := config.Kubernetes.DefaultNamespace
if defaultNamespace == "" {
defaultNamespace = secretValues["default-namespace"]
}
// Build the Kube config options
kubeConfigOptions := &kubernetes.ConfigOptions{
ClusterName: config.Kubernetes.Cluster,
ClusterServer: secretValues["cluster-server"],
ClusterCA: secretValues["cluster-ca"],
AuthName: config.Kubernetes.Cluster + "-" + config.Kubernetes.ServiceAccount,
AuthToken: secretValues["user-token"],
ContextName: config.Kubernetes.Cluster,
ContextSetCurrent: true,
ContextDefaultNamespace: defaultNamespace,
}
kc = kubernetes.NewConfigFromPath(kubeConfigFilePath)
err = kc.Modify(kubeConfigOptions)
if err != nil {
stim.log.Fatal("Stim: Error writing kubeconfig for environment. {}", err)
}
// Tell the environment to use the kubeconfig in the environment PATH
e.AddEnvVars([]string{fmt.Sprintf("%s=%s", "KUBECONFIG", kubeConfigFilePath)}...)
}
// If requiring secrets, set those up
if config.Vault != nil && len(config.Vault.SecretItems) > 0 {
vault := stim.Vault()
vaultAddress, err := vault.GetAddress()
if err != nil {
stim.log.Fatal("Stim: Unable to get Vault address for environment. {}", err)
}
vaultToken, err := vault.GetToken()
if err != nil {
stim.log.Fatal("Stim: Unable to get Vault token for environment. {}", err)
}
v2e := vaulttoenvs.NewVaultToEnvs(&vaulttoenvs.Config{
VaultAddr: vaultAddress,
})
v2e.SetVaultToken(vaultToken)
v2e.AddSecretItems(config.Vault.SecretItems...)
sleepTime := time.Duration(time.Second * 2)
var secretEnvs []string
for i := 0; i < 5; i++ {
secretEnvs, err = v2e.GetEnvs()
if err != nil {
if stim.ConfigGetBool("vault.retryOnThrottle") && strings.Contains(err.Error(), "Throttling: Rate exceeded") {
stim.log.Info("Stim: Got Throttling error waiting {} then trying again, try number:{}", sleepTime, i+1)
time.Sleep(sleepTime)
sleepTime += sleepTime
continue
}
stim.log.Fatal("Stim: Unable to get Vault secrets for environment. {}", err)
}
break
}
if err != nil {
stim.log.Fatal("Stim: Unable to get Vault secrets for environment. {}", err)
}
e.AddEnvVars(secretEnvs...)
}
// if requiring any CLI tools, download and link them here
for toolName, toolParams := range config.Tools {
version := toolParams.Version
if version == "" {
stim.log.Debug("Detecting tool version for: {}", toolName)
} else {
stim.log.Debug("Setting tool version {}/{} based on configuration", toolName, version)
}
var dl downloader.Downloader
cacheDir := stim.ConfigGetCacheDir(filepath.Join("bin", runtime.GOOS))
switch toolName {
case "vault":
if version == "" {
version, err = stim.Vault().Version()
if err != nil {
stim.log.Fatal("Unable to determine version for {}: {}", toolName, err)
}
}
dl = downloader.NewVaultDownloader(version, cacheDir)
case "kubectl":
if version == "" {
if kc == nil {
stim.log.Fatal("Kubernetes server not specified, cannot determine version")
}
k, err := kubernetes.New(kc)
if err != nil {
stim.log.Fatal("Unable to load Kube config, cannot determine version")
}
version, err = k.Version()
if err != nil {
stim.log.Fatal("Unable to determine version for {}: {}", toolName, err)
}
}
dl = downloader.NewKubeDownloader(version, cacheDir)
case "helm":
if version == "" {
stim.log.Fatal("Version detection not supported for helm, please specify a version in the config")
}
dl = downloader.NewHelmDownloader(version, cacheDir)
default:
stim.log.Fatal("Unknown deploy tool: {}", toolName)
}
result, err := dl.Download()
if err != nil {
stim.log.Fatal("Download failed: {} {}", result, err)
}
if !result.FileExists {
stim.log.Debug("Downloaded {} in {}", result.RenderedURL, result.DownloadDuration)
}
stim.log.Debug("Linking binary from {} to PATH location {}/{}", dl.GetBinPath(), e.GetPath(), toolName)
e.Link(dl.GetBinPath(), toolName)
}
return e
}