15954: Don't return from Stop() until child processes end.
[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         "fmt"
12         "os"
13         "os/exec"
14         "path/filepath"
15         "strings"
16         "time"
17
18         "git.arvados.org/arvados.git/sdk/go/arvados"
19         "github.com/lib/pq"
20 )
21
22 type runPostgreSQL struct{}
23
24 func (runPostgreSQL) String() string {
25         return "postgresql"
26 }
27
28 func (runPostgreSQL) Run(ctx context.Context, fail func(error), boot *Booter) error {
29         err := boot.wait(ctx, createCertificates{})
30         if err != nil {
31                 return err
32         }
33
34         buf := bytes.NewBuffer(nil)
35         err = boot.RunProgram(ctx, boot.tempdir, buf, nil, "pg_config", "--bindir")
36         if err != nil {
37                 return err
38         }
39         datadir := filepath.Join(boot.tempdir, "pgdata")
40
41         err = os.Mkdir(datadir, 0755)
42         if err != nil {
43                 return err
44         }
45         bindir := strings.TrimSpace(buf.String())
46
47         err = boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "initdb"), "-D", datadir)
48         if err != nil {
49                 return err
50         }
51
52         err = boot.RunProgram(ctx, boot.tempdir, nil, nil, "cp", "server.crt", "server.key", datadir)
53         if err != nil {
54                 return err
55         }
56
57         port := boot.cluster.PostgreSQL.Connection["port"]
58
59         boot.waitShutdown.Add(1)
60         go func() {
61                 defer boot.waitShutdown.Done()
62                 fail(boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "postgres"),
63                         "-l",          // enable ssl
64                         "-D", datadir, // data dir
65                         "-k", datadir, // socket dir
66                         "-p", boot.cluster.PostgreSQL.Connection["port"],
67                 ))
68         }()
69
70         for {
71                 if ctx.Err() != nil {
72                         return ctx.Err()
73                 }
74                 if exec.CommandContext(ctx, "pg_isready", "--timeout=10", "--host="+boot.cluster.PostgreSQL.Connection["host"], "--port="+port).Run() == nil {
75                         break
76                 }
77                 time.Sleep(time.Second / 2)
78         }
79         db, err := sql.Open("postgres", arvados.PostgreSQLConnection{
80                 "host":   datadir,
81                 "port":   port,
82                 "dbname": "postgres",
83         }.String())
84         if err != nil {
85                 return fmt.Errorf("db open failed: %s", err)
86         }
87         defer db.Close()
88         conn, err := db.Conn(ctx)
89         if err != nil {
90                 return fmt.Errorf("db conn failed: %s", err)
91         }
92         defer conn.Close()
93         _, err = conn.ExecContext(ctx, `CREATE USER `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["user"])+` WITH SUPERUSER ENCRYPTED PASSWORD `+pq.QuoteLiteral(boot.cluster.PostgreSQL.Connection["password"]))
94         if err != nil {
95                 return fmt.Errorf("createuser failed: %s", err)
96         }
97         _, err = conn.ExecContext(ctx, `CREATE DATABASE `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["dbname"]))
98         if err != nil {
99                 return fmt.Errorf("createdb failed: %s", err)
100         }
101         return nil
102 }