/
apply.go
340 lines (297 loc) · 12.4 KB
/
apply.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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
package kubekit
import (
"fmt"
"os"
"path/filepath"
"github.com/liferaft/kubekit/cli"
"github.com/spf13/cobra"
"github.com/liferaft/kubekit/pkg/kluster"
)
var (
doProvision,
doConfigure,
doCerts,
forceCerts,
doExportTF,
doExportK8s,
doPlan,
doPkgBackup bool
)
// applyCmd represents the apply command
var applyCmd = &cobra.Command{
Use: "apply [cluster] NAME",
Aliases: []string{"a"},
Short: "Applies the configuration into your infrastructure",
Long: `Apply is used to apply the configuration into your infrastructure. This means
apply will create the cluster if it doesn't exists or will update or apply the
configuration changes if it exists.`,
RunE: applyClusterRun,
}
// applyClusterCmd represents the 'apply cluster' command
var applyClusterCmd = &cobra.Command{
Hidden: true,
Aliases: []string{"c"},
Use: "cluster NAME",
Short: "Applies the configuration into your infrastructure",
Long: `Apply is used to apply the configuration into your infrastructure. This means
apply will create the cluster if it doesn't exists or will update or apply the
configuration changes if it exists.`,
RunE: applyClusterRun,
}
// applyPackageCmd represents the 'apply package' command
var applyPackageCmd = &cobra.Command{
Hidden: true,
Aliases: []string{"pkg"},
Use: "package CLUSTER-NAME",
Short: "Applies or install the package to every cluster node",
Long: `Apply is used to install a package to every cluster node. Useful for development
or testing.`,
RunE: applyPackageRun,
}
func addApplyCmd() {
// apply [cluster] NAME --provision --configure --certificates --generate-certs --export --plan --CERT-key-file FILE --CERT-cert-file FILE
RootCmd.AddCommand(applyCmd)
applyCmd.Flags().BoolVarP(&doProvision, "provision", "p", false, "only apply the provisioning. If possible for the cluster platform, creates or updates the nodes of the cluster")
applyCmd.Flags().BoolVarP(&doConfigure, "configure", "c", false, "only apply the configuration. The cluster must exists. Generate the certificates (if doesn't exists), install and configure Kubernetes on the existing cluster")
applyCmd.Flags().StringP("package-file", "f", "", "package to install before configure. By default will be at the cluster directory named 'kubekit.rpm' or '.deb'")
// It's been discussed by the team if keep or remove --certificates. Kubernetes can do it with kubectl
// applyCmd.Flags().BoolVar(&doCerts, "certificates", false, "only apply the certificates. The Kubernetes cluster must exists. If the certificates doesn't exists then will be created")
applyCmd.Flags().BoolVar(&forceCerts, "generate-certs", false, "Overwrite or force the certificates generation even if they exists")
applyCmd.Flags().BoolVar(&doExportTF, "export-tf", false, "don't apply, just export the Terraform templates to the cluster config directory")
applyCmd.Flags().BoolVar(&doExportK8s, "export-k8s", false, "don't apply, just export the Kubernetes manifests templates to the cluster config directory")
applyCmd.Flags().Bool("force-pkg", false, "force install of package")
// Advance command, do not print in help:
// applyCmd.Flags().MarkHidden("export")
applyCmd.Flags().BoolVar(&doPlan, "plan", false, "don't apply, just print the provisioning changes")
// Advance command, do not print in help:
// applyCmd.Flags().MarkHidden("plan")
addCertFlags(applyCmd)
applyCmd.AddCommand(applyClusterCmd)
applyClusterCmd.Flags().BoolVarP(&doProvision, "provision", "p", false, "only apply the provisioning. If possible for the cluster platform, creates or updates the nodes of the cluster")
applyClusterCmd.Flags().BoolVarP(&doConfigure, "configure", "c", false, "only apply the configuration. The cluster must exists. Generate the certificates (if doesn't exists), install and configure Kubernetes on the existing cluster")
applyClusterCmd.Flags().StringP("package-file", "f", "", "package to install before configure. By default will be at the cluster directory named 'kubekit.rpm' or '.deb'")
// It's been discussed by the team if keep or remove --certificates. Kubernetes can do it with kubectl
// applyClusterCmd.Flags().BoolVar(&doCerts, "certificates", false, "only apply the certificates. The Kubernetes cluster must exists. If the certificates doesn't exists then will be created")
applyClusterCmd.Flags().BoolVar(&forceCerts, "generate-certs", false, "Overwrite or force the certificates generation even if they exists")
applyClusterCmd.Flags().BoolVar(&doExportTF, "export-tf", false, "don't apply, just export the Terraform templates to the cluster config directory")
applyClusterCmd.Flags().BoolVar(&doExportK8s, "export-k8s", false, "don't apply, just export the Kubernetes manifests templates to the cluster config directory")
// Advance command, do not print in help:
// applyClusterCmd.Flags().MarkHidden("export")
applyClusterCmd.Flags().BoolVar(&doPlan, "plan", false, "don't apply, just print the provisioning changes")
applyClusterCmd.Flags().Bool("force-pkg", false, "force install of package")
// Advance command, do not print in help:
// applyClusterCmd.Flags().MarkHidden("plan")
addCertFlags(applyClusterCmd)
applyCmd.AddCommand(applyPackageCmd)
applyPackageCmd.Flags().BoolVar(&doPkgBackup, "backup", false, "backup the package at the cluster node if there was a previous package file")
applyPackageCmd.Flags().StringP("package-file", "f", "", "package file to apply. By default will be at the cluster directory named 'kubekit.rpm' or '.deb'")
applyPackageCmd.Flags().Bool("force-pkg", false, "force install of package")
}
func applyClusterRun(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("requires a cluster name")
}
if len(args) != 1 {
return fmt.Errorf("accepts 1 cluster name, received %d. %v", len(args), args)
}
clusterName := args[0]
if len(clusterName) == 0 {
return fmt.Errorf("cluster name cannot be empty")
}
// the cluster config file must exists. This command should be executed after 'init' or will fail
cluster, err := loadCluster(clusterName)
if err != nil {
return err
}
// generate (if doesn't exists) the SSH keys, required for the terraform templates and provisioner
if err := cluster.HandleKeys(); err != nil {
return err
}
// If so, that's all, export them and return
if doExportTF {
return cluster.ExportTF()
}
// If so, that's all, print the plan and return
if doPlan {
return cluster.Plan(false)
}
// if one of these flags is set, then do not apply the entire process, just
// the explicit actions specified by the flags
explicitActions := doProvision || doConfigure // || doCerts
// if not explicit action was set, then do provisioning as part of the e2e
// process. Otherwise, do provisioning only if explicitly requested
if (!explicitActions || doProvision) && !doExportK8s {
if err := provision(cluster); err != nil {
return err
}
// ... apply the package (if any) ...
pkgFilename := cmd.Flags().Lookup("package-file").Value.String()
forcePkg := cmd.Flags().Lookup("force-pkg").Value.String() == "true"
platform := cluster.Platform()
if pkgFilename != "" && (platform == "aks" || platform == "eks") {
// warn about not needing package
msg := "It will NOT be copied and installed without --force-pkg option"
if forcePkg {
msg = "It will be copied and installed as --force-pkg was set"
}
config.UI.Log.Warnf("package file is specified and is not required for eks or aks. %s", msg)
}
//if (not eks AND not aks) OR we are forcing, copy and install
if (platform != "aks" && platform != "eks") || forcePkg {
if err := copyAndExecPackage(cluster, pkgFilename, forcePkg); err != nil {
return err
}
}
}
// if not explicit action was set, then do configuration as part of the e2e
// process. Otherwise, do configuration only if explicitly requested
if !explicitActions || doConfigure {
// for platforms that are not "eks" and "aks", generate the certificates and apply the package (if any)
// initialize the certificates if they don't exists, unless requested with forceCerts ...
userCACertsFiles, err := cli.GetCertFlags(cmd)
if err != nil {
return err
}
// TODO; Fix the overwrite of certificates. Now is set to overwrite
forceCerts = true
if err := initCertificates(clusterName, cluster, forceCerts, userCACertsFiles); err != nil {
return err
}
// ... generate the kubeconfig file
if err := cluster.LoadState(); err != nil {
return err
}
if err := cluster.CreateKubeConfigFile(); err != nil {
return err
}
if doExportK8s {
return cluster.ExportK8s()
}
// ... and do the configuration
return configure(cluster)
}
// DEBUG:
// var provisionFlag, configureFlag, certificatesFlag, generateCertsFlag, exportFlag, planFlag string
// if doProvision {
// provisionFlag = " --provision"
// }
// if doConfigure {
// configureFlag = " --configure"
// }
// if doCerts {
// certificatesFlag = " --certificates"
// }
// if forceCerts {
// generateCertsFlag = " --generate-certs"
// }
// if doExportTF {
// exportFlagTF = " --export-tf"
// }
// if doExportK8s {
// exportFlagK8s = " --export-k8s"
// }
// if doPlan {
// planFlag = " --plan"
// }
// cmd.Printf("apply%s%s%s%s%s%s%s%s --CERT-key-file FILE --CERT-cert-file FILE\n", clusterName, provisionFlag, configureFlag, certificatesFlag, generateCertsFlag, exportFlagTF, exportFlagK8s, planFlag)
return nil
}
func applyPackageRun(cmd *cobra.Command, args []string) error {
return actionPackageRun(cmd, args, true)
}
func findPackage(clusterPath string) string {
formats := []string{"rpm", "deb"}
pkgFile := filepath.Join(clusterPath, "kubekit.")
for _, f := range formats {
if _, err := os.Stat(pkgFile + f); err == nil {
return pkgFile + f
}
}
return ""
}
func loadCluster(clusterName string) (*kluster.Kluster, error) {
return kluster.LoadCluster(clusterName, config.ClustersDir(), config.UI)
}
func provision(cluster *kluster.Kluster) error {
errP := cluster.Create()
// TODO: Should it save the cluster if fail?
// If there is an active state, and fail to re-provision, the state is empty and will be deleted.
// If the provision fails but something was provisioned, should the cluster state/config be saved/updated?
errS := cluster.Save()
if errP != nil && errS != nil {
return fmt.Errorf("failed to create one of the platform(s) and to save the cluster configuration file.\n%s\n%s", errP, errS)
}
if errP != nil {
return errP
}
return errS
}
func copyAndExecPackage(cluster *kluster.Kluster, pkgFilename string, forcePkg bool) error {
clusterPath := cluster.Dir()
if len(pkgFilename) == 0 {
pkgFilename = findPackage(clusterPath)
if len(pkgFilename) == 0 {
config.UI.Log.Warnf("cannot find any package file in the cluster directory")
return nil
}
}
if err := copyPackage(cluster, pkgFilename, true); err != nil {
return err
}
filename := filepath.Base(pkgFilename)
pkgFilepath := filepath.Join("/tmp", filename)
return execPackage(cluster, pkgFilepath, forcePkg)
}
func configure(cluster *kluster.Kluster) error {
errC := cluster.Configure()
errS := cluster.Save()
if errC != nil && errS != nil {
return fmt.Errorf("failed to configure Kubernetes and to save the cluster configuration file.\n%s\n%s", errC, errS)
}
if errC != nil {
return errC
}
return errS
}
func actionPackageRun(cmd *cobra.Command, args []string, doExec bool) error {
if len(args) == 0 {
return fmt.Errorf("requires a cluster name")
}
if len(args) != 1 {
return fmt.Errorf("accepts 1 cluster name, received %d. %v", len(args), args)
}
clusterName := args[0]
if len(clusterName) == 0 {
return fmt.Errorf("cluster name cannot be empty")
}
pkgFilename := cmd.Flags().Lookup("package-file").Value.String()
cluster, err := loadCluster(clusterName)
if err != nil {
return err
}
clusterPath := cluster.Dir()
if len(pkgFilename) == 0 {
pkgFilename = findPackage(clusterPath)
if len(pkgFilename) == 0 {
return fmt.Errorf("cannot find the package name for the cluster %s", clusterName)
}
}
// // DEBUG:
// var backupFlag string
// actionStr := "copy"
// if doPkgBackup {
// backupFlag = " --backup"
// }
// if doExec {
// actionStr = "apply"
// }
// cmd.Printf("%s package %s --package-file %s %s\n", actionStr, clusterName, pkgFilename, backupFlag)
err = copyPackage(cluster, pkgFilename, doPkgBackup)
if err != nil || !doExec {
return err
}
filename := filepath.Base(pkgFilename)
pkgFilepath := filepath.Join("/tmp", filename)
forcePkg := cmd.Flags().Lookup("force-pkg").Value.String() == "true"
return execPackage(cluster, pkgFilepath, forcePkg)
}