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:
Todd authored and Johan Brandhorst-Satzkorn committed 2024-01-05 07:43:20 -08:00
1 parent 2f7459fa40
commit 3a7ea64a31
27 files changed
+429 -419

No files matched your search

+20 -16
View File
@@ -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
+3 -3
View File
@@ -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)
}
+1 -12
View File
@@ -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