2016-10-26 07:59:07 +03:00
package main
import (
2018-10-27 00:57:52 +03:00
"os"
2016-10-26 07:59:07 +03:00
"strconv"
"strings"
2018-05-08 21:37:30 +03:00
log "github.com/sirupsen/logrus"
2017-03-20 06:56:35 +03:00
"github.com/skeema/mybase"
2018-10-27 00:57:52 +03:00
"github.com/skeema/skeema/fs"
2018-05-25 23:31:31 +03:00
"github.com/skeema/tengo"
2016-10-26 07:59:07 +03:00
)
func init ( ) {
summary := "Add a new named environment to an existing host directory"
desc := ` Modifies the . skeema file in an existing host directory to add a new named
environment . For example , if ` + " ` " + `skeema init` + " ` " + ` was previously used to create a dir
for a host with the default "production" environment , ` + " ` " + `skeema add-environment` + " ` " + `
2019-05-22 21:37:38 +03:00
could be used to define a "staging" or "development" environment pointing at a
different host and port , or perhaps a "local" environment pointing at localhost
and a socket path .
This command currently only handles very simple cases . For many situations ,
editing . skeema files directly is a better approach . `
2016-10-26 07:59:07 +03:00
2017-03-20 06:56:35 +03:00
cmd := mybase . NewCommand ( "add-environment" , summary , desc , AddEnvHandler )
cmd . AddOption ( mybase . StringOption ( "host" , 'h' , "" , "Database hostname or IP address" ) )
cmd . AddOption ( mybase . StringOption ( "port" , 'P' , "3306" , "Port to use for database host" ) )
cmd . AddOption ( mybase . StringOption ( "socket" , 'S' , "/tmp/mysql.sock" , "Absolute path to Unix socket file used if host is localhost" ) )
cmd . AddOption ( mybase . StringOption ( "dir" , 'd' , "." , "Base dir for this host's schemas" ) )
2016-10-26 07:59:07 +03:00
cmd . AddArg ( "environment" , "" , true )
CommandSuite . AddSubCommand ( cmd )
}
2016-11-09 10:31:39 +03:00
// AddEnvHandler is the handler method for `skeema add-environment`
2017-03-20 06:56:35 +03:00
func AddEnvHandler ( cfg * mybase . Config ) error {
2019-08-21 02:39:18 +03:00
dir , err := dirForAddEnv ( cfg )
2016-10-26 07:59:07 +03:00
if err != nil {
return err
}
environment := cfg . Get ( "environment" )
if environment == "" || strings . ContainsAny ( environment , "[]\n\r" ) {
Provide more informative exit codes
Previously, Skeema simply returned 0 on no error or 1 if error. This made usage
difficult in automated CI pipelines and commit hook linters.
The exit codes returned now vary by command. In general, fatal errors now use
an exit code of 2 or greater.
For push and pull, code 1 now means a partial or non-fatal failure -- for
example, if one database instance was unreachable, and operations on that
database had to be skipped. Fatal errors will return a code of 2 or higher.
For diff, code 1 means that at least one difference was detected but no errors
occurred. If no differences were detected, diff returns 0. If some fatal error
occurred, diff returns 2 or greater.
For lint, code 1 means that at least one file was reformatted, but no SQL syntax
errors were found. If all files were already properly formatted and valid, lint
returns 0. If at least one file had invalid SQL and/or some other fatal error
occurred, lint returns 2 or greater.
Other commands (init, add-environment) return 2 or greater if an error occurred.
When possible, the exit codes loosely follow BSD's `man sysexits` conventions.
2016-11-09 02:58:19 +03:00
return NewExitValue ( CodeBadConfig , "Environment name \"%s\" is invalid" , environment )
2016-10-26 07:59:07 +03:00
}
2018-10-27 00:57:52 +03:00
if dir . OptionFile . HasSection ( environment ) {
return NewExitValue ( CodeBadConfig , "Environment name \"%s\" already defined in %s" , environment , dir . OptionFile . Path ( ) )
2016-10-26 07:59:07 +03:00
}
2018-10-27 00:57:52 +03:00
if ! dir . OptionFile . SomeSectionHasOption ( "host" ) {
Provide more informative exit codes
Previously, Skeema simply returned 0 on no error or 1 if error. This made usage
difficult in automated CI pipelines and commit hook linters.
The exit codes returned now vary by command. In general, fatal errors now use
an exit code of 2 or greater.
For push and pull, code 1 now means a partial or non-fatal failure -- for
example, if one database instance was unreachable, and operations on that
database had to be skipped. Fatal errors will return a code of 2 or higher.
For diff, code 1 means that at least one difference was detected but no errors
occurred. If no differences were detected, diff returns 0. If some fatal error
occurred, diff returns 2 or greater.
For lint, code 1 means that at least one file was reformatted, but no SQL syntax
errors were found. If all files were already properly formatted and valid, lint
returns 0. If at least one file had invalid SQL and/or some other fatal error
occurred, lint returns 2 or greater.
Other commands (init, add-environment) return 2 or greater if an error occurred.
When possible, the exit codes loosely follow BSD's `man sysexits` conventions.
2016-11-09 02:58:19 +03:00
return NewExitValue ( CodeBadConfig , "This command should be run against a --dir whose .skeema file already defines a host for another environment" )
2016-10-26 07:59:07 +03:00
}
2018-05-25 23:31:31 +03:00
// Create a tengo.Instance representing the supplied host. We intentionally
// don't actually test connectivity here though, since this command only
// manipulates the option file. We can't use dir.FirstInstance() here since
// that checks connectivity.
var inst * tengo . Instance
2016-10-26 07:59:07 +03:00
if ! cfg . OnCLI ( "host" ) {
Provide more informative exit codes
Previously, Skeema simply returned 0 on no error or 1 if error. This made usage
difficult in automated CI pipelines and commit hook linters.
The exit codes returned now vary by command. In general, fatal errors now use
an exit code of 2 or greater.
For push and pull, code 1 now means a partial or non-fatal failure -- for
example, if one database instance was unreachable, and operations on that
database had to be skipped. Fatal errors will return a code of 2 or higher.
For diff, code 1 means that at least one difference was detected but no errors
occurred. If no differences were detected, diff returns 0. If some fatal error
occurred, diff returns 2 or greater.
For lint, code 1 means that at least one file was reformatted, but no SQL syntax
errors were found. If all files were already properly formatted and valid, lint
returns 0. If at least one file had invalid SQL and/or some other fatal error
occurred, lint returns 2 or greater.
Other commands (init, add-environment) return 2 or greater if an error occurred.
When possible, the exit codes loosely follow BSD's `man sysexits` conventions.
2016-11-09 02:58:19 +03:00
return NewExitValue ( CodeBadConfig , "`skeema add-environment` requires --host to be supplied on CLI" )
2016-10-26 07:59:07 +03:00
}
2018-05-25 23:31:31 +03:00
if instances , err := dir . Instances ( ) ; err != nil {
2016-10-26 07:59:07 +03:00
return err
2018-05-25 23:31:31 +03:00
} else if len ( instances ) == 0 {
Provide more informative exit codes
Previously, Skeema simply returned 0 on no error or 1 if error. This made usage
difficult in automated CI pipelines and commit hook linters.
The exit codes returned now vary by command. In general, fatal errors now use
an exit code of 2 or greater.
For push and pull, code 1 now means a partial or non-fatal failure -- for
example, if one database instance was unreachable, and operations on that
database had to be skipped. Fatal errors will return a code of 2 or higher.
For diff, code 1 means that at least one difference was detected but no errors
occurred. If no differences were detected, diff returns 0. If some fatal error
occurred, diff returns 2 or greater.
For lint, code 1 means that at least one file was reformatted, but no SQL syntax
errors were found. If all files were already properly formatted and valid, lint
returns 0. If at least one file had invalid SQL and/or some other fatal error
occurred, lint returns 2 or greater.
Other commands (init, add-environment) return 2 or greater if an error occurred.
When possible, the exit codes loosely follow BSD's `man sysexits` conventions.
2016-11-09 02:58:19 +03:00
return NewExitValue ( CodeBadConfig , "Command line did not specify which instance to connect to" )
2018-05-25 23:31:31 +03:00
} else {
inst = instances [ 0 ]
2016-10-26 07:59:07 +03:00
}
2018-10-27 00:57:52 +03:00
dir . OptionFile . SetOptionValue ( environment , "host" , inst . Host )
2016-10-26 07:59:07 +03:00
if inst . Host == "localhost" && inst . SocketPath != "" {
2018-10-27 00:57:52 +03:00
dir . OptionFile . SetOptionValue ( environment , "socket" , inst . SocketPath )
2016-10-26 07:59:07 +03:00
} else {
2018-10-27 00:57:52 +03:00
dir . OptionFile . SetOptionValue ( environment , "port" , strconv . Itoa ( inst . Port ) )
2016-10-26 07:59:07 +03:00
}
2019-04-10 00:07:28 +03:00
if flavor := inst . Flavor ( ) ; ! flavor . Known ( ) {
log . Warnf ( "Unable to automatically determine database vendor or version. To set manually, use the \"flavor\" option in %s" , dir . OptionFile )
} else {
2020-01-28 09:17:41 +03:00
dir . OptionFile . SetOptionValue ( environment , "flavor" , flavor . Family ( ) . String ( ) )
2018-08-02 00:05:56 +03:00
}
2018-10-30 07:43:01 +03:00
for _ , persistOpt := range [ ] string { "user" , "ignore-schema" , "ignore-table" , "connect-options" } {
if cfg . OnCLI ( persistOpt ) {
dir . OptionFile . SetOptionValue ( environment , persistOpt , cfg . Get ( persistOpt ) )
}
2016-10-26 07:59:07 +03:00
}
// Write the option file
2018-10-27 00:57:52 +03:00
if err := dir . OptionFile . Write ( true ) ; err != nil {
2016-10-26 07:59:07 +03:00
return err
}
2018-10-27 00:57:52 +03:00
log . Infof ( "Added environment [%s] to %s" , environment , dir . OptionFile . Path ( ) )
2016-10-26 07:59:07 +03:00
return nil
}
2019-08-21 02:39:18 +03:00
func dirForAddEnv ( cfg * mybase . Config ) ( * fs . Dir , error ) {
dirPath := cfg . Get ( "dir" )
fi , err := os . Stat ( dirPath )
if err == nil && ! fi . IsDir ( ) {
return nil , NewExitValue ( CodeBadConfig , "--dir=%s already exists but is not a directory" , dirPath )
} else if os . IsNotExist ( err ) {
return nil , NewExitValue ( CodeBadConfig , "In add-environment, --dir must refer to a directory that already exists" )
} else if err != nil {
return nil , err
}
dir , err := fs . ParseDir ( dirPath , cfg )
if err != nil {
return nil , err
}
if dir . OptionFile == nil {
return nil , NewExitValue ( CodeBadConfig , "Dir %s does not have an existing .skeema file! Can only use `skeema add-environment` on a dir previously created by `skeema init`" , dir )
}
return dir , nil
}