-
Notifications
You must be signed in to change notification settings - Fork 41
/
install.go
294 lines (246 loc) · 11 KB
/
install.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
package cmd
import (
"errors"
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
_ "github.com/lib/pq" // pg driver
"github.com/sethvargo/go-password/password"
"github.com/spf13/cobra"
)
// These variables are set at build time via ldflags
var eeImage string
var pauseImage string
var quayImage string
var redisImage string
var postgresImage string
// imageArchivePath is the optional location of the OCI image archive containing required install images
var imageArchivePath string
// executableDir is the optional location of the OCI image archive containing unpacked required install images
var executableDir string
// sshKey is the optional location of the SSH key you would like to use to connect to your host.
var sshKey string
// sslCert is the path to the SSL certitificate
var sslCert string
// sslKey is the path to the SSL key
var sslKey string
// sslCheckSkip holds whether or not to check the SSL certificate
var sslCheckSkip bool
// targetHostname is the hostname of the server you wish to install Quay on
var targetHostname string
// targetUsername is the name of the user on the target host to connect with SSH
var targetUsername string
// initUser is the initial username.
var initUser string
// initPassword is the password of the initial user.
var initPassword string
// quayHostname is the value to set SERVER_HOSTNAME in the Quay config.yaml
var quayHostname string
// askBecomePass holds whether or not to ask for password during SSH connection
var askBecomePass bool
// quayRoot is the directory where all the quay config data is stored
var quayRoot string
// quayStorage is the directory where all the Quay data is stored
var quayStorage string
// pgStorage is the directory where all the Postgres data is stored
var pgStorage string
// additionalArgs are arguments that you would like to append to the end of the ansible-playbook call (used mostly for development)
var additionalArgs string
// installCmd represents the install command
var installCmd = &cobra.Command{
Use: "install",
Short: "Install Quay and its required dependencies.",
Run: func(cmd *cobra.Command, args []string) {
install()
},
}
func init() {
// Add install command
rootCmd.AddCommand(installCmd)
installCmd.Flags().StringVarP(&targetHostname, "targetHostname", "H", getFQDN(), "The hostname of the target you wish to install Quay to. This defaults to $HOST")
installCmd.Flags().StringVarP(&targetUsername, "targetUsername", "u", os.Getenv("USER"), "The user on the target host which will be used for SSH. This defaults to $USER")
installCmd.Flags().StringVarP(&sshKey, "ssh-key", "k", os.Getenv("HOME")+"/.ssh/quay_installer", "The path of your ssh identity key. This defaults to ~/.ssh/quay_installer")
installCmd.Flags().StringVarP(&sslCert, "sslCert", "", "", "The path to the SSL certificate Quay should use")
installCmd.Flags().StringVarP(&sslKey, "sslKey", "", "", "The path to the SSL key Quay should use")
installCmd.Flags().BoolVarP(&sslCheckSkip, "sslCheckSkip", "", false, "Whether or not to check the certificate hostname against the SERVER_HOSTNAME in config.yaml.")
installCmd.Flags().StringVarP(&initUser, "initUser", "", "init", "The username of the initial user. This defaults to init.")
installCmd.Flags().StringVarP(&initPassword, "initPassword", "", "", "The password of the initial user. If not specified, this will be randomly generated.")
installCmd.Flags().StringVarP(&quayHostname, "quayHostname", "", "", "The value to set SERVER_HOSTNAME in the Quay config.yaml. This defaults to <targetHostname>:8443")
installCmd.Flags().StringVarP(&imageArchivePath, "image-archive", "i", "", "An archive containing images")
installCmd.Flags().BoolVarP(&askBecomePass, "askBecomePass", "", false, "Whether or not to ask for sudo password during SSH connection.")
installCmd.Flags().StringVarP(&quayRoot, "quayRoot", "r", "~/quay-install", "The folder where quay persistent data are saved. This defaults to ~/quay-install")
installCmd.Flags().StringVarP(&quayStorage, "quayStorage", "", "quay-storage", "The folder where quay persistent storage data is saved. This defaults to a Podman named volume 'quay-storage'. Root is required to uninstall.")
installCmd.Flags().StringVarP(&pgStorage, "pgStorage", "", "pg-storage", "The folder where postgres persistent storage data is saved. This defaults to a Podman named volume 'pg-storage'. Root is required to uninstall.")
installCmd.Flags().StringVarP(&additionalArgs, "additionalArgs", "", "", "Additional arguments you would like to append to the ansible-playbook call. Used mostly for development.")
}
func install() {
var err error
log.Printf("Install has begun")
log.Debug("Ansible Execution Environment Image: " + eeImage)
log.Debug("Pause Image: " + pauseImage)
log.Debug("Quay Image: " + quayImage)
log.Debug("Redis Image: " + redisImage)
log.Debug("Postgres Image: " + postgresImage)
// Load execution environment
err = loadExecutionEnvironment()
check(err)
// Set quayHostname if not already set
if quayHostname == "" {
quayHostname = targetHostname + ":8443"
}
// Load the SSL certificate and the key
err = loadCerts(sslCert, sslKey, strings.Split(quayHostname, ":")[0], sslCheckSkip)
check(err)
// Check that SSH key is present, and generate if not
err = loadSSHKeys()
check(err)
// Handle Image Archive Defaulting
var imageArchiveMountFlag string
if imageArchivePath == "" {
executableDir, err := os.Executable()
check(err)
defaultArchivePath := path.Join(path.Dir(executableDir), "image-archive.tar")
if pathExists(defaultArchivePath) {
imageArchivePath = defaultArchivePath
}
} else {
if !pathExists(imageArchivePath) {
check(errors.New("Could not find image-archive.tar at " + imageArchivePath))
}
}
if imageArchivePath != "" {
imageArchiveMountFlag = fmt.Sprintf("-v %s:/runner/image-archive.tar", imageArchivePath)
log.Info("Found image archive at " + imageArchivePath)
if isLocalInstall() {
log.Printf("Unpacking image archive from %s", imageArchivePath)
cmd := exec.Command("tar", "-xvf", imageArchivePath)
if verbose {
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
}
err = cmd.Run()
check(err)
// Load Pause image
pauseArchivePath := path.Join(path.Dir(executableDir), "pause.tar")
log.Printf("Loading pause image archive from %s", pauseArchivePath)
statement := getImageMetadata("pause", pauseImage, pauseArchivePath)
pauseImport := exec.Command("/bin/bash", "-c", statement)
if verbose {
pauseImport.Stderr = os.Stderr
pauseImport.Stdout = os.Stdout
}
log.Debug("Importing Pause with command: ", pauseImport)
err = pauseImport.Run()
check(err)
// Load Redis image
redisArchivePath := path.Join(path.Dir(executableDir), "redis.tar")
log.Printf("Loading redis image archive from %s", redisArchivePath)
statement = getImageMetadata("redis", redisImage, redisArchivePath)
redisImport := exec.Command("/bin/bash", "-c", statement)
if verbose {
redisImport.Stderr = os.Stderr
redisImport.Stdout = os.Stdout
}
log.Debug("Importing Redis with command: ", redisImport)
err = redisImport.Run()
check(err)
// Load Postgres image
postgresArchivePath := path.Join(path.Dir(executableDir), "postgres.tar")
log.Printf("Loading postgres image archive from %s", postgresArchivePath)
statement = getImageMetadata("postgres", postgresImage, postgresArchivePath)
postgresImport := exec.Command("/bin/bash", "-c", statement)
if verbose {
postgresImport.Stderr = os.Stderr
postgresImport.Stdout = os.Stdout
}
log.Debug("Importing Postgres with command: ", postgresImport)
err = postgresImport.Run()
check(err)
// Load Quay image
quayArchivePath := path.Join(path.Dir(executableDir), "quay.tar")
log.Printf("Loading Quay image archive from %s", quayArchivePath)
statement = getImageMetadata("quay", quayImage, quayArchivePath)
quayImport := exec.Command("/bin/bash", "-c", statement)
if verbose {
quayImport.Stderr = os.Stderr
quayImport.Stdout = os.Stdout
}
log.Debug("Importing Quay with command: ", quayImport)
err = quayImport.Run()
check(err)
}
log.Infof("Attempting to set SELinux rules on image archive")
cmd := exec.Command("chcon", "-Rt", "svirt_sandbox_file_t", imageArchivePath)
if verbose {
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
}
if err := cmd.Run(); err != nil {
log.Warn("Could not set SELinux rule. If your system does not have SELinux enabled, you may ignore this.")
}
}
// Generate password if none provided
if initPassword == "" {
initPassword, err = password.Generate(32, 10, 0, false, false)
check(err)
}
// Set quayHostname if not already set
if quayHostname == "" {
quayHostname = targetHostname + ":8443"
}
// Add port if not present
if !strings.Contains(quayHostname, ":") {
quayHostname = quayHostname + ":8443"
}
// Set askBecomePass flag if true
var askBecomePassFlag string
if askBecomePass {
askBecomePassFlag = "-K"
}
// Set the SSL flag if cert and key are defined
var sslCertKeyFlag string
if sslCert != "" && sslKey != "" {
sslCertAbs, err := filepath.Abs(sslCert)
if err != nil {
check(errors.New("Unable to get absolute path of " + sslCert))
}
sslKeyAbs, err := filepath.Abs(sslKey)
if err != nil {
check(errors.New("Unable to get absolute path of " + sslKey))
}
sslCertKeyFlag = fmt.Sprintf(" -v %s:/runner/certs/quay.cert:Z -v %s:/runner/certs/quay.key:Z", sslCertAbs, sslKeyAbs)
}
// Run playbook
log.Printf("Running install playbook. This may take some time. To see playbook output run the installer with -v (verbose) flag.")
quayVersion := strings.Split(quayImage, ":")[1]
podmanCmd := fmt.Sprintf(`podman run `+
`--rm --interactive --tty `+
`--workdir /runner/project `+
`--net host `+
imageArchiveMountFlag+ // optional image archive flag
sslCertKeyFlag+ // optional ssl cert/key flag
` -v %s:/runner/env/ssh_key `+
`-e RUNNER_OMIT_EVENTS=False `+
`-e RUNNER_ONLY_FAILED_EVENTS=False `+
`-e ANSIBLE_HOST_KEY_CHECKING=False `+
`-e ANSIBLE_CONFIG=/runner/project/ansible.cfg `+
fmt.Sprintf("-e ANSIBLE_NOCOLOR=%t ", noColor)+
`--quiet `+
`--name ansible_runner_instance `+
fmt.Sprintf("%s ", eeImage)+
`ansible-playbook -i %s@%s, --private-key /runner/env/ssh_key -e "init_user=%s init_password=%s quay_image=%s quay_version=%s redis_image=%s postgres_image=%s pause_image=%s quay_hostname=%s local_install=%s quay_root=%s quay_storage=%s pg_storage=%s" install_mirror_appliance.yml %s %s`,
sshKey, targetUsername, targetHostname, initUser, initPassword, quayImage, quayVersion, redisImage, postgresImage, pauseImage, quayHostname, strconv.FormatBool(isLocalInstall()), quayRoot, quayStorage, pgStorage, askBecomePassFlag, additionalArgs)
log.Debug("Running command: " + podmanCmd)
cmd := exec.Command("bash", "-c", podmanCmd)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
err = cmd.Run()
check(err)
log.Printf("Quay installed successfully, config data is stored in %s", quayRoot)
log.Printf("Quay is available at %s with credentials (%s, %s)", "https://"+quayHostname, initUser, initPassword)
}