1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
20 "git.arvados.org/arvados.git/sdk/go/arvados"
24 // Run a postgresql server in a private data directory. Set up a db
25 // user, database, and TCP listener that match the supervisor's
26 // configured database connection info.
27 type runPostgreSQL struct{}
29 func (runPostgreSQL) String() string {
33 func (runPostgreSQL) Run(ctx context.Context, fail func(error), super *Supervisor) error {
34 err := super.wait(ctx, createCertificates{})
40 if u, err := user.Current(); err != nil {
41 return fmt.Errorf("user.Current(): %s", err)
42 } else if u.Uid == "0" {
46 buf := bytes.NewBuffer(nil)
47 err = super.RunProgram(ctx, super.tempdir, buf, nil, "pg_config", "--bindir")
51 bindir := strings.TrimSpace(buf.String())
53 datadir := filepath.Join(super.tempdir, "pgdata")
54 err = os.Mkdir(datadir, 0700)
58 prog, args := filepath.Join(bindir, "initdb"), []string{"-D", datadir, "-E", "utf8"}
60 postgresUser, err := user.Lookup("postgres")
62 return fmt.Errorf("user.Lookup(\"postgres\"): %s", err)
64 postgresUID, err := strconv.Atoi(postgresUser.Uid)
66 return fmt.Errorf("user.Lookup(\"postgres\"): non-numeric uid?: %q", postgresUser.Uid)
68 postgresGid, err := strconv.Atoi(postgresUser.Gid)
70 return fmt.Errorf("user.Lookup(\"postgres\"): non-numeric gid?: %q", postgresUser.Gid)
72 err = os.Chown(super.tempdir, 0, postgresGid)
76 err = os.Chmod(super.tempdir, 0710)
80 err = os.Chown(datadir, postgresUID, 0)
84 // We can't use "sudo -u" here because it creates an
85 // intermediate process that interferes with our
86 // ability to reliably kill postgres. The setuidgid
87 // program just calls exec without forking, so it
88 // doesn't have this problem.
89 args = append([]string{"postgres", prog}, args...)
92 err = super.RunProgram(ctx, super.tempdir, nil, nil, prog, args...)
97 err = super.RunProgram(ctx, super.tempdir, nil, nil, "cp", "server.crt", "server.key", datadir)
102 err = super.RunProgram(ctx, super.tempdir, nil, nil, "chown", "postgres", datadir+"/server.crt", datadir+"/server.key")
108 port := super.cluster.PostgreSQL.Connection["port"]
110 super.waitShutdown.Add(1)
112 defer super.waitShutdown.Done()
113 prog, args := filepath.Join(bindir, "postgres"), []string{
115 "-D", datadir, // data dir
116 "-k", datadir, // socket dir
117 "-p", super.cluster.PostgreSQL.Connection["port"],
120 args = append([]string{"postgres", prog}, args...)
123 fail(super.RunProgram(ctx, super.tempdir, nil, nil, prog, args...))
127 if ctx.Err() != nil {
130 if exec.CommandContext(ctx, "pg_isready", "--timeout=10", "--host="+super.cluster.PostgreSQL.Connection["host"], "--port="+port).Run() == nil {
133 time.Sleep(time.Second / 2)
135 pgconn := arvados.PostgreSQLConnection{
138 "dbname": "postgres",
141 pgconn["user"] = "postgres"
143 db, err := sql.Open("postgres", pgconn.String())
145 return fmt.Errorf("db open failed: %s", err)
148 conn, err := db.Conn(ctx)
150 return fmt.Errorf("db conn failed: %s", err)
153 _, err = conn.ExecContext(ctx, `CREATE USER `+pq.QuoteIdentifier(super.cluster.PostgreSQL.Connection["user"])+` WITH SUPERUSER ENCRYPTED PASSWORD `+pq.QuoteLiteral(super.cluster.PostgreSQL.Connection["password"]))
155 return fmt.Errorf("createuser failed: %s", err)
157 _, err = conn.ExecContext(ctx, `CREATE DATABASE `+pq.QuoteIdentifier(super.cluster.PostgreSQL.Connection["dbname"])+` WITH TEMPLATE template0 ENCODING 'utf8'`)
159 return fmt.Errorf("createdb failed: %s", err)