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
@@ -64,8 +64,19 @@ func (c *AddTokenCommand) Run(args []string) int {
|
||||
c.PrintCliError(err)
|
||||
return base.CommandUserError
|
||||
}
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
c.PrintCliError(err)
|
||||
return base.CommandCliError
|
||||
}
|
||||
|
||||
apiErr, err := c.Add(ctx)
|
||||
keyringType, tokenName, err := c.DiscoverKeyringTokenInfo()
|
||||
if err != nil {
|
||||
c.PrintCliError(err)
|
||||
return base.CommandCliError
|
||||
}
|
||||
|
||||
apiErr, err := c.Add(ctx, client, keyringType, tokenName)
|
||||
if err != nil {
|
||||
c.PrintCliError(err)
|
||||
return base.CommandCliError
|
||||
@@ -78,24 +89,14 @@ func (c *AddTokenCommand) Run(args []string) int {
|
||||
return base.CommandSuccess
|
||||
}
|
||||
|
||||
func (c *AddTokenCommand) Add(ctx context.Context) (*api.Error, error) {
|
||||
func (c *AddTokenCommand) Add(ctx context.Context, client *api.Client, keyringType, tokenName string) (*api.Error, error) {
|
||||
const op = "daemon.(AddTokenCommand).Add"
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyringType, tokenName, err := c.DiscoverKeyringTokenInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pa := daemon.UpsertTokenRequest{
|
||||
BoundaryAddr: client.Addr(),
|
||||
}
|
||||
switch keyringType {
|
||||
case "", base.NoneKeyring:
|
||||
keyringType = base.NoneKeyring
|
||||
token := client.Token()
|
||||
if parts := strings.SplitN(token, "_", 4); len(parts) == 3 {
|
||||
pa.AuthTokenId = strings.Join(parts[:2], "_")
|
||||
@@ -129,12 +130,15 @@ func addToken(ctx context.Context, daemonPath string, p *daemon.UpsertTokenReque
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
addr := daemon.SocketAddress(daemonPath)
|
||||
_, err = os.Stat(strings.TrimPrefix(addr, "unix://"))
|
||||
if strings.HasPrefix(addr, "unix://") && err != nil {
|
||||
addr, err := daemon.SocketAddress(daemonPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if err := client.SetAddr(addr); err != nil {
|
||||
_, err = os.Stat(addr.Path)
|
||||
if strings.EqualFold(addr.Scheme, "unix") && err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if err := client.SetAddr(addr.String()); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
// Because this is using the real lib it can pick up from stored locations
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -69,12 +69,15 @@ func (s *StopCommand) stop(ctx context.Context) error {
|
||||
func stopThroughHandler(ctx context.Context, dotPath string) (*api.Error, error) {
|
||||
const op = "daemon.stopThroughHandler"
|
||||
|
||||
sockAddr := daemon.SocketAddress(dotPath)
|
||||
sockAddr, err := daemon.SocketAddress(dotPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
client, err := api.NewClient(nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if err := client.SetAddr(sockAddr); err != nil {
|
||||
if err := client.SetAddr(sockAddr.String()); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
// Because this is using the real lib it can pick up from stored locations
|
||||
|
||||
@@ -169,14 +169,14 @@ func search(ctx context.Context, daemonPath string, fb filterBy) (*api.Response,
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
addr := daemon.SocketAddress(daemonPath)
|
||||
addr, err := daemon.SocketAddress(daemonPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if _, err := os.Stat(strings.TrimPrefix(addr, "unix://")); strings.HasPrefix(addr, "unix://") && err == os.ErrNotExist {
|
||||
if _, err := os.Stat(addr.Path); addr.Scheme == "unix" && err == os.ErrNotExist {
|
||||
return nil, errors.New(ctx, errors.Internal, op, "daemon unix socket is not setup")
|
||||
}
|
||||
if err := client.SetAddr(addr); err != nil {
|
||||
if err := client.SetAddr(addr.String()); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
|
||||
@@ -25,24 +25,12 @@ type testCommander struct {
|
||||
at *authtokens.AuthToken
|
||||
}
|
||||
|
||||
func (r *testCommander) keyring() string {
|
||||
return r.at.Id
|
||||
}
|
||||
|
||||
func (r *testCommander) tokenName() string {
|
||||
return r.at.Id
|
||||
}
|
||||
|
||||
func (r *testCommander) Client(opt ...base.Option) (*api.Client, error) {
|
||||
client, err := api.NewClient(nil)
|
||||
require.NoError(r.t, err)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (r *testCommander) DiscoverKeyringTokenInfo() (string, string, error) {
|
||||
return r.keyring(), r.tokenName(), nil
|
||||
}
|
||||
|
||||
func (r *testCommander) ReadTokenFromKeyring(k, a string) *authtokens.AuthToken {
|
||||
return r.at
|
||||
}
|
||||
@@ -72,6 +60,7 @@ func TestSearch(t *testing.T) {
|
||||
}()
|
||||
// Give the store some time to get initialized
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
srv.AddKeyringToken(t, "address", "keyringtype", "tokenname", at.Id, boundaryTokenReaderFn)
|
||||
|
||||
errorCases := []struct {
|
||||
name string
|
||||
|
||||
Reference in new issue
Block a user