15954: Start own postgresql server.
[arvados.git] / lib / boot / postgresql.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package boot
6
7 import (
8         "bytes"
9         "context"
10         "database/sql"
11         "os"
12         "os/exec"
13         "path/filepath"
14         "strings"
15         "time"
16
17         "git.arvados.org/arvados.git/sdk/go/arvados"
18         "github.com/lib/pq"
19 )
20
21 func runPostgres(ctx context.Context, boot *Booter, ready chan<- bool) error {
22         buf := bytes.NewBuffer(nil)
23         err := boot.RunProgram(ctx, boot.tempdir, buf, nil, "pg_config", "--bindir")
24         if err != nil {
25                 return err
26         }
27         datadir := filepath.Join(boot.tempdir, "pgdata")
28
29         err = os.Mkdir(datadir, 0755)
30         if err != nil {
31                 return err
32         }
33         bindir := strings.TrimSpace(buf.String())
34
35         err = boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "initdb"), "-D", datadir)
36         if err != nil {
37                 return err
38         }
39
40         err = boot.RunProgram(ctx, boot.tempdir, nil, nil, "cp", "server.crt", "server.key", datadir)
41         if err != nil {
42                 return err
43         }
44
45         port := boot.cluster.PostgreSQL.Connection["port"]
46
47         ctx, cancel := context.WithCancel(ctx)
48         defer cancel()
49
50         go func() {
51                 for {
52                         if ctx.Err() != nil {
53                                 return
54                         }
55                         if exec.CommandContext(ctx, "pg_isready", "--timeout=10", "--host="+boot.cluster.PostgreSQL.Connection["host"], "--port="+port).Run() == nil {
56                                 break
57                         }
58                         time.Sleep(time.Second / 2)
59                 }
60                 db, err := sql.Open("postgres", arvados.PostgreSQLConnection{
61                         "host":   datadir,
62                         "port":   port,
63                         "dbname": "postgres",
64                 }.String())
65                 if err != nil {
66                         boot.logger.WithError(err).Error("db open failed")
67                         cancel()
68                         return
69                 }
70                 defer db.Close()
71                 conn, err := db.Conn(ctx)
72                 if err != nil {
73                         boot.logger.WithError(err).Error("db conn failed")
74                         cancel()
75                         return
76                 }
77                 defer conn.Close()
78                 _, err = conn.ExecContext(ctx, `CREATE USER `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["user"])+` WITH SUPERUSER ENCRYPTED PASSWORD `+pq.QuoteLiteral(boot.cluster.PostgreSQL.Connection["password"]))
79                 if err != nil {
80                         boot.logger.WithError(err).Error("createuser failed")
81                         cancel()
82                         return
83                 }
84                 _, err = conn.ExecContext(ctx, `CREATE DATABASE `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["dbname"]))
85                 if err != nil {
86                         boot.logger.WithError(err).Error("createdb failed")
87                         cancel()
88                         return
89                 }
90                 close(ready)
91                 return
92         }()
93
94         return boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "postgres"),
95                 "-l",          // enable ssl
96                 "-D", datadir, // data dir
97                 "-k", datadir, // socket dir
98                 "-p", boot.cluster.PostgreSQL.Connection["port"],
99         )
100 }