diff --git a/internal/daemon/cluster/handlers/worker_service_test.go b/internal/daemon/cluster/handlers/worker_service_test.go index 8ed7552de..401b5b497 100644 --- a/internal/daemon/cluster/handlers/worker_service_test.go +++ b/internal/daemon/cluster/handlers/worker_service_test.go @@ -276,6 +276,9 @@ func TestAuthorizeConnection(t *testing.T) { require.NoError(t, kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader))) currentConnFn := connectionRouteFn + t.Cleanup(func() { + connectionRouteFn = currentConnFn + }) connectionRouteFn = singleHopConnectionRoute org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) @@ -422,7 +425,6 @@ func TestAuthorizeConnection(t *testing.T) { assert.Empty(t, cmp.Diff(resp, tc.want, protocmp.Transform())) }) } - connectionRouteFn = currentConnFn } func TestCancelSession(t *testing.T) { diff --git a/internal/daemon/common/worker_list.go b/internal/daemon/common/worker_list.go index ff2a923be..2ba6e53ca 100644 --- a/internal/daemon/common/worker_list.go +++ b/internal/daemon/common/worker_list.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/boundary/internal/daemon/controller/handlers" "github.com/hashicorp/boundary/internal/server" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" + "github.com/hashicorp/boundary/version" "github.com/hashicorp/go-bexpr" "github.com/mitchellh/pointerstructure" "google.golang.org/grpc/codes" @@ -43,6 +44,19 @@ func (w WorkerList) WorkerInfos() []*pb.WorkerInfo { return ret } +// SupportsFeature returns a new WorkerList composed of all workers in this +// WorkerList which supports the provided feature. +func (w WorkerList) SupportsFeature(f version.Feature) WorkerList { + var ret []*server.Worker + for _, worker := range w { + sv := version.FromVersionString(worker.GetReleaseVersion()).Semver() + if version.SupportsFeature(sv, f) { + ret = append(ret, worker) + } + } + return ret +} + // filtered returns a new workerList where all elements contained in it are the // ones which from the original workerList that pass the evaluator's evaluation. func (w WorkerList) Filtered(eval *bexpr.Evaluator) (WorkerList, error) { diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index 4f1d1f8d1..7519b4137 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -625,7 +625,7 @@ func (s Service) RemoveTargetCredentialSources(ctx context.Context, req *pbs.Rem // If set, use the worker_filter or egress_worker_filter to filtere the selected workers // and ensure we have workers available to service this request. -func AuthorizeSessionWithWorkerFilter(_ context.Context, t target.Target, selectedWorkers []*server.Worker, _ common.Downstreamers) ([]*server.Worker, error) { +func AuthorizeSessionWithWorkerFilter(_ context.Context, t target.Target, selectedWorkers wl.WorkerList, _ common.Downstreamers) (wl.WorkerList, error) { if len(selectedWorkers) > 0 { var eval *bexpr.Evaluator var err error @@ -641,7 +641,7 @@ func AuthorizeSessionWithWorkerFilter(_ context.Context, t target.Target, select return nil, err } - selectedWorkers, err = wl.WorkerList(selectedWorkers).Filtered(eval) + selectedWorkers, err = selectedWorkers.Filtered(eval) if err != nil { return nil, err } diff --git a/internal/session/query.go b/internal/session/query.go index 648da953a..1b5d219ff 100644 --- a/internal/session/query.go +++ b/internal/session/query.go @@ -109,13 +109,14 @@ session_connection_limit(expiration_time, connection_limit) as ( select s.expiration_time, s.connection_limit, - s.egress_worker_filter + s.egress_worker_filter, + s.ingress_worker_filter from session s where s.public_id = @session_id ) -select expiration_time, connection_limit, egress_worker_filter, current_connection_count +select expiration_time, connection_limit, egress_worker_filter, ingress_worker_filter, current_connection_count from session_connection_limit, session_connection_count; ` diff --git a/internal/session/repository_session.go b/internal/session/repository_session.go index a1d19cea0..b64810bcb 100644 --- a/internal/session/repository_session.go +++ b/internal/session/repository_session.go @@ -446,6 +446,7 @@ type AuthzSummary struct { ConnectionLimit int32 CurrentConnectionCount uint32 EgressWorkerFilter string + IngressWorkerFilter string } func (r *Repository) sessionAuthzSummary(ctx context.Context, sessionId string) (*AuthzSummary, error) { diff --git a/version/version.go b/version/version.go index 22066a136..479f097c3 100644 --- a/version/version.go +++ b/version/version.go @@ -3,6 +3,9 @@ package version import ( "bytes" "fmt" + "strings" + + gvers "github.com/hashicorp/go-version" ) const BoundaryPrefix = "Boundary v" @@ -54,6 +57,52 @@ func (c *Info) VersionNumber() string { return version } +// Semver returns a *gvers.Version if the Info is parseable as +// a semantic version. Otherwise it returns nil. +func (c *Info) Semver() *gvers.Version { + if c == nil { + return nil + } + v, err := gvers.NewSemver(c.VersionNumber()) + if err != nil { + return nil + } + return v +} + +// FromVersionString returns an *Info containing the version, or nil if the +// string was unable to be parsed. +func FromVersionString(s string) *Info { + s = strings.TrimSpace(s) + s = strings.TrimPrefix(s, BoundaryPrefix) + i := Info{} + + // Get the revision + startOfRevIdx := strings.LastIndex(s, "(") + endOfRevIdx := strings.LastIndex(s, ")") + if startOfRevIdx > 0 && endOfRevIdx > 0 { + if endOfRevIdx < startOfRevIdx { + return nil + } + i.Revision, s = s[startOfRevIdx+1:endOfRevIdx], strings.TrimSpace(s[:startOfRevIdx]) + } + + v, err := gvers.NewSemver(s) + if err != nil { + return nil + } + + if md := v.Metadata(); len(md) > 0 { + i.VersionMetadata = md + } + if pr := v.Prerelease(); len(pr) > 0 { + i.VersionPrerelease = pr + } + i.Version = v.Core().String() + + return &i +} + func (c *Info) FullVersionNumber(rev bool) string { var versionString bytes.Buffer diff --git a/version/version_test.go b/version/version_test.go new file mode 100644 index 000000000..0327cd2c0 --- /dev/null +++ b/version/version_test.go @@ -0,0 +1,118 @@ +package version + +import ( + "testing" + + gvers "github.com/hashicorp/go-version" + "github.com/stretchr/testify/assert" +) + +func TestFromVersionString(t *testing.T) { + cases := []struct { + input string + want *Info + }{ + { + input: "Boundary v0.12.0-beta+hcp.int (revision)", + want: &Info{ + Revision: "revision", + Version: "0.12.0", + VersionPrerelease: "beta", + VersionMetadata: "hcp.int", + }, + }, + { + input: "Boundary v0.12.0+hcp.int", + want: &Info{ + Version: "0.12.0", + VersionMetadata: "hcp.int", + }, + }, + { + input: "Boundary v0.12.0", + want: &Info{ + Version: "0.12.0", + }, + }, + { + input: "0.12.0-alpha+hcp.int (revision)", + want: &Info{ + Revision: "revision", + Version: "0.12.0", + VersionPrerelease: "alpha", + VersionMetadata: "hcp.int", + }, + }, + { + input: " 0.12.0-hcp+int ", + want: &Info{ + Version: "0.12.0", + VersionPrerelease: "hcp", + VersionMetadata: "int", + }, + }, + { + input: "0.12.0- spaces in the prerelease are invalid", + want: nil, + }, + { + input: "Boundary (version unknown)", + want: nil, + }, + { + input: "(version unknown)", + want: nil, + }, + } + for _, tc := range cases { + t.Run(tc.input, func(t *testing.T) { + got := FromVersionString(tc.input) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestSemver(t *testing.T) { + cases := []struct { + name string + toTest *Info + want *gvers.Version + }{ + { + name: "0.12.0+hcp.int", + toTest: &Info{ + Version: "0.12.0", + VersionMetadata: "hcp.int", + }, + want: gvers.Must(gvers.NewSemver("0.12.0+hcp.int")), + }, + { + name: "0.12.0+hcp.int (revision)", + toTest: &Info{ + Version: "0.12.0", + VersionMetadata: "hcp.int", + Revision: "revision", + }, + want: gvers.Must(gvers.NewSemver("0.12.0+hcp.int")), + }, + { + name: "unparsable", + toTest: &Info{ + Version: "some unparsable string", + VersionMetadata: "hcp.int", + }, + want: nil, + }, + { + name: "nil info", + toTest: nil, + want: nil, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := tc.toTest.Semver() + assert.Equal(t, tc.want, got) + }) + } +}