-
Notifications
You must be signed in to change notification settings - Fork 0
/
backup.go
217 lines (171 loc) · 6.33 KB
/
backup.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
/*
Copyright © 2023-2024 Red Hat
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 cmd
/*
"backup" subcommand creates a backup of a deployment.
*/
import (
"errors"
"os"
"path/filepath"
cliutils "github.com/softwarefactory-project/sf-operator/cli/cmd/utils"
controllers "github.com/softwarefactory-project/sf-operator/controllers"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
apiv1 "k8s.io/api/core/v1"
ctrl "sigs.k8s.io/controller-runtime"
)
const (
zuulBackupPod = "zuul-scheduler-0"
dbBackupPod = "mariadb-0"
DBBackupPath = "mariadb/db-zuul.sql"
ZuulBackupPath = "zuul/zuul.keys"
SecretsBackupPath = "secrets/"
)
// Short legend what to backup
// - ca-cert - this is the local CA root certificate material. We might need keep it because it is used to
// generate the zookeeper-client-tls and zookeeper-server-tls secrets.
// The zookeeper-client-tls will be used by external zuul component like the executor
// - zookeeper-client-tls
// - zookeeper-server-tls
// - nodepool-builder-ssh-key - this key pair is used to connect on an image-builder machine. The builder machine
// have the pub key part in the .ssh/authorized_keys file
// - zuul-ssh-key This is the key pair used by Zuul to connect on external system - like gerrit.
// This key is added as authorized keys on external system
// - zuul-keystore-password - this is the key used to encrypt/decrypt key pairs stored into zookeeper
// - zuul-auth-secret - this contains the secret for the zuul-client connection
var SecretsToBackup = []string{
"ca-cert",
"zookeeper-client-tls",
"zookeeper-server-tls",
"nodepool-builder-ssh-key",
"zuul-ssh-key",
"zuul-keystore-password",
"zuul-auth-secret",
}
func createSecretBackup(backupDir string, env cliutils.ENV) {
ctrl.Log.Info("Creating secrets backup...")
secretsDir := backupDir + "/" + SecretsBackupPath
cliutils.CreateDirectory(secretsDir, 0755)
for _, secName := range SecretsToBackup {
secret := apiv1.Secret{}
cliutils.GetMOrDie(&env, secName, &secret)
// convert secret content to string (was bytes)
strMap := cliutils.ConvertMapOfBytesToMapOfStrings(secret.Data)
// create new map with important content
dataMap := map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]string{
"name": secret.Name,
},
"type": secret.Type,
"data": strMap,
}
// dump to yaml
yamlData, err := yaml.Marshal(dataMap)
if err != nil {
ctrl.Log.Error(err, "Can not dump to yaml")
os.Exit(1)
}
// write to file
cliutils.WriteContentToFile(secretsDir+"/"+secret.Name+".yaml", yamlData, 0640)
}
ctrl.Log.Info("Finished doing secret backup!")
}
func createZuulKeypairBackup(backupDir string, kubeContext string, env cliutils.ENV) {
ctrl.Log.Info("Doing Zuul keys backup...")
pod := apiv1.Pod{}
cliutils.GetMOrDie(&env, zuulBackupPod, &pod)
// https://zuul-ci.org/docs/zuul/latest/client.html
zuulBackupPath := backupDir + "/" + ZuulBackupPath
zuulBackupDir := filepath.Dir(zuulBackupPath)
cliutils.CreateDirectory(zuulBackupDir, 0755)
backupZuulCMD := []string{
"zuul-admin",
"export-keys",
"/tmp/zuul-backup",
}
backupZuulPrintCMD := []string{
"cat",
"/tmp/zuul-backup",
}
backupZuulRemoveCMD := []string{
"rm",
"/tmp/zuul-backup",
}
// Execute command for backup
cliutils.RunRemoteCmd(kubeContext, env.Ns, pod.Name, controllers.ZuulSchedulerIdent, backupZuulCMD)
// Take output of the backup
commandBuffer := cliutils.RunRemoteCmd(kubeContext, env.Ns, pod.Name, controllers.ZuulSchedulerIdent, backupZuulPrintCMD)
// write stdout to file
cliutils.WriteContentToFile(zuulBackupPath, commandBuffer.Bytes(), 0640)
// Remove key file from the pod
cliutils.RunRemoteCmd(kubeContext, env.Ns, pod.Name, controllers.ZuulSchedulerIdent, backupZuulRemoveCMD)
ctrl.Log.Info("Finished doing Zuul private keys backup!")
}
func createMySQLBackup(backupDir string, kubeContext string, env cliutils.ENV) {
ctrl.Log.Info("Doing DB backup...")
// create MariaDB dir
mariadbBackupPath := backupDir + "/" + DBBackupPath
mariaDBBackupDir := filepath.Dir(mariadbBackupPath)
cliutils.CreateDirectory(mariaDBBackupDir, 0755)
pod := apiv1.Pod{}
cliutils.GetMOrDie(&env, dbBackupPod, &pod)
// NOTE: We use option: --single-transaction to avoid error:
// "The user specified as a definer ('mariadb.sys'@'localhost') does not exist" when using LOCK TABLES
backupZuulCMD := []string{
"mysqldump",
"--databases",
"zuul",
"--single-transaction",
}
// just create Zuul DB backup
commandBuffer := cliutils.RunRemoteCmd(kubeContext, env.Ns, pod.Name, controllers.MariaDBIdent, backupZuulCMD)
// write stdout to file
cliutils.WriteContentToFile(mariadbBackupPath, commandBuffer.Bytes(), 0640)
ctrl.Log.Info("Finished doing DBs backup!")
}
func backupCmd(kmd *cobra.Command, args []string) {
backupDir, _ := kmd.Flags().GetString("backup_dir")
if backupDir == "" {
ctrl.Log.Error(errors.New("no backup dir set"), "You need to set --backup_dir parameter!")
os.Exit(1)
}
cliutils.CreateDirectory(backupDir, 0755)
kubeContext, env := cliutils.GetCLIENV(kmd)
if env.Ns == "" {
ctrl.Log.Error(errors.New("no namespace set"), "You need to specify the namespace!")
os.Exit(1)
}
ctrl.Log.Info("Starting backup process for services in namespace: " + env.Ns)
// create secret backup
createSecretBackup(backupDir, env)
// create zuul backup
createZuulKeypairBackup(backupDir, kubeContext, env)
// create DB backup
createMySQLBackup(backupDir, kubeContext, env)
}
func MkBackupCmd() *cobra.Command {
var (
backupDir string
backupCmd = &cobra.Command{
Use: "backup",
Short: "Create a backup of a deployment",
Long: `This command will do a backup of important resources`,
Run: backupCmd,
}
)
backupCmd.Flags().StringVar(&backupDir, "backup_dir", "", "The path to the backup directory")
return backupCmd
}