Start of DB initialisation functions

Not properly working yet
This commit is contained in:
Sybren A. Stüvel 2022-02-21 15:25:56 +01:00
parent df5e990693
commit 6bd1a86337
4 changed files with 187 additions and 0 deletions

@ -22,6 +22,7 @@ package main
import (
"context"
"flag"
"net"
"net/http"
"time"
@ -43,11 +44,29 @@ import (
"gitlab.com/blender/flamenco-ng-poc/pkg/api"
)
var cliArgs struct {
version bool
initDB bool
}
func main() {
output := zerolog.ConsoleWriter{Out: colorable.NewColorableStdout(), TimeFormat: time.RFC3339}
log.Logger = log.Output(output)
log.Info().Str("version", appinfo.ApplicationVersion).Msgf("starting %v", appinfo.ApplicationName)
parseCliArgs()
if cliArgs.version {
return
}
if cliArgs.initDB {
log.Info().Msg("creating databases")
err := persistence.InitialSetup()
if err != nil {
log.Fatal().Err(err).Msg("problem performing initial setup")
}
return
}
// Open the database.
dbCtx, dbCtxCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer dbCtxCancel()
@ -113,3 +132,27 @@ func buildWebService(flamenco api.ServerInterface, persist api_impl.PersistenceS
return e
}
func parseCliArgs() {
var quiet, debug, trace bool
flag.BoolVar(&cliArgs.version, "version", false, "Shows the application version, then exits.")
flag.BoolVar(&cliArgs.initDB, "initdb", false, "Create the database; requires admin access to PostgreSQL.")
flag.BoolVar(&quiet, "quiet", false, "Only log warning-level and worse.")
flag.BoolVar(&debug, "debug", false, "Enable debug-level logging.")
flag.BoolVar(&trace, "trace", false, "Enable trace-level logging.")
flag.Parse()
var logLevel zerolog.Level
switch {
case trace:
logLevel = zerolog.TraceLevel
case debug:
logLevel = zerolog.DebugLevel
case quiet:
logLevel = zerolog.WarnLevel
default:
logLevel = zerolog.InfoLevel
}
zerolog.SetGlobalLevel(logLevel)
}

1
go.mod

@ -54,6 +54,7 @@ require (
github.com/valyala/fasttemplate v1.2.1 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
golang.org/x/tools v0.1.7 // indirect

2
go.sum

@ -306,6 +306,8 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1U
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

@ -0,0 +1,141 @@
package persistence
/* ***** BEGIN GPL LICENSE BLOCK *****
*
* Original Code Copyright (C) 2022 Blender Foundation.
*
* This file is part of Flamenco.
*
* Flamenco is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* Flamenco is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Flamenco. If not, see <https://www.gnu.org/licenses/>.
*
* ***** END GPL LICENSE BLOCK ***** */
import (
"bufio"
"errors"
"fmt"
"os"
"runtime"
"github.com/rs/zerolog/log"
"golang.org/x/term"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
var errInputTooLong = errors.New("input is too long")
const adminDSN = "host=localhost user=postgres password=%s dbname=%s TimeZone=Europe/Amsterdam"
// InitialSetup uses the `postgres` admin user to set up the database.
// TODO: distinguish between production and development setups.
func InitialSetup() error {
// Get the password of the 'postgres' user.
adminPass, err := readPassword()
if err != nil {
return fmt.Errorf("unable to read password: %w", err)
}
// Connect to the 'postgres' database so we can create other databases.
db, err := connectDBAsAdmin(adminPass, "postgres")
if err != nil {
return fmt.Errorf("unable to connect to the database: %w", err)
}
// TODO: get username / password / database name from some config file, user input, CLI args, whatevah.
// Has to be used by the regular Flamenco Manager runs as well, though.
username := "flamenco"
userPass := "flamenco"
// tx := db.Exec("CREATE USER flamenco PASSWORD ? NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN", userPass)
// if tx.Error != nil {
// return fmt.Errorf("unable to create database user '%s': %w", username, tx.Error)
// }
{
sqlDB, err := db.DB()
if err != nil {
panic(err)
}
_, err = sqlDB.Exec("CREATE USER flamenco WITH PASSWORD $1::string NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN", userPass)
if err != nil {
panic(err)
}
}
// Create the databases.
tx := db.Debug().Exec("CREATE DATABASE flamenco OWNER ? ENCODING 'utf8'", username)
if tx.Error != nil {
return fmt.Errorf("unable to create database 'flamenco': %w", tx.Error)
}
tx = db.Exec("CREATE DATABASE flamenco-test OWNER ? ENCODING 'utf8'", username)
if tx.Error != nil {
return fmt.Errorf("unable to create database 'flamenco': %w", tx.Error)
}
// Close the connection so we can reconnect.
sqlDB, err := db.DB()
if err != nil {
fmt.Printf("error closing the database connection, please report this issue: %v", err)
} else {
sqlDB.Close()
}
// Allow 'flamenco' user to completely nuke and recreate the flamenco-test database, without needing 'CREATEDB' permission.
db, err = connectDBAsAdmin(adminPass, "flamenco-test")
if err != nil {
return fmt.Errorf("unable to reconnect to the database: %w", err)
}
tx = db.Exec("ALTER SCHEMA public OWNER TO ?", username)
if tx.Error != nil {
fmt.Printf("Unable to allow database user '%s' to reset the test database: %v\n", username, tx.Error)
fmt.Println("This is not an issue, unless you want to develop Flamenco yourself.")
}
return nil
}
func readPassword() (string, error) {
if pwFromEnv := os.Getenv("PSQL_ADMIN"); pwFromEnv != "" {
log.Info().Msg("getting password from PSQL_ADMIN environment variable")
return pwFromEnv, nil
}
fmt.Print("PostgreSQL admin password: ")
var (
line []byte
err error
)
if runtime.GOOS == "windows" {
// term.ReadPassword() doesn't work reliably on Windows, especially when you
// use a MingW terminal (like Git Bash). See
// https://github.com/golang/go/issues/11914#issuecomment-613715787 for more
// info.
//
// The downside is that this echoes the password to the terminal.
buf := bufio.NewReader(os.Stdin)
line, _, err = buf.ReadLine()
} else {
fd := int(os.Stdin.Fd())
line, err = term.ReadPassword(fd)
}
if err != nil {
return "", err
}
return string(line), nil
}
func connectDBAsAdmin(password, database string) (*gorm.DB, error) {
dsn := fmt.Sprintf(adminDSN, password, database)
return gorm.Open(postgres.Open(dsn), &gorm.Config{})
}