Files
threshold/internal/cmd/commands/dev/dev.go
T

444 lines
12 KiB
Go
Raw Normal View History

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"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/servers/controller"
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
"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{}
Config *config.Config
controller *controller.Controller
worker *worker.Worker
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".`,
})
f.StringVar(&base.StringVar{
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-02-12 13:53:27 -05:00
f.StringVar(&base.StringVar{
Name: "password",
Target: &c.flagPassword,
2020-09-21 23:09:40 +08:00
EnvVar: "BOUNDARY_DEV_PASSWORD",
Usage: "Initial login password.",
2020-02-12 13:53:27 -05:00
})
f.StringVar(&base.StringVar{
Name: "login-name",
Target: &c.flagLoginName,
2020-09-21 23:09:40 +08:00
EnvVar: "BOUNDARY_DEV_LOGIN_NAME",
Usage: "Initial admin login name.",
})
2020-02-12 13:53:27 -05:00
f.StringVar(&base.StringVar{
Name: "api-listen-address",
Target: &c.flagControllerAPIListenAddr,
2020-08-10 17:15:02 -04:00
EnvVar: "BOUNDARY_DEV_CONTROLLER_API_LISTEN_ADDRESS",
Usage: "Address to bind to for controller \"api\" purpose.",
})
f.StringVar(&base.StringVar{
Name: "cluster-listen-address",
Target: &c.flagControllerClusterListenAddr,
2020-08-10 17:15:02 -04:00
EnvVar: "BOUNDARY_DEV_CONTROLLER_CLUSTER_LISTEN_ADDRESS",
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{
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{
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.",
})
f.BoolVar(&base.BoolVar{
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
})
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
}
//childShutdownCh := make(chan struct{})
2020-02-12 13:53:27 -05: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
}
if c.flagIdSuffix != "" {
if len(c.flagIdSuffix) != 10 {
c.UI.Error("Invalid ID suffix, must be exactly 10 characters")
return 1
}
if !handlers.ValidId("abc", "abc_"+c.flagIdSuffix) {
c.UI.Error("Invalid ID suffix, must be in the set A-Za-z0-9")
return 1
}
c.DevAuthMethodId = fmt.Sprintf("%s_%s", password.AuthMethodPrefix, c.flagIdSuffix)
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)
}
if c.flagLoginName != "" {
c.DevLoginName = c.flagLoginName
}
if c.flagPassword != "" {
c.DevPassword = c.flagPassword
}
c.Config.PassthroughDirectory = c.flagPassthroughDirectory
2020-02-12 13:53:27 -05:00
for _, l := range c.Config.Listeners {
if len(l.Purpose) != 1 {
c.UI.Error("Only one purpose supported for each listener")
return 1
}
switch l.Purpose[0] {
case "api":
if c.flagControllerAPIListenAddr != "" {
l.Address = c.flagControllerAPIListenAddr
}
case "cluster":
if c.flagControllerClusterListenAddr != "" {
l.Address = c.flagControllerClusterListenAddr
}
2020-09-22 08:28:10 -04:00
case "proxy":
if c.flagWorkerProxyListenAddr != "" {
l.Address = c.flagWorkerProxyListenAddr
2020-09-22 08:28:10 -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
}
base.StartMemProfiler(c.Logger)
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
}
if c.flagRecoveryKey != "" {
c.Config.DevRecoveryKey = c.flagRecoveryKey
}
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")
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")
c.Info["[Worker-Auth] AEAD Key Bytes"] = c.Config.DevWorkerAuthKey
c.InfoKeys = append(c.InfoKeys, "[Recovery] AEAD Key Bytes")
c.Info["[Recovery] AEAD Key Bytes"] = c.Config.DevRecoveryKey
2020-02-12 13:53:27 -05:00
// Initialize the listeners
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
}
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")
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
if err := c.StorePidFile(c.Config.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
var opts []base.Option
if err := c.CreateDevDatabase("postgres", opts...); err != nil {
c.UI.Error(fmt.Errorf("Error creating dev database container: %w", err).Error())
2020-02-12 13:53:27 -05:00
return 1
}
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
c.PrintInfo(c.UI)
2020-02-12 13:53:27 -05:00
c.ReleaseLogGate()
{
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
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
}
{
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
}
/*
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
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
/*
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
/*
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]))
}
}
//shutdownWg.Wait()
2020-02-12 13:53:27 -05:00
return 0
}