2020-09-23 01:23:10 -04:00
package server
2020-02-12 13:53:27 -05:00
import (
2021-02-05 12:58:54 -07:00
"context"
2020-02-12 13:53:27 -05:00
"fmt"
2020-12-18 12:12:25 -05:00
"net"
2020-02-12 13:53:27 -05:00
"runtime"
"strings"
2021-02-05 12:58:54 -07:00
"time"
2020-02-12 13:53:27 -05:00
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/config"
2021-01-25 14:43:50 -07:00
"github.com/hashicorp/boundary/internal/db"
2021-01-19 10:40:41 -07:00
"github.com/hashicorp/boundary/internal/db/schema"
2021-01-25 14:43:50 -07:00
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/servers/controller"
2020-09-23 01:23:10 -04:00
"github.com/hashicorp/boundary/internal/servers/worker"
2021-01-25 14:43:50 -07:00
"github.com/hashicorp/boundary/internal/types/scope"
2020-09-09 01:24:25 -04:00
"github.com/hashicorp/boundary/sdk/wrapper"
2020-02-12 13:53:27 -05:00
"github.com/hashicorp/go-hclog"
2020-08-27 16:38:44 -04:00
wrapping "github.com/hashicorp/go-kms-wrapping"
2020-02-12 13:53:27 -05:00
"github.com/hashicorp/go-multierror"
2021-06-15 12:35:29 -05:00
"github.com/hashicorp/shared-secure-libs/configutil"
2020-02-12 13:53:27 -05:00
"github.com/hashicorp/vault/sdk/helper/mlock"
"github.com/mitchellh/cli"
"github.com/posener/complete"
2021-02-25 17:57:10 -05:00
"go.uber.org/atomic"
2020-02-12 13:53:27 -05:00
)
2021-01-07 10:02:32 -05:00
var (
_ cli . Command = ( * Command )( nil )
_ 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
2021-02-25 17:57:10 -05:00
SighupCh chan struct {}
SigUSR2Ch chan struct {}
2020-02-12 13:53:27 -05:00
2020-03-22 20:17:23 -04:00
Config * config . Config
controller * controller . Controller
2020-09-23 01:23:10 -04:00
worker * worker . Worker
2020-02-12 13:53:27 -05:00
2020-08-27 16:38:44 -04:00
configWrapper wrapping . Wrapper
2020-06-24 13:08:13 -07:00
2020-09-22 21:37:37 -04:00
flagConfig string
flagConfigKms string
flagLogLevel string
flagLogFormat string
flagCombineLogs bool
2021-02-25 17:57:10 -05:00
reloadedCh chan struct {} // for tests
startedCh chan struct {} // for tests
presetConfig * atomic . String // for tests
// for tests -- Prometheus seems to use a global collector so if package
// tests are run it errors out even if they're not run in parallel 🙄 cause
// Go runs the tests within the same overall process
skipMetrics bool
2020-02-12 13:53:27 -05:00
}
2020-03-10 15:48:04 -04:00
func ( c * Command ) Synopsis () string {
2020-09-23 01:23:10 -04:00
return "Start a Boundary server"
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-09-23 01:23:10 -04:00
Usage: boundary server [options]
2020-02-12 13:53:27 -05:00
2020-09-23 01:23:10 -04:00
Start a server (controller, worker, or both) with a configuration file:
2020-02-12 13:53:27 -05:00
2020-09-23 01:23:10 -04:00
$ boundary server -config=/etc/boundary/controller.hcl
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-09-22 21:37:37 -04:00
set := c . FlagSet ( base . FlagSetNone )
2020-02-12 13:53:27 -05:00
f := set . NewFlagSet ( "Command Options" )
f . StringVar ( & base . StringVar {
Name : "config" ,
Target : & c . flagConfig ,
Completion : complete . PredictOr (
complete . PredictFiles ( "*.hcl" ),
complete . PredictFiles ( "*.json" ),
),
2020-08-27 16:38:44 -04:00
Usage : "Path to the configuration file." ,
})
f . StringVar ( & base . StringVar {
Name : "config-kms" ,
Target : & c . flagConfigKms ,
Completion : complete . PredictOr (
complete . PredictFiles ( "*.hcl" ),
complete . PredictFiles ( "*.json" ),
),
Usage : `Path to a configuration file containing a "kms" block marked for "config" purpose, to perform decryption of the main configuration file. If not set, will look for such a block in the main configuration file, which has some drawbacks; see the help output for "boundary config encrypt -h" for details.` ,
2020-02-12 13:53:27 -05:00
})
f . StringVar ( & base . StringVar {
Name : "log-level" ,
Target : & c . flagLogLevel ,
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 ,
Completion : complete . PredictSet ( "standard" , "json" ),
Usage : `Log format. Supported values are "standard" and "json".` ,
})
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
if result := c . ParseFlagsAndConfig ( args ); result > 0 {
return result
}
2020-08-27 16:38:44 -04:00
if c . configWrapper != nil {
defer func () {
if err := c . configWrapper . Finalize ( c . Context ); err != nil {
c . UI . Warn ( fmt . Errorf ( "Error finalizing config kms: %w" , err ). Error ())
}
}()
}
2020-02-12 13:53:27 -05:00
if err := c . SetupLogging ( c . flagLogLevel , c . flagLogFormat , c . Config . LogLevel , c . Config . LogFormat ); err != nil {
c . UI . Error ( err . Error ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-02-12 13:53:27 -05:00
}
2021-07-08 15:50:25 -04:00
if err := c . SetupEventing ( c . Logger , c . StderrLock , base . WithEventerConfig ( c . Config . Eventing )); err != nil {
c . UI . Error ( err . Error ())
return base . CommandUserError
}
2020-03-20 12:51:39 -04:00
base . StartMemProfiler ( c . Logger )
2020-03-19 15:17:40 -04:00
2021-02-25 17:57:10 -05:00
if ! c . skipMetrics {
if err := c . SetupMetrics ( c . UI , c . Config . Telemetry ); err != nil {
c . UI . Error ( err . Error ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2021-02-25 17:57:10 -05:00
}
2020-02-12 13:53:27 -05:00
}
2020-08-20 13:34:03 -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 ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-05-11 13:27:54 -04:00
}
2020-09-23 01:23:10 -04:00
if c . Config . Controller != nil {
if c . RootKms == nil {
c . UI . Error ( "Root KMS not found after parsing KMS blocks" )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-23 01:23:10 -04:00
}
2020-05-11 13:27:54 -04:00
}
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" )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-02-12 13:53:27 -05:00
}
if c . Config . DefaultMaxRequestDuration != 0 {
globals . DefaultMaxRequestDuration = c . Config . DefaultMaxRequestDuration
}
// If mlockall(2) isn't supported, show a warning. We disable this in dev
2020-08-10 17:15:02 -04:00
// because it is quite scary to see when first using Boundary. We also disable
2020-02-12 13:53:27 -05:00
// this if the user has explicitly disabled mlock in configuration.
2020-09-22 21:37:37 -04:00
if ! c . Config . DisableMlock && ! mlock . Supported () {
2020-02-12 13:53:27 -05:00
c . UI . Warn ( base . WrapAtLength (
"WARNING! mlock is not supported on this system! An mlockall(2)-like " +
"syscall to prevent memory from being swapped to disk is not " +
2020-08-10 17:15:02 -04:00
"supported on this system. For better security, only run Boundary on " +
"systems where this call is supported. If you are running Boundary" +
2020-02-12 13:53:27 -05:00
"in a Docker container, provide the IPC_LOCK cap to the container." ))
}
2020-03-22 08:27:24 -04:00
// Perform controller-specific listener checks here before setup
2020-09-24 13:14:45 -04:00
var clusterAddr string
2020-09-23 01:23:10 -04:00
var foundApi bool
var foundProxy bool
2020-03-22 08:27:24 -04:00
for _ , lnConfig := range c . Config . Listeners {
switch len ( lnConfig . Purpose ) {
2020-09-23 01:23:10 -04:00
case 0 :
c . UI . Error ( "Listener specified without a purpose" )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-23 01:23:10 -04:00
2020-03-22 08:27:24 -04:00
case 1 :
2020-09-23 01:23:10 -04:00
purpose := lnConfig . Purpose [ 0 ]
switch purpose {
2020-03-22 08:27:24 -04:00
case "cluster" :
2020-09-24 13:14:45 -04:00
clusterAddr = lnConfig . Address
if clusterAddr == "" {
clusterAddr = "127.0.0.1:9201"
2021-02-10 20:22:02 -05:00
lnConfig . Address = clusterAddr
2020-09-24 13:14:45 -04:00
}
2020-03-22 08:27:24 -04:00
case "api" :
2020-09-23 01:23:10 -04:00
foundApi = true
2020-09-03 13:24:34 -04:00
case "proxy" :
2020-09-23 01:23:10 -04:00
foundProxy = true
2021-02-11 20:10:48 -05:00
if lnConfig . Address == "" {
lnConfig . Address = "127.0.0.1:9202"
}
2020-03-22 08:27:24 -04:00
default :
c . UI . Error ( fmt . Sprintf ( "Unknown listener purpose %q" , lnConfig . Purpose [ 0 ]))
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-03-22 08:27:24 -04:00
}
2020-09-23 01:23:10 -04:00
default :
c . UI . Error ( "Specifying a listener with more than one purpose is not supported" )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-03-22 08:27:24 -04:00
}
}
2020-09-23 01:23:10 -04:00
if c . Config . Controller != nil {
if ! foundApi {
c . UI . Error ( `Config activates controller but no listener with "api" purpose found` )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-23 01:23:10 -04:00
}
2020-09-24 13:14:45 -04:00
if clusterAddr == "" {
2020-09-23 01:23:10 -04:00
c . UI . Error ( `Config activates controller but no listener with "cluster" purpose found` )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-23 01:23:10 -04:00
}
2021-02-10 20:22:02 -05:00
if err := c . SetupControllerPublicClusterAddress ( c . Config , "" ); err != nil {
c . UI . Error ( err . Error ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2021-02-10 20:22:02 -05:00
}
2021-02-11 20:10:48 -05:00
c . InfoKeys = append ( c . InfoKeys , "controller public cluster addr" )
c . Info [ "controller public cluster addr" ] = c . Config . Controller . PublicClusterAddr
2020-03-22 08:27:24 -04:00
}
2021-02-10 20:22:02 -05:00
2020-09-23 01:23:10 -04:00
if c . Config . Worker != nil {
if ! foundProxy {
c . UI . Error ( `Config activates worker but no listener with "proxy" purpose found` )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-23 01:23:10 -04:00
}
2021-02-10 20:22:02 -05:00
if c . Config . Worker != nil {
if err := c . SetupWorkerPublicAddress ( c . Config , "" ); err != nil {
c . UI . Error ( err . Error ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2021-02-10 20:22:02 -05:00
}
2021-02-11 20:10:48 -05:00
c . InfoKeys = append ( c . InfoKeys , "worker public proxy addr" )
c . Info [ "worker public proxy addr" ] = c . Config . Worker . PublicAddr
2021-02-10 20:22:02 -05:00
}
2020-09-24 13:14:45 -04:00
if c . Config . Controller != nil {
switch len ( c . Config . Worker . Controllers ) {
case 0 :
2021-02-03 08:58:42 -05:00
if c . Config . Controller . PublicClusterAddr != "" {
clusterAddr = c . Config . Controller . PublicClusterAddr
}
2020-12-18 12:12:25 -05:00
c . Config . Worker . Controllers = [] string { clusterAddr }
2020-09-24 13:14:45 -04:00
case 1 :
if c . Config . Worker . Controllers [ 0 ] == clusterAddr {
break
}
2021-02-03 08:58:42 -05:00
if c . Config . Controller . PublicClusterAddr != "" &&
c . Config . Worker . Controllers [ 0 ] == c . Config . Controller . PublicClusterAddr {
break
}
2020-12-18 12:12:25 -05:00
// Best effort see if it's a domain name and if not assume it must match
host , _ , err := net . SplitHostPort ( c . Config . Worker . Controllers [ 0 ])
if err != nil && strings . Contains ( err . Error (), "missing port in address" ) {
err = nil
host = c . Config . Worker . Controllers [ 0 ]
}
if err == nil {
ip := net . ParseIP ( host )
if ip == nil {
// Assume it's a domain name
break
}
}
2020-09-24 13:14:45 -04:00
fallthrough
default :
2020-12-18 12:12:25 -05:00
c . UI . Error ( `When running a combined controller and worker, it's invalid to specify a "controllers" key in the worker block with any value other than the controller cluster address/port when using IPs rather than DNS names` )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-24 13:14:45 -04:00
}
2020-12-18 12:12:25 -05:00
}
for _ , controller := range c . Config . Worker . Controllers {
host , _ , err := net . SplitHostPort ( controller )
if err != nil {
if strings . Contains ( err . Error (), "missing port in address" ) {
host = controller
} else {
c . UI . Error ( fmt . Errorf ( "Invalid controller address %q: %w" , controller , err ). Error ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-12-18 12:12:25 -05:00
}
}
ip := net . ParseIP ( host )
if ip != nil {
var errMsg string
switch {
case ip . IsUnspecified ():
errMsg = "an unspecified"
case ip . IsMulticast ():
errMsg = "a multicast"
}
if errMsg != "" {
c . UI . Error ( fmt . Sprintf ( "Controller address %q is invalid: cannot be %s address" , controller , errMsg ))
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-12-18 12:12:25 -05:00
}
}
2020-09-24 13:14:45 -04:00
}
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 ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-02-12 13:53:27 -05:00
}
2020-10-30 16:54:53 -04:00
if c . Config . Controller != nil {
2020-12-18 12:12:25 -05:00
for _ , ln := range c . Config . Listeners {
for _ , purpose := range ln . Purpose {
if purpose != "cluster" {
continue
}
host , _ , err := net . SplitHostPort ( ln . Address )
if err != nil {
if strings . Contains ( err . Error (), "missing port in address" ) {
host = ln . Address
} else {
c . UI . Error ( fmt . Errorf ( "Invalid cluster listener address %q: %w" , ln . Address , err ). Error ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-12-18 12:12:25 -05:00
}
}
ip := net . ParseIP ( host )
if ip != nil {
if ip . IsUnspecified () && c . Config . Controller . PublicClusterAddr == "" {
c . UI . Error ( fmt . Sprintf ( "When %q listener has an unspecified address, %q must be set" , "cluster" , "public_cluster_addr" ))
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-12-18 12:12:25 -05:00
}
}
}
}
2020-10-30 16:54:53 -04:00
}
2020-09-23 01:23:10 -04:00
2020-02-12 13:53:27 -05:00
// Write out the PID to the file now that server has successfully started
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 ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-02-12 13:53:27 -05:00
}
2020-09-23 01:23:10 -04:00
if c . Config . Controller != nil {
if c . Config . Controller . Database == nil || c . Config . Controller . Database . Url == "" {
c . UI . Error ( `"url" not specified in "controller.database" config block"` )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-23 01:23:10 -04:00
}
2021-01-25 14:43:50 -07:00
var err error
2021-06-15 12:35:29 -05:00
c . DatabaseUrl , err = configutil . ParsePath ( c . Config . Controller . Database . Url )
if err != nil && ! errors . Is ( err , configutil . ErrNotAUrl ) {
2020-09-23 01:23:10 -04:00
c . UI . Error ( fmt . Errorf ( "Error parsing database url: %w" , err ). Error ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-23 01:23:10 -04:00
}
2021-05-03 23:42:18 +01:00
c . DatabaseMaxOpenConnections = c . Config . Controller . Database . MaxOpenConnections
2020-09-23 01:23:10 -04:00
if err := c . ConnectToDatabase ( "postgres" ); err != nil {
c . UI . Error ( fmt . Errorf ( "Error connecting to database: %w" , err ). Error ())
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2020-09-23 01:23:10 -04:00
}
2021-01-19 10:40:41 -07:00
sMan , err := schema . NewManager ( c . Context , "postgres" , c . Database . DB ())
if err != nil {
c . UI . Error ( fmt . Errorf ( "Can't get schema manager: %w." , err ). Error ())
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2021-01-19 10:40:41 -07:00
}
// This is an advisory locks on the DB which is released when the db session ends.
if err := sMan . SharedLock ( c . Context ); err != nil {
c . UI . Error ( fmt . Errorf ( "Unable to gain shared access to the database: %w" , err ). Error ())
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2021-01-19 10:40:41 -07:00
}
defer func () {
2021-05-14 16:26:02 -04:00
// The base context has already been canceled so we shouldn't use it here.
2021-02-05 12:58:54 -07:00
// 1 second is chosen so the shutdown is still responsive and this is a mostly
// non critical step since the lock should be released when the session with the
// database is closed.
2021-02-09 13:42:14 -05:00
ctx , cancel := context . WithTimeout ( context . Background (), 1 * time . Second )
2021-02-05 12:58:54 -07:00
defer cancel ()
if err := sMan . SharedUnlock ( ctx ); err != nil {
2021-01-19 10:40:41 -07:00
c . UI . Error ( fmt . Errorf ( "Unable to release shared lock to the database: %w" , err ). Error ())
}
}()
ckState , err := sMan . CurrentState ( c . Context )
if err != nil {
c . UI . Error ( fmt . Errorf ( "Error checking schema state: %w" , err ). Error ())
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2021-01-19 10:40:41 -07:00
}
2021-01-28 14:30:08 -07:00
if ! ckState . InitializationStarted {
c . UI . Error ( base . WrapAtLength ( "The database has not been initialized. Please run 'boundary database init'." ))
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2021-01-28 14:30:08 -07:00
}
2021-01-19 10:40:41 -07:00
if ckState . Dirty {
c . UI . Error ( base . WrapAtLength ( "Database is in a bad state. Please revert the database into the last known good state." ))
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2021-01-19 10:40:41 -07:00
}
if ckState . BinarySchemaVersion > ckState . DatabaseSchemaVersion {
2021-02-09 13:42:14 -05:00
c . UI . Error ( base . WrapAtLength ( "Database schema must be updated to use this version. Run 'boundary database migrate' to update the database. NOTE: Boundary does not currently support live migration; ensure all controllers are shut down before running the migration command." ))
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2021-01-19 10:40:41 -07:00
}
if ckState . BinarySchemaVersion < ckState . DatabaseSchemaVersion {
c . UI . Error ( base . WrapAtLength ( fmt . Sprintf ( "Newer schema version (%d) " +
"than this binary expects. Please use a newer version of the boundary " +
"binary." , ckState . DatabaseSchemaVersion )))
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2021-01-19 10:40:41 -07:00
}
2021-01-25 14:43:50 -07:00
if err := c . verifyKmsSetup (); err != nil {
c . UI . Error ( base . WrapAtLength ( "Database is in a bad state. Please revert the database into the last known good state." ))
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2021-01-25 14:43:50 -07:00
}
2020-02-12 13:53:27 -05:00
}
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-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
if c . Config . Controller != nil {
if err := c . StartController (); err != nil {
c . UI . Error ( err . Error ())
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2020-09-23 01:23:10 -04:00
}
}
if c . Config . Worker != nil {
if err := c . StartWorker (); err != nil {
c . UI . Error ( err . Error ())
if err := c . controller . Shutdown ( false ); err != nil {
c . UI . Error ( fmt . Errorf ( "Error with controller shutdown: %w" , err ). Error ())
}
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2020-09-23 01:23:10 -04:00
}
2020-03-22 20:17:23 -04:00
}
2021-02-25 17:57:10 -05:00
// Inform any tests that the server is ready
if c . startedCh != nil {
close ( c . startedCh )
}
2020-03-22 20:17:23 -04:00
return c . WaitForInterrupt ()
2020-02-12 13:53:27 -05:00
}
2020-03-10 15:48:04 -04:00
func ( c * Command ) ParseFlagsAndConfig ( args [] string ) int {
2020-02-12 13:53:27 -05:00
var err error
f := c . Flags ()
if err = f . Parse ( args ); err != nil {
c . UI . Error ( err . Error ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-02-12 13:53:27 -05:00
}
2021-02-25 17:57:10 -05:00
if len ( c . flagConfig ) == 0 && c . presetConfig == nil {
2020-09-22 21:37:37 -04:00
c . UI . Error ( "Must specify a config file using -config" )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-22 21:37:37 -04:00
}
2020-03-22 08:27:24 -04:00
2021-02-25 17:57:10 -05:00
switch {
case c . presetConfig != nil :
c . Config , err = config . Parse ( c . presetConfig . Load ())
default :
wrapperPath := c . flagConfig
if c . flagConfigKms != "" {
wrapperPath = c . flagConfigKms
}
var configWrapper wrapping . Wrapper
if wrapperPath != "" {
configWrapper , err = wrapper . GetWrapperFromPath ( wrapperPath , "config" )
if err != nil {
c . UI . Error ( err . Error ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2021-02-25 17:57:10 -05:00
}
if configWrapper != nil {
c . configWrapper = configWrapper
if err := configWrapper . Init ( c . Context ); err != nil {
c . UI . Error ( fmt . Errorf ( "Could not initialize kms: %w" , err ). Error ())
2021-03-04 20:05:17 -05:00
return base . CommandCliError
2021-02-25 17:57:10 -05:00
}
}
}
c . Config , err = config . LoadFile ( c . flagConfig , configWrapper )
}
2020-09-22 21:37:37 -04:00
if err != nil {
c . UI . Error ( "Error parsing config: " + err . Error ())
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-02-12 13:53:27 -05:00
}
2020-09-23 01:23:10 -04:00
if c . Config . Controller == nil && c . Config . Worker == nil {
c . UI . Error ( "Neither worker nor controller specified in configuration file." )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-23 01:23:10 -04:00
}
if c . Config . Controller != nil && c . Config . Controller . Name == "" {
c . UI . Error ( "Controller has no name set. It must be the unique name of this instance." )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-23 01:23:10 -04:00
}
if c . Config . Worker != nil && c . Config . Worker . Name == "" {
c . UI . Error ( "Worker has no name set. It must be the unique name of this instance." )
2021-03-04 20:05:17 -05:00
return base . CommandUserError
2020-09-23 01:23:10 -04:00
}
2021-03-04 20:05:17 -05:00
return base . CommandSuccess
2020-02-12 13:53:27 -05:00
}
2020-09-23 01:23:10 -04:00
func ( c * Command ) StartController () error {
2020-03-19 15:17:40 -04:00
conf := & controller . Config {
2020-03-10 15:48:04 -04:00
RawConfig : c . Config ,
Server : c . Server ,
2020-02-12 13:53:27 -05:00
}
2020-03-22 20:17:23 -04:00
var err error
c . controller , err = controller . New ( conf )
2020-02-12 13:53:27 -05:00
if err != nil {
2020-03-22 20:17:23 -04:00
return fmt . Errorf ( "Error initializing controller: %w" , err )
2020-02-12 13:53:27 -05:00
}
2020-03-22 20:17:23 -04:00
if err := c . controller . Start (); err != nil {
retErr := fmt . Errorf ( "Error starting controller: %w" , err )
2020-08-12 00:40:39 -04:00
if err := c . controller . Shutdown ( false ); err != nil {
2020-03-22 20:17:23 -04:00
c . UI . Error ( retErr . Error ())
2020-09-23 01:23:10 -04:00
retErr = fmt . Errorf ( "Error shutting down controller: %w" , err )
}
return retErr
}
return nil
}
func ( c * Command ) StartWorker () error {
conf := & worker . Config {
RawConfig : c . Config ,
Server : c . Server ,
}
var err error
c . worker , err = worker . New ( conf )
if err != nil {
return fmt . Errorf ( "Error initializing worker: %w" , err )
}
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 )
2020-02-12 13:53:27 -05:00
}
2020-03-22 20:17:23 -04:00
return retErr
2020-02-12 13:53:27 -05:00
}
2020-03-22 20:17:23 -04:00
return nil
}
func ( c * Command ) WaitForInterrupt () int {
2020-02-12 13:53:27 -05:00
// Wait for shutdown
shutdownTriggered := false
for ! shutdownTriggered {
select {
2021-02-25 17:57:10 -05:00
case <- c . ShutdownCh :
2020-09-23 01:23:10 -04:00
c . UI . Output ( "==> Boundary server shutdown triggered" )
2020-02-12 13:53:27 -05:00
2020-09-23 01:23:10 -04:00
if c . Config . Worker != nil {
if err := c . worker . Shutdown ( false ); err != nil {
c . UI . Error ( fmt . Errorf ( "Error shutting down worker: %w" , err ). Error ())
}
}
if c . Config . Controller != nil {
if err := c . controller . Shutdown ( c . Config . Worker != nil ); err != nil {
c . UI . Error ( fmt . Errorf ( "Error shutting down controller: %w" , err ). Error ())
}
2020-02-12 13:53:27 -05:00
}
shutdownTriggered = true
case <- c . SighupCh :
2021-01-21 16:27:20 -05:00
c . UI . Output ( "==> Boundary server reload triggered" )
2020-02-12 13:53:27 -05:00
// Check for new log level
var level hclog . Level
var err error
var newConf * config . Config
2021-02-25 17:57:10 -05:00
if c . flagConfig == "" && c . presetConfig == nil {
2020-02-12 13:53:27 -05:00
goto RUNRELOADFUNCS
}
2021-02-25 17:57:10 -05:00
if c . presetConfig != nil {
newConf , err = config . Parse ( c . presetConfig . Load ())
} else {
newConf , err = config . LoadFile ( c . flagConfig , c . configWrapper )
}
2020-02-12 13:53:27 -05:00
if err != nil {
c . Logger . Error ( "could not reload config" , "path" , c . flagConfig , "error" , err )
goto RUNRELOADFUNCS
}
// Ensure at least one config was found.
if newConf == nil {
c . Logger . Error ( "no config found at reload time" )
goto RUNRELOADFUNCS
}
if newConf . LogLevel != "" {
configLogLevel := strings . ToLower ( strings . TrimSpace ( newConf . LogLevel ))
switch configLogLevel {
case "trace" :
level = hclog . Trace
case "debug" :
level = hclog . Debug
case "notice" , "info" , "" :
level = hclog . Info
case "warn" , "warning" :
level = hclog . Warn
case "err" , "error" :
level = hclog . Error
default :
c . Logger . Error ( "unknown log level found on reload" , "level" , newConf . LogLevel )
goto RUNRELOADFUNCS
}
2020-05-11 13:30:38 -04:00
c . Logger . SetLevel ( level )
2020-02-12 13:53:27 -05:00
}
RUNRELOADFUNCS :
2021-02-25 17:57:10 -05:00
if err := c . Reload ( newConf ); err != nil {
c . UI . Error ( fmt . Errorf ( "Error(s) were encountered during reload: %w" , err ). Error ())
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 ]))
}
}
2021-03-04 20:05:17 -05:00
return base . CommandSuccess
2020-02-12 13:53:27 -05:00
}
2021-02-25 17:57:10 -05:00
func ( c * Command ) Reload ( newConf * config . Config ) error {
2020-02-12 13:53:27 -05:00
c . ReloadFuncsLock . RLock ()
defer c . ReloadFuncsLock . RUnlock ()
var reloadErrors * multierror . Error
2021-02-25 17:57:10 -05:00
for _ , relFunc := range c . ReloadFuncs [ "listeners" ] {
if relFunc != nil {
if err := relFunc (); err != nil {
reloadErrors = multierror . Append ( reloadErrors , fmt . Errorf ( "error encountered reloading listener: %w" , err ))
2020-02-12 13:53:27 -05:00
}
}
}
2021-02-25 17:57:10 -05:00
if newConf != nil && c . worker != nil {
c . worker . ParseAndStoreTags ( newConf . Worker . Tags )
}
2020-02-12 13:53:27 -05:00
// Send a message that we reloaded. This prevents "guessing" sleep times
// in tests.
2021-02-25 17:57:10 -05:00
if c . reloadedCh != nil {
select {
case c . reloadedCh <- struct {}{}:
default :
}
2020-02-12 13:53:27 -05:00
}
return reloadErrors . ErrorOrNil ()
}
2021-01-25 14:43:50 -07:00
func ( c * Command ) verifyKmsSetup () error {
const op = "server.(Command).verifyKmsExists"
rw := db . New ( c . Database )
kmsRepo , err := kms . NewRepository ( rw , rw )
if err != nil {
return fmt . Errorf ( "error creating kms repository: %w" , err )
}
rks , err := kmsRepo . ListRootKeys ( c . Context , kms . WithLimit ( 1 ))
if err != nil {
return err
}
for _ , rk := range rks {
if rk . GetScopeId () == scope . Global . String () {
return nil
}
}
return errors . New ( errors . MigrationIntegrity , op , "can't find global scoped root key" )
}