You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
boundary/internal/cmd/commands/dev/dev.go

266 lines
6.6 KiB

package dev
import (
"fmt"
"runtime"
"strings"
"sync"
"github.com/hashicorp/watchtower/internal/cmd/base"
controllercmd "github.com/hashicorp/watchtower/internal/cmd/commands/controller"
controllerconfig "github.com/hashicorp/watchtower/internal/cmd/commands/controller/config"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
var _ cli.Command = (*Command)(nil)
var _ cli.CommandAutocomplete = (*Command)(nil)
var memProfilerEnabled = false
type Command struct {
*base.Command
*base.Server
ShutdownCh chan struct{}
SighupCh chan struct{}
childSighupCh []chan struct{}
ReloadedCh chan struct{}
SigUSR2Ch chan struct{}
cleanupGuard sync.Once
flagConfig string
flagLogLevel string
flagLogFormat string
flagDev bool
flagDevAdminToken string
flagDevControllerListenAddr string
flagCombineLogs bool
}
func (c *Command) Synopsis() string {
return "Start a Watchtower dev environment"
}
func (c *Command) Help() string {
helpText := `
Usage: watchtower dev [options]
Start a dev environment:
$ watchtower dev
For a full list of examples, please see the documentation.
` + c.Flags().Help()
return strings.TrimSpace(helpText)
}
func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP)
f := set.NewFlagSet("Command Options")
f.StringVar(&base.StringVar{
Name: "log-level",
Target: &c.flagLogLevel,
Default: base.NotSetValue,
EnvVar: "WATCHTOWER_LOG_LEVEL",
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")
f.StringVar(&base.StringVar{
Name: "dev-admin-token",
Target: &c.flagDevAdminToken,
Default: "",
EnvVar: "WATCHTWER_DEV_ADMIN_TOKEN",
Usage: "Initial admin token. This only applies when running in \"dev\" " +
"mode.",
})
f.StringVar(&base.StringVar{
Name: "dev-listen-address",
Target: &c.flagDevControllerListenAddr,
Default: "127.0.0.1:9200",
EnvVar: "WATCHTOWER_DEV_LISTEN_ADDRESS",
Usage: "Address to bind against.",
})
f.BoolVar(&base.BoolVar{
Name: "combine-logs",
Target: &c.flagCombineLogs,
Default: false,
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.",
})
return set
}
func (c *Command) AutocompleteArgs() complete.Predictor {
return complete.PredictNothing
}
func (c *Command) AutocompleteFlags() complete.Flags {
return c.Flags().Completions()
}
func (c *Command) Run(args []string) int {
c.Server = base.NewServer()
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{})
devControllerConfig, err := controllerconfig.Dev()
if err != nil {
c.UI.Error(fmt.Sprintf("Error creating controller dev config: %s", err))
return 1
}
if err := c.SetupLogging(c.flagLogLevel, c.flagLogFormat, "", ""); err != nil {
c.UI.Error(err.Error())
return 1
}
if memProfilerEnabled {
base.StartMemProfiler(c.Logger)
}
if err := c.SetupMetrics(c.UI, devControllerConfig.Telemetry); err != nil {
c.UI.Error(err.Error())
return 1
}
if err := c.SetupKMSes(c.UI, devControllerConfig.SharedConfig, 2); err != nil {
c.UI.Error(err.Error())
return 1
}
// Initialize the listeners
if err := c.SetupListeners(c.UI, devControllerConfig.SharedConfig); err != nil {
c.UI.Error(err.Error())
return 1
}
// Write out the PID to the file now that server has successfully started
if err := c.StorePidFile(devControllerConfig.PidFile); err != nil {
c.UI.Error(fmt.Sprintf("Error storing PID: %w", err))
return 1
}
/*
// If we're in Dev mode, then initialize the core
if c.flagDev && !c.flagDevSkipInit {
init, err := c.enableDev(core, coreConfig)
if err != nil {
c.UI.Error(fmt.Sprintf("Error initializing Dev mode: %w", err))
return 1
}
// Print the big dev mode warning!
c.UI.Warn(base.WrapAtLength(
"WARNING! dev mode is enabled! In this mode, Watchtower runs entirely " +
"in-memory and all state is lost upon shutdown."))
c.UI.Warn("")
c.UI.Warn("You may need to set the following environment variable:")
c.UI.Warn("")
endpointURL := "http://" + config.Listeners[0].Config["address"].(string)
if runtime.GOOS == "windows" {
c.UI.Warn("PowerShell:")
c.UI.Warn(fmt.Sprintf(" $env:WATCHTOWER_ADDR=\"%s\"", endpointURL))
c.UI.Warn("cmd.exe:")
c.UI.Warn(fmt.Sprintf(" set WATCHTOWER_ADDR=%s", endpointURL))
} else {
c.UI.Warn(fmt.Sprintf(" $ export VAULT_ADDR='%s'", endpointURL))
}
c.UI.Warn(fmt.Sprintf("Root Token: %s", init.RootToken))
c.UI.Warn("")
c.UI.Warn(base.WrapAtLength(
"Development mode should NOT be used in production installations!"))
c.UI.Warn("")
}
*/
defer c.RunShutdownFuncs(c.UI)
if err := c.CreateDevDatabase(); err != nil {
c.UI.Error(fmt.Sprintf("Error creating dev database container: %s", err.Error()))
return 1
}
c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase)
c.PrintInfo(c.UI, "dev mode")
c.ReleaseLogGate()
// Instantiate the wait group
shutdownWg := &sync.WaitGroup{}
shutdownWg.Add(1)
controllerSighupCh := make(chan struct{})
c.childSighupCh = append(c.childSighupCh, controllerSighupCh)
go func() {
defer shutdownWg.Done()
devController := &controllercmd.Command{
Command: c.Command,
Server: c.Server,
ShutdownCh: childShutdownCh,
SighupCh: controllerSighupCh,
Config: devControllerConfig,
}
devController.Start()
}()
// Wait for shutdown
shutdownTriggered := false
for !shutdownTriggered {
select {
case <-c.ShutdownCh:
c.UI.Output("==> Watchtower dev environment shutdown triggered")
close(childShutdownCh)
shutdownTriggered = true
case <-c.SighupCh:
c.UI.Output("==> Watchtower dev environment reload triggered")
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]))
}
}
// Wait for dependent goroutines to complete
shutdownWg.Wait()
return 0
}