Skip to content

Commit

Permalink
Collect resource info in repository_<resource>s.go (#3769)
Browse files Browse the repository at this point in the history
* Collect resource info in repository_<resource>s.go

* Move search logic into the domain (#3794)
  • Loading branch information
talanknight committed Oct 16, 2023
1 parent fbe41a0 commit f7f2f1b
Show file tree
Hide file tree
Showing 11 changed files with 663 additions and 250 deletions.
94 changes: 28 additions & 66 deletions internal/cmd/commands/daemon/search_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/hashicorp/boundary/api/sessions"
"github.com/hashicorp/boundary/api/targets"
"github.com/hashicorp/boundary/internal/daemon/cache"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/util"
)
Expand All @@ -32,27 +31,31 @@ const (
authTokenIdKey = "auth_token_id"
)

func newSearchTargetsHandlerFunc(ctx context.Context, repo *cache.Repository) (http.HandlerFunc, error) {
const op = "daemon.newSearchTargetsHandlerFunc"
func newSearchHandlerFunc(ctx context.Context, repo *cache.Repository) (http.HandlerFunc, error) {
const op = "daemon.newSearchHandlerFunc"
switch {
case util.IsNil(repo):
return nil, errors.New(ctx, errors.InvalidParameter, op, "repository is missing")
}

s, err := cache.NewSearchService(ctx, repo)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}

return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
filter, err := handlers.NewFilter(ctx, r.URL.Query().Get(filterKey))
if err != nil {
writeError(w, err.Error(), http.StatusBadRequest)
return
}

resource := r.URL.Query().Get(resourceKey)
authTokenId := r.URL.Query().Get(authTokenIdKey)

searchableResource := cache.ToSearchableResource(resource)
switch {
case resource == "":
writeError(w, "resource is a required field but was empty", http.StatusBadRequest)
return
case !searchableResource.Valid():
writeError(w, "provided resource is not a valid searchable resource", http.StatusBadRequest)
return
case authTokenId == "":
writeError(w, fmt.Sprintf("%s is a required field but was empty", authTokenIdKey), http.StatusBadRequest)
return
Expand All @@ -65,31 +68,30 @@ func newSearchTargetsHandlerFunc(ctx context.Context, repo *cache.Repository) (h
}

query := r.URL.Query().Get(queryKey)

var res *SearchResult
switch resource {
case "targets":
res, err = searchTargets(r.Context(), repo, authTokenId, query, filter)
case "sessions":
res, err = searchSessions(r.Context(), repo, authTokenId, query, filter)
default:
writeError(w, fmt.Sprintf("search doesn't support %q resource", resource), http.StatusBadRequest)
return
}

filter := r.URL.Query().Get(filterKey)

res, err := s.Search(ctx, cache.SearchParams{
AuthTokenId: authTokenId,
Resource: searchableResource,
Query: query,
Filter: filter,
})
if err != nil {
switch {
case errors.Match(errors.T(errors.InvalidParameter), err):
writeError(w, err.Error(), http.StatusBadRequest)
default:
writeError(w, err.Error(), http.StatusInternalServerError)
}
return
}
if res == nil {
writeError(w, "nil SearchResult generated", http.StatusInternalServerError)
return
}

j, err := json.Marshal(res)
apiRes := toApiResult(res)
j, err := json.Marshal(apiRes)
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
Expand All @@ -99,50 +101,10 @@ func newSearchTargetsHandlerFunc(ctx context.Context, repo *cache.Repository) (h
}, nil
}

func searchTargets(ctx context.Context, repo *cache.Repository, authTokenId, query string, filter *handlers.Filter) (*SearchResult, error) {
var found []*targets.Target
var err error
switch query {
case "":
found, err = repo.ListTargets(ctx, authTokenId)
default:
found, err = repo.QueryTargets(ctx, authTokenId, query)
}
if err != nil {
return nil, err
}

finalTars := make([]*targets.Target, 0, len(found))
for _, item := range found {
if filter.Match(item) {
finalTars = append(finalTars, item)
}
}
// toApiResult converts a domain search result to an api search result
func toApiResult(sr *cache.SearchResult) *SearchResult {
return &SearchResult{
Targets: finalTars,
}, nil
}

func searchSessions(ctx context.Context, repo *cache.Repository, authTokenId, query string, filter *handlers.Filter) (*SearchResult, error) {
var found []*sessions.Session
var err error
switch query {
case "":
found, err = repo.ListSessions(ctx, authTokenId)
default:
found, err = repo.QuerySessions(ctx, authTokenId, query)
Targets: sr.Targets,
Sessions: sr.Sessions,
}
if err != nil {
return nil, err
}

finalSess := make([]*sessions.Session, 0, len(found))
for _, item := range found {
if filter.Match(item) {
finalSess = append(finalSess, item)
}
}
return &SearchResult{
Sessions: finalSess,
}, nil
}
2 changes: 1 addition & 1 deletion internal/cmd/commands/daemon/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func (s *cacheServer) serve(ctx context.Context, cmd Commander, l net.Listener,
}()

mux := http.NewServeMux()
searchTargetsFn, err := newSearchTargetsHandlerFunc(ctx, repo)
searchTargetsFn, err := newSearchHandlerFunc(ctx, repo)
if err != nil {
return errors.Wrap(ctx, err, op)
}
Expand Down
6 changes: 3 additions & 3 deletions internal/cmd/commands/search/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func TestSearch(t *testing.T) {
flagQuery: "name=name",
resource: "hosts",
},
apiErrContains: "doesn't support \"hosts\" resource",
apiErrContains: "provided resource is not a valid searchable resource",
},
{
name: "unknown auth token id",
Expand All @@ -105,10 +105,10 @@ func TestSearch(t *testing.T) {
apiErrContains: "Forbidden",
},
{
name: "query on unsupported column",
name: "unsupported column",
fb: filterBy{
authTokenId: at.Id,
flagQuery: "item % tar",
flagQuery: "item % 'tar'",
resource: "targets",
},
apiErrContains: "invalid column \"item\"",
Expand Down
89 changes: 5 additions & 84 deletions internal/daemon/cache/repository_refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,14 @@ package cache
import (
"context"
stderrors "errors"
"fmt"

"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/authtokens"
"github.com/hashicorp/boundary/api/sessions"
"github.com/hashicorp/boundary/api/targets"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/event"
"github.com/hashicorp/boundary/internal/util"
)

// TargetRetrievalFunc is a function that retrieves targets
// from the provided boundary addr using the provided token.
type TargetRetrievalFunc func(ctx context.Context, addr, token string) ([]*targets.Target, error)

func defaultTargetFunc(ctx context.Context, addr, token string) ([]*targets.Target, error) {
const op = "cache.defaultTargetFunc"
client, err := api.NewClient(&api.Config{
Addr: addr,
Token: token,
})
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
tarClient := targets.NewClient(client)
l, err := tarClient.List(ctx, "global", targets.WithRecursive(true))
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
return l.Items, nil
}

// SessionRetrievalFunc is a function that retrieves sessions
// from the provided boundary addr using the provided token.
type SessionRetrievalFunc func(ctx context.Context, addr, token string) ([]*sessions.Session, error)

func defaultSessionFunc(ctx context.Context, addr, token string) ([]*sessions.Session, error) {
const op = "cache.defaultSessionFunc"
client, err := api.NewClient(&api.Config{
Addr: addr,
Token: token,
})
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
sClient := sessions.NewClient(client)
l, err := sClient.List(ctx, "global", sessions.WithRecursive(true))
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
return l.Items, nil
}

// cleanAndPickAuthTokens removes from the cache all auth tokens which are
// evicted from the cache or no longer stored in a keyring and returns the
// remaining ones.
Expand Down Expand Up @@ -139,22 +94,10 @@ func (r *Repository) Refresh(ctx context.Context, opt ...Option) error {
return errors.Wrap(ctx, err, op)
}

opts, err := getOpts(opt...)
if err != nil {
return errors.Wrap(ctx, err, op)
}

us, err := r.listUsers(ctx)
if err != nil {
return errors.Wrap(ctx, err, op)
}
if opts.withTargetRetrievalFunc == nil {
opts.withTargetRetrievalFunc = defaultTargetFunc
}
if opts.withSessionRetrievalFunc == nil {
opts.withSessionRetrievalFunc = defaultSessionFunc
}

var retErr error
for _, u := range us {
tokens, err := r.cleanAndPickAuthTokens(ctx, u)
Expand All @@ -163,35 +106,13 @@ func (r *Repository) Refresh(ctx context.Context, opt ...Option) error {
continue
}

// Find and use a token for retrieving targets
for at, t := range tokens {
resp, err := opts.withTargetRetrievalFunc(ctx, u.Address, t)
if err != nil {
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op, errors.WithMsg("for token %q", at.Id)))
continue
}

event.WriteSysEvent(ctx, op, fmt.Sprintf("updating %d targets for user %v", len(resp), u))
if err := r.refreshTargets(ctx, u, resp); err != nil {
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op, errors.WithMsg("for user %v", u)))
}
break
if err := r.refreshTargets(ctx, u, tokens, opt...); err != nil {
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op))
}

// Find and use a token for retrieving sessions
for at, t := range tokens {
resp, err := opts.withSessionRetrievalFunc(ctx, u.Address, t)
if err != nil {
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op, errors.WithMsg("for token %q", at.Id)))
continue
}

event.WriteSysEvent(ctx, op, fmt.Sprintf("updating %d sessions for user %v", len(resp), u))
if err := r.refreshSessions(ctx, u, resp); err != nil {
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op, errors.WithMsg("for user %v", u)))
}
break
if err := r.refreshSessions(ctx, u, tokens, opt...); err != nil {
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op))
}

}
return retErr
}
Loading

0 comments on commit f7f2f1b

Please sign in to comment.