2020-02-12 13:53:27 -05:00
package dev
import (
"fmt"
"runtime"
"strings"
"sync"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/cmd/base"
controllercmd "github.com/hashicorp/boundary/internal/cmd/commands/controller"
workercmd "github.com/hashicorp/boundary/internal/cmd/commands/worker"
"github.com/hashicorp/boundary/internal/cmd/config"
2020-02-12 13:53:27 -05:00
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
2020-03-10 15:48:04 -04:00
var _ cli . Command = ( * Command )( nil )
var _ cli . CommandAutocomplete = ( * Command )( nil )
2020-02-12 13:53:27 -05:00
2020-05-21 16:12:31 -04:00
var devOnlyControllerFlags = func ( * Command , * base . FlagSet ) {}
2020-03-10 15:48:04 -04:00
type Command struct {
* base . Server
2020-02-12 13:53:27 -05:00
SighupCh chan struct {}
childSighupCh [] chan struct {}
ReloadedCh chan struct {}
SigUSR2Ch chan struct {}
cleanupGuard sync . Once
2020-03-22 08:27:24 -04:00
flagConfig string
flagLogLevel string
flagLogFormat string
2020-05-21 16:12:31 -04:00
flagCombineLogs bool
2020-03-22 08:27:24 -04:00
flagDev bool
2020-08-11 17:25:00 -04:00
flagDevLoginName string
2020-08-07 10:18:40 -07:00
flagDevPassword string
2020-05-11 17:03:23 -04:00
flagDevOrgId string
2020-07-14 14:32:47 -07:00
flagDevAuthMethodId string
2020-03-22 08:27:24 -04:00
flagDevControllerAPIListenAddr string
flagDevControllerClusterListenAddr string
2020-05-21 16:12:31 -04:00
flagDevPassthroughDirectory string
2020-02-12 13:53:27 -05:00
}
2020-03-10 15:48:04 -04:00
func ( c * Command ) Synopsis () string {
2020-08-10 17:15:02 -04:00
return "Start a Boundary dev environment"
2020-02-12 13:53:27 -05:00
}
2020-03-10 15:48:04 -04:00
func ( c * Command ) Help () string {
2020-02-12 13:53:27 -05:00
helpText := `
2020-08-10 17:15:02 -04:00
Usage: boundary dev [options]
2020-02-12 13:53:27 -05:00
Start a dev environment:
2020-08-10 17:15:02 -04:00
$ boundary dev
2020-02-12 13:53:27 -05:00
For a full list of examples, please see the documentation.
` + c . Flags (). Help ()
return strings . TrimSpace ( helpText )
}
2020-03-10 15:48:04 -04:00
func ( c * Command ) Flags () * base . FlagSets {
2020-02-12 13:53:27 -05:00
set := c . FlagSet ( base . FlagSetHTTP )
f := set . NewFlagSet ( "Command Options" )
f . StringVar ( & base . StringVar {
Name : "log-level" ,
Target : & c . flagLogLevel ,
Default : base . NotSetValue ,
2020-08-10 17:15:02 -04:00
EnvVar : "BOUNDARY_LOG_LEVEL" ,
2020-02-12 13:53:27 -05:00
Completion : complete . PredictSet ( "trace" , "debug" , "info" , "warn" , "err" ),
Usage : "Log verbosity level. Supported values (in order of more detail to less) are " +
"\"trace\", \"debug\", \"info\", \"warn\", and \"err\"." ,
})
f . StringVar ( & base . StringVar {
Name : "log-format" ,
Target : & c . flagLogFormat ,
Default : base . NotSetValue ,
Completion : complete . PredictSet ( "standard" , "json" ),
Usage : `Log format. Supported values are "standard" and "json".` ,
})
f = set . NewFlagSet ( "Dev Options" )
2020-05-11 17:03:23 -04:00
f . StringVar ( & base . StringVar {
2020-05-21 16:12:31 -04:00
Name : "dev-org-id" ,
Target : & c . flagDevOrgId ,
EnvVar : "WATCHTWER_DEV_ORG_ID" ,
2020-07-11 12:00:49 -04:00
Usage : "Auto-created org ID. This only applies when running in \"dev\" " +
2020-05-11 17:03:23 -04:00
"mode." ,
})
2020-07-14 14:32:47 -07:00
f . StringVar ( & base . StringVar {
Name : "dev-auth-method-id" ,
Target : & c . flagDevAuthMethodId ,
EnvVar : "WATCHTWER_DEV_AUTH_METHOD_ID" ,
Usage : "Auto-created auth method ID. This only applies when running in \"dev\" " +
"mode." ,
})
2020-02-12 13:53:27 -05:00
f . StringVar ( & base . StringVar {
2020-08-07 10:18:40 -07:00
Name : "dev-password" ,
Target : & c . flagDevPassword ,
EnvVar : "WATCHTWER_DEV_PASSWORD" ,
2020-03-20 12:51:39 -04:00
Usage : "Initial admin password. This only applies when running in \"dev\" " +
2020-02-12 13:53:27 -05:00
"mode." ,
})
2020-08-07 10:18:40 -07:00
f . StringVar ( & base . StringVar {
2020-08-11 17:25:00 -04:00
Name : "dev-login-name" ,
Target : & c . flagDevLoginName ,
EnvVar : "WATCHTWER_DEV_LOGIN_NAME" ,
Usage : "Initial admin login name. This only applies when running in \"dev\" " +
2020-08-07 10:18:40 -07:00
"mode." ,
})
2020-02-12 13:53:27 -05:00
f . StringVar ( & base . StringVar {
2020-03-22 08:27:24 -04:00
Name : "dev-api-listen-address" ,
Target : & c . flagDevControllerAPIListenAddr ,
2020-08-10 17:15:02 -04:00
EnvVar : "BOUNDARY_DEV_CONTROLLER_API_LISTEN_ADDRESS" ,
2020-03-22 08:27:24 -04:00
Usage : "Address to bind to for controller \"api\" purpose." ,
})
f . StringVar ( & base . StringVar {
Name : "dev-cluster-listen-address" ,
Target : & c . flagDevControllerClusterListenAddr ,
2020-08-10 17:15:02 -04:00
EnvVar : "BOUNDARY_DEV_CONTROLLER_CLUSTER_LISTEN_ADDRESS" ,
2020-03-22 08:27:24 -04:00
Usage : "Address to bind to for controller \"cluster\" purpose." ,
2020-02-12 13:53:27 -05:00
})
f . BoolVar ( & base . BoolVar {
2020-05-21 16:12:31 -04:00
Name : "combine-logs" ,
Target : & c . flagCombineLogs ,
Usage : "If set, both startup information and logs will be sent to stdout. If not set (the default), startup information will go to stdout and logs will be sent to stderr." ,
2020-02-12 13:53:27 -05:00
})
2020-05-21 16:12:31 -04:00
devOnlyControllerFlags ( c , f )
2020-02-12 13:53:27 -05:00
return set
}
2020-03-10 15:48:04 -04:00
func ( c * Command ) AutocompleteArgs () complete . Predictor {
2020-02-12 13:53:27 -05:00
return complete . PredictNothing
}
2020-03-10 15:48:04 -04:00
func ( c * Command ) AutocompleteFlags () complete . Flags {
2020-02-12 13:53:27 -05:00
return c . Flags (). Completions ()
}
2020-03-10 15:48:04 -04:00
func ( c * Command ) Run ( args [] string ) int {
2020-02-12 13:53:27 -05:00
c . CombineLogs = c . flagCombineLogs
var err error
f := c . Flags ()
if err = f . Parse ( args ); err != nil {
c . UI . Error ( err . Error ())
return 1
}
childShutdownCh := make ( chan struct {})
2020-08-12 00:40:39 -04:00
devConfig , err := config . DevCombined ()
2020-02-12 13:53:27 -05:00
if err != nil {
2020-05-11 17:03:23 -04:00
c . UI . Error ( fmt . Errorf ( "Error creating controller dev config: %w" , err ). Error ())
2020-02-12 13:53:27 -05:00
return 1
}
2020-07-14 14:32:47 -07:00
if c . flagDevAuthMethodId != "" {
2020-08-07 10:18:40 -07:00
prefix := password . AuthMethodPrefix + "_"
if ! strings . HasPrefix ( c . flagDevAuthMethodId , prefix ) {
c . UI . Error ( fmt . Sprintf ( "Invalid dev auth method ID, must start with %q" , prefix ))
2020-07-14 14:32:47 -07:00
return 1
}
2020-08-13 11:20:22 -04:00
if len ( c . flagDevAuthMethodId ) != 15 {
2020-08-07 10:18:40 -07:00
c . UI . Error ( fmt . Sprintf ( "Invalid dev auth method ID, must be 10 base62 characters after %q" , prefix ))
2020-07-14 14:32:47 -07:00
return 1
}
c . DevAuthMethodId = c . flagDevAuthMethodId
}
2020-08-11 17:25:00 -04:00
if c . flagDevLoginName != "" {
c . DevLoginName = c . flagDevLoginName
2020-08-07 10:18:40 -07:00
}
if c . flagDevPassword != "" {
c . DevPassword = c . flagDevPassword
}
2020-07-14 14:32:47 -07:00
2020-05-21 16:12:31 -04:00
devConfig . PassthroughDirectory = c . flagDevPassthroughDirectory
2020-02-12 13:53:27 -05:00
2020-03-22 08:27:24 -04:00
for _ , l := range devConfig . Listeners {
if len ( l . Purpose ) != 1 {
continue
}
switch l . Purpose [ 0 ] {
case "api" :
if c . flagDevControllerAPIListenAddr != "" {
l . Address = c . flagDevControllerAPIListenAddr
}
case "cluster" :
if c . flagDevControllerClusterListenAddr != "" {
l . Address = c . flagDevControllerClusterListenAddr
}
}
}
2020-02-12 13:53:27 -05:00
if err := c . SetupLogging ( c . flagLogLevel , c . flagLogFormat , "" , "" ); err != nil {
c . UI . Error ( err . Error ())
return 1
}
2020-03-20 12:51:39 -04:00
base . StartMemProfiler ( c . Logger )
2020-03-19 15:17:40 -04:00
2020-03-20 12:51:39 -04:00
if err := c . SetupMetrics ( c . UI , devConfig . Telemetry ); err != nil {
2020-02-12 13:53:27 -05:00
c . UI . Error ( err . Error ())
return 1
}
2020-08-14 10:12:31 -04:00
if err := c . SetupKMSes ( c . UI , devConfig . SharedConfig , [] string { "root" , "worker-auth" }); err != nil {
2020-02-12 13:53:27 -05:00
c . UI . Error ( err . Error ())
return 1
}
2020-05-11 13:27:54 -04:00
if c . ControllerKMS == nil {
c . UI . Error ( "Controller KMS not found after parsing KMS blocks" )
return 1
}
if c . WorkerAuthKMS == nil {
c . UI . Error ( "Worker Auth KMS not found after parsing KMS blocks" )
return 1
}
2020-08-12 00:40:39 -04:00
c . InfoKeys = append ( c . InfoKeys , "[Controller] AEAD Key Bytes" )
c . Info [ "[Controller] AEAD Key Bytes" ] = devConfig . Controller . DevControllerKey
c . InfoKeys = append ( c . InfoKeys , "[Worker-Auth] AEAD Key Bytes" )
c . Info [ "[Worker-Auth] AEAD Key Bytes" ] = devConfig . Controller . DevWorkerAuthKey
if c . WorkerAuthKMS == nil {
c . UI . Error ( "Worker Auth KMS not found after parsing KMS blocks" )
return 1
}
2020-02-12 13:53:27 -05:00
// Initialize the listeners
2020-08-12 00:40:39 -04:00
if err := c . SetupListeners ( c . UI , devConfig . SharedConfig , [] string { "api" , "cluster" , "worker-alpn-tls" }); err != nil {
2020-02-12 13:53:27 -05:00
c . UI . Error ( err . Error ())
return 1
}
// Write out the PID to the file now that server has successfully started
2020-03-20 12:51:39 -04:00
if err := c . StorePidFile ( devConfig . PidFile ); err != nil {
c . UI . Error ( fmt . Errorf ( "Error storing PID: %w" , err ). Error ())
2020-02-12 13:53:27 -05:00
return 1
}
2020-05-15 10:19:55 -04:00
defer func () {
if err := c . RunShutdownFuncs (); err != nil {
c . UI . Error ( fmt . Errorf ( "Error running shutdown tasks: %w" , err ). Error ())
}
}()
2020-02-12 13:53:27 -05:00
2020-05-11 17:03:23 -04:00
if err := c . CreateDevDatabase ( "postgres" ); err != nil {
2020-03-20 12:51:39 -04:00
c . UI . Error ( fmt . Errorf ( "Error creating dev database container: %w" , err ). Error ())
2020-02-12 13:53:27 -05:00
return 1
}
c . ShutdownFuncs = append ( c . ShutdownFuncs , c . DestroyDevDatabase )
c . PrintInfo ( c . UI , "dev mode" )
c . ReleaseLogGate ()
// Instantiate the wait group
shutdownWg := & sync . WaitGroup {}
2020-03-20 12:51:39 -04:00
shutdownWg . Add ( 2 )
2020-02-12 13:53:27 -05:00
controllerSighupCh := make ( chan struct {})
c . childSighupCh = append ( c . childSighupCh , controllerSighupCh )
2020-03-22 20:17:23 -04:00
devController := & controllercmd . Command {
2020-05-04 23:04:55 -04:00
Server : c . Server ,
ExtShutdownCh : childShutdownCh ,
SighupCh : controllerSighupCh ,
Config : devConfig ,
2020-03-22 20:17:23 -04:00
}
if err := devController . Start (); err != nil {
c . UI . Error ( err . Error ())
return 1
}
workerSighupCh := make ( chan struct {})
c . childSighupCh = append ( c . childSighupCh , workerSighupCh )
devWorker := & workercmd . Command {
2020-05-04 23:04:55 -04:00
Server : c . Server ,
ExtShutdownCh : childShutdownCh ,
SighupCh : workerSighupCh ,
Config : devConfig ,
2020-03-22 20:17:23 -04:00
}
if err := devWorker . Start (); err != nil {
c . UI . Error ( err . Error ())
return 1
}
2020-02-12 13:53:27 -05:00
go func () {
defer shutdownWg . Done ()
2020-03-22 20:17:23 -04:00
devController . WaitForInterrupt ()
2020-02-12 13:53:27 -05:00
}()
2020-03-20 12:51:39 -04:00
go func () {
defer shutdownWg . Done ()
2020-03-22 20:17:23 -04:00
devWorker . WaitForInterrupt ()
2020-03-20 12:51:39 -04:00
}()
2020-02-12 13:53:27 -05:00
// Wait for shutdown
shutdownTriggered := false
for ! shutdownTriggered {
select {
case <- c . ShutdownCh :
2020-08-10 17:15:02 -04:00
c . UI . Output ( "==> Boundary dev environment shutdown triggered" )
2020-02-12 13:53:27 -05:00
2020-05-04 23:04:55 -04:00
childShutdownCh <- struct {}{}
childShutdownCh <- struct {}{}
2020-02-12 13:53:27 -05:00
shutdownTriggered = true
case <- c . SighupCh :
2020-08-10 17:15:02 -04:00
c . UI . Output ( "==> Boundary dev environment reload triggered" )
2020-02-12 13:53:27 -05:00
for _ , v := range c . childSighupCh {
v <- struct {}{}
}
case <- c . SigUSR2Ch :
buf := make ([] byte , 32 * 1024 * 1024 )
n := runtime . Stack ( buf [:], true )
c . Logger . Info ( "goroutine trace" , "stack" , string ( buf [: n ]))
}
}
shutdownWg . Wait ()
return 0
}