mirror of
https://github.com/gravitational/teleport
synced 2024-10-19 08:43:58 +00:00
Benchmark label expressions (#27474)
* add benchmark * performance improvements * update RFD
This commit is contained in:
parent
a9e4284255
commit
fb4826ed63
|
@ -1185,16 +1185,26 @@ func TestSessionRecordingConfigRBAC(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// time go test ./lib/auth -bench=. -run=^$ -v
|
||||
// go test ./lib/auth -bench=. -run=^$ -v -benchtime 1x
|
||||
// goos: darwin
|
||||
// goarch: amd64
|
||||
// pkg: github.com/gravitational/teleport/lib/auth
|
||||
// cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
|
||||
// BenchmarkListNodes
|
||||
// BenchmarkListNodes-16 1 1000469673 ns/op 518721960 B/op 8344858 allocs/op
|
||||
// BenchmarkListNodes/simple_labels
|
||||
// BenchmarkListNodes/simple_labels-16 1 1079886286 ns/op 525128104 B/op 8831939 allocs/op
|
||||
// BenchmarkListNodes/simple_expression
|
||||
// BenchmarkListNodes/simple_expression-16 1 770118479 ns/op 432667432 B/op 6514790 allocs/op
|
||||
// BenchmarkListNodes/labels
|
||||
// BenchmarkListNodes/labels-16 1 1931843502 ns/op 741444360 B/op 15159333 allocs/op
|
||||
// BenchmarkListNodes/expression
|
||||
// BenchmarkListNodes/expression-16 1 1040855282 ns/op 509643128 B/op 8120970 allocs/op
|
||||
// BenchmarkListNodes/complex_labels
|
||||
// BenchmarkListNodes/complex_labels-16 1 2274376396 ns/op 792948904 B/op 17084107 allocs/op
|
||||
// BenchmarkListNodes/complex_expression
|
||||
// BenchmarkListNodes/complex_expression-16 1 1518800599 ns/op 738532920 B/op 12483748 allocs/op
|
||||
// PASS
|
||||
// ok github.com/gravitational/teleport/lib/auth 3.695s
|
||||
// go test ./lib/auth -bench=. -run=^$ -v 19.02s user 3.87s system 244% cpu 9.376 total
|
||||
// ok github.com/gravitational/teleport/lib/auth 11.679s
|
||||
func BenchmarkListNodes(b *testing.B) {
|
||||
const nodeCount = 50_000
|
||||
const roleCount = 32
|
||||
|
@ -1208,47 +1218,149 @@ func BenchmarkListNodes(b *testing.B) {
|
|||
ctx := context.Background()
|
||||
srv := newTestTLSServer(b)
|
||||
|
||||
var values []string
|
||||
var ids []string
|
||||
for i := 0; i < roleCount; i++ {
|
||||
values = append(values, uuid.New().String())
|
||||
ids = append(ids, uuid.New().String())
|
||||
}
|
||||
|
||||
values[0] = "hidden"
|
||||
ids[0] = "hidden"
|
||||
|
||||
var hiddenNodes int
|
||||
// Create test nodes.
|
||||
for i := 0; i < nodeCount; i++ {
|
||||
name := uuid.New().String()
|
||||
val := values[i%len(values)]
|
||||
if val == "hidden" {
|
||||
id := ids[i%len(ids)]
|
||||
if id == "hidden" {
|
||||
hiddenNodes++
|
||||
}
|
||||
node, err := types.NewServerWithLabels(
|
||||
name,
|
||||
types.KindNode,
|
||||
types.ServerSpecV2{},
|
||||
map[string]string{"key": val},
|
||||
map[string]string{
|
||||
"key": id,
|
||||
"group": "users",
|
||||
},
|
||||
)
|
||||
require.NoError(b, err)
|
||||
|
||||
_, err = srv.Auth().UpsertNode(ctx, node)
|
||||
require.NoError(b, err)
|
||||
}
|
||||
|
||||
testNodes, err := srv.Auth().GetNodes(ctx, defaults.Namespace)
|
||||
require.NoError(b, err)
|
||||
require.Len(b, testNodes, nodeCount)
|
||||
|
||||
var roles []types.Role
|
||||
for _, val := range values {
|
||||
role, err := types.NewRole(fmt.Sprintf("role-%s", val), types.RoleSpecV6{})
|
||||
require.NoError(b, err)
|
||||
for _, tc := range []struct {
|
||||
desc string
|
||||
editRole func(types.Role, string)
|
||||
}{
|
||||
{
|
||||
desc: "simple labels",
|
||||
editRole: func(r types.Role, id string) {
|
||||
if id == "hidden" {
|
||||
r.SetNodeLabels(types.Deny, types.Labels{"key": {id}})
|
||||
} else {
|
||||
r.SetNodeLabels(types.Allow, types.Labels{"key": {id}})
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "simple expression",
|
||||
editRole: func(r types.Role, id string) {
|
||||
if id == "hidden" {
|
||||
err = r.SetLabelMatchers(types.Deny, types.KindNode, types.LabelMatchers{
|
||||
Expression: `labels.key == "hidden"`,
|
||||
})
|
||||
require.NoError(b, err)
|
||||
} else {
|
||||
err := r.SetLabelMatchers(types.Allow, types.KindNode, types.LabelMatchers{
|
||||
Expression: fmt.Sprintf(`labels.key == %q`, id),
|
||||
})
|
||||
require.NoError(b, err)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "labels",
|
||||
editRole: func(r types.Role, id string) {
|
||||
r.SetNodeLabels(types.Allow, types.Labels{
|
||||
"key": {id},
|
||||
"group": {"{{external.group}}"},
|
||||
})
|
||||
r.SetNodeLabels(types.Deny, types.Labels{"key": {"hidden"}})
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "expression",
|
||||
editRole: func(r types.Role, id string) {
|
||||
err := r.SetLabelMatchers(types.Allow, types.KindNode, types.LabelMatchers{
|
||||
Expression: fmt.Sprintf(`labels.key == %q && contains(user.spec.traits["group"], labels["group"])`,
|
||||
id),
|
||||
})
|
||||
require.NoError(b, err)
|
||||
err = r.SetLabelMatchers(types.Deny, types.KindNode, types.LabelMatchers{
|
||||
Expression: `labels.key == "hidden"`,
|
||||
})
|
||||
require.NoError(b, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "complex labels",
|
||||
editRole: func(r types.Role, id string) {
|
||||
r.SetNodeLabels(types.Allow, types.Labels{
|
||||
"key": {"other", id, "another"},
|
||||
"group": {
|
||||
`{{regexp.replace(external.group, "^(.*)$", "$1")}}`,
|
||||
"{{email.local(external.email)}}",
|
||||
},
|
||||
})
|
||||
r.SetNodeLabels(types.Deny, types.Labels{"key": {"hidden"}})
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "complex expression",
|
||||
editRole: func(r types.Role, id string) {
|
||||
expr := fmt.Sprintf(
|
||||
`(labels.key == "other" || labels.key == %q || labels.key == "another") &&
|
||||
(contains(email.local(user.spec.traits["email"]), labels["group"]) ||
|
||||
contains(regexp.replace(user.spec.traits["group"], "^(.*)$", "$1"), labels["group"]))`,
|
||||
id)
|
||||
err := r.SetLabelMatchers(types.Allow, types.KindNode, types.LabelMatchers{
|
||||
Expression: expr,
|
||||
})
|
||||
require.NoError(b, err)
|
||||
err = r.SetLabelMatchers(types.Deny, types.KindNode, types.LabelMatchers{
|
||||
Expression: `labels.key == "hidden"`,
|
||||
})
|
||||
require.NoError(b, err)
|
||||
},
|
||||
},
|
||||
} {
|
||||
b.Run(tc.desc, func(b *testing.B) {
|
||||
benchmarkListNodes(
|
||||
b, ctx,
|
||||
nodeCount, roleCount, hiddenNodes,
|
||||
srv,
|
||||
ids,
|
||||
tc.editRole,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if val == "hidden" {
|
||||
role.SetNodeLabels(types.Deny, types.Labels{"key": {val}})
|
||||
} else {
|
||||
role.SetNodeLabels(types.Allow, types.Labels{"key": {val}})
|
||||
}
|
||||
func benchmarkListNodes(
|
||||
b *testing.B, ctx context.Context,
|
||||
nodeCount, roleCount, hiddenNodes int,
|
||||
srv *TestTLSServer,
|
||||
ids []string,
|
||||
editRole func(r types.Role, id string),
|
||||
) {
|
||||
var roles []types.Role
|
||||
for _, id := range ids {
|
||||
role, err := types.NewRole(fmt.Sprintf("role-%s", id), types.RoleSpecV6{})
|
||||
require.NoError(b, err)
|
||||
editRole(role, id)
|
||||
roles = append(roles, role)
|
||||
}
|
||||
|
||||
|
@ -1257,6 +1369,12 @@ func BenchmarkListNodes(b *testing.B) {
|
|||
|
||||
user, err := CreateUser(srv.Auth(), username, roles...)
|
||||
require.NoError(b, err)
|
||||
user.SetTraits(map[string][]string{
|
||||
"group": {"users"},
|
||||
"email": {"test@example.com"},
|
||||
})
|
||||
err = srv.Auth().UpsertUser(user)
|
||||
require.NoError(b, err)
|
||||
identity := TestUser(user.GetName())
|
||||
clt, err := srv.NewClient(identity)
|
||||
require.NoError(b, err)
|
||||
|
|
|
@ -515,7 +515,7 @@ func (a *accessChecker) CheckDatabaseRoles(database types.Database) (create bool
|
|||
rolesMap := make(map[string]struct{})
|
||||
var matched bool
|
||||
for _, role := range autoCreateRoles {
|
||||
match, _, err := checkRoleLabelsMatch(types.Allow, role, a.info.Traits, database)
|
||||
match, _, err := checkRoleLabelsMatch(types.Allow, role, a.info.Traits, database, false)
|
||||
if err != nil {
|
||||
return false, nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -528,7 +528,7 @@ func (a *accessChecker) CheckDatabaseRoles(database types.Database) (create bool
|
|||
matched = true
|
||||
}
|
||||
for _, role := range autoCreateRoles {
|
||||
match, _, err := checkRoleLabelsMatch(types.Deny, role, a.info.Traits, database)
|
||||
match, _, err := checkRoleLabelsMatch(types.Deny, role, a.info.Traits, database, false)
|
||||
if err != nil {
|
||||
return false, nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -735,7 +735,7 @@ func (a *accessChecker) CheckAccessToRemoteCluster(rc types.RemoteCluster) error
|
|||
// the deny role set prohibits access.
|
||||
var errs []error
|
||||
for _, role := range a.RoleSet {
|
||||
matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Deny, role, a.info.Traits, rc)
|
||||
matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Deny, role, a.info.Traits, rc, isDebugEnabled)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -749,7 +749,7 @@ func (a *accessChecker) CheckAccessToRemoteCluster(rc types.RemoteCluster) error
|
|||
|
||||
// Check allow rules: label has to match in any role in the role set to be granted access.
|
||||
for _, role := range a.RoleSet {
|
||||
matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Allow, role, a.info.Traits, rc)
|
||||
matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Allow, role, a.info.Traits, rc, isDebugEnabled)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -780,7 +780,7 @@ func (a *accessChecker) CheckAccessToRemoteCluster(rc types.RemoteCluster) error
|
|||
func (a *accessChecker) DesktopGroups(s types.WindowsDesktop) ([]string, error) {
|
||||
groups := make(map[string]struct{})
|
||||
for _, role := range a.RoleSet {
|
||||
result, _, err := checkRoleLabelsMatch(types.Allow, role, a.info.Traits, s)
|
||||
result, _, err := checkRoleLabelsMatch(types.Allow, role, a.info.Traits, s, false)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -799,7 +799,7 @@ func (a *accessChecker) DesktopGroups(s types.WindowsDesktop) ([]string, error)
|
|||
}
|
||||
}
|
||||
for _, role := range a.RoleSet {
|
||||
result, _, err := checkRoleLabelsMatch(types.Deny, role, a.info.Traits, s)
|
||||
result, _, err := checkRoleLabelsMatch(types.Deny, role, a.info.Traits, s, false)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -828,7 +828,7 @@ func (a *accessChecker) HostUsers(s types.Server) (*HostUsersInfo, error) {
|
|||
|
||||
seenSudoers := make(map[string]struct{})
|
||||
for _, role := range roleSet {
|
||||
result, _, err := checkRoleLabelsMatch(types.Allow, role, a.info.Traits, s)
|
||||
result, _, err := checkRoleLabelsMatch(types.Allow, role, a.info.Traits, s, false)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -856,7 +856,7 @@ func (a *accessChecker) HostUsers(s types.Server) (*HostUsersInfo, error) {
|
|||
|
||||
var finalSudoers []string
|
||||
for _, role := range roleSet {
|
||||
result, _, err := checkRoleLabelsMatch(types.Deny, role, a.info.Traits, s)
|
||||
result, _, err := checkRoleLabelsMatch(types.Deny, role, a.info.Traits, s, false)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -2277,7 +2277,7 @@ func (l *kubernetesClusterLabelMatcher) Match(role types.Role, typ types.RoleCon
|
|||
if err != nil {
|
||||
return false, trace.Wrap(err)
|
||||
}
|
||||
ok, _, err := checkLabelsMatch(typ, labelMatchers, l.userTraits, mapLabelGetter(l.clusterLabels))
|
||||
ok, _, err := checkLabelsMatch(typ, labelMatchers, l.userTraits, mapLabelGetter(l.clusterLabels), false)
|
||||
return ok, trace.Wrap(err)
|
||||
}
|
||||
|
||||
|
@ -2353,7 +2353,7 @@ func (set RoleSet) checkAccess(r AccessCheckable, traits wrappers.Traits, state
|
|||
continue
|
||||
}
|
||||
|
||||
matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Deny, role, traits, r)
|
||||
matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Deny, role, traits, r, isDebugEnabled)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -2397,7 +2397,7 @@ func (set RoleSet) checkAccess(r AccessCheckable, traits wrappers.Traits, state
|
|||
continue
|
||||
}
|
||||
|
||||
matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Allow, role, traits, r)
|
||||
matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Allow, role, traits, r, isDebugEnabled)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -2489,12 +2489,13 @@ func checkRoleLabelsMatch(
|
|||
role types.Role,
|
||||
userTraits wrappers.Traits,
|
||||
resource AccessCheckable,
|
||||
debug bool,
|
||||
) (bool, string, error) {
|
||||
labelMatchers, err := role.GetLabelMatchers(condition, resource.GetKind())
|
||||
if err != nil {
|
||||
return false, "", trace.Wrap(err)
|
||||
}
|
||||
return checkLabelsMatch(condition, labelMatchers, userTraits, resource)
|
||||
return checkLabelsMatch(condition, labelMatchers, userTraits, resource, debug)
|
||||
}
|
||||
|
||||
// checkLabelsMatch checks if the [labelMatchers] match the labels of [resource]
|
||||
|
@ -2515,40 +2516,52 @@ func checkLabelsMatch(
|
|||
labelMatchers types.LabelMatchers,
|
||||
userTraits wrappers.Traits,
|
||||
resource LabelGetter,
|
||||
debug bool,
|
||||
) (bool, string, error) {
|
||||
if labelMatchers.Empty() {
|
||||
return false, "no label matchers or label expression", nil
|
||||
}
|
||||
|
||||
var matches []bool
|
||||
var messages []string
|
||||
var message string
|
||||
labelsUnsetOrMatch, expressionUnsetOrMatch := true, true
|
||||
|
||||
if len(labelMatchers.Labels) > 0 {
|
||||
match, message, err := MatchLabelGetter(labelMatchers.Labels, resource)
|
||||
match, msg, err := MatchLabelGetter(labelMatchers.Labels, resource)
|
||||
if err != nil {
|
||||
return false, "", trace.Wrap(err)
|
||||
}
|
||||
matches = append(matches, match)
|
||||
messages = append(messages, "label="+message)
|
||||
if debug {
|
||||
message += "label=" + msg
|
||||
}
|
||||
// Deny rules are greedy, if either matches, it's a match.
|
||||
if condition == types.Deny && match {
|
||||
return true, message, nil
|
||||
}
|
||||
labelsUnsetOrMatch = match
|
||||
}
|
||||
|
||||
if len(labelMatchers.Expression) > 0 {
|
||||
match, message, err := matchLabelExpression(labelMatchers.Expression, resource, userTraits)
|
||||
match, msg, err := matchLabelExpression(labelMatchers.Expression, resource, userTraits)
|
||||
if err != nil {
|
||||
return false, "", trace.Wrap(err)
|
||||
}
|
||||
matches = append(matches, match)
|
||||
messages = append(messages, "expression="+message)
|
||||
if debug {
|
||||
message = strings.Join([]string{message, "expression=" + msg}, ", ")
|
||||
}
|
||||
// Deny rules are greedy, if either matches, it's a match.
|
||||
if condition == types.Deny {
|
||||
return match, message, nil
|
||||
}
|
||||
expressionUnsetOrMatch = match
|
||||
}
|
||||
|
||||
message := strings.Join(messages, ", ")
|
||||
|
||||
// Deny rules are greedy, if either matched, it's a match.
|
||||
if condition == types.Deny {
|
||||
return slices.Contains(matches, true), message, nil
|
||||
// Either branch would have returned if it was a match.
|
||||
return false, message, nil
|
||||
}
|
||||
|
||||
// Allow rules are not greedy, both must match if they are set.
|
||||
return !slices.Contains(matches, false), message, nil
|
||||
return labelsUnsetOrMatch && expressionUnsetOrMatch, message, nil
|
||||
}
|
||||
|
||||
func matchLabelExpression(labelExpression string, resource LabelGetter, userTraits wrappers.Traits) (bool, string, error) {
|
||||
|
@ -2803,7 +2816,7 @@ func (set RoleSet) CheckAccessToRule(ctx RuleContext, namespace string, resource
|
|||
// GetKubeResources returns allowed and denied list of Kubernetes Resources configured in the RoleSet.
|
||||
func (set RoleSet) GetKubeResources(cluster types.KubeCluster, userTraits wrappers.Traits) (allowed, denied []types.KubernetesResource) {
|
||||
for _, role := range set {
|
||||
matchLabels, _, err := checkRoleLabelsMatch(types.Allow, role, userTraits, cluster)
|
||||
matchLabels, _, err := checkRoleLabelsMatch(types.Allow, role, userTraits, cluster, false)
|
||||
if err != nil || !matchLabels {
|
||||
continue
|
||||
}
|
||||
|
@ -2811,7 +2824,7 @@ func (set RoleSet) GetKubeResources(cluster types.KubeCluster, userTraits wrappe
|
|||
}
|
||||
|
||||
for _, role := range set {
|
||||
matchLabels, _, err := checkRoleLabelsMatch(types.Deny, role, userTraits, cluster)
|
||||
matchLabels, _, err := checkRoleLabelsMatch(types.Deny, role, userTraits, cluster, false)
|
||||
if err != nil || !matchLabels {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -735,7 +735,7 @@ func (e ternaryVariadicFuncExpr[TEnv, TArg1, TArg2, TVarArgs, TResult]) Evaluate
|
|||
|
||||
type booleanOperator[TEnv, TArgs any] struct {
|
||||
name string
|
||||
f func(a, b TArgs) bool
|
||||
f func(env TEnv, a, b Expression[TEnv, TArgs]) (bool, error)
|
||||
}
|
||||
|
||||
func (b booleanOperator[TEnv, TArgs]) buildExpression(lhs, rhs any) (Expression[TEnv, bool], error) {
|
||||
|
@ -752,47 +752,87 @@ func (b booleanOperator[TEnv, TArgs]) buildExpression(lhs, rhs any) (Expression[
|
|||
|
||||
type booleanOperatorExpr[TEnv, TArgs any] struct {
|
||||
name string
|
||||
f func(a, b TArgs) bool
|
||||
f func(env TEnv, a, b Expression[TEnv, TArgs]) (bool, error)
|
||||
lhsExpr, rhsExpr Expression[TEnv, TArgs]
|
||||
}
|
||||
|
||||
func (b booleanOperatorExpr[TEnv, TArgs]) Evaluate(env TEnv) (bool, error) {
|
||||
lhs, err := b.lhsExpr.Evaluate(env)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err, "evaluating lhs of (%s) operator", b.name)
|
||||
}
|
||||
rhs, err := b.rhsExpr.Evaluate(env)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err, "evaluating rhs of (%s) operator", b.name)
|
||||
}
|
||||
return b.f(lhs, rhs), nil
|
||||
return b.f(env, b.lhsExpr, b.rhsExpr)
|
||||
}
|
||||
|
||||
func and[TEnv any]() func(lhs, rhs any) (Expression[TEnv, bool], error) {
|
||||
return booleanOperator[TEnv, bool]{
|
||||
name: "&&",
|
||||
f: func(lhs, rhs bool) bool { return lhs && rhs },
|
||||
f: func(env TEnv, lhsExpr, rhsExpr Expression[TEnv, bool]) (bool, error) {
|
||||
lhs, err := lhsExpr.Evaluate(env)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err, "evaluating lhs of (&&) operator")
|
||||
}
|
||||
// Short-circuit if possible.
|
||||
if !lhs {
|
||||
return false, nil
|
||||
}
|
||||
rhs, err := rhsExpr.Evaluate(env)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err, "evaluating rhs of (&&) operator")
|
||||
}
|
||||
return rhs, nil
|
||||
},
|
||||
}.buildExpression
|
||||
}
|
||||
|
||||
func or[TEnv any]() func(lhs, rhs any) (Expression[TEnv, bool], error) {
|
||||
return booleanOperator[TEnv, bool]{
|
||||
name: "||",
|
||||
f: func(lhs, rhs bool) bool { return lhs || rhs },
|
||||
f: func(env TEnv, lhsExpr, rhsExpr Expression[TEnv, bool]) (bool, error) {
|
||||
lhs, err := lhsExpr.Evaluate(env)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err, "evaluating lhs of (||) operator")
|
||||
}
|
||||
// Short-circuit if possible.
|
||||
if lhs {
|
||||
return true, nil
|
||||
}
|
||||
rhs, err := rhsExpr.Evaluate(env)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err, "evaluating rhs of (||) operator")
|
||||
}
|
||||
return rhs, nil
|
||||
},
|
||||
}.buildExpression
|
||||
}
|
||||
|
||||
func eq[TEnv any]() func(lhs, rhs any) (Expression[TEnv, bool], error) {
|
||||
return booleanOperator[TEnv, string]{
|
||||
name: "==",
|
||||
f: func(lhs, rhs string) bool { return lhs == rhs },
|
||||
f: func(env TEnv, lhsExpr, rhsExpr Expression[TEnv, string]) (bool, error) {
|
||||
lhs, err := lhsExpr.Evaluate(env)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err, "evaluating lhs of (==) operator")
|
||||
}
|
||||
rhs, err := rhsExpr.Evaluate(env)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err, "evaluating rhs of (==) operator")
|
||||
}
|
||||
return lhs == rhs, nil
|
||||
},
|
||||
}.buildExpression
|
||||
}
|
||||
|
||||
func neq[TEnv any]() func(lhs, rhs any) (Expression[TEnv, bool], error) {
|
||||
return booleanOperator[TEnv, string]{
|
||||
name: "!=",
|
||||
f: func(lhs, rhs string) bool { return lhs != rhs },
|
||||
f: func(env TEnv, lhsExpr, rhsExpr Expression[TEnv, string]) (bool, error) {
|
||||
lhs, err := lhsExpr.Evaluate(env)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err, "evaluating lhs of (!=) operator")
|
||||
}
|
||||
rhs, err := rhsExpr.Evaluate(env)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err, "evaluating rhs of (!=) operator")
|
||||
}
|
||||
return lhs != rhs, nil
|
||||
},
|
||||
}.buildExpression
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
authors: Nic Klaassen (nic@goteleport.com)
|
||||
state: draft
|
||||
state: implemented (v13.2.0)
|
||||
---
|
||||
|
||||
# RFD 116 - RBAC Label Expressions
|
||||
|
@ -235,10 +235,33 @@ Ultimately, performance will be benchmarked for multiple scenarios with the goal
|
|||
of staying within 10% of the performance of the existing implementation.
|
||||
Benchmarks will be written comparing similar RBAC constraints written with both
|
||||
the existing label matchers and the new label expressions.
|
||||
Benchmarks will run `ListResources` with 50k unique (simulated) nodes, 64 unique
|
||||
roles, and 2k unique users.
|
||||
Benchmarks will run `ListResources` with 50k unique (simulated) nodes and 32
|
||||
unique roles.
|
||||
|
||||
Benchmark results: TBD
|
||||
Benchmark results:
|
||||
|
||||
```
|
||||
$ go test ./lib/auth -bench=. -run=^$ -v -benchtime 1x
|
||||
goos: darwin
|
||||
goarch: amd64
|
||||
pkg: github.com/gravitational/teleport/lib/auth
|
||||
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
|
||||
BenchmarkListNodes
|
||||
BenchmarkListNodes/simple_labels
|
||||
BenchmarkListNodes/simple_labels-16 1 1079886286 ns/op 525128104 B/op 8831939 allocs/op
|
||||
BenchmarkListNodes/simple_expression
|
||||
BenchmarkListNodes/simple_expression-16 1 770118479 ns/op 432667432 B/op 6514790 allocs/op
|
||||
BenchmarkListNodes/labels
|
||||
BenchmarkListNodes/labels-16 1 1931843502 ns/op 741444360 B/op 15159333 allocs/op
|
||||
BenchmarkListNodes/expression
|
||||
BenchmarkListNodes/expression-16 1 1040855282 ns/op 509643128 B/op 8120970 allocs/op
|
||||
BenchmarkListNodes/complex_labels
|
||||
BenchmarkListNodes/complex_labels-16 1 2274376396 ns/op 792948904 B/op 17084107 allocs/op
|
||||
BenchmarkListNodes/complex_expression
|
||||
BenchmarkListNodes/complex_expression-16 1 1518800599 ns/op 738532920 B/op 12483748 allocs/op
|
||||
PASS
|
||||
ok github.com/gravitational/teleport/lib/auth 11.679s
|
||||
```
|
||||
|
||||
#### Caching
|
||||
|
||||
|
|
Loading…
Reference in a new issue