1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
19 "git.arvados.org/arvados.git/sdk/go/arvados"
23 // Run a postgresql server in a private data directory. Set up a db
24 // user, database, and TCP listener that match the supervisor's
25 // configured database connection info.
26 type runPostgreSQL struct{}
28 func (runPostgreSQL) String() string {
32 func (runPostgreSQL) Run(ctx context.Context, fail func(error), super *Supervisor) error {
33 err := super.wait(ctx, createCertificates{})
38 if super.ClusterType == "production" {
42 postgresUser, err := user.Current()
43 iamroot := postgresUser.Uid == "0"
45 return fmt.Errorf("user.Current(): %w", err)
47 postgresUser, err = user.Lookup("postgres")
49 return fmt.Errorf("user.Lookup(\"postgres\"): %s", err)
53 buf := bytes.NewBuffer(nil)
54 err = super.RunProgram(ctx, super.tempdir, runOptions{output: buf}, "pg_config", "--bindir")
58 bindir := strings.TrimSpace(buf.String())
60 datadir := filepath.Join(super.tempdir, "pgdata")
61 err = os.Mkdir(datadir, 0700)
65 prog, args := filepath.Join(bindir, "initdb"), []string{"-D", datadir, "-E", "utf8"}
67 opts.env = append(opts.env,
68 "PGHOST="+super.cluster.PostgreSQL.Connection["host"],
69 "PGPORT="+super.cluster.PostgreSQL.Connection["port"],
70 "PGUSER="+postgresUser.Username,
75 postgresUID, err := strconv.Atoi(postgresUser.Uid)
77 return fmt.Errorf("user.Lookup(\"postgres\"): non-numeric uid?: %q", postgresUser.Uid)
79 postgresGid, err := strconv.Atoi(postgresUser.Gid)
81 return fmt.Errorf("user.Lookup(\"postgres\"): non-numeric gid?: %q", postgresUser.Gid)
83 err = os.Chown(super.tempdir, 0, postgresGid)
87 err = os.Chmod(super.tempdir, 0710)
91 err = os.Chown(datadir, postgresUID, 0)
95 opts.user = "postgres"
97 err = super.RunProgram(ctx, super.tempdir, opts, prog, args...)
102 err = super.RunProgram(ctx, super.tempdir, runOptions{}, "cp", "server.crt", "server.key", datadir)
107 err = super.RunProgram(ctx, super.tempdir, runOptions{}, "chown", "postgres", datadir+"/server.crt", datadir+"/server.key")
113 super.waitShutdown.Add(1)
115 defer super.waitShutdown.Done()
116 prog, args := filepath.Join(bindir, "postgres"), []string{
118 "-D", datadir, // data dir
119 "-k", datadir, // socket dir
120 "-h", super.cluster.PostgreSQL.Connection["host"],
121 "-p", super.cluster.PostgreSQL.Connection["port"],
123 fail(super.RunProgram(ctx, super.tempdir, opts, prog, args...))
127 if ctx.Err() != nil {
130 err := super.RunProgram(ctx, super.tempdir, opts, "pg_isready", "--timeout=10")
134 time.Sleep(time.Second / 2)
136 pgconn := arvados.PostgreSQLConnection{
138 "port": super.cluster.PostgreSQL.Connection["port"],
139 "user": postgresUser.Username,
140 "dbname": "postgres",
142 db, err := sql.Open("postgres", pgconn.String())
144 return fmt.Errorf("db open failed: %s", err)
147 conn, err := db.Conn(ctx)
149 return fmt.Errorf("db conn failed: %s", err)
152 _, err = conn.ExecContext(ctx, `CREATE USER `+pq.QuoteIdentifier(super.cluster.PostgreSQL.Connection["user"])+` WITH SUPERUSER ENCRYPTED PASSWORD `+pq.QuoteLiteral(super.cluster.PostgreSQL.Connection["password"]))
154 return fmt.Errorf("createuser failed: %s", err)
156 _, err = conn.ExecContext(ctx, `CREATE DATABASE `+pq.QuoteIdentifier(super.cluster.PostgreSQL.Connection["dbname"])+` WITH TEMPLATE template0 ENCODING 'utf8'`)
158 return fmt.Errorf("createdb failed: %s", err)