15954: Rename booter to supervisor, tidy up.
[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 // Run a postgresql server in a private data directory. Set up a db
23 // user, database, and TCP listener that match the supervisor's
24 // configured database connection info.
25 type runPostgreSQL struct{}
26
27 func (runPostgreSQL) String() string {
28         return "postgresql"
29 }
30
31 func (runPostgreSQL) Run(ctx context.Context, fail func(error), super *Supervisor) error {
32         err := super.wait(ctx, createCertificates{})
33         if err != nil {
34                 return err
35         }
36
37         buf := bytes.NewBuffer(nil)
38         err = super.RunProgram(ctx, super.tempdir, buf, nil, "pg_config", "--bindir")
39         if err != nil {
40                 return err
41         }
42         bindir := strings.TrimSpace(buf.String())
43
44         datadir := filepath.Join(super.tempdir, "pgdata")
45         err = os.Mkdir(datadir, 0755)
46         if err != nil {
47                 return err
48         }
49         err = super.RunProgram(ctx, super.tempdir, nil, nil, filepath.Join(bindir, "initdb"), "-D", datadir)
50         if err != nil {
51                 return err
52         }
53
54         err = super.RunProgram(ctx, super.tempdir, nil, nil, "cp", "server.crt", "server.key", datadir)
55         if err != nil {
56                 return err
57         }
58
59         port := super.cluster.PostgreSQL.Connection["port"]
60
61         super.waitShutdown.Add(1)
62         go func() {
63                 defer super.waitShutdown.Done()
64                 fail(super.RunProgram(ctx, super.tempdir, nil, nil, filepath.Join(bindir, "postgres"),
65                         "-l",          // enable ssl
66                         "-D", datadir, // data dir
67                         "-k", datadir, // socket dir
68                         "-p", super.cluster.PostgreSQL.Connection["port"],
69                 ))
70         }()
71
72         for {
73                 if ctx.Err() != nil {
74                         return ctx.Err()
75                 }
76                 if exec.CommandContext(ctx, "pg_isready", "--timeout=10", "--host="+super.cluster.PostgreSQL.Connection["host"], "--port="+port).Run() == nil {
77                         break
78                 }
79                 time.Sleep(time.Second / 2)
80         }
81         db, err := sql.Open("postgres", arvados.PostgreSQLConnection{
82                 "host":   datadir,
83                 "port":   port,
84                 "dbname": "postgres",
85         }.String())
86         if err != nil {
87                 return fmt.Errorf("db open failed: %s", err)
88         }
89         defer db.Close()
90         conn, err := db.Conn(ctx)
91         if err != nil {
92                 return fmt.Errorf("db conn failed: %s", err)
93         }
94         defer conn.Close()
95         _, err = conn.ExecContext(ctx, `CREATE USER `+pq.QuoteIdentifier(super.cluster.PostgreSQL.Connection["user"])+` WITH SUPERUSER ENCRYPTED PASSWORD `+pq.QuoteLiteral(super.cluster.PostgreSQL.Connection["password"]))
96         if err != nil {
97                 return fmt.Errorf("createuser failed: %s", err)
98         }
99         _, err = conn.ExecContext(ctx, `CREATE DATABASE `+pq.QuoteIdentifier(super.cluster.PostgreSQL.Connection["dbname"]))
100         if err != nil {
101                 return fmt.Errorf("createdb failed: %s", err)
102         }
103         return nil
104 }