9f0de2dd67ef734d9779ebbe80c08aa7cad36217
[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         bindir := strings.TrimSpace(buf.String())
40
41         datadir := filepath.Join(boot.tempdir, "pgdata")
42         err = os.Mkdir(datadir, 0755)
43         if err != nil {
44                 return err
45         }
46         err = boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "initdb"), "-D", datadir)
47         if err != nil {
48                 return err
49         }
50
51         err = boot.RunProgram(ctx, boot.tempdir, nil, nil, "cp", "server.crt", "server.key", datadir)
52         if err != nil {
53                 return err
54         }
55
56         port := boot.cluster.PostgreSQL.Connection["port"]
57
58         boot.waitShutdown.Add(1)
59         go func() {
60                 defer boot.waitShutdown.Done()
61                 fail(boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "postgres"),
62                         "-l",          // enable ssl
63                         "-D", datadir, // data dir
64                         "-k", datadir, // socket dir
65                         "-p", boot.cluster.PostgreSQL.Connection["port"],
66                 ))
67         }()
68
69         for {
70                 if ctx.Err() != nil {
71                         return ctx.Err()
72                 }
73                 if exec.CommandContext(ctx, "pg_isready", "--timeout=10", "--host="+boot.cluster.PostgreSQL.Connection["host"], "--port="+port).Run() == nil {
74                         break
75                 }
76                 time.Sleep(time.Second / 2)
77         }
78         db, err := sql.Open("postgres", arvados.PostgreSQLConnection{
79                 "host":   datadir,
80                 "port":   port,
81                 "dbname": "postgres",
82         }.String())
83         if err != nil {
84                 return fmt.Errorf("db open failed: %s", err)
85         }
86         defer db.Close()
87         conn, err := db.Conn(ctx)
88         if err != nil {
89                 return fmt.Errorf("db conn failed: %s", err)
90         }
91         defer conn.Close()
92         _, err = conn.ExecContext(ctx, `CREATE USER `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["user"])+` WITH SUPERUSER ENCRYPTED PASSWORD `+pq.QuoteLiteral(boot.cluster.PostgreSQL.Connection["password"]))
93         if err != nil {
94                 return fmt.Errorf("createuser failed: %s", err)
95         }
96         _, err = conn.ExecContext(ctx, `CREATE DATABASE `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["dbname"]))
97         if err != nil {
98                 return fmt.Errorf("createdb failed: %s", err)
99         }
100         return nil
101 }