15954: Merge branch 'master'
[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         go func() {
60                 fail(boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "postgres"),
61                         "-l",          // enable ssl
62                         "-D", datadir, // data dir
63                         "-k", datadir, // socket dir
64                         "-p", boot.cluster.PostgreSQL.Connection["port"],
65                 ))
66         }()
67
68         for {
69                 if ctx.Err() != nil {
70                         return ctx.Err()
71                 }
72                 if exec.CommandContext(ctx, "pg_isready", "--timeout=10", "--host="+boot.cluster.PostgreSQL.Connection["host"], "--port="+port).Run() == nil {
73                         break
74                 }
75                 time.Sleep(time.Second / 2)
76         }
77         db, err := sql.Open("postgres", arvados.PostgreSQLConnection{
78                 "host":   datadir,
79                 "port":   port,
80                 "dbname": "postgres",
81         }.String())
82         if err != nil {
83                 return fmt.Errorf("db open failed: %s", err)
84         }
85         defer db.Close()
86         conn, err := db.Conn(ctx)
87         if err != nil {
88                 return fmt.Errorf("db conn failed: %s", err)
89         }
90         defer conn.Close()
91         _, err = conn.ExecContext(ctx, `CREATE USER `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["user"])+` WITH SUPERUSER ENCRYPTED PASSWORD `+pq.QuoteLiteral(boot.cluster.PostgreSQL.Connection["password"]))
92         if err != nil {
93                 return fmt.Errorf("createuser failed: %s", err)
94         }
95         _, err = conn.ExecContext(ctx, `CREATE DATABASE `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["dbname"]))
96         if err != nil {
97                 return fmt.Errorf("createdb failed: %s", err)
98         }
99         return nil
100 }