Add skip daemon flag and send recently authed tokens to daemon (#3883)
* Add skip daemon flag and send recently authed tokens to daemon * Rename flag skip-daemon to skip-cache-daemon * adding option test * move common.Options to base.Option * SocketAddress returns *url.URL, fix search test
This commit is contained in:
1 parent
2f7459fa40
commit
3a7ea64a31
27 files changed
+429
-419
No files matched your search
@@ -10,24 +10,31 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/clientcache/internal/daemon"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
// Keep this interface aligned with the interface at internal/cmd/commands.go
|
||||
type cacheEnabledCommand interface {
|
||||
cli.Command
|
||||
BaseCommand() *base.Command
|
||||
}
|
||||
|
||||
// CommandWrapper starts the boundary daemon after the command was Run and attempts
|
||||
// to send the current persona to any running daemon.
|
||||
type CommandWrapper struct {
|
||||
cli.Command
|
||||
ui cli.Ui
|
||||
cacheEnabledCommand
|
||||
}
|
||||
|
||||
// Wrap returns a cli.CommandFactory that returns a command wrapped in the CommandWrapper.
|
||||
func Wrap(ui cli.Ui, wrapped cli.Command) cli.CommandFactory {
|
||||
func Wrap(c cacheEnabledCommand) cli.CommandFactory {
|
||||
return func() (cli.Command, error) {
|
||||
return &CommandWrapper{
|
||||
Command: wrapped,
|
||||
ui: ui,
|
||||
cacheEnabledCommand: c,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
@@ -35,11 +42,18 @@ func Wrap(ui cli.Ui, wrapped cli.Command) cli.CommandFactory {
|
||||
// Run runs the wrapped command and then attempts to start the boundary daemon and send
|
||||
// the current persona
|
||||
func (w *CommandWrapper) Run(args []string) int {
|
||||
r := w.Command.Run(args)
|
||||
if w.BaseCommand().FlagSkipCacheDaemon {
|
||||
return w.cacheEnabledCommand.Run(args)
|
||||
}
|
||||
|
||||
// potentially intercept the token in case it isn't stored in the keyring
|
||||
var token string
|
||||
w.cacheEnabledCommand.BaseCommand().Opts = append(w.cacheEnabledCommand.BaseCommand().Opts, base.WithInterceptedToken(&token))
|
||||
r := w.cacheEnabledCommand.Run(args)
|
||||
|
||||
ctx := context.Background()
|
||||
if w.startDaemon(ctx) {
|
||||
w.addPersonaInCache(ctx)
|
||||
w.addTokenToCache(ctx, token)
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -49,7 +63,7 @@ func (w *CommandWrapper) Run(args []string) int {
|
||||
func (w *CommandWrapper) startDaemon(ctx context.Context) bool {
|
||||
cmdName, err := os.Executable()
|
||||
if err != nil {
|
||||
w.ui.Error(fmt.Sprintf("unable to find boundary binary for daemon startup: %s", err.Error()))
|
||||
w.BaseCommand().UI.Error(fmt.Sprintf("unable to find boundary binary for daemon startup: %s", err.Error()))
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -64,10 +78,59 @@ func (w *CommandWrapper) startDaemon(ctx context.Context) bool {
|
||||
return err == nil || strings.Contains(stdErr.String(), "already running")
|
||||
}
|
||||
|
||||
// addPersonaInCache runs AddPersonaCommand
|
||||
func (w *CommandWrapper) addPersonaInCache(ctx context.Context) bool {
|
||||
c := AddTokenCommand{Command: base.NewCommand(w.ui)}
|
||||
c.Flags()
|
||||
apiErr, err := c.Add(ctx)
|
||||
// addTokenToCache runs AddTokenCommand with the token used in, or retrieved by
|
||||
// the wrapped command.
|
||||
func (w *CommandWrapper) addTokenToCache(ctx context.Context, token string) bool {
|
||||
com := AddTokenCommand{Command: base.NewCommand(w.BaseCommand().UI)}
|
||||
client, err := w.BaseCommand().Client()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
keyringType, tokName, err := w.BaseCommand().DiscoverKeyringTokenInfo()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if token != "" {
|
||||
client.SetToken(token)
|
||||
}
|
||||
|
||||
// Since the daemon might have just started, we need to wait until it can
|
||||
// respond to our requests
|
||||
waitCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||
defer cancel()
|
||||
if err := waitForDaemon(waitCtx); err != nil {
|
||||
// TODO: Print the result of this out into a log in the dot directory
|
||||
return false
|
||||
}
|
||||
|
||||
apiErr, err := com.Add(ctx, client, keyringType, tokName)
|
||||
return err == nil && apiErr == nil
|
||||
}
|
||||
|
||||
// waitForDaemon continually looks for the unix socket until it is found or the
|
||||
// provided context is done. It returns an error if the unix socket is not found
|
||||
// before the context is done.
|
||||
func waitForDaemon(ctx context.Context) error {
|
||||
const op = "daemon.waitForDaemon"
|
||||
dotPath, err := DefaultDotDirectory(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timer := time.NewTimer(0)
|
||||
|
||||
addr, err := daemon.SocketAddress(dotPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
_, err = os.Stat(addr.Path)
|
||||
for os.IsNotExist(err) {
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
_, err = os.Stat(addr.Path)
|
||||
timer.Reset(10 * time.Millisecond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user