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 <[email protected]>
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())

Reply via email to