clientcache commands now can output curl strings (#4042)
This commit is contained in:
1 parent
b71d134e06
commit
9aab26e91b
8 files changed
+131
-21
No files matched your search
@@ -19,10 +19,18 @@ var LastOutputStringError *OutputStringError
|
||||
|
||||
type OutputStringError struct {
|
||||
*retryablehttp.Request
|
||||
unixSocket string
|
||||
parsingError error
|
||||
parsedCurlString string
|
||||
}
|
||||
|
||||
func NewOutputDomainSocketCurlStringError(req *retryablehttp.Request, socketAddr string) *OutputStringError {
|
||||
return &OutputStringError{
|
||||
Request: req,
|
||||
unixSocket: socketAddr,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *OutputStringError) Error() string {
|
||||
if d.parsedCurlString == "" {
|
||||
d.parseRequest()
|
||||
@@ -46,6 +54,9 @@ func (d *OutputStringError) parseRequest() {
|
||||
if d.Request.Method != "GET" {
|
||||
d.parsedCurlString = fmt.Sprintf("%s -X %s", d.parsedCurlString, d.Request.Method)
|
||||
}
|
||||
if d.unixSocket != "" {
|
||||
d.parsedCurlString = fmt.Sprintf("%s --unix-socket %s", d.parsedCurlString, d.unixSocket)
|
||||
}
|
||||
for k, v := range d.Request.Header {
|
||||
for _, h := range v {
|
||||
if strings.ToLower(k) == "authorization" {
|
||||
|
||||
@@ -109,7 +109,11 @@ func (c *AddTokenCommand) Add(ctx context.Context, apiClient *api.Client, keyrin
|
||||
} else {
|
||||
return nil, nil, errors.New("The found auth token is not in the proper format.")
|
||||
}
|
||||
pa.AuthToken = token
|
||||
if c.FlagOutputCurlString {
|
||||
pa.AuthToken = "/*token*/"
|
||||
} else {
|
||||
pa.AuthToken = token
|
||||
}
|
||||
default:
|
||||
at := c.ReadTokenFromKeyring(keyringType, tokenName)
|
||||
if at == nil {
|
||||
@@ -121,15 +125,19 @@ func (c *AddTokenCommand) Add(ctx context.Context, apiClient *api.Client, keyrin
|
||||
}
|
||||
pa.AuthTokenId = at.Id
|
||||
}
|
||||
var opts []client.Option
|
||||
if c.FlagOutputCurlString {
|
||||
opts = append(opts, client.WithOutputCurlString())
|
||||
}
|
||||
|
||||
dotPath, err := DefaultDotDirectory(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return addToken(ctx, dotPath, &pa)
|
||||
return addToken(ctx, dotPath, &pa, opts...)
|
||||
}
|
||||
|
||||
func addToken(ctx context.Context, daemonPath string, p *daemon.UpsertTokenRequest) (*api.Response, *api.Error, error) {
|
||||
func addToken(ctx context.Context, daemonPath string, p *daemon.UpsertTokenRequest, opt ...client.Option) (*api.Response, *api.Error, error) {
|
||||
addr, err := daemon.SocketAddress(daemonPath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error when retrieving the socket address: %w", err)
|
||||
@@ -143,7 +151,7 @@ func addToken(ctx context.Context, daemonPath string, p *daemon.UpsertTokenReque
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error when making a new client: %w.", err)
|
||||
}
|
||||
resp, err := c.Post(ctx, "/v1/tokens", p)
|
||||
resp, err := c.Post(ctx, "/v1/tokens", p, opt...)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error when sending request to the daemon: %w.", err)
|
||||
}
|
||||
|
||||
@@ -96,11 +96,15 @@ func (c *StatusCommand) Status(ctx context.Context) (*api.Response, *daemon.Stat
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
var opts []client.Option
|
||||
if c.FlagOutputCurlString {
|
||||
opts = append(opts, client.WithOutputCurlString())
|
||||
}
|
||||
|
||||
return status(ctx, dotPath)
|
||||
return status(ctx, dotPath, opts...)
|
||||
}
|
||||
|
||||
func status(ctx context.Context, daemonPath string) (*api.Response, *daemon.StatusResult, *api.Error, error) {
|
||||
func status(ctx context.Context, daemonPath string, opt ...client.Option) (*api.Response, *daemon.StatusResult, *api.Error, error) {
|
||||
const op = "daemon.status"
|
||||
addr, err := daemon.SocketAddress(daemonPath)
|
||||
if err != nil {
|
||||
@@ -115,7 +119,7 @@ func status(ctx context.Context, daemonPath string) (*api.Response, *daemon.Stat
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
resp, err := c.Get(ctx, "/v1/status", nil)
|
||||
resp, err := c.Get(ctx, "/v1/status", nil, opt...)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("client do failed"))
|
||||
}
|
||||
|
||||
@@ -132,11 +132,11 @@ func (c *SearchCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
func (c *SearchCommand) Search(ctx context.Context) (*api.Response, *daemon.SearchResult, *api.Error, error) {
|
||||
client, err := c.Client()
|
||||
cl, err := c.Client()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
t := client.Token()
|
||||
t := cl.Token()
|
||||
if t == "" {
|
||||
return nil, nil, nil, fmt.Errorf("Auth Token selected for searching is empty.")
|
||||
}
|
||||
@@ -150,15 +150,19 @@ func (c *SearchCommand) Search(ctx context.Context) (*api.Response, *daemon.Sear
|
||||
resource: c.flagResource,
|
||||
authTokenId: strings.Join(tSlice[:2], "_"),
|
||||
}
|
||||
var opts []client.Option
|
||||
if c.FlagOutputCurlString {
|
||||
opts = append(opts, client.WithOutputCurlString())
|
||||
}
|
||||
|
||||
dotPath, err := daemoncmd.DefaultDotDirectory(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return search(ctx, dotPath, tf)
|
||||
return search(ctx, dotPath, tf, opts...)
|
||||
}
|
||||
|
||||
func search(ctx context.Context, daemonPath string, fb filterBy) (*api.Response, *daemon.SearchResult, *api.Error, error) {
|
||||
func search(ctx context.Context, daemonPath string, fb filterBy, opt ...client.Option) (*api.Response, *daemon.SearchResult, *api.Error, error) {
|
||||
addr, err := daemon.SocketAddress(daemonPath)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("Error when retrieving the socket address: %w", err)
|
||||
@@ -175,7 +179,7 @@ func search(ctx context.Context, daemonPath string, fb filterBy) (*api.Response,
|
||||
q.Add("auth_token_id", fb.authTokenId)
|
||||
q.Add("resource", fb.resource)
|
||||
q.Add("query", fb.flagQuery)
|
||||
resp, err := c.Get(ctx, "/v1/search", q)
|
||||
resp, err := c.Get(ctx, "/v1/search", q, opt...)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("Error when sending request to the daemon: %w.", err)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
)
|
||||
|
||||
const hostHeader = "api.boundary.localhost"
|
||||
const hostHeader = "clientcache.boundary.localhost"
|
||||
|
||||
type Client struct {
|
||||
client *retryablehttp.Client
|
||||
@@ -56,11 +56,19 @@ func New(ctx context.Context, address *url.URL) (*Client, error) {
|
||||
|
||||
// Get sends a GET http request to the provided path. The vals provided are
|
||||
// encoded and attached to the request if present.
|
||||
func (c *Client) Get(ctx context.Context, path string, vals *url.Values) (*api.Response, error) {
|
||||
func (c *Client) Get(ctx context.Context, path string, vals *url.Values, opt ...Option) (*api.Response, error) {
|
||||
req := request(ctx, "GET", path)
|
||||
if vals != nil {
|
||||
req.URL.RawQuery = vals.Encode()
|
||||
}
|
||||
opts, err := getOpts(opt...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.withOutputCurlString {
|
||||
api.LastOutputStringError = api.NewOutputDomainSocketCurlStringError(req, c.domainSocketPath)
|
||||
return nil, api.LastOutputStringError
|
||||
}
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -70,7 +78,7 @@ func (c *Client) Get(ctx context.Context, path string, vals *url.Values) (*api.R
|
||||
|
||||
// Post sends a POST http request to the provided path. The body is marshaled
|
||||
// to json and added to the request body.
|
||||
func (c *Client) Post(ctx context.Context, path string, body any) (*api.Response, error) {
|
||||
func (c *Client) Post(ctx context.Context, path string, body any, opt ...Option) (*api.Response, error) {
|
||||
req := request(ctx, "POST", path)
|
||||
if body != nil {
|
||||
b, err := json.Marshal(body)
|
||||
@@ -79,6 +87,14 @@ func (c *Client) Post(ctx context.Context, path string, body any) (*api.Response
|
||||
}
|
||||
req.SetBody(b)
|
||||
}
|
||||
opts, err := getOpts(opt...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.withOutputCurlString {
|
||||
api.LastOutputStringError = api.NewOutputDomainSocketCurlStringError(req, c.domainSocketPath)
|
||||
return nil, api.LastOutputStringError
|
||||
}
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package client
|
||||
|
||||
// GetOpts - iterate the inbound Options and return a struct.
|
||||
func getOpts(opt ...Option) (options, error) {
|
||||
opts := getDefaultOptions()
|
||||
for _, o := range opt {
|
||||
if o != nil {
|
||||
err := o(&opts)
|
||||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// Option - how Options are passed as arguments.
|
||||
type Option func(*options) error
|
||||
|
||||
// options - how options are represented.
|
||||
type options struct {
|
||||
withOutputCurlString bool
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
return options{}
|
||||
}
|
||||
|
||||
// WithOutputCurlString specifies that the client should return an
|
||||
// OutputStringError that prints out the curl string for the request being generated.
|
||||
func WithOutputCurlString() Option {
|
||||
return func(o *options) error {
|
||||
o.withOutputCurlString = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_GetOpts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("default", func(t *testing.T) {
|
||||
opts, err := getOpts()
|
||||
assert.NoError(t, err)
|
||||
testOpts := options{}
|
||||
assert.Equal(t, opts, testOpts)
|
||||
})
|
||||
t.Run("WithOutputCurlString", func(t *testing.T) {
|
||||
opts, err := getOpts(WithOutputCurlString())
|
||||
assert.NoError(t, err)
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withOutputCurlString = true
|
||||
assert.Equal(t, opts, testOpts)
|
||||
})
|
||||
}
|
||||
@@ -24,23 +24,23 @@ type refresher interface {
|
||||
// KeyringToken has keyring held auth token information.
|
||||
type KeyringToken struct {
|
||||
// The keyring type used by boundary to access the auth token
|
||||
KeyringType string `json:"keyring_type"`
|
||||
KeyringType string `json:"keyring_type,omitempty"`
|
||||
// The token identifier for the provided keyring type that holds the auth token
|
||||
TokenName string `json:"token_name"`
|
||||
TokenName string `json:"token_name,omitempty"`
|
||||
}
|
||||
|
||||
// userTokenToAdd is the request body to this handler.
|
||||
type UpsertTokenRequest struct {
|
||||
// BoundaryAddr is a required field for all requests
|
||||
BoundaryAddr string `json:"boundary_addr"`
|
||||
BoundaryAddr string `json:"boundary_addr,omitempty"`
|
||||
// The id of the auth token asserted to be attempted to be added
|
||||
AuthTokenId string `json:"auth_token_id"`
|
||||
AuthTokenId string `json:"auth_token_id,omitempty"`
|
||||
// The raw auth token for this user. Either this field or the Keyring field
|
||||
// must be set but not both.
|
||||
AuthToken string `json:"auth_token"`
|
||||
AuthToken string `json:"auth_token,omitempty"`
|
||||
// Keyring is the keyring info used when adding an auth token held in
|
||||
// keyring to the daemon.
|
||||
Keyring *KeyringToken `json:"keyring"`
|
||||
Keyring *KeyringToken `json:"keyring,omitempty"`
|
||||
}
|
||||
|
||||
func newTokenHandlerFunc(ctx context.Context, repo *cache.Repository, refresher refresher) (http.HandlerFunc, error) {
|
||||
|
||||
Reference in new issue
Block a user