2020-02-12 13:53:27 -05:00
package dev
import (
"fmt"
"runtime"
"strings"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/config"
2020-09-23 01:23:10 -04:00
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/servers/controller"
2020-09-22 21:37:37 -04:00
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
2020-09-23 01:23:10 -04:00
"github.com/hashicorp/boundary/internal/servers/worker"
"github.com/hashicorp/boundary/internal/types/scope"
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-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 {}
2020-09-23 01:23:10 -04:00
Config * config . Config
controller * controller . Controller
worker * worker . Worker
2020-09-22 21:37:37 -04:00
flagLogLevel string
flagLogFormat string
flagCombineLogs bool
flagLoginName string
flagPassword string
flagIdSuffix string
flagControllerAPIListenAddr string
flagControllerClusterListenAddr string
flagWorkerProxyListenAddr string
flagWorkerPublicAddr string
flagPassthroughDirectory string
flagRecoveryKey string
flagDisableDatabaseDestruction bool
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".` ,
})
2020-07-14 14:32:47 -07:00
f . StringVar ( & base . StringVar {
2020-09-22 21:37:37 -04:00
Name : "id-suffix" ,
Target : & c . flagIdSuffix ,
EnvVar : "BOUNDARY_DEV_ID_SUFFIX" ,
Usage : `If set, auto-created resources will use this value for their identifier (along with their resource-specific prefix). Must be 10 alphanumeric characters. As an example, if this is set to "1234567890", the generated password auth method ID will be "ampw_1234567890", the generated TCP target ID will be "ttcp_1234567890", and so on.` ,
2020-07-14 14:32:47 -07:00
})
2020-02-12 13:53:27 -05:00
f . StringVar ( & base . StringVar {
2020-09-22 21:37:37 -04:00
Name : "password" ,
Target : & c . flagPassword ,
2020-09-21 23:09:40 +08:00
EnvVar : "BOUNDARY_DEV_PASSWORD" ,
2020-09-22 21:37:37 -04:00
Usage : "Initial login password." ,
2020-02-12 13:53:27 -05:00
})
2020-08-07 10:18:40 -07:00
f . StringVar ( & base . StringVar {
2020-09-22 21:37:37 -04:00
Name : "login-name" ,
Target : & c . flagLoginName ,
2020-09-21 23:09:40 +08:00
EnvVar : "BOUNDARY_DEV_LOGIN_NAME" ,
2020-09-22 21:37:37 -04:00
Usage : "Initial admin login name." ,
2020-08-07 10:18:40 -07:00
})
2020-02-12 13:53:27 -05:00
f . StringVar ( & base . StringVar {
2020-09-22 21:37:37 -04:00
Name : "api-listen-address" ,
Target : & c . flagControllerAPIListenAddr ,
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 {
2020-09-22 21:37:37 -04:00
Name : "cluster-listen-address" ,
Target : & c . flagControllerClusterListenAddr ,
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
})
2020-09-22 08:28:10 -04:00
f . StringVar ( & base . StringVar {
2020-09-22 21:37:37 -04:00
Name : "proxy-listen-address" ,
Target : & c . flagWorkerProxyListenAddr ,
2020-09-22 08:28:10 -04:00
EnvVar : "BOUNDARY_DEV_WORKER_PROXY_LISTEN_ADDRESS" ,
Usage : "Address to bind to for worker \"proxy\" purpose." ,
})
f . StringVar ( & base . StringVar {
2020-09-22 21:37:37 -04:00
Name : "worker-public-address" ,
Target : & c . flagWorkerPublicAddr ,
2020-09-22 08:28:10 -04:00
EnvVar : "BOUNDARY_DEV_WORKER_PUBLIC_ADDRESS" ,
Usage : "Public address at which the worker is reachable for session proxying." ,
})
2020-08-26 23:27:46 -04:00
f . BoolVar ( & base . BoolVar {
2020-09-22 21:37:37 -04:00
Name : "disable-database-destruction" ,
Target : & c . flagDisableDatabaseDestruction ,
2020-09-16 10:10:06 -04:00
Usage : "If set, if a database is created automatically in Docker, it will not be removed when the dev server is shut down." ,
})
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-09-22 21:37:37 -04:00
f . StringVar ( & base . StringVar {
Name : "passthrough-directory" ,
Target : & c . flagPassthroughDirectory ,
EnvVar : "BOUNDARY_DEV_PASSTHROUGH_DIRECTORY" ,
Usage : "Enables a passthrough directory in the webserver at /" ,
})
f . StringVar ( & base . StringVar {
Name : "recovery-key" ,
Target : & c . flagRecoveryKey ,
EnvVar : "BOUNDARY_DEV_RECOVERY_KEY" ,
Usage : "Specifies the base64'd 256-bit AES key to use for recovery operations" ,
})
2020-05-21 16:12:31 -04:00
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
}
2020-09-23 01:23:10 -04:00
//childShutdownCh := make(chan struct{})
2020-02-12 13:53:27 -05:00
2020-09-23 01:23:10 -04:00
c . Config , 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-09-22 21:37:37 -04:00
if c . flagIdSuffix != "" {
if len ( c . flagIdSuffix ) != 10 {
c . UI . Error ( "Invalid ID suffix, must be exactly 10 characters" )
2020-07-14 14:32:47 -07:00
return 1
}
2020-09-22 21:37:37 -04:00
if ! handlers . ValidId ( "abc" , "abc_" + c . flagIdSuffix ) {
c . UI . Error ( "Invalid ID suffix, must be in the set A-Za-z0-9" )
2020-07-14 14:32:47 -07:00
return 1
}
2020-09-22 21:37:37 -04:00
c . DevAuthMethodId = fmt . Sprintf ( "%s_%s" , password . AuthMethodPrefix , c . flagIdSuffix )
2020-09-23 01:23:10 -04:00
c . DevOrgId = fmt . Sprintf ( "%s_%s" , scope . Org . Prefix (), c . flagIdSuffix )
c . DevProjectId = fmt . Sprintf ( "%s_%s" , scope . Project . Prefix (), c . flagIdSuffix )
c . DevHostCatalogId = fmt . Sprintf ( "%s_%s" , static . HostCatalogPrefix , c . flagIdSuffix )
c . DevHostSetId = fmt . Sprintf ( "%s_%s" , static . HostSetPrefix , c . flagIdSuffix )
c . DevHostId = fmt . Sprintf ( "%s_%s" , static . HostPrefix , c . flagIdSuffix )
2020-07-14 14:32:47 -07:00
}
2020-09-22 21:37:37 -04:00
if c . flagLoginName != "" {
c . DevLoginName = c . flagLoginName
2020-08-07 10:18:40 -07:00
}
2020-09-22 21:37:37 -04:00
if c . flagPassword != "" {
c . DevPassword = c . flagPassword
2020-08-07 10:18:40 -07:00
}
2020-07-14 14:32:47 -07:00
2020-09-23 01:23:10 -04:00
c . Config . PassthroughDirectory = c . flagPassthroughDirectory
2020-02-12 13:53:27 -05:00
2020-09-23 01:23:10 -04:00
for _ , l := range c . Config . Listeners {
2020-03-22 08:27:24 -04:00
if len ( l . Purpose ) != 1 {
2020-09-23 01:23:10 -04:00
c . UI . Error ( "Only one purpose supported for each listener" )
return 1
2020-03-22 08:27:24 -04:00
}
switch l . Purpose [ 0 ] {
case "api" :
2020-09-22 21:37:37 -04:00
if c . flagControllerAPIListenAddr != "" {
l . Address = c . flagControllerAPIListenAddr
2020-03-22 08:27:24 -04:00
}
case "cluster" :
2020-09-22 21:37:37 -04:00
if c . flagControllerClusterListenAddr != "" {
l . Address = c . flagControllerClusterListenAddr
2020-03-22 08:27:24 -04:00
}
2020-09-22 08:28:10 -04:00
case "proxy" :
2020-09-22 21:37:37 -04:00
if c . flagWorkerProxyListenAddr != "" {
l . Address = c . flagWorkerProxyListenAddr
2020-09-22 08:28:10 -04:00
}
2020-03-22 08:27:24 -04:00
}
}
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-09-23 01:23:10 -04:00
if err := c . SetupMetrics ( c . UI , c . Config . Telemetry ); err != nil {
2020-02-12 13:53:27 -05:00
c . UI . Error ( err . Error ())
return 1
}
2020-09-23 01:23:10 -04:00
if c . flagRecoveryKey != "" {
c . Config . DevRecoveryKey = c . flagRecoveryKey
2020-08-20 13:34:03 -04:00
}
2020-09-23 01:23:10 -04:00
if err := c . SetupKMSes ( c . UI , c . Config ); err != nil {
2020-02-12 13:53:27 -05:00
c . UI . Error ( err . Error ())
return 1
}
2020-08-19 16:47:18 -04:00
if c . RootKms == nil {
2020-05-11 13:27:54 -04:00
c . UI . Error ( "Controller KMS not found after parsing KMS blocks" )
return 1
}
2020-08-19 16:47:18 -04:00
if c . WorkerAuthKms == nil {
2020-05-11 13:27:54 -04:00
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" )
2020-09-23 01:23:10 -04:00
c . Info [ "[Controller] AEAD Key Bytes" ] = c . Config . DevControllerKey
2020-08-12 00:40:39 -04:00
c . InfoKeys = append ( c . InfoKeys , "[Worker-Auth] AEAD Key Bytes" )
2020-09-23 01:23:10 -04:00
c . Info [ "[Worker-Auth] AEAD Key Bytes" ] = c . Config . DevWorkerAuthKey
2020-08-20 13:34:03 -04:00
c . InfoKeys = append ( c . InfoKeys , "[Recovery] AEAD Key Bytes" )
2020-09-23 01:23:10 -04:00
c . Info [ "[Recovery] AEAD Key Bytes" ] = c . Config . DevRecoveryKey
2020-02-12 13:53:27 -05:00
// Initialize the listeners
2020-09-23 01:23:10 -04:00
if err := c . SetupListeners ( c . UI , c . Config . SharedConfig , [] string { "api" , "cluster" , "proxy" }); err != nil {
2020-02-12 13:53:27 -05:00
c . UI . Error ( err . Error ())
return 1
}
2020-09-23 01:23:10 -04:00
if err := c . SetupWorkerPublicAddress ( c . Config , c . flagWorkerPublicAddr ); err != nil {
2020-09-22 08:28:10 -04:00
c . UI . Error ( err . Error ())
return 1
}
c . InfoKeys = append ( c . InfoKeys , "worker public addr" )
2020-09-23 01:23:10 -04:00
c . Info [ "worker public addr" ] = c . Config . Worker . PublicAddr
2020-09-22 08:28:10 -04:00
2020-02-12 13:53:27 -05:00
// Write out the PID to the file now that server has successfully started
2020-09-23 01:23:10 -04:00
if err := c . StorePidFile ( c . Config . PidFile ); err != nil {
2020-03-20 12:51:39 -04:00
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-08-26 23:27:46 -04:00
var opts [] base . Option
if err := c . CreateDevDatabase ( "postgres" , opts ... ); 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
}
2020-09-22 21:37:37 -04:00
if ! c . flagDisableDatabaseDestruction {
2020-09-16 10:10:06 -04:00
c . ShutdownFuncs = append ( c . ShutdownFuncs , c . DestroyDevDatabase )
}
2020-02-12 13:53:27 -05:00
2020-09-23 01:23:10 -04:00
c . PrintInfo ( c . UI )
2020-02-12 13:53:27 -05:00
c . ReleaseLogGate ()
2020-09-23 01:23:10 -04:00
{
conf := & controller . Config {
RawConfig : c . Config ,
Server : c . Server ,
}
var err error
c . controller , err = controller . New ( conf )
if err != nil {
c . UI . Error ( fmt . Errorf ( "Error initializing controller: %w" , err ). Error ())
return 1
}
2020-03-22 20:17:23 -04:00
2020-09-23 01:23:10 -04:00
if err := c . controller . Start (); err != nil {
retErr := fmt . Errorf ( "Error starting controller: %w" , err )
if err := c . controller . Shutdown ( false ); err != nil {
c . UI . Error ( retErr . Error ())
retErr = fmt . Errorf ( "Error shutting down controller: %w" , err )
}
c . UI . Error ( retErr . Error ())
return 1
}
2020-03-22 20:17:23 -04:00
}
2020-09-23 01:23:10 -04:00
{
conf := & worker . Config {
RawConfig : c . Config ,
Server : c . Server ,
}
var err error
c . worker , err = worker . New ( conf )
if err != nil {
c . UI . Error ( fmt . Errorf ( "Error initializing controller: %w" , err ). Error ())
return 1
}
if err := c . worker . Start (); err != nil {
retErr := fmt . Errorf ( "Error starting worker: %w" , err )
if err := c . worker . Shutdown ( false ); err != nil {
c . UI . Error ( retErr . Error ())
retErr = fmt . Errorf ( "Error shutting down worker: %w" , err )
}
c . UI . Error ( retErr . Error ())
if err := c . controller . Shutdown ( false ); err != nil {
c . UI . Error ( fmt . Errorf ( "Error with controller shutdown: %w" , err ). Error ())
}
return 1
}
2020-03-22 20:17:23 -04:00
}
2020-09-23 01:23:10 -04:00
/*
shutdownWg := &sync.WaitGroup{}
shutdownWg.Add(2)
controllerSighupCh := make(chan struct{})
c.childSighupCh = append(c.childSighupCh, controllerSighupCh)
devController := &controllercmd.Command{
Server: c.Server,
ExtShutdownCh: childShutdownCh,
SighupCh: controllerSighupCh,
Config: c.Config,
}
if err := devController.Start(); err != nil {
c.UI.Error(err.Error())
return 1
}
2020-03-22 20:17:23 -04:00
2020-09-23 01:23:10 -04:00
workerSighupCh := make(chan struct{})
c.childSighupCh = append(c.childSighupCh, workerSighupCh)
devWorker := &workercmd.Command{
Server: c.Server,
ExtShutdownCh: childShutdownCh,
SighupCh: workerSighupCh,
Config: c.Config,
}
if err := devWorker.Start(); err != nil {
c.UI.Error(err.Error())
return 1
}
go func() {
defer shutdownWg.Done()
devController.WaitForInterrupt()
}()
go func() {
defer shutdownWg.Done()
devWorker.WaitForInterrupt()
}()
*/
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-09-23 01:23:10 -04:00
/*
childShutdownCh <- struct{}{}
childShutdownCh <- struct{}{}
*/
if err := c . worker . Shutdown ( false ); err != nil {
c . UI . Error ( fmt . Errorf ( "Error shutting down worker: %w" , err ). Error ())
}
if err := c . controller . Shutdown ( false ); err != nil {
c . UI . Error ( fmt . Errorf ( "Error shutting down controller: %w" , err ). Error ())
}
2020-02-12 13:53:27 -05:00
shutdownTriggered = true
2020-09-23 01:23:10 -04:00
/*
case <-c.SighupCh:
c.UI.Output("==> Boundary dev environment reload triggered")
for _, v := range c.childSighupCh {
v <- struct{}{}
}
*/
2020-02-12 13:53:27 -05:00
case <- c . SigUSR2Ch :
buf := make ([] byte , 32 * 1024 * 1024 )
n := runtime . Stack ( buf [:], true )
c . Logger . Info ( "goroutine trace" , "stack" , string ( buf [: n ]))
}
}
2020-09-23 01:23:10 -04:00
//shutdownWg.Wait()
2020-02-12 13:53:27 -05:00
return 0
}