You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by sh...@apache.org on 2021/10/22 01:18:10 UTC
[trafficcontrol] branch master updated: rework admin.go to allow
running outside of the strict RPM install location (#6189)
This is an automated email from the ASF dual-hosted git repository.
shamrick pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new 0d7c1b5 rework admin.go to allow running outside of the strict RPM install location (#6189)
0d7c1b5 is described below
commit 0d7c1b51a02925e3d1fa0099b4b6c551ea455625
Author: ocket8888 <oc...@apache.org>
AuthorDate: Thu Oct 21 19:17:57 2021 -0600
rework admin.go to allow running outside of the strict RPM install location (#6189)
* rework admin.go to allow running outside of the strict RPM install location
* Fix binding seeds path to env variable
* Fix logging the wrong path when the schema cannot be read
This was an issue before, but only when using --trafficvault
* Fix using the incorrect path for db configs with -v/--trafficvault
---
traffic_ops/app/db/admin.go | 301 ++++++++++++++++++++++++++++----------------
1 file changed, 191 insertions(+), 110 deletions(-)
diff --git a/traffic_ops/app/db/admin.go b/traffic_ops/app/db/admin.go
index 7f62287..2be3f20 100644
--- a/traffic_ops/app/db/admin.go
+++ b/traffic_ops/app/db/admin.go
@@ -103,22 +103,33 @@ const (
CmdSeed = "seed"
CmdLoadSchema = "load_schema"
CmdPatch = "patch"
+)
+
+// Default file system paths for TODB files.
+const (
+ defaultDBDir = "db/"
+ defaultDBConfigPath = defaultDBDir + "dbconf.yml"
+ defaultDBMigrationsPath = defaultDBDir + "migrations"
+ defaultDBSeedsPath = defaultDBDir + "seeds.sql"
+ defaultDBSchemaPath = defaultDBDir + "create_tables.sql"
+ defaultDBPatchesPath = defaultDBDir + "patches.sql"
+)
- dbDir = "db/"
- DBConfigPath = dbDir + "dbconf.yml"
- DBMigrationsPath = dbDir + "migrations"
- DBMigrationsSource = "file:" + DBMigrationsPath
- DBSeedsPath = dbDir + "seeds.sql"
- DBSchemaPath = dbDir + "create_tables.sql"
- DBPatchesPath = dbDir + "patches.sql"
- DefaultEnvironment = EnvDevelopment
- DefaultDBSuperUser = "postgres"
-
- TrafficVaultDBConfigPath = TrafficVaultDir + "dbconf.yml"
- TrafficVaultMigrationsSource = "file:" + TrafficVaultDir + "migrations"
- TrafficVaultDir = dbDir + "trafficvault/"
- TrafficVaultSchemaPath = TrafficVaultDir + "create_tables.sql"
+// Default file system paths for TV files.
+const (
+ defaultTrafficVaultDir = defaultDBDir + "trafficvault/"
+ defaultTrafficVaultDBConfigPath = defaultTrafficVaultDir + "dbconf.yml"
+ defaultTrafficVaultMigrationsPath = defaultTrafficVaultDir + "migrations"
+ defaultTrafficVaultSchemaPath = defaultTrafficVaultDir + "create_tables.sql"
+)
+
+// Default connection information
+const (
+ defaultEnvironment = EnvDevelopment
+ defaultDBSuperUser = "postgres"
+)
+const (
LastSquashedMigrationTimestamp uint = 2021012200000000 // 2021012200000000_max_request_header_bytes_default_zero.sql
FirstMigrationTimestamp uint = 2021012700000000 // 2021012700000000_update_interfaces_multiple_routers.up.sql
)
@@ -134,7 +145,7 @@ var (
ConnectionString string
DBDriver string
DBName string
- DBSuperUser = DefaultDBSuperUser
+ DBSuperUser = defaultDBSuperUser
DBUser string
DBPassword string
HostIP string
@@ -144,14 +155,32 @@ var (
MigrationName string
)
+// Actual TODB file paths.
+var (
+ dbConfigPath = defaultDBConfigPath
+ dbMigrationsDir = defaultDBMigrationsPath
+ dbSeedsPath = defaultDBSeedsPath
+ dbSchemaPath = defaultDBSchemaPath
+ dbPatchesPath = defaultDBPatchesPath
+)
+
+// Actual TV file paths.
+var (
+ trafficVaultDBConfigPath = defaultTrafficVaultDBConfigPath
+ trafficVaultMigrationsPath = defaultTrafficVaultMigrationsPath
+ trafficVaultSchemaPath = defaultTrafficVaultSchemaPath
+)
+
func parseDBConfig() error {
- dbConfigPath := DBConfigPath
+ var cfgPath string
if TrafficVault {
- dbConfigPath = TrafficVaultDBConfigPath
+ cfgPath = trafficVaultDBConfigPath
+ } else {
+ cfgPath = dbConfigPath
}
- confBytes, err := ioutil.ReadFile(dbConfigPath)
+ confBytes, err := ioutil.ReadFile(cfgPath)
if err != nil {
- return errors.New("reading DB conf '" + dbConfigPath + "': " + err.Error())
+ return fmt.Errorf("reading DB conf '%s': %w", cfgPath, err)
}
dbConfig := DBConfig{}
@@ -201,7 +230,7 @@ func createDB() {
out, err := dbExistsCmd.Output()
// An error is returned if the database could not be found, which is to be expected. Don't exit on this error.
if err != nil {
- fmt.Println("unable to check if DB already exists: " + err.Error() + ", stderr: " + stderr.String())
+ fmt.Fprintln(os.Stderr, "unable to check if DB already exists: "+err.Error()+", stderr: "+stderr.String())
}
if len(out) > 0 {
fmt.Println("Database " + DBName + " already exists")
@@ -211,7 +240,7 @@ func createDB() {
out, err = createDBCmd.CombinedOutput()
fmt.Printf("%s", out)
if err != nil {
- die("Can't create db " + DBName)
+ die("Can't create db " + DBName + ": " + err.Error())
}
}
@@ -221,7 +250,7 @@ func dropDB() {
out, err := cmd.CombinedOutput()
fmt.Printf("%s", out)
if err != nil {
- die("Can't drop db " + DBName)
+ die("Can't drop db " + DBName + ": " + err.Error())
}
}
@@ -245,15 +274,15 @@ func createMigration() {
`
var err error
- if err = os.MkdirAll(DBMigrationsPath, os.ModePerm); err != nil {
- die("Creating migrations directory " + DBMigrationsPath + ": " + err.Error())
+ if err = os.MkdirAll(dbMigrationsDir, os.ModePerm); err != nil {
+ die("Creating migrations directory " + dbMigrationsDir + ": " + err.Error())
}
migrationTime := time.Now()
formattedMigrationTime := migrationTime.Format("20060102150405") + fmt.Sprintf("%02d", migrationTime.Nanosecond()%100)
for _, direction := range []string{"up", "down"} {
var migrationFile *os.File
basename := fmt.Sprintf("%s_%s.%s.sql", formattedMigrationTime, MigrationName, direction)
- filename := filepath.Join(DBMigrationsPath, basename)
+ filename := filepath.Join(dbMigrationsDir, basename)
if migrationFile, err = os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644); err != nil {
die("Creating migration " + filename + ": " + err.Error())
}
@@ -273,7 +302,7 @@ func createUser() {
out, err := userExistsCmd.Output()
// An error is returned if the user could not be found, which is to be expected. Don't exit on this error.
if err != nil {
- fmt.Println("unable to check if user already exists: " + err.Error() + ", stderr: " + stderr.String())
+ fmt.Fprintln(os.Stderr, "unable to check if user already exists: "+err.Error()+", stderr: "+stderr.String())
}
if len(out) > 0 {
fmt.Println("User " + DBUser + " already exists")
@@ -340,7 +369,7 @@ func maybeMigrateFromGoose() bool {
// runFirstMigration is essentially Migrate.Migrate(FirstMigrationTimestamp) but without the obligatory Migrate.versionExists() call.
// If calling Migrate.versionExists() is made optional, runFirstMigration() can be replaced.
func runFirstMigration() error {
- sourceDriver, sourceDriverErr := source.Open(DBMigrationsSource)
+ sourceDriver, sourceDriverErr := source.Open("file:" + dbMigrationsDir)
if sourceDriverErr != nil {
return fmt.Errorf("opening the migration source driver: " + sourceDriverErr.Error())
}
@@ -420,9 +449,9 @@ func seed() {
die("seed not supported for trafficvault environment")
}
fmt.Println("Seeding database w/ required data.")
- seedsBytes, err := ioutil.ReadFile(DBSeedsPath)
+ seedsBytes, err := ioutil.ReadFile(dbSeedsPath)
if err != nil {
- die("unable to read '" + DBSeedsPath + "': " + err.Error())
+ die("unable to read '" + dbSeedsPath + "': " + err.Error())
}
cmd := exec.Command("psql", "-h", HostIP, "-p", HostPort, "-d", DBName, "-U", DBUser, "-e", "-v", "ON_ERROR_STOP=1")
cmd.Stdin = bytes.NewBuffer(seedsBytes)
@@ -436,13 +465,13 @@ func seed() {
func loadSchema() {
fmt.Println("Creating database tables.")
- schemaPath := DBSchemaPath
+ schemaPath := dbSchemaPath
if TrafficVault {
- schemaPath = TrafficVaultSchemaPath
+ schemaPath = trafficVaultSchemaPath
}
schemaBytes, err := ioutil.ReadFile(schemaPath)
if err != nil {
- die("unable to read '" + DBSchemaPath + "': " + err.Error())
+ die("unable to read '" + schemaPath + "': " + err.Error())
}
cmd := exec.Command("psql", "-h", HostIP, "-p", HostPort, "-d", DBName, "-U", DBUser, "-e", "-v", "ON_ERROR_STOP=1")
cmd.Stdin = bytes.NewBuffer(schemaBytes)
@@ -459,9 +488,9 @@ func patch() {
die("patch not supported for trafficvault environment")
}
fmt.Println("Patching database with required data fixes.")
- patchesBytes, err := ioutil.ReadFile(DBPatchesPath)
+ patchesBytes, err := ioutil.ReadFile(dbPatchesPath)
if err != nil {
- die("unable to read '" + DBPatchesPath + "': " + err.Error())
+ die("unable to read '" + dbPatchesPath + "': " + err.Error())
}
cmd := exec.Command("psql", "-h", HostIP, "-p", HostPort, "-d", DBName, "-U", DBUser, "-e", "-v", "ON_ERROR_STOP=1")
cmd.Stdin = bytes.NewBuffer(patchesBytes)
@@ -474,87 +503,140 @@ func patch() {
}
func die(message string) {
- fmt.Println(message)
+ fmt.Fprintln(os.Stderr, message)
os.Exit(1)
}
func usage() string {
programName := os.Args[0]
- home := "$HOME"
- home = os.Getenv("HOME")
- return `
-Usage: ` + programName + ` [--trafficvault] [--env (development|test|production|integration)] [arguments]
-
-Example: ` + programName + ` --env=test reset
-
-Purpose: This script is used to manage the Traffic Ops database and Traffic Vault PostgreSQL backend database.
- The Traffic Ops environments and database names are defined in the dbconf.yml, and for Traffic Vault
- they are defined in trafficvault/dbconf.yml. In order to execute commands against the Traffic Vault
- database, the the --trafficvault option.
-
-NOTE:
-Postgres Superuser: The 'postgres' superuser needs to be created to run ` + programName + ` and setup databases.
-If the 'postgres' superuser has not been created or password has not been set then run the following commands accordingly.
-
-Create the 'postgres' user as a super user (if not created):
-
- $ createuser postgres --superuser --createrole --createdb --login --pwprompt
-
-Modify your ` + home + `/.pgpass file which allows for easy command line access by defaulting the user and password for the database
-without prompts.
-
- Postgres .pgpass file format:
- hostname:port:database:username:password
-
- ----------------------
- Example Contents
- ----------------------
- *:*:*:postgres:your-postgres-password
- *:*:*:traffic_ops:the-password-in-dbconf.yml
- *:*:*:traffic_vault:the-password-in-trafficvault-dbconf.yml
- ----------------------
-
- Save the following example into this file ` + home + `/.pgpass with the permissions of this file
- so only your user can read and write.
-
- $ chmod 0600 ` + home + `/.pgpass
-
-===================================================================================================================
-` + programName + ` arguments:
-
-migrate - Execute migrate (without seeds or patches) on the database for the
- current environment.
-up - Alias for 'migrate'
-down - Roll back a single migration from the current version.
-createdb - Execute db 'createdb' the database for the current environment.
-dropdb - Execute db 'dropdb' on the database for the current environment.
-create_migration NAME
- - Creates a pair of timestamped up/down migrations titled NAME.
-create_user - Execute 'create_user' the user for the current environment
- (traffic_ops).
-dbversion - Prints the current migration timestamp
-drop_user - Execute 'drop_user' the user for the current environment
- (traffic_ops).
-patch - Execute sql from db/patches.sql for loading post-migration data
- patches (NOTE: not supported with --trafficvault option).
-redo - Roll back the most recently applied migration, then run it again.
-reset - Execute db 'dropdb', 'createdb', load_schema, migrate on the
- database for the current environment.
-seed - Execute sql from db/seeds.sql for loading static data (NOTE: not
- supported with --trafficvault option).
-show_users - Execute sql to show all of the user for the current environment.
-status - Prints the current migration timestamp (Deprecated, status is now an
- alias for dbversion and will be removed in a future Traffic
- Control release).
-upgrade - Execute migrate, seed, and patches on the database for the current
- environment.
-`
+ var buff strings.Builder
+ buff.WriteString("Usage: ")
+ buff.WriteString(programName)
+ buff.WriteString(` [OPTION] OPERATION [ARGUMENT(S)]
+
+-c, --config CFG Provide a path to a database configuration file,
+ instead of using the default (./db/dbconf.yml or
+ ./db/trafficvault/dbconf.yml for Traffic Vault)
+-e, --env ENV Use configuration for environment ENV (defined in
+ the database configuration file)
+-h, --help Show usage information and exit
+-m, --migrations-dir DIR Use DIR as the migrations directory, instead of the
+ default (./db/migrations/ or
+ ./db/trafficvault/migrations for Traffic Vault)
+-p, --patches PATCH Provide a path to a set of database patch statements,
+ instead of using the default (./db/patches.sql)
+-s, --schema SCHEMA Provide a path to a schema file, instead of using the
+ default (./db/create_tables.sql or
+ ./db/trafficvault/create_tables.sql for Traffic Vault)
+-S, --seeds SEEDS Provide a path to a seeds statements file, instead of
+ using the default (./db/seeds.sql)
+-v, --trafficvault Perform operations for Traffic Vault instead of the
+ Traffic Ops database
+
+OPERATION The operation to perform; one of the following:
+
+ migrate - Execute migrate (without seeds or patches) on the database for the
+ current environment.
+ up - Alias for 'migrate'
+ down - Roll back a single migration from the current version.
+ createdb - Execute db 'createdb' the database for the current environment.
+ dropdb - Execute db 'dropdb' on the database for the current environment.
+ create_migration NAME
+ - Creates a pair of timestamped up/down migrations titled NAME.
+ create_user - Execute 'create_user' the user for the current environment
+ (traffic_ops).
+ dbversion - Prints the current migration timestamp
+ drop_user - Execute 'drop_user' the user for the current environment
+ (traffic_ops).
+ patch - Execute sql from db/patches.sql for loading post-migration data
+ patches (NOTE: not supported with --trafficvault option).
+ redo - Roll back the most recently applied migration, then run it again.
+ reset - Execute db 'dropdb', 'createdb', load_schema, migrate on the
+ database for the current environment.
+ seed - Execute sql from db/seeds.sql for loading static data (NOTE: not
+ supported with --trafficvault option).
+ show_users - Execute sql to show all of the user for the current environment.
+ status - Prints the current migration timestamp (Deprecated, status is now an
+ alias for dbversion and will be removed in a future Traffic
+ Control release).
+ upgrade - Execute migrate, seed, and patches on the database for the current
+ environment.`)
+ return buff.String()
+}
+
+// collapses two options for 'name', using a default if given, stored into 'dest'.
+// if the two option values conflict, the whole program dies and an error is printed to
+// stderr.
+func collapse(o1, o2, name, def string, dest *string) {
+ if o1 == "" {
+ if o2 == "" {
+ *dest = def
+ return
+ }
+ *dest = o2
+ return
+ }
+ if o2 == "" {
+ *dest = o1
+ return
+ }
+ if o1 != o2 {
+ die("conflicting definitions of '" + name + "' - must be specified only once\n" + usage())
+ }
+ *dest = o1
+ return
}
func main() {
- flag.StringVar(&Environment, "env", DefaultEnvironment, "The environment to use (defined in "+DBConfigPath+").")
- flag.BoolVar(&TrafficVault, "trafficvault", false, "Run this for the Traffic Vault database")
+ flag.Usage = func() { fmt.Fprintln(os.Stderr, usage()) }
+
+ var shortCfg string
+ var longCfg string
+ flag.StringVar(&shortCfg, "c", "", "Provide a path to a database configuration file, instead of using the default (./db/dbconf.yml or ./db/trafficvault/dbconf.yml for Traffic Vault)")
+ flag.StringVar(&longCfg, "config", "", "Provide a path to a database configuration file, instead of using the default (./db/dbconf.yml or ./db/trafficvault/dbconf.yml for Traffic Vault)")
+
+ var shortEnv string
+ var longEnv string
+ flag.StringVar(&shortEnv, "e", "", "Use configuration for environment ENV (defined in the database configuration file)")
+ flag.StringVar(&longEnv, "env", "", "Use configuration for environment ENV (defined in the database configuration file)")
+
+ var shortMigrations string
+ var longMigrations string
+ flag.StringVar(&shortMigrations, "m", "", "Use DIR as the migrations directory, instead of the default (./db/migrations/ or ./db/trafficvault/migrations for Traffic Vault)")
+ flag.StringVar(&longMigrations, "migrations-dir", "", "Use DIR as the migrations directory, instead of the default (./db/migrations/ or ./db/trafficvault/migrations for Traffic Vault)")
+
+ var shortPatches string
+ var longPatches string
+ flag.StringVar(&shortPatches, "p", "", "Provide a path to a set of database patch statements, instead of using the default (./db/patches.sql)")
+ flag.StringVar(&longPatches, "patches", "", "Provide a path to a set of database patch statements, instead of using the default (./db/patches.sql)")
+
+ var shortSchema string
+ var longSchema string
+ flag.StringVar(&shortSchema, "s", "", "Provide a path to a schema file, instead of using the default (./db/create_tables.sql or ./db/trafficvault/create_tables.sql for Traffic Vault)")
+ flag.StringVar(&longSchema, "schema", "", "Provide a path to a schema file, instead of using the default (./db/create_tables.sql or ./db/trafficvault/create_tables.sql for Traffic Vault)")
+
+ var shortSeeds string
+ var longSeeds string
+ flag.StringVar(&shortSeeds, "S", "", "Provide a path to a seeds statements file, instead of using the default (./db/seeds.sql)")
+ flag.StringVar(&longSeeds, "seeds", "", "Provide a path to a seeds statements file, instead of using the default (./db/seeds.sql)")
+
+ flag.BoolVar(&TrafficVault, "v", false, "Perform operations for Traffic Vault instead of the Traffic Ops database")
+ flag.BoolVar(&TrafficVault, "trafficvault", false, "Perform operations for Traffic Vault instead of the Traffic Ops database")
flag.Parse()
+
+ if TrafficVault {
+ collapse(shortCfg, longCfg, "config", defaultTrafficVaultDBConfigPath, &trafficVaultDBConfigPath)
+ collapse(shortMigrations, longMigrations, "migrations-dir", defaultTrafficVaultMigrationsPath, &trafficVaultMigrationsPath)
+ collapse(shortSchema, longSchema, "schema", defaultTrafficVaultSchemaPath, &trafficVaultSchemaPath)
+ } else {
+ collapse(shortCfg, longCfg, "config", defaultDBConfigPath, &dbConfigPath)
+ collapse(shortMigrations, longMigrations, "migrations-dir", defaultDBMigrationsPath, &dbMigrationsDir)
+ collapse(shortSchema, longSchema, "schema", defaultDBSchemaPath, &dbSchemaPath)
+ }
+ collapse(shortEnv, longEnv, "environment", defaultEnvironment, &Environment)
+ collapse(shortPatches, longPatches, "patches", defaultDBPatchesPath, &dbPatchesPath)
+ collapse(shortSeeds, longSeeds, "seeds", defaultDBSeedsPath, &dbSeedsPath)
+
if flag.Arg(0) == CmdCreateMigration {
if len(flag.Args()) != 2 {
die(usage())
@@ -593,8 +675,7 @@ func main() {
if cmd, ok := commands[userCmd]; ok {
cmd()
} else {
- fmt.Println(usage())
- die("invalid command: " + userCmd)
+ die("invalid command: " + userCmd + "\n" + usage())
}
}
@@ -602,9 +683,9 @@ func initMigrate() bool {
var err error
ConnectionString = fmt.Sprintf("%s://%s:%s@%s:%s/%s?sslmode=%s", DBDriver, DBUser, DBPassword, HostIP, HostPort, DBName, SSLMode)
if TrafficVault {
- Migrate, err = migrate.New(TrafficVaultMigrationsSource, ConnectionString)
+ Migrate, err = migrate.New("file:"+trafficVaultMigrationsPath, ConnectionString)
} else {
- Migrate, err = migrate.New(DBMigrationsSource, ConnectionString)
+ Migrate, err = migrate.New("file:"+dbMigrationsDir, ConnectionString)
}
if err != nil {
die("Starting Migrate: " + err.Error())