mirror of
https://github.com/openziti/ziti.git
synced 2026-10-14 16:19:57 +00:00
Merge pull request #257 from openziti/model-performance
Model performance
This commit is contained in:
@@ -147,7 +147,7 @@ func (handler *EdgeRouterHandler) ListForIdentityAndServiceWithTx(tx *bbolt.Tx,
|
||||
return nil, errors.Errorf("no service with id %v found", serviceId)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`anyOf(edgeRouterPolicies.identities) = "%v" and anyOf(serviceEdgeRouterPolicies.services) = "%v"`, identityId, service.Id)
|
||||
query := fmt.Sprintf(`anyOf(identities) = "%v" and anyOf(services) = "%v"`, identityId, service.Id)
|
||||
|
||||
if limit != nil {
|
||||
query += " limit " + strconv.Itoa(*limit)
|
||||
|
||||
@@ -87,7 +87,7 @@ func (advisor *PolicyAdvisor) getServicePermissions(identityId, serviceId string
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if servicePolicyStore.IsEntityRelated(tx, servicePolicyId, persistence.EntityTypeServices, serviceId) {
|
||||
if servicePolicyStore.IsEntityRelated(tx, servicePolicyId, db.EntityTypeServices, serviceId) {
|
||||
if !stringz.Contains(permissions, servicePolicy.GetPolicyTypeName()) {
|
||||
permissions = append(permissions, servicePolicy.GetPolicyTypeName())
|
||||
}
|
||||
@@ -243,7 +243,7 @@ func (advisor *PolicyAdvisor) getServicePolicies(identityId, serviceId string) (
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if policyStore.IsEntityRelated(tx, policyId, persistence.EntityTypeServices, serviceId) {
|
||||
if policyStore.IsEntityRelated(tx, policyId, db.EntityTypeServices, serviceId) {
|
||||
result = append(result, policy)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -96,19 +96,14 @@ func (handler *EdgeServiceHandler) ReadForIdentityInTx(tx *bbolt.Tx, id string,
|
||||
}
|
||||
|
||||
func (handler *EdgeServiceHandler) ReadForNonAdminIdentityInTx(tx *bbolt.Tx, id string, identityId string) (*ServiceDetail, error) {
|
||||
query := `id = "%v" and not isEmpty(from servicePolicies where (type = %v and anyOf(identities.id) = "%v"))`
|
||||
edgeServiceStore := handler.env.GetStores().EdgeService
|
||||
isBindable := edgeServiceStore.IsBindableByIdentity(tx, id, identityId)
|
||||
isDialable := edgeServiceStore.IsDialableByIdentity(tx, id, identityId)
|
||||
|
||||
dialQuery := fmt.Sprintf(query, id, persistence.PolicyTypeDial, identityId)
|
||||
_, dialCount, err := handler.GetStore().QueryIds(tx, dialQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if !isBindable && !isDialable {
|
||||
return nil, boltz.NewNotFoundError(handler.GetStore().GetSingularEntityType(), "id", id)
|
||||
}
|
||||
|
||||
bindQuery := fmt.Sprintf(query, id, persistence.PolicyTypeBind, identityId)
|
||||
_, bindCount, err := handler.GetStore().QueryIds(tx, bindQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := handler.readInTx(tx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -116,13 +111,10 @@ func (handler *EdgeServiceHandler) ReadForNonAdminIdentityInTx(tx *bbolt.Tx, id
|
||||
if result == nil {
|
||||
return nil, boltz.NewNotFoundError(handler.GetStore().GetSingularEntityType(), "id", id)
|
||||
}
|
||||
if bindCount > 0 {
|
||||
if isBindable {
|
||||
result.Permissions = append(result.Permissions, persistence.PolicyTypeBindName)
|
||||
} else if dialCount == 0 {
|
||||
return nil, boltz.NewNotFoundError(handler.GetStore().GetSingularEntityType(), "id", id)
|
||||
}
|
||||
|
||||
if dialCount > 0 {
|
||||
if isDialable {
|
||||
result.Permissions = append(result.Permissions, persistence.PolicyTypeDialName)
|
||||
}
|
||||
return result, nil
|
||||
@@ -148,7 +140,7 @@ func (handler *EdgeServiceHandler) PublicQueryForIdentity(sessionIdentity *Ident
|
||||
}
|
||||
|
||||
func (handler *EdgeServiceHandler) QueryForIdentity(identityId string, configTypes map[string]struct{}, query ast.Query) (*ServiceListResult, error) {
|
||||
idFilterQueryString := fmt.Sprintf(`not isEmpty(from servicePolicies where anyOf(identities) = "%v")`, identityId)
|
||||
idFilterQueryString := fmt.Sprintf(`(anyOf(dialIdentities) = "%v" or anyOf(bindIdentities) = "%v")`, identityId, identityId)
|
||||
idFilterQuery, err := ast.Parse(handler.Store, idFilterQueryString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package persistence
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"github.com/openziti/foundation/storage/boltz"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
EntityTypeApiSessions = "apiSessions"
|
||||
@@ -28,7 +31,6 @@ const (
|
||||
EntityTypeGeoRegions = "geoRegions"
|
||||
EntityTypeIdentities = "identities"
|
||||
EntityTypeIdentityTypes = "identityTypes"
|
||||
EntityTypeServices = "services"
|
||||
EntityTypeServicePolicies = "servicePolicies"
|
||||
EntityTypeServiceEdgeRouterPolicies = "serviceEdgeRouterPolicies"
|
||||
EntityTypeSessions = "sessions"
|
||||
@@ -75,3 +77,8 @@ func toStringInterfaceMap(m map[string]string) map[string]interface{} {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type Policy interface {
|
||||
boltz.NamedExtEntity
|
||||
GetSemantic() string
|
||||
}
|
||||
|
||||
@@ -17,15 +17,12 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"github.com/openziti/fabric/controller/network"
|
||||
"github.com/openziti/foundation/storage/ast"
|
||||
"github.com/openziti/foundation/storage/boltz"
|
||||
"github.com/openziti/foundation/util/errorz"
|
||||
"github.com/openziti/foundation/util/stringz"
|
||||
"github.com/openziti/foundation/validation"
|
||||
"github.com/pkg/errors"
|
||||
"go.etcd.io/bbolt"
|
||||
"strings"
|
||||
)
|
||||
@@ -81,11 +78,6 @@ func (store *baseStore) addUniqueNameField() boltz.ReadIndex {
|
||||
return store.AddUniqueIndex(symbolName)
|
||||
}
|
||||
|
||||
func (store *baseStore) addRoleAttributesField() boltz.SetReadIndex {
|
||||
symbol := store.AddSetSymbol(FieldRoleAttributes, ast.NodeTypeString)
|
||||
return store.AddSetIndex(symbol)
|
||||
}
|
||||
|
||||
func (store *baseStore) initializeIndexes(tx *bbolt.Tx, errorHolder errorz.ErrorHolder) {
|
||||
store.InitializeIndexes(tx, errorHolder)
|
||||
}
|
||||
@@ -102,7 +94,6 @@ func (store *baseStore) baseLoadOneById(tx *bbolt.Tx, id string, entity boltz.En
|
||||
}
|
||||
|
||||
func (store *baseStore) deleteEntityReferences(tx *bbolt.Tx, entity boltz.NamedExtEntity, rolesSymbol boltz.EntitySetSymbol) error {
|
||||
// If the entity name is the same as entity ID, don't remove any of those references as id references take precedence
|
||||
idRef := entityRef(entity.GetId())
|
||||
|
||||
for _, policyHolderId := range store.GetRelatedEntitiesIdList(tx, entity.GetId(), rolesSymbol.GetStore().GetEntityType()) {
|
||||
@@ -118,91 +109,6 @@ func (store *baseStore) deleteEntityReferences(tx *bbolt.Tx, entity boltz.NamedE
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *baseStore) getEntityIdsForRoleSet(tx *bbolt.Tx, field string, roleSet []string, semantic string, index boltz.SetReadIndex, targetStore NameIndexedStore) ([]string, error) {
|
||||
entityStore := index.GetSymbol().GetStore()
|
||||
if stringz.Contains(roleSet, AllRole) {
|
||||
ids, _, err := entityStore.QueryIds(tx, "true")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
roles, ids, err := splitRolesAndIds(roleSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateEntityIds(tx, targetStore, field, ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var roleIds []string
|
||||
if strings.EqualFold(semantic, SemanticAllOf) {
|
||||
roleIds = store.FindMatching(tx, index, roles)
|
||||
} else if strings.EqualFold(semantic, SemanticAnyOf) {
|
||||
roleIds = store.FindMatchingAnyOf(tx, index, roles)
|
||||
} else {
|
||||
return nil, errors.Errorf("unsupported policy semantic %v", semantic)
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
if entityStore.IsEntityPresent(tx, id) {
|
||||
roleIds = append(roleIds, id)
|
||||
}
|
||||
}
|
||||
return roleIds, nil
|
||||
}
|
||||
|
||||
func validateEntityIds(tx *bbolt.Tx, store NameIndexedStore, field string, ids []string) error {
|
||||
var invalid []string
|
||||
for _, val := range ids {
|
||||
if !store.IsEntityPresent(tx, val) {
|
||||
invalid = append(invalid, val)
|
||||
}
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return validation.NewFieldError(fmt.Sprintf("no %v found with the given ids", store.GetEntityType()), field, invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateRelatedRoles(tx *bbolt.Tx, entityId string, roleSymbol boltz.EntitySetSymbol,
|
||||
linkCollection boltz.LinkCollection, new []boltz.FieldTypeAndValue, holder errorz.ErrorHolder, semanticSymbol boltz.EntitySymbol) {
|
||||
ids, _, err := roleSymbol.GetStore().QueryIds(tx, "true")
|
||||
holder.SetError(err)
|
||||
|
||||
entityRoles := FieldValuesToIds(new)
|
||||
|
||||
for _, id := range ids {
|
||||
roleSet := roleSymbol.EvalStringList(tx, []byte(id))
|
||||
roles, ids, err := splitRolesAndIds(roleSet)
|
||||
if err != nil {
|
||||
holder.SetError(err)
|
||||
return
|
||||
}
|
||||
semantic := SemanticAllOf
|
||||
if semanticSymbol != nil {
|
||||
if _, semanticValue := semanticSymbol.Eval(tx, []byte(id)); semanticValue != nil {
|
||||
semantic = string(semanticValue)
|
||||
}
|
||||
}
|
||||
|
||||
if stringz.Contains(ids, entityId) || stringz.Contains(roles, "all") {
|
||||
err = linkCollection.AddLinks(tx, id, entityId)
|
||||
} else if strings.EqualFold(semantic, SemanticAllOf) && len(roles) > 0 && stringz.ContainsAll(entityRoles, roles...) {
|
||||
err = linkCollection.AddLinks(tx, id, entityId)
|
||||
} else if strings.EqualFold(semantic, SemanticAnyOf) && len(roles) > 0 && stringz.ContainsAny(entityRoles, roles...) {
|
||||
err = linkCollection.AddLinks(tx, id, entityId)
|
||||
} else {
|
||||
err = linkCollection.RemoveLinks(tx, id, entityId)
|
||||
}
|
||||
|
||||
holder.SetError(err)
|
||||
if holder.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type NameIndexedStore interface {
|
||||
Store
|
||||
GetNameIndex() boltz.ReadIndex
|
||||
|
||||
@@ -18,6 +18,7 @@ package persistence
|
||||
|
||||
import (
|
||||
"github.com/openziti/edge/eid"
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"github.com/openziti/foundation/storage/ast"
|
||||
"github.com/openziti/foundation/storage/boltz"
|
||||
"go.etcd.io/bbolt"
|
||||
@@ -100,7 +101,7 @@ func (store *configStoreImpl) initializeLocal() {
|
||||
store.indexName = store.addUniqueNameField()
|
||||
store.symbolType = store.AddFkSymbol(FieldConfigType, store.stores.configType)
|
||||
store.AddMapSymbol(FieldConfigData, ast.NodeTypeAnyType, FieldConfigData)
|
||||
store.symbolServices = store.AddFkSetSymbol(EntityTypeServices, store.stores.edgeService)
|
||||
store.symbolServices = store.AddFkSetSymbol(db.EntityTypeServices, store.stores.edgeService)
|
||||
store.symbolIdentityServices = store.AddSetSymbol(FieldConfigIdentityService, ast.NodeTypeOther)
|
||||
store.identityServicesLinks = &boltz.LinkedSetSymbol{EntitySymbol: store.symbolIdentityServices}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"fmt"
|
||||
"github.com/openziti/edge/eid"
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"github.com/openziti/foundation/storage/ast"
|
||||
"github.com/openziti/foundation/storage/boltz"
|
||||
"github.com/openziti/foundation/util/stringz"
|
||||
@@ -30,6 +31,10 @@ func (entity *EdgeRouterPolicy) GetName() string {
|
||||
return entity.Name
|
||||
}
|
||||
|
||||
func (entity *EdgeRouterPolicy) GetSemantic() string {
|
||||
return entity.Semantic
|
||||
}
|
||||
|
||||
func (entity *EdgeRouterPolicy) LoadValues(_ boltz.CrudStore, bucket *boltz.TypedBucket) {
|
||||
entity.LoadBaseValues(bucket)
|
||||
entity.Name = bucket.GetStringOrError(FieldName)
|
||||
@@ -83,6 +88,7 @@ type EdgeRouterPolicyStore interface {
|
||||
NameIndexedStore
|
||||
LoadOneById(tx *bbolt.Tx, id string) (*EdgeRouterPolicy, error)
|
||||
LoadOneByName(tx *bbolt.Tx, id string) (*EdgeRouterPolicy, error)
|
||||
CheckIntegrity(tx *bbolt.Tx, fix bool, errorSink func(error)) error
|
||||
}
|
||||
|
||||
func newEdgeRouterPolicyStore(stores *stores) *edgeRouterPolicyStoreImpl {
|
||||
@@ -154,16 +160,55 @@ Optimizations
|
||||
and just add/remove/ignore
|
||||
3. Related entity deletes should be handled automatically by FK Indexes on those entities (need to verify the reverse as well/deleting policy)
|
||||
*/
|
||||
func (store *edgeRouterPolicyStoreImpl) edgeRouterRolesUpdated(ctx *boltz.PersistContext, policy *EdgeRouterPolicy) {
|
||||
roleIds, err := store.getEntityIdsForRoleSet(ctx.Bucket.Tx(), "edgeRouterRoles", policy.EdgeRouterRoles, policy.Semantic, store.stores.edgeRouter.indexRoleAttributes, store.stores.edgeRouter)
|
||||
if !ctx.Bucket.SetError(err) {
|
||||
ctx.Bucket.SetError(store.edgeRouterCollection.SetLinks(ctx.Bucket.Tx(), policy.Id, roleIds))
|
||||
func (store *edgeRouterPolicyStoreImpl) edgeRouterRolesUpdated(persistCtx *boltz.PersistContext, policy *EdgeRouterPolicy) {
|
||||
ctx := &roleAttributeChangeContext{
|
||||
tx: persistCtx.Bucket.Tx(),
|
||||
rolesSymbol: store.symbolEdgeRouterRoles,
|
||||
linkCollection: store.edgeRouterCollection,
|
||||
relatedLinkCollection: store.identityCollection,
|
||||
denormLinkCollection: store.stores.edgeRouter.identitiesCollection,
|
||||
ErrorHolder: persistCtx.Bucket,
|
||||
}
|
||||
EvaluatePolicy(ctx, policy, store.stores.edgeRouter.symbolRoleAttributes)
|
||||
}
|
||||
|
||||
func (store *edgeRouterPolicyStoreImpl) identityRolesUpdated(ctx *boltz.PersistContext, policy *EdgeRouterPolicy) {
|
||||
roleIds, err := store.getEntityIdsForRoleSet(ctx.Bucket.Tx(), "identityRoles", policy.IdentityRoles, policy.Semantic, store.stores.identity.indexRoleAttributes, store.stores.identity)
|
||||
if !ctx.Bucket.SetError(err) {
|
||||
ctx.Bucket.SetError(store.identityCollection.SetLinks(ctx.Bucket.Tx(), policy.Id, roleIds))
|
||||
func (store *edgeRouterPolicyStoreImpl) identityRolesUpdated(persistCtx *boltz.PersistContext, policy *EdgeRouterPolicy) {
|
||||
ctx := &roleAttributeChangeContext{
|
||||
tx: persistCtx.Bucket.Tx(),
|
||||
rolesSymbol: store.symbolIdentityRoles,
|
||||
linkCollection: store.identityCollection,
|
||||
relatedLinkCollection: store.edgeRouterCollection,
|
||||
denormLinkCollection: store.stores.identity.edgeRoutersCollection,
|
||||
ErrorHolder: persistCtx.Bucket,
|
||||
}
|
||||
EvaluatePolicy(ctx, policy, store.stores.identity.symbolRoleAttributes)
|
||||
}
|
||||
|
||||
func (store *edgeRouterPolicyStoreImpl) DeleteById(ctx boltz.MutateContext, id string) error {
|
||||
policy, err := store.LoadOneById(ctx.Tx(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
policy.EdgeRouterRoles = nil
|
||||
policy.IdentityRoles = nil
|
||||
err = store.Update(ctx, policy, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure while clearing policy before delete: %w", err)
|
||||
}
|
||||
return store.BaseStore.DeleteById(ctx, id)
|
||||
}
|
||||
|
||||
func (store *edgeRouterPolicyStoreImpl) CheckIntegrity(tx *bbolt.Tx, fix bool, errorSink func(error)) error {
|
||||
ctx := &denormCheckCtx{
|
||||
tx: tx,
|
||||
sourceStore: store.stores.identity,
|
||||
targetStore: store.stores.edgeRouter,
|
||||
policyStore: store,
|
||||
sourceCollection: store.identityCollection,
|
||||
targetCollection: store.edgeRouterCollection,
|
||||
targetDenormCollection: store.stores.identity.edgeRoutersCollection,
|
||||
repair: fix,
|
||||
errorSink: errorSink,
|
||||
}
|
||||
return validatePolicyDenormalization(ctx)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/openziti/edge/eid"
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"github.com/openziti/foundation/storage/boltz"
|
||||
"github.com/openziti/foundation/util/errorz"
|
||||
"github.com/openziti/foundation/util/stringz"
|
||||
"go.etcd.io/bbolt"
|
||||
"sort"
|
||||
@@ -116,7 +117,7 @@ func (ctx *TestContext) testEdgeRouterPolicyUpdateDeleteRefs(t *testing.T) {
|
||||
|
||||
policy.IdentityRoles = []string{entityRef(identity.Id)}
|
||||
ctx.RequireCreate(policy)
|
||||
ctx.validateEdgeRouterPolicyIdentities([]*Identity{identity}, []*EdgeRouterPolicy{policy})
|
||||
ctx.validateEdgeRouterPolicies([]*Identity{identity}, nil, []*EdgeRouterPolicy{policy})
|
||||
ctx.RequireDelete(identity)
|
||||
ctx.RequireReload(policy)
|
||||
ctx.Equal(0, len(policy.IdentityRoles), "identity id should have been removed from identity roles")
|
||||
@@ -182,14 +183,14 @@ func (ctx *TestContext) testEdgeRouterPolicyRoleEvaluation(t *testing.T) {
|
||||
|
||||
identityTypeId := ctx.getIdentityTypeId()
|
||||
|
||||
var identities []*Identity
|
||||
identities := make([]*Identity, 0) // goland complains of potential nil panic if we use var identities []*Identities
|
||||
for i := 0; i < 5; i++ {
|
||||
identity := newIdentity(eid.New(), identityTypeId)
|
||||
ctx.RequireCreate(identity)
|
||||
identities = append(identities, identity)
|
||||
}
|
||||
|
||||
var edgeRouters []*EdgeRouter
|
||||
edgeRouters := make([]*EdgeRouter, 0)
|
||||
for i := 0; i < 5; i++ {
|
||||
edgeRouter := newEdgeRouter(eid.New())
|
||||
ctx.RequireCreate(edgeRouter)
|
||||
@@ -255,8 +256,7 @@ func (ctx *TestContext) testEdgeRouterPolicyRoleEvaluation(t *testing.T) {
|
||||
edgeRouters = append(edgeRouters, edgeRouter)
|
||||
})
|
||||
|
||||
ctx.validateEdgeRouterPolicyIdentities(identities, policies)
|
||||
ctx.validateEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateEdgeRouterPolicies(identities, edgeRouters, policies)
|
||||
|
||||
for _, identity := range identities {
|
||||
ctx.RequireDelete(identity)
|
||||
@@ -285,8 +285,7 @@ func (ctx *TestContext) testEdgeRouterPolicyRoleEvaluation(t *testing.T) {
|
||||
edgeRouters = append(edgeRouters, edgeRouter)
|
||||
})
|
||||
|
||||
ctx.validateEdgeRouterPolicyIdentities(identities, policies)
|
||||
ctx.validateEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateEdgeRouterPolicies(identities, edgeRouters, policies)
|
||||
|
||||
// ensure policies get cleaned up
|
||||
for _, policy := range policies {
|
||||
@@ -296,8 +295,7 @@ func (ctx *TestContext) testEdgeRouterPolicyRoleEvaluation(t *testing.T) {
|
||||
// test with policies created after identities/edge routers
|
||||
policies = ctx.createEdgeRouterPolicies(identityRoles, edgeRouterRoles, identities, edgeRouters, true)
|
||||
|
||||
ctx.validateEdgeRouterPolicyIdentities(identities, policies)
|
||||
ctx.validateEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateEdgeRouterPolicies(identities, edgeRouters, policies)
|
||||
|
||||
for _, policy := range policies {
|
||||
ctx.RequireDelete(policy)
|
||||
@@ -306,8 +304,7 @@ func (ctx *TestContext) testEdgeRouterPolicyRoleEvaluation(t *testing.T) {
|
||||
// test with policies created after identities/edge routers and roles added after created
|
||||
policies = ctx.createEdgeRouterPolicies(identityRoles, edgeRouterRoles, identities, edgeRouters, false)
|
||||
|
||||
ctx.validateEdgeRouterPolicyIdentities(identities, policies)
|
||||
ctx.validateEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateEdgeRouterPolicies(identities, edgeRouters, policies)
|
||||
|
||||
for _, identity := range identities {
|
||||
if len(identity.RoleAttributes) > 0 {
|
||||
@@ -333,8 +330,7 @@ func (ctx *TestContext) testEdgeRouterPolicyRoleEvaluation(t *testing.T) {
|
||||
ctx.RequireUpdate(policy)
|
||||
}
|
||||
|
||||
ctx.validateEdgeRouterPolicyIdentities(identities, policies)
|
||||
ctx.validateEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateEdgeRouterPolicies(identities, edgeRouters, policies)
|
||||
}
|
||||
|
||||
func (ctx *TestContext) createEdgeRouterPolicies(identityRoles, edgeRouterRoles []string, identities []*Identity, edgeRouters []*EdgeRouter, oncreate bool) []*EdgeRouterPolicy {
|
||||
@@ -391,6 +387,12 @@ func (ctx *TestContext) createEdgeRouterPolicies(identityRoles, edgeRouterRoles
|
||||
return policies
|
||||
}
|
||||
|
||||
func (ctx *TestContext) validateEdgeRouterPolicies(identities []*Identity, edgeRouters []*EdgeRouter, policies []*EdgeRouterPolicy) {
|
||||
ctx.validateEdgeRouterPolicyIdentities(identities, policies)
|
||||
ctx.validateEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateEdgeRouterPolicyDenormalization()
|
||||
}
|
||||
|
||||
func (ctx *TestContext) validateEdgeRouterPolicyIdentities(identities []*Identity, policies []*EdgeRouterPolicy) {
|
||||
for _, policy := range policies {
|
||||
count := 0
|
||||
@@ -413,6 +415,16 @@ func (ctx *TestContext) validateEdgeRouterPolicyIdentities(identities []*Identit
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *TestContext) validateEdgeRouterPolicyDenormalization() {
|
||||
errorHolder := &errorz.ErrorHolderImpl{}
|
||||
errorHolder.SetError(ctx.db.View(func(tx *bbolt.Tx) error {
|
||||
return ctx.stores.EdgeRouterPolicy.CheckIntegrity(tx, false, func(err error) {
|
||||
errorHolder.SetError(err)
|
||||
})
|
||||
}))
|
||||
ctx.NoError(errorHolder.GetError())
|
||||
}
|
||||
|
||||
func (ctx *TestContext) validateEdgeRouterPolicyEdgeRouters(edgeRouters []*EdgeRouter, policies []*EdgeRouterPolicy) {
|
||||
for _, policy := range policies {
|
||||
count := 0
|
||||
|
||||
@@ -120,9 +120,16 @@ type edgeRouterStoreImpl struct {
|
||||
indexName boltz.ReadIndex
|
||||
indexRoleAttributes boltz.SetReadIndex
|
||||
|
||||
symbolRoleAttributes boltz.EntitySetSymbol
|
||||
symbolEdgeRouterPolicies boltz.EntitySetSymbol
|
||||
symbolServiceEdgeRouterPolicies boltz.EntitySetSymbol
|
||||
symbolEnrollments boltz.EntitySetSymbol
|
||||
|
||||
symbolIdentities boltz.EntitySetSymbol
|
||||
symbolServices boltz.EntitySetSymbol
|
||||
|
||||
identitiesCollection boltz.RefCountedLinkCollection
|
||||
servicesCollection boltz.RefCountedLinkCollection
|
||||
}
|
||||
|
||||
func (store *edgeRouterStoreImpl) NewStoreEntity() boltz.Entity {
|
||||
@@ -136,35 +143,53 @@ func (store *edgeRouterStoreImpl) GetRoleAttributesIndex() boltz.SetReadIndex {
|
||||
func (store *edgeRouterStoreImpl) initializeLocal() {
|
||||
store.GetParentStore().GrantSymbols(store)
|
||||
|
||||
store.symbolRoleAttributes = store.AddSetSymbol(FieldRoleAttributes, ast.NodeTypeString)
|
||||
|
||||
store.indexName = store.GetParentStore().(db.RouterStore).GetNameIndex()
|
||||
store.indexRoleAttributes = store.addRoleAttributesField()
|
||||
store.indexRoleAttributes = store.AddSetIndex(store.symbolRoleAttributes)
|
||||
|
||||
store.AddSymbol(FieldEdgeRouterIsVerified, ast.NodeTypeBool)
|
||||
store.symbolEnrollments = store.AddFkSetSymbol(FieldEdgeRouterEnrollments, store.stores.enrollment)
|
||||
store.symbolEdgeRouterPolicies = store.AddFkSetSymbol(EntityTypeEdgeRouterPolicies, store.stores.edgeRouterPolicy)
|
||||
store.symbolServiceEdgeRouterPolicies = store.AddFkSetSymbol(EntityTypeServiceEdgeRouterPolicies, store.stores.serviceEdgeRouterPolicy)
|
||||
|
||||
store.symbolIdentities = store.AddFkSetSymbol(EntityTypeIdentities, store.stores.identity)
|
||||
store.symbolServices = store.AddFkSetSymbol(db.EntityTypeServices, store.stores.edgeService)
|
||||
|
||||
store.indexRoleAttributes.AddListener(store.rolesChanged)
|
||||
}
|
||||
|
||||
func (store *edgeRouterStoreImpl) rolesChanged(tx *bbolt.Tx, rowId []byte, _ []boltz.FieldTypeAndValue, new []boltz.FieldTypeAndValue, holder errorz.ErrorHolder) {
|
||||
// Recalculate edge router policy links
|
||||
rolesSymbol := store.stores.edgeRouterPolicy.symbolEdgeRouterRoles
|
||||
linkCollection := store.stores.edgeRouterPolicy.edgeRouterCollection
|
||||
semanticSymbol := store.stores.edgeRouterPolicy.symbolSemantic
|
||||
UpdateRelatedRoles(tx, string(rowId), rolesSymbol, linkCollection, new, holder, semanticSymbol)
|
||||
|
||||
// Recalculate service edge router policy links
|
||||
rolesSymbol = store.stores.serviceEdgeRouterPolicy.symbolEdgeRouterRoles
|
||||
linkCollection = store.stores.serviceEdgeRouterPolicy.edgeRouterCollection
|
||||
semanticSymbol = store.stores.serviceEdgeRouterPolicy.symbolSemantic
|
||||
UpdateRelatedRoles(tx, string(rowId), rolesSymbol, linkCollection, new, holder, semanticSymbol)
|
||||
}
|
||||
|
||||
func (store *edgeRouterStoreImpl) initializeLinked() {
|
||||
store.AddLinkCollection(store.symbolEdgeRouterPolicies, store.stores.edgeRouterPolicy.symbolEdgeRouters)
|
||||
store.AddLinkCollection(store.symbolServiceEdgeRouterPolicies, store.stores.serviceEdgeRouterPolicy.symbolEdgeRouters)
|
||||
store.AddLinkCollection(store.symbolEnrollments, store.stores.enrollment.symbolEdgeRouter)
|
||||
|
||||
store.identitiesCollection = store.AddRefCountedLinkCollection(store.symbolIdentities, store.stores.identity.symbolEdgeRouters)
|
||||
store.servicesCollection = store.AddRefCountedLinkCollection(store.symbolServices, store.stores.edgeService.symbolEdgeRouters)
|
||||
}
|
||||
|
||||
func (store *edgeRouterStoreImpl) rolesChanged(tx *bbolt.Tx, rowId []byte, _ []boltz.FieldTypeAndValue, new []boltz.FieldTypeAndValue, holder errorz.ErrorHolder) {
|
||||
// Recalculate edge router policy links
|
||||
ctx := &roleAttributeChangeContext{
|
||||
tx: tx,
|
||||
rolesSymbol: store.stores.edgeRouterPolicy.symbolEdgeRouterRoles,
|
||||
linkCollection: store.stores.edgeRouterPolicy.edgeRouterCollection,
|
||||
relatedLinkCollection: store.stores.edgeRouterPolicy.identityCollection,
|
||||
denormLinkCollection: store.identitiesCollection,
|
||||
ErrorHolder: holder,
|
||||
}
|
||||
UpdateRelatedRoles(ctx, rowId, new, store.stores.edgeRouterPolicy.symbolSemantic)
|
||||
|
||||
// Recalculate service edge router policy links
|
||||
ctx = &roleAttributeChangeContext{
|
||||
tx: tx,
|
||||
rolesSymbol: store.stores.serviceEdgeRouterPolicy.symbolEdgeRouterRoles,
|
||||
linkCollection: store.stores.serviceEdgeRouterPolicy.edgeRouterCollection,
|
||||
relatedLinkCollection: store.stores.serviceEdgeRouterPolicy.serviceCollection,
|
||||
denormLinkCollection: store.servicesCollection,
|
||||
ErrorHolder: holder,
|
||||
}
|
||||
UpdateRelatedRoles(ctx, rowId, new, store.stores.serviceEdgeRouterPolicy.symbolSemantic)
|
||||
}
|
||||
|
||||
func (store *edgeRouterStoreImpl) GetNameIndex() boltz.ReadIndex {
|
||||
|
||||
@@ -27,6 +27,11 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
FieldEdgeServiceDialIdentities = "dialIdentities"
|
||||
FieldEdgeServiceBindIdentities = "bindIdentities"
|
||||
)
|
||||
|
||||
type EdgeService struct {
|
||||
db.Service
|
||||
RoleAttributes []string
|
||||
@@ -75,7 +80,8 @@ type EdgeServiceStore interface {
|
||||
|
||||
LoadOneById(tx *bbolt.Tx, id string) (*EdgeService, error)
|
||||
LoadOneByName(tx *bbolt.Tx, id string) (*EdgeService, error)
|
||||
|
||||
IsBindableByIdentity(tx *bbolt.Tx, id string, identityId string) bool
|
||||
IsDialableByIdentity(tx *bbolt.Tx, id string, identityId string) bool
|
||||
GetRoleAttributesIndex() boltz.SetReadIndex
|
||||
GetRoleAttributesCursorProvider(values []string, semantic string) (ast.SetCursorProvider, error)
|
||||
}
|
||||
@@ -93,12 +99,21 @@ type edgeServiceStoreImpl struct {
|
||||
|
||||
indexName boltz.ReadIndex
|
||||
indexRoleAttributes boltz.SetReadIndex
|
||||
symbolClusters boltz.EntitySetSymbol
|
||||
symbolSessions boltz.EntitySetSymbol
|
||||
|
||||
symbolRoleAttributes boltz.EntitySetSymbol
|
||||
symbolSessions boltz.EntitySetSymbol
|
||||
symbolConfigs boltz.EntitySetSymbol
|
||||
|
||||
symbolServicePolicies boltz.EntitySetSymbol
|
||||
symbolServiceEdgeRouterPolicies boltz.EntitySetSymbol
|
||||
symbolConfigs boltz.EntitySetSymbol
|
||||
|
||||
symbolDialIdentities boltz.EntitySetSymbol
|
||||
symbolBindIdentities boltz.EntitySetSymbol
|
||||
symbolEdgeRouters boltz.EntitySetSymbol
|
||||
|
||||
bindIdentitiesCollection boltz.RefCountedLinkCollection
|
||||
dialIdentitiesCollection boltz.RefCountedLinkCollection
|
||||
edgeRoutersCollection boltz.RefCountedLinkCollection
|
||||
}
|
||||
|
||||
func (store *edgeServiceStoreImpl) NewStoreEntity() boltz.Entity {
|
||||
@@ -112,36 +127,32 @@ func (store *edgeServiceStoreImpl) GetRoleAttributesIndex() boltz.SetReadIndex {
|
||||
func (store *edgeServiceStoreImpl) initializeLocal() {
|
||||
store.GetParentStore().GrantSymbols(store)
|
||||
|
||||
store.symbolRoleAttributes = store.AddSetSymbol(FieldRoleAttributes, ast.NodeTypeString)
|
||||
|
||||
store.indexName = store.GetParentStore().(db.ServiceStore).GetNameIndex()
|
||||
store.indexRoleAttributes = store.addRoleAttributesField()
|
||||
store.indexRoleAttributes = store.AddSetIndex(store.symbolRoleAttributes)
|
||||
|
||||
store.symbolSessions = store.AddFkSetSymbol(EntityTypeSessions, store.stores.session)
|
||||
store.symbolServiceEdgeRouterPolicies = store.AddFkSetSymbol(EntityTypeServiceEdgeRouterPolicies, store.stores.serviceEdgeRouterPolicy)
|
||||
store.symbolServicePolicies = store.AddFkSetSymbol(EntityTypeServicePolicies, store.stores.servicePolicy)
|
||||
store.symbolConfigs = store.AddFkSetSymbol(EntityTypeConfigs, store.stores.config)
|
||||
|
||||
store.symbolBindIdentities = store.AddFkSetSymbol(FieldEdgeServiceBindIdentities, store.stores.identity)
|
||||
store.symbolDialIdentities = store.AddFkSetSymbol(FieldEdgeServiceDialIdentities, store.stores.identity)
|
||||
store.symbolEdgeRouters = store.AddFkSetSymbol(migrationEntityTypeEdgeRouters, store.stores.edgeRouter)
|
||||
|
||||
store.indexRoleAttributes.AddListener(store.rolesChanged)
|
||||
}
|
||||
|
||||
func (store *edgeServiceStoreImpl) rolesChanged(tx *bbolt.Tx, rowId []byte, _ []boltz.FieldTypeAndValue, new []boltz.FieldTypeAndValue, holder errorz.ErrorHolder) {
|
||||
// Recalculate service policy links
|
||||
rolesSymbol := store.stores.servicePolicy.symbolServiceRoles
|
||||
linkCollection := store.stores.servicePolicy.serviceCollection
|
||||
semanticSymbol := store.stores.servicePolicy.symbolSemantic
|
||||
UpdateRelatedRoles(tx, string(rowId), rolesSymbol, linkCollection, new, holder, semanticSymbol)
|
||||
|
||||
// Recalculate service edge router policy links
|
||||
rolesSymbol = store.stores.serviceEdgeRouterPolicy.symbolServiceRoles
|
||||
linkCollection = store.stores.serviceEdgeRouterPolicy.serviceCollection
|
||||
semanticSymbol = store.stores.serviceEdgeRouterPolicy.symbolSemantic
|
||||
UpdateRelatedRoles(tx, string(rowId), rolesSymbol, linkCollection, new, holder, semanticSymbol)
|
||||
}
|
||||
|
||||
func (store *edgeServiceStoreImpl) initializeLinked() {
|
||||
store.AddLinkCollection(store.symbolServiceEdgeRouterPolicies, store.stores.serviceEdgeRouterPolicy.symbolServices)
|
||||
store.AddLinkCollection(store.symbolServicePolicies, store.stores.servicePolicy.symbolServices)
|
||||
store.AddLinkCollection(store.symbolConfigs, store.stores.config.symbolServices)
|
||||
|
||||
store.bindIdentitiesCollection = store.AddRefCountedLinkCollection(store.symbolBindIdentities, store.stores.identity.symbolBindServices)
|
||||
store.dialIdentitiesCollection = store.AddRefCountedLinkCollection(store.symbolDialIdentities, store.stores.identity.symbolDialServices)
|
||||
store.edgeRoutersCollection = store.AddRefCountedLinkCollection(store.symbolEdgeRouters, store.stores.edgeRouter.symbolServices)
|
||||
|
||||
store.EventEmmiter.AddListener(boltz.EventUpdate, func(i ...interface{}) {
|
||||
if len(i) != 1 {
|
||||
return
|
||||
@@ -156,6 +167,29 @@ func (store *edgeServiceStoreImpl) initializeLinked() {
|
||||
})
|
||||
}
|
||||
|
||||
func (store *edgeServiceStoreImpl) rolesChanged(tx *bbolt.Tx, rowId []byte, _ []boltz.FieldTypeAndValue, new []boltz.FieldTypeAndValue, holder errorz.ErrorHolder) {
|
||||
// Recalculate service policy links
|
||||
ctx := &roleAttributeChangeContext{
|
||||
tx: tx,
|
||||
rolesSymbol: store.stores.servicePolicy.symbolServiceRoles,
|
||||
linkCollection: store.stores.servicePolicy.serviceCollection,
|
||||
relatedLinkCollection: store.stores.servicePolicy.identityCollection,
|
||||
ErrorHolder: holder,
|
||||
}
|
||||
store.updateServicePolicyRelatedRoles(ctx, rowId, new)
|
||||
|
||||
// Recalculate service edge router policy links
|
||||
ctx = &roleAttributeChangeContext{
|
||||
tx: tx,
|
||||
rolesSymbol: store.stores.serviceEdgeRouterPolicy.symbolServiceRoles,
|
||||
linkCollection: store.stores.serviceEdgeRouterPolicy.serviceCollection,
|
||||
relatedLinkCollection: store.stores.serviceEdgeRouterPolicy.edgeRouterCollection,
|
||||
denormLinkCollection: store.edgeRoutersCollection,
|
||||
ErrorHolder: holder,
|
||||
}
|
||||
UpdateRelatedRoles(ctx, rowId, new, store.stores.serviceEdgeRouterPolicy.symbolSemantic)
|
||||
}
|
||||
|
||||
func (store *edgeServiceStoreImpl) GetNameIndex() boltz.ReadIndex {
|
||||
return store.indexName
|
||||
}
|
||||
@@ -209,3 +243,13 @@ func (store *edgeServiceStoreImpl) DeleteById(ctx boltz.MutateContext, id string
|
||||
func (store *edgeServiceStoreImpl) GetRoleAttributesCursorProvider(values []string, semantic string) (ast.SetCursorProvider, error) {
|
||||
return store.getRoleAttributesCursorProvider(store.indexRoleAttributes, values, semantic)
|
||||
}
|
||||
|
||||
func (store *edgeServiceStoreImpl) IsBindableByIdentity(tx *bbolt.Tx, id string, identityId string) bool {
|
||||
linkCount := store.bindIdentitiesCollection.GetLinkCount(tx, []byte(id), []byte(identityId))
|
||||
return linkCount != nil && *linkCount > 0
|
||||
}
|
||||
|
||||
func (store *edgeServiceStoreImpl) IsDialableByIdentity(tx *bbolt.Tx, id string, identityId string) bool {
|
||||
linkCount := store.dialIdentitiesCollection.GetLinkCount(tx, []byte(id), []byte(identityId))
|
||||
return linkCount != nil && *linkCount > 0
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package persistence
|
||||
import (
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/openziti/edge/eid"
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"github.com/openziti/foundation/storage/ast"
|
||||
"github.com/openziti/foundation/storage/boltz"
|
||||
"github.com/openziti/foundation/util/errorz"
|
||||
@@ -43,6 +44,9 @@ const (
|
||||
FieldIdentitySdkInfoRevision = "sdkInfoRevision"
|
||||
FieldIdentitySdkInfoType = "sdkInfoType"
|
||||
FieldIdentitySdkInfoVersion = "sdkInfoVersion"
|
||||
|
||||
FieldIdentityBindServices = "bindServices"
|
||||
FieldIdentityDialServices = "dialServices"
|
||||
)
|
||||
|
||||
func newIdentity(name string, identityTypeId string, roleAttributes ...string) *Identity {
|
||||
@@ -184,12 +188,21 @@ type identityStoreImpl struct {
|
||||
indexName boltz.ReadIndex
|
||||
indexRoleAttributes boltz.SetReadIndex
|
||||
|
||||
symbolApiSessions boltz.EntitySetSymbol
|
||||
symbolAuthenticators boltz.EntitySetSymbol
|
||||
symbolRoleAttributes boltz.EntitySetSymbol
|
||||
symbolApiSessions boltz.EntitySetSymbol
|
||||
symbolAuthenticators boltz.EntitySetSymbol
|
||||
symbolIdentityTypeId boltz.EntitySymbol
|
||||
symbolEnrollments boltz.EntitySetSymbol
|
||||
|
||||
symbolEdgeRouterPolicies boltz.EntitySetSymbol
|
||||
symbolEnrollments boltz.EntitySetSymbol
|
||||
symbolServicePolicies boltz.EntitySetSymbol
|
||||
symbolIdentityTypeId boltz.EntitySymbol
|
||||
symbolEdgeRouters boltz.EntitySetSymbol
|
||||
symbolBindServices boltz.EntitySetSymbol
|
||||
symbolDialServices boltz.EntitySetSymbol
|
||||
|
||||
edgeRoutersCollection boltz.RefCountedLinkCollection
|
||||
bindServicesCollection boltz.RefCountedLinkCollection
|
||||
dialServicesCollection boltz.RefCountedLinkCollection
|
||||
}
|
||||
|
||||
func (store *identityStoreImpl) NewStoreEntity() boltz.Entity {
|
||||
@@ -202,10 +215,15 @@ func (store *identityStoreImpl) GetRoleAttributesIndex() boltz.SetReadIndex {
|
||||
|
||||
func (store *identityStoreImpl) initializeLocal() {
|
||||
store.AddExtEntitySymbols()
|
||||
store.indexRoleAttributes = store.addRoleAttributesField()
|
||||
|
||||
store.symbolRoleAttributes = store.AddSetSymbol(FieldRoleAttributes, ast.NodeTypeString)
|
||||
store.indexRoleAttributes = store.AddSetIndex(store.symbolRoleAttributes)
|
||||
|
||||
store.indexName = store.addUniqueNameField()
|
||||
store.symbolApiSessions = store.AddFkSetSymbol(FieldIdentityApiSessions, store.stores.apiSession)
|
||||
store.symbolEdgeRouters = store.AddFkSetSymbol(db.EntityTypeRouters, store.stores.edgeRouter)
|
||||
store.symbolBindServices = store.AddFkSetSymbol(FieldIdentityBindServices, store.stores.edgeService)
|
||||
store.symbolDialServices = store.AddFkSetSymbol(FieldIdentityDialServices, store.stores.edgeService)
|
||||
store.symbolEdgeRouterPolicies = store.AddFkSetSymbol(EntityTypeEdgeRouterPolicies, store.stores.edgeRouterPolicy)
|
||||
store.symbolServicePolicies = store.AddFkSetSymbol(EntityTypeServicePolicies, store.stores.servicePolicy)
|
||||
store.symbolEnrollments = store.AddFkSetSymbol(FieldIdentityEnrollments, store.stores.enrollment)
|
||||
@@ -219,23 +237,36 @@ func (store *identityStoreImpl) initializeLocal() {
|
||||
store.indexRoleAttributes.AddListener(store.rolesChanged)
|
||||
}
|
||||
|
||||
func (store *identityStoreImpl) rolesChanged(tx *bbolt.Tx, rowId []byte, _ []boltz.FieldTypeAndValue, new []boltz.FieldTypeAndValue, holder errorz.ErrorHolder) {
|
||||
rolesSymbol := store.stores.edgeRouterPolicy.symbolIdentityRoles
|
||||
linkCollection := store.stores.edgeRouterPolicy.identityCollection
|
||||
semanticSymbol := store.stores.edgeRouterPolicy.symbolSemantic
|
||||
UpdateRelatedRoles(tx, string(rowId), rolesSymbol, linkCollection, new, holder, semanticSymbol)
|
||||
|
||||
rolesSymbol = store.stores.servicePolicy.symbolIdentityRoles
|
||||
linkCollection = store.stores.servicePolicy.identityCollection
|
||||
semanticSymbol = store.stores.servicePolicy.symbolSemantic
|
||||
UpdateRelatedRoles(tx, string(rowId), rolesSymbol, linkCollection, new, holder, semanticSymbol)
|
||||
}
|
||||
|
||||
func (store *identityStoreImpl) initializeLinked() {
|
||||
store.AddLinkCollection(store.symbolAuthenticators, store.stores.authenticator.symbolIdentityId)
|
||||
store.AddLinkCollection(store.symbolEnrollments, store.stores.enrollment.symbolIdentity)
|
||||
store.AddLinkCollection(store.symbolEdgeRouterPolicies, store.stores.edgeRouterPolicy.symbolIdentities)
|
||||
store.AddLinkCollection(store.symbolServicePolicies, store.stores.servicePolicy.symbolIdentities)
|
||||
|
||||
store.edgeRoutersCollection = store.AddRefCountedLinkCollection(store.symbolEdgeRouters, store.stores.edgeRouter.symbolIdentities)
|
||||
store.bindServicesCollection = store.AddRefCountedLinkCollection(store.symbolBindServices, store.stores.edgeService.symbolBindIdentities)
|
||||
store.dialServicesCollection = store.AddRefCountedLinkCollection(store.symbolDialServices, store.stores.edgeService.symbolDialIdentities)
|
||||
}
|
||||
|
||||
func (store *identityStoreImpl) rolesChanged(tx *bbolt.Tx, rowId []byte, _ []boltz.FieldTypeAndValue, new []boltz.FieldTypeAndValue, holder errorz.ErrorHolder) {
|
||||
ctx := &roleAttributeChangeContext{
|
||||
tx: tx,
|
||||
rolesSymbol: store.stores.edgeRouterPolicy.symbolIdentityRoles,
|
||||
linkCollection: store.stores.edgeRouterPolicy.identityCollection,
|
||||
relatedLinkCollection: store.stores.edgeRouterPolicy.edgeRouterCollection,
|
||||
denormLinkCollection: store.edgeRoutersCollection,
|
||||
ErrorHolder: holder,
|
||||
}
|
||||
UpdateRelatedRoles(ctx, rowId, new, store.stores.edgeRouterPolicy.symbolSemantic)
|
||||
|
||||
ctx = &roleAttributeChangeContext{
|
||||
tx: tx,
|
||||
rolesSymbol: store.stores.servicePolicy.symbolIdentityRoles,
|
||||
linkCollection: store.stores.servicePolicy.identityCollection,
|
||||
relatedLinkCollection: store.stores.servicePolicy.serviceCollection,
|
||||
ErrorHolder: holder,
|
||||
}
|
||||
store.updateServicePolicyRelatedRoles(ctx, rowId, new)
|
||||
}
|
||||
|
||||
func (store *identityStoreImpl) GetNameIndex() boltz.ReadIndex {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/openziti/foundation/storage/boltz"
|
||||
)
|
||||
|
||||
func (m *Migrations) denormalizePolicies(step *boltz.MigrationStep) {
|
||||
log := pfxlog.Logger()
|
||||
|
||||
err := m.stores.EdgeRouterPolicy.CheckIntegrity(step.Ctx.Tx(), true, func(err error) {
|
||||
log.WithError(err).Debug("updating edge router policies")
|
||||
})
|
||||
|
||||
if step.SetError(err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = m.stores.ServiceEdgeRouterPolicy.CheckIntegrity(step.Ctx.Tx(), true, func(err error) {
|
||||
log.WithError(err).Debug("updating service edge router policies")
|
||||
})
|
||||
|
||||
if step.SetError(err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = m.stores.ServicePolicy.CheckIntegrity(step.Ctx.Tx(), true, func(err error) {
|
||||
log.WithError(err).Debug("updating service policies")
|
||||
})
|
||||
|
||||
if step.SetError(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
CurrentDbVersion = 7
|
||||
CurrentDbVersion = 8
|
||||
FieldVersion = "version"
|
||||
)
|
||||
|
||||
@@ -78,6 +78,10 @@ func (m *Migrations) migrate(step *boltz.MigrationStep) int {
|
||||
m.fixAuthenticatorCertFingerprints(step)
|
||||
}
|
||||
|
||||
if step.CurrentVersion < 8 {
|
||||
m.denormalizePolicies(step)
|
||||
}
|
||||
|
||||
// current version
|
||||
if step.CurrentVersion <= CurrentDbVersion {
|
||||
return CurrentDbVersion
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/openziti/foundation/storage/ast"
|
||||
"github.com/openziti/foundation/storage/boltz"
|
||||
"github.com/openziti/foundation/util/errorz"
|
||||
"github.com/openziti/foundation/util/stringz"
|
||||
"github.com/openziti/foundation/validation"
|
||||
"github.com/pkg/errors"
|
||||
"go.etcd.io/bbolt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type roleAttributeChangeContext struct {
|
||||
tx *bbolt.Tx
|
||||
rolesSymbol boltz.EntitySetSymbol
|
||||
linkCollection boltz.LinkCollection
|
||||
relatedLinkCollection boltz.LinkCollection
|
||||
denormLinkCollection boltz.RefCountedLinkCollection
|
||||
errorz.ErrorHolder
|
||||
}
|
||||
|
||||
func (store *baseStore) updateServicePolicyRelatedRoles(ctx *roleAttributeChangeContext, entityId []byte, newRoleAttributes []boltz.FieldTypeAndValue) {
|
||||
cursor := ctx.rolesSymbol.GetStore().IterateIds(ctx.tx, ast.BoolNodeTrue)
|
||||
|
||||
entityRoles := FieldValuesToIds(newRoleAttributes)
|
||||
|
||||
semanticSymbol := store.stores.servicePolicy.symbolSemantic
|
||||
policyTypeSymbol := store.stores.servicePolicy.symbolPolicyType
|
||||
|
||||
isServices := ctx.rolesSymbol == store.stores.servicePolicy.symbolServiceRoles
|
||||
|
||||
for ; cursor.IsValid(); cursor.Next() {
|
||||
policyId := cursor.Current()
|
||||
roleSet := ctx.rolesSymbol.EvalStringList(ctx.tx, policyId)
|
||||
roles, ids, err := splitRolesAndIds(roleSet)
|
||||
if err != nil {
|
||||
ctx.SetError(err)
|
||||
return
|
||||
}
|
||||
|
||||
semantic := SemanticAllOf
|
||||
if _, semanticValue := semanticSymbol.Eval(ctx.tx, policyId); semanticValue != nil {
|
||||
semantic = string(semanticValue)
|
||||
}
|
||||
policyType := PolicyTypeDial
|
||||
if fieldType, policyTypeValue := policyTypeSymbol.Eval(ctx.tx, policyId); fieldType == boltz.TypeInt32 {
|
||||
policyType = *boltz.BytesToInt32(policyTypeValue)
|
||||
}
|
||||
if policyType == PolicyTypeDial {
|
||||
if isServices {
|
||||
ctx.denormLinkCollection = store.stores.edgeService.dialIdentitiesCollection
|
||||
} else {
|
||||
ctx.denormLinkCollection = store.stores.identity.dialServicesCollection
|
||||
}
|
||||
} else if isServices {
|
||||
ctx.denormLinkCollection = store.stores.edgeService.bindIdentitiesCollection
|
||||
} else {
|
||||
ctx.denormLinkCollection = store.stores.identity.bindServicesCollection
|
||||
}
|
||||
evaluatePolicyAgainstEntity(ctx, semantic, entityId, policyId, ids, roles, entityRoles)
|
||||
}
|
||||
}
|
||||
|
||||
func EvaluatePolicy(ctx *roleAttributeChangeContext, policy Policy, roleAttributesSymbol boltz.EntitySetSymbol) {
|
||||
policyId := []byte(policy.GetId())
|
||||
|
||||
roleSet := ctx.rolesSymbol.EvalStringList(ctx.tx, policyId)
|
||||
roles, ids, err := splitRolesAndIds(roleSet)
|
||||
if err != nil {
|
||||
ctx.SetError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateEntityIds(ctx.tx, ctx.linkCollection.GetLinkedSymbol().GetStore(), ctx.rolesSymbol.GetName(), ids); err != nil {
|
||||
ctx.SetError(err)
|
||||
return
|
||||
}
|
||||
|
||||
cursor := roleAttributesSymbol.GetStore().IterateIds(ctx.tx, ast.BoolNodeTrue)
|
||||
for ; cursor.IsValid(); cursor.Next() {
|
||||
entityId := cursor.Current()
|
||||
entityRoleAttributes := roleAttributesSymbol.EvalStringList(ctx.tx, entityId)
|
||||
evaluatePolicyAgainstEntity(ctx, policy.GetSemantic(), entityId, policyId, ids, roles, entityRoleAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
func validateEntityIds(tx *bbolt.Tx, store boltz.ListStore, field string, ids []string) error {
|
||||
var invalid []string
|
||||
for _, val := range ids {
|
||||
if !store.IsEntityPresent(tx, val) {
|
||||
invalid = append(invalid, val)
|
||||
}
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return validation.NewFieldError(fmt.Sprintf("no %v found with the given ids", store.GetEntityType()), field, invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateRelatedRoles(ctx *roleAttributeChangeContext, entityId []byte, newRoleAttributes []boltz.FieldTypeAndValue, semanticSymbol boltz.EntitySymbol) {
|
||||
cursor := ctx.rolesSymbol.GetStore().IterateIds(ctx.tx, ast.BoolNodeTrue)
|
||||
|
||||
entityRoles := FieldValuesToIds(newRoleAttributes)
|
||||
|
||||
for ; cursor.IsValid(); cursor.Next() {
|
||||
policyId := cursor.Current()
|
||||
roleSet := ctx.rolesSymbol.EvalStringList(ctx.tx, policyId)
|
||||
roles, ids, err := splitRolesAndIds(roleSet)
|
||||
if err != nil {
|
||||
ctx.SetError(err)
|
||||
return
|
||||
}
|
||||
|
||||
semantic := SemanticAllOf
|
||||
if _, semanticValue := semanticSymbol.Eval(ctx.tx, policyId); semanticValue != nil {
|
||||
semantic = string(semanticValue)
|
||||
}
|
||||
evaluatePolicyAgainstEntity(ctx, semantic, entityId, policyId, ids, roles, entityRoles)
|
||||
}
|
||||
}
|
||||
|
||||
func evaluatePolicyAgainstEntity(ctx *roleAttributeChangeContext, semantic string, entityId, policyId []byte, ids, roles, roleAttributes []string) {
|
||||
if stringz.Contains(ids, string(entityId)) || stringz.Contains(roles, "all") ||
|
||||
(strings.EqualFold(semantic, SemanticAllOf) && len(roles) > 0 && stringz.ContainsAll(roleAttributes, roles...)) ||
|
||||
(strings.EqualFold(semantic, SemanticAnyOf) && len(roles) > 0 && stringz.ContainsAny(roleAttributes, roles...)) {
|
||||
ProcessEntityPolicyMatched(ctx, entityId, policyId)
|
||||
} else {
|
||||
ProcessEntityPolicyUnmatched(ctx, entityId, policyId)
|
||||
}
|
||||
}
|
||||
|
||||
func ProcessEntityPolicyMatched(ctx *roleAttributeChangeContext, entityId, policyId []byte) {
|
||||
if added, err := ctx.linkCollection.AddLink(ctx.tx, policyId, entityId); ctx.SetError(err) || !added {
|
||||
return
|
||||
}
|
||||
cursor := ctx.relatedLinkCollection.IterateLinks(ctx.tx, policyId)
|
||||
for ; cursor.IsValid(); cursor.Next() {
|
||||
relatedEntityId := cursor.Current()
|
||||
_, err := ctx.denormLinkCollection.IncrementLinkCount(ctx.tx, entityId, relatedEntityId)
|
||||
if ctx.SetError(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ProcessEntityPolicyUnmatched(ctx *roleAttributeChangeContext, entityId, policyId []byte) {
|
||||
if removed, err := ctx.linkCollection.RemoveLink(ctx.tx, policyId, entityId); ctx.SetError(err) || !removed {
|
||||
return
|
||||
}
|
||||
|
||||
cursor := ctx.relatedLinkCollection.IterateLinks(ctx.tx, policyId)
|
||||
for ; cursor.IsValid(); cursor.Next() {
|
||||
relatedEntityId := cursor.Current()
|
||||
_, err := ctx.denormLinkCollection.DecrementLinkCount(ctx.tx, entityId, relatedEntityId)
|
||||
if ctx.SetError(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type denormCheckCtx struct {
|
||||
tx *bbolt.Tx
|
||||
sourceStore boltz.CrudStore
|
||||
targetStore boltz.CrudStore
|
||||
policyStore boltz.CrudStore
|
||||
sourceCollection boltz.LinkCollection
|
||||
targetCollection boltz.LinkCollection
|
||||
targetDenormCollection boltz.RefCountedLinkCollection
|
||||
policyFilter func(policyId []byte) bool
|
||||
errorSink func(err error)
|
||||
repair bool
|
||||
}
|
||||
|
||||
func validatePolicyDenormalization(ctx *denormCheckCtx) error {
|
||||
for sourceCursor := ctx.sourceStore.IterateIds(ctx.tx, ast.BoolNodeTrue); sourceCursor.IsValid(); sourceCursor.Next() {
|
||||
sourceEntityId := sourceCursor.Current()
|
||||
for targetCursor := ctx.targetStore.IterateIds(ctx.tx, ast.BoolNodeTrue); targetCursor.IsValid(); targetCursor.Next() {
|
||||
targetEntityId := targetCursor.Current()
|
||||
|
||||
var relatedPolicies []string
|
||||
|
||||
for policyCursor := ctx.policyStore.IterateIds(ctx.tx, ast.BoolNodeTrue); policyCursor.IsValid(); policyCursor.Next() {
|
||||
policyId := policyCursor.Current()
|
||||
if ctx.policyFilter == nil || ctx.policyFilter(policyId) {
|
||||
sourceRelated := isRelatedByLinkCollection(ctx.tx, ctx.sourceCollection, policyId, sourceEntityId)
|
||||
targetRelated := isRelatedByLinkCollection(ctx.tx, ctx.targetCollection, policyId, targetEntityId)
|
||||
if sourceRelated && targetRelated {
|
||||
relatedPolicies = append(relatedPolicies, string(policyId))
|
||||
}
|
||||
}
|
||||
}
|
||||
linkCount := len(relatedPolicies)
|
||||
var sourceLinkCount, targetLinkCount *int32
|
||||
var err error
|
||||
if ctx.repair {
|
||||
sourceLinkCount, targetLinkCount, err = ctx.targetDenormCollection.SetLinkCount(ctx.tx, sourceEntityId, targetEntityId, linkCount)
|
||||
} else {
|
||||
sourceLinkCount, targetLinkCount = ctx.targetDenormCollection.GetLinkCounts(ctx.tx, sourceEntityId, targetEntityId)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logDiscrepencies(ctx, linkCount, sourceEntityId, targetEntityId, sourceLinkCount, targetLinkCount)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func logDiscrepencies(ctx *denormCheckCtx, count int, sourceId, targetId []byte, sourceLinkCount, targetLinkCount *int32) {
|
||||
oldValuesMatch := (sourceLinkCount == nil && targetLinkCount == nil) || (sourceLinkCount != nil && targetLinkCount != nil && *sourceLinkCount == *targetLinkCount)
|
||||
if !oldValuesMatch {
|
||||
err := errors.Errorf("fixed mismatched link counts. was %v %v (%v) <-> %v %v (%v), now both are %v",
|
||||
ctx.sourceStore.GetSingularEntityType(), string(sourceId), sourceLinkCount,
|
||||
ctx.targetStore.GetSingularEntityType(), string(targetId), targetLinkCount, count)
|
||||
ctx.errorSink(err)
|
||||
}
|
||||
|
||||
if ((sourceLinkCount == nil || *sourceLinkCount == 0) && count != 0) ||
|
||||
(sourceLinkCount != nil && *sourceLinkCount != int32(count)) {
|
||||
sourceCount := int32(0)
|
||||
if sourceLinkCount != nil {
|
||||
sourceCount = *sourceLinkCount
|
||||
}
|
||||
err := errors.Errorf("fixed incorrect link counts for %v %v <-> %v %v was %v, is now %v",
|
||||
ctx.sourceStore.GetSingularEntityType(), string(sourceId),
|
||||
ctx.targetStore.GetSingularEntityType(), string(targetId),
|
||||
sourceCount, count)
|
||||
ctx.errorSink(err)
|
||||
}
|
||||
}
|
||||
|
||||
func isRelatedByLinkCollection(tx *bbolt.Tx, linkCollection boltz.LinkCollection, entityId, relatedId []byte) bool {
|
||||
cursor := linkCollection.IterateLinks(tx, entityId)
|
||||
cursor.Seek(relatedId)
|
||||
return bytes.Equal(cursor.Current(), relatedId)
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"fmt"
|
||||
"github.com/openziti/edge/eid"
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"github.com/openziti/foundation/storage/ast"
|
||||
"github.com/openziti/foundation/storage/boltz"
|
||||
"github.com/openziti/foundation/util/stringz"
|
||||
@@ -30,6 +31,10 @@ func (entity *ServiceEdgeRouterPolicy) GetName() string {
|
||||
return entity.Name
|
||||
}
|
||||
|
||||
func (entity *ServiceEdgeRouterPolicy) GetSemantic() string {
|
||||
return entity.Semantic
|
||||
}
|
||||
|
||||
func (entity *ServiceEdgeRouterPolicy) LoadValues(_ boltz.CrudStore, bucket *boltz.TypedBucket) {
|
||||
entity.LoadBaseValues(bucket)
|
||||
entity.Name = bucket.GetStringOrError(FieldName)
|
||||
@@ -83,6 +88,7 @@ type ServiceEdgeRouterPolicyStore interface {
|
||||
NameIndexedStore
|
||||
LoadOneById(tx *bbolt.Tx, id string) (*ServiceEdgeRouterPolicy, error)
|
||||
LoadOneByName(tx *bbolt.Tx, id string) (*ServiceEdgeRouterPolicy, error)
|
||||
CheckIntegrity(tx *bbolt.Tx, fix bool, errorSink func(error)) error
|
||||
}
|
||||
|
||||
func newServiceEdgeRouterPolicyStore(stores *stores) *serviceEdgeRouterPolicyStoreImpl {
|
||||
@@ -122,7 +128,7 @@ func (store *serviceEdgeRouterPolicyStoreImpl) initializeLocal() {
|
||||
store.symbolSemantic = store.AddSymbol(FieldSemantic, ast.NodeTypeString)
|
||||
store.symbolServiceRoles = store.AddSetSymbol(FieldServiceRoles, ast.NodeTypeString)
|
||||
store.symbolEdgeRouterRoles = store.AddSetSymbol(FieldEdgeRouterRoles, ast.NodeTypeString)
|
||||
store.symbolServices = store.AddFkSetSymbol(EntityTypeServices, store.stores.edgeService)
|
||||
store.symbolServices = store.AddFkSetSymbol(db.EntityTypeServices, store.stores.edgeService)
|
||||
store.symbolEdgeRouters = store.AddFkSetSymbol(db.EntityTypeRouters, store.stores.edgeRouter)
|
||||
}
|
||||
|
||||
@@ -154,16 +160,55 @@ Optimizations
|
||||
and just add/remove/ignore
|
||||
3. Related entity deletes should be handled automatically by FK Indexes on those entities (need to verify the reverse as well/deleting policy)
|
||||
*/
|
||||
func (store *serviceEdgeRouterPolicyStoreImpl) edgeRouterRolesUpdated(ctx *boltz.PersistContext, policy *ServiceEdgeRouterPolicy) {
|
||||
roleIds, err := store.getEntityIdsForRoleSet(ctx.Bucket.Tx(), FieldEdgeRouterRoles, policy.EdgeRouterRoles, policy.Semantic, store.stores.edgeRouter.indexRoleAttributes, store.stores.edgeRouter)
|
||||
if !ctx.Bucket.SetError(err) {
|
||||
ctx.Bucket.SetError(store.edgeRouterCollection.SetLinks(ctx.Bucket.Tx(), policy.Id, roleIds))
|
||||
func (store *serviceEdgeRouterPolicyStoreImpl) edgeRouterRolesUpdated(persistCtx *boltz.PersistContext, policy *ServiceEdgeRouterPolicy) {
|
||||
ctx := &roleAttributeChangeContext{
|
||||
tx: persistCtx.Bucket.Tx(),
|
||||
rolesSymbol: store.symbolEdgeRouterRoles,
|
||||
linkCollection: store.edgeRouterCollection,
|
||||
relatedLinkCollection: store.serviceCollection,
|
||||
denormLinkCollection: store.stores.edgeRouter.servicesCollection,
|
||||
ErrorHolder: persistCtx.Bucket,
|
||||
}
|
||||
EvaluatePolicy(ctx, policy, store.stores.edgeRouter.symbolRoleAttributes)
|
||||
}
|
||||
|
||||
func (store *serviceEdgeRouterPolicyStoreImpl) serviceRolesUpdated(ctx *boltz.PersistContext, policy *ServiceEdgeRouterPolicy) {
|
||||
roleIds, err := store.getEntityIdsForRoleSet(ctx.Bucket.Tx(), FieldServiceRoles, policy.ServiceRoles, policy.Semantic, store.stores.edgeService.indexRoleAttributes, store.stores.edgeService)
|
||||
if !ctx.Bucket.SetError(err) {
|
||||
ctx.Bucket.SetError(store.serviceCollection.SetLinks(ctx.Bucket.Tx(), policy.Id, roleIds))
|
||||
func (store *serviceEdgeRouterPolicyStoreImpl) serviceRolesUpdated(persistCtx *boltz.PersistContext, policy *ServiceEdgeRouterPolicy) {
|
||||
ctx := &roleAttributeChangeContext{
|
||||
tx: persistCtx.Bucket.Tx(),
|
||||
rolesSymbol: store.symbolServiceRoles,
|
||||
linkCollection: store.serviceCollection,
|
||||
relatedLinkCollection: store.edgeRouterCollection,
|
||||
denormLinkCollection: store.stores.edgeService.edgeRoutersCollection,
|
||||
ErrorHolder: persistCtx.Bucket,
|
||||
}
|
||||
EvaluatePolicy(ctx, policy, store.stores.edgeService.symbolRoleAttributes)
|
||||
}
|
||||
|
||||
func (store *serviceEdgeRouterPolicyStoreImpl) DeleteById(ctx boltz.MutateContext, id string) error {
|
||||
policy, err := store.LoadOneById(ctx.Tx(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
policy.EdgeRouterRoles = nil
|
||||
policy.ServiceRoles = nil
|
||||
err = store.Update(ctx, policy, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure while clearing policy before delete: %w", err)
|
||||
}
|
||||
return store.BaseStore.DeleteById(ctx, id)
|
||||
}
|
||||
|
||||
func (store *serviceEdgeRouterPolicyStoreImpl) CheckIntegrity(tx *bbolt.Tx, fix bool, errorSink func(error)) error {
|
||||
ctx := &denormCheckCtx{
|
||||
tx: tx,
|
||||
sourceStore: store.stores.edgeService,
|
||||
targetStore: store.stores.edgeRouter,
|
||||
policyStore: store,
|
||||
sourceCollection: store.serviceCollection,
|
||||
targetCollection: store.edgeRouterCollection,
|
||||
targetDenormCollection: store.stores.edgeService.edgeRoutersCollection,
|
||||
errorSink: errorSink,
|
||||
repair: fix,
|
||||
}
|
||||
return validatePolicyDenormalization(ctx)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"github.com/openziti/edge/eid"
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"github.com/openziti/foundation/util/errorz"
|
||||
"github.com/openziti/foundation/util/stringz"
|
||||
"go.etcd.io/bbolt"
|
||||
"sort"
|
||||
@@ -30,7 +31,7 @@ func (ctx *TestContext) testCreateServiceEdgeRouterPolicy(_ *testing.T) {
|
||||
|
||||
err := ctx.GetDb().View(func(tx *bbolt.Tx) error {
|
||||
ctx.Equal(0, len(ctx.stores.ServiceEdgeRouterPolicy.GetRelatedEntitiesIdList(tx, policy.Id, db.EntityTypeRouters)))
|
||||
ctx.Equal(0, len(ctx.stores.ServiceEdgeRouterPolicy.GetRelatedEntitiesIdList(tx, policy.Id, EntityTypeServices)))
|
||||
ctx.Equal(0, len(ctx.stores.ServiceEdgeRouterPolicy.GetRelatedEntitiesIdList(tx, policy.Id, db.EntityTypeServices)))
|
||||
|
||||
testPolicy, err := ctx.stores.ServiceEdgeRouterPolicy.LoadOneByName(tx, policy.Name)
|
||||
ctx.NoError(err)
|
||||
@@ -205,7 +206,7 @@ func (ctx *TestContext) testServiceEdgeRouterPolicyRoleEvaluation(_ *testing.T)
|
||||
|
||||
for i := 0; i < 9; i++ {
|
||||
relatedEdgeRouters := ctx.getRelatedIds(policies[i], db.EntityTypeRouters)
|
||||
relatedServices := ctx.getRelatedIds(policies[i], EntityTypeServices)
|
||||
relatedServices := ctx.getRelatedIds(policies[i], db.EntityTypeServices)
|
||||
if i == 3 {
|
||||
ctx.Equal([]string{edgeRouters[0].Id}, relatedEdgeRouters)
|
||||
ctx.Equal([]string{services[0].Id}, relatedServices)
|
||||
@@ -245,8 +246,7 @@ func (ctx *TestContext) testServiceEdgeRouterPolicyRoleEvaluation(_ *testing.T)
|
||||
edgeRouters = append(edgeRouters, edgeRouter)
|
||||
})
|
||||
|
||||
ctx.validateServiceEdgeRouterPolicyServices(services, policies)
|
||||
ctx.validateServiceEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateServiceEdgeRouterPolicies(services, edgeRouters, policies)
|
||||
|
||||
for _, service := range services {
|
||||
ctx.RequireDelete(service)
|
||||
@@ -275,8 +275,7 @@ func (ctx *TestContext) testServiceEdgeRouterPolicyRoleEvaluation(_ *testing.T)
|
||||
edgeRouters = append(edgeRouters, edgeRouter)
|
||||
})
|
||||
|
||||
ctx.validateServiceEdgeRouterPolicyServices(services, policies)
|
||||
ctx.validateServiceEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateServiceEdgeRouterPolicies(services, edgeRouters, policies)
|
||||
|
||||
// ensure policies get cleaned up
|
||||
for _, policy := range policies {
|
||||
@@ -286,8 +285,7 @@ func (ctx *TestContext) testServiceEdgeRouterPolicyRoleEvaluation(_ *testing.T)
|
||||
// test with policies created after services/edge routers
|
||||
policies = ctx.createServiceEdgeRouterPolicies(serviceRoles, edgeRouterRoles, services, edgeRouters, true)
|
||||
|
||||
ctx.validateServiceEdgeRouterPolicyServices(services, policies)
|
||||
ctx.validateServiceEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateServiceEdgeRouterPolicies(services, edgeRouters, policies)
|
||||
|
||||
for _, policy := range policies {
|
||||
ctx.RequireDelete(policy)
|
||||
@@ -296,8 +294,7 @@ func (ctx *TestContext) testServiceEdgeRouterPolicyRoleEvaluation(_ *testing.T)
|
||||
// test with policies created after services/edge routers and roles added after created
|
||||
policies = ctx.createServiceEdgeRouterPolicies(serviceRoles, edgeRouterRoles, services, edgeRouters, false)
|
||||
|
||||
ctx.validateServiceEdgeRouterPolicyServices(services, policies)
|
||||
ctx.validateServiceEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateServiceEdgeRouterPolicies(services, edgeRouters, policies)
|
||||
|
||||
for _, service := range services {
|
||||
if len(service.RoleAttributes) > 0 {
|
||||
@@ -323,8 +320,7 @@ func (ctx *TestContext) testServiceEdgeRouterPolicyRoleEvaluation(_ *testing.T)
|
||||
ctx.RequireUpdate(policy)
|
||||
}
|
||||
|
||||
ctx.validateServiceEdgeRouterPolicyServices(services, policies)
|
||||
ctx.validateServiceEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateServiceEdgeRouterPolicies(services, edgeRouters, policies)
|
||||
}
|
||||
|
||||
func (ctx *TestContext) createServiceEdgeRouterPolicies(serviceRoles, edgeRouterRoles []string, services []*EdgeService, edgeRouters []*EdgeRouter, oncreate bool) []*ServiceEdgeRouterPolicy {
|
||||
@@ -381,10 +377,16 @@ func (ctx *TestContext) createServiceEdgeRouterPolicies(serviceRoles, edgeRouter
|
||||
return policies
|
||||
}
|
||||
|
||||
func (ctx *TestContext) validateServiceEdgeRouterPolicies(services []*EdgeService, edgeRouters []*EdgeRouter, policies []*ServiceEdgeRouterPolicy) {
|
||||
ctx.validateServiceEdgeRouterPolicyServices(services, policies)
|
||||
ctx.validateServiceEdgeRouterPolicyEdgeRouters(edgeRouters, policies)
|
||||
ctx.validateServiceEdgeRouterPolicyDenormalization()
|
||||
}
|
||||
|
||||
func (ctx *TestContext) validateServiceEdgeRouterPolicyServices(services []*EdgeService, policies []*ServiceEdgeRouterPolicy) {
|
||||
for _, policy := range policies {
|
||||
count := 0
|
||||
relatedServices := ctx.getRelatedIds(policy, EntityTypeServices)
|
||||
relatedServices := ctx.getRelatedIds(policy, db.EntityTypeServices)
|
||||
for _, service := range services {
|
||||
relatedPolicies := ctx.getRelatedIds(service, EntityTypeServiceEdgeRouterPolicies)
|
||||
shouldContain := ctx.policyShouldMatch(policy.Semantic, policy.ServiceRoles, service, service.RoleAttributes)
|
||||
@@ -423,3 +425,13 @@ func (ctx *TestContext) validateServiceEdgeRouterPolicyEdgeRouters(edgeRouters [
|
||||
ctx.Equal(count, len(relatedEdgeRouters))
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *TestContext) validateServiceEdgeRouterPolicyDenormalization() {
|
||||
errorHolder := &errorz.ErrorHolderImpl{}
|
||||
errorHolder.SetError(ctx.db.View(func(tx *bbolt.Tx) error {
|
||||
return ctx.stores.ServiceEdgeRouterPolicy.CheckIntegrity(tx, false, func(err error) {
|
||||
errorHolder.SetError(err)
|
||||
})
|
||||
}))
|
||||
ctx.NoError(errorHolder.GetError())
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openziti/edge/eid"
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"github.com/openziti/foundation/storage/ast"
|
||||
"github.com/openziti/foundation/storage/boltz"
|
||||
"github.com/openziti/foundation/util/stringz"
|
||||
"github.com/openziti/foundation/validation"
|
||||
"go.etcd.io/bbolt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
FieldServicePolicyType = "type"
|
||||
|
||||
PolicyTypeDialName = "Dial"
|
||||
PolicyTypeBindName = "Bind"
|
||||
PolicyTypeInvalidName = "Invalid"
|
||||
PolicyTypeDialName = "Dial"
|
||||
PolicyTypeBindName = "Bind"
|
||||
|
||||
PolicyTypeInvalid int32 = iota
|
||||
PolicyTypeDial
|
||||
@@ -22,9 +26,14 @@ const (
|
||||
)
|
||||
|
||||
func newServicePolicy(name string) *ServicePolicy {
|
||||
policyType := PolicyTypeDial
|
||||
if rand.Int()%2 == 0 {
|
||||
policyType = PolicyTypeBind
|
||||
}
|
||||
return &ServicePolicy{
|
||||
BaseExtEntity: boltz.BaseExtEntity{Id: eid.New()},
|
||||
Name: name,
|
||||
PolicyType: policyType,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +50,10 @@ func (entity *ServicePolicy) GetName() string {
|
||||
return entity.Name
|
||||
}
|
||||
|
||||
func (entity *ServicePolicy) GetSemantic() string {
|
||||
return entity.Semantic
|
||||
}
|
||||
|
||||
func (entity *ServicePolicy) LoadValues(_ boltz.CrudStore, bucket *boltz.TypedBucket) {
|
||||
entity.LoadBaseValues(bucket)
|
||||
entity.Name = bucket.GetStringOrError(FieldName)
|
||||
@@ -55,6 +68,10 @@ func (entity *ServicePolicy) SetValues(ctx *boltz.PersistContext) {
|
||||
entity.Semantic = SemanticAllOf
|
||||
}
|
||||
|
||||
if entity.PolicyType != PolicyTypeBind {
|
||||
entity.PolicyType = PolicyTypeDial
|
||||
}
|
||||
|
||||
if err := validateRolesAndIds(FieldIdentityRoles, entity.IdentityRoles); err != nil {
|
||||
ctx.Bucket.SetError(err)
|
||||
}
|
||||
@@ -91,17 +108,25 @@ func (entity *ServicePolicy) GetEntityType() string {
|
||||
return EntityTypeServicePolicies
|
||||
}
|
||||
|
||||
func (entity *ServicePolicy) GetPolicyTypeName() string {
|
||||
if entity.PolicyType == PolicyTypeBind {
|
||||
func getPolicyTypeName(policyType int32) string {
|
||||
if policyType == PolicyTypeBind {
|
||||
return PolicyTypeBindName
|
||||
}
|
||||
return PolicyTypeDialName
|
||||
if policyType == PolicyTypeDial {
|
||||
return PolicyTypeDialName
|
||||
}
|
||||
return PolicyTypeInvalidName
|
||||
}
|
||||
|
||||
func (entity *ServicePolicy) GetPolicyTypeName() string {
|
||||
return getPolicyTypeName(entity.PolicyType)
|
||||
}
|
||||
|
||||
type ServicePolicyStore interface {
|
||||
NameIndexedStore
|
||||
LoadOneById(tx *bbolt.Tx, id string) (*ServicePolicy, error)
|
||||
LoadOneByName(tx *bbolt.Tx, id string) (*ServicePolicy, error)
|
||||
CheckIntegrity(tx *bbolt.Tx, fix bool, errorSink func(error)) error
|
||||
}
|
||||
|
||||
func newServicePolicyStore(stores *stores) *servicePolicyStoreImpl {
|
||||
@@ -116,6 +141,7 @@ type servicePolicyStoreImpl struct {
|
||||
*baseStore
|
||||
|
||||
indexName boltz.ReadIndex
|
||||
symbolPolicyType boltz.EntitySymbol
|
||||
symbolSemantic boltz.EntitySymbol
|
||||
symbolIdentityRoles boltz.EntitySetSymbol
|
||||
symbolServiceRoles boltz.EntitySetSymbol
|
||||
@@ -138,12 +164,12 @@ func (store *servicePolicyStoreImpl) initializeLocal() {
|
||||
store.AddExtEntitySymbols()
|
||||
|
||||
store.indexName = store.addUniqueNameField()
|
||||
store.AddSymbol(FieldServicePolicyType, ast.NodeTypeInt64)
|
||||
store.symbolPolicyType = store.AddSymbol(FieldServicePolicyType, ast.NodeTypeInt64)
|
||||
store.symbolSemantic = store.AddSymbol(FieldSemantic, ast.NodeTypeString)
|
||||
store.symbolIdentityRoles = store.AddSetSymbol(FieldIdentityRoles, ast.NodeTypeString)
|
||||
store.symbolServiceRoles = store.AddSetSymbol(FieldServiceRoles, ast.NodeTypeString)
|
||||
store.symbolIdentities = store.AddFkSetSymbol(EntityTypeIdentities, store.stores.identity)
|
||||
store.symbolServices = store.AddFkSetSymbol(EntityTypeServices, store.stores.edgeService)
|
||||
store.symbolServices = store.AddFkSetSymbol(db.EntityTypeServices, store.stores.edgeService)
|
||||
}
|
||||
|
||||
func (store *servicePolicyStoreImpl) initializeLinked() {
|
||||
@@ -174,16 +200,95 @@ Optimizations
|
||||
and just add/remove/ignore
|
||||
3. Related entity deletes should be handled automatically by FK Indexes on those entities (need to verify the reverse as well/deleting policy)
|
||||
*/
|
||||
func (store *servicePolicyStoreImpl) serviceRolesUpdated(ctx *boltz.PersistContext, policy *ServicePolicy) {
|
||||
roleIds, err := store.getEntityIdsForRoleSet(ctx.Bucket.Tx(), "serviceRoles", policy.ServiceRoles, policy.Semantic, store.stores.edgeService.indexRoleAttributes, store.stores.edgeService)
|
||||
if !ctx.Bucket.SetError(err) {
|
||||
ctx.Bucket.SetError(store.serviceCollection.SetLinks(ctx.Bucket.Tx(), policy.Id, roleIds))
|
||||
func (store *servicePolicyStoreImpl) serviceRolesUpdated(persistCtx *boltz.PersistContext, policy *ServicePolicy) {
|
||||
ctx := &roleAttributeChangeContext{
|
||||
tx: persistCtx.Bucket.Tx(),
|
||||
rolesSymbol: store.symbolServiceRoles,
|
||||
linkCollection: store.serviceCollection,
|
||||
relatedLinkCollection: store.identityCollection,
|
||||
ErrorHolder: persistCtx.Bucket,
|
||||
}
|
||||
if policy.PolicyType == PolicyTypeDial {
|
||||
ctx.denormLinkCollection = store.stores.edgeService.dialIdentitiesCollection
|
||||
} else {
|
||||
ctx.denormLinkCollection = store.stores.edgeService.bindIdentitiesCollection
|
||||
}
|
||||
EvaluatePolicy(ctx, policy, store.stores.edgeService.symbolRoleAttributes)
|
||||
}
|
||||
|
||||
func (store *servicePolicyStoreImpl) identityRolesUpdated(ctx *boltz.PersistContext, policy *ServicePolicy) {
|
||||
roleIds, err := store.getEntityIdsForRoleSet(ctx.Bucket.Tx(), "identityRoles", policy.IdentityRoles, policy.Semantic, store.stores.identity.indexRoleAttributes, store.stores.identity)
|
||||
if !ctx.Bucket.SetError(err) {
|
||||
ctx.Bucket.SetError(store.identityCollection.SetLinks(ctx.Bucket.Tx(), policy.Id, roleIds))
|
||||
func (store *servicePolicyStoreImpl) identityRolesUpdated(persistCtx *boltz.PersistContext, policy *ServicePolicy) {
|
||||
ctx := &roleAttributeChangeContext{
|
||||
tx: persistCtx.Bucket.Tx(),
|
||||
rolesSymbol: store.symbolIdentityRoles,
|
||||
linkCollection: store.identityCollection,
|
||||
relatedLinkCollection: store.serviceCollection,
|
||||
ErrorHolder: persistCtx.Bucket,
|
||||
}
|
||||
|
||||
if policy.PolicyType == PolicyTypeDial {
|
||||
ctx.denormLinkCollection = store.stores.identity.dialServicesCollection
|
||||
} else {
|
||||
ctx.denormLinkCollection = store.stores.identity.bindServicesCollection
|
||||
}
|
||||
|
||||
EvaluatePolicy(ctx, policy, store.stores.identity.symbolRoleAttributes)
|
||||
}
|
||||
|
||||
func (store *servicePolicyStoreImpl) DeleteById(ctx boltz.MutateContext, id string) error {
|
||||
policy, err := store.LoadOneById(ctx.Tx(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
policy.IdentityRoles = nil
|
||||
policy.ServiceRoles = nil
|
||||
err = store.Update(ctx, policy, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure while clearing policy before delete: %w", err)
|
||||
}
|
||||
return store.BaseStore.DeleteById(ctx, id)
|
||||
}
|
||||
|
||||
func (store *servicePolicyStoreImpl) CheckIntegrity(tx *bbolt.Tx, fix bool, errorSink func(error)) error {
|
||||
ctx := &denormCheckCtx{
|
||||
tx: tx,
|
||||
sourceStore: store.stores.identity,
|
||||
targetStore: store.stores.edgeService,
|
||||
policyStore: store,
|
||||
sourceCollection: store.identityCollection,
|
||||
targetCollection: store.serviceCollection,
|
||||
targetDenormCollection: store.stores.identity.bindServicesCollection,
|
||||
errorSink: errorSink,
|
||||
repair: fix,
|
||||
policyFilter: func(policyId []byte) bool {
|
||||
policyType := PolicyTypeInvalid
|
||||
if result := boltz.FieldToInt32(store.symbolPolicyType.Eval(tx, policyId)); result != nil {
|
||||
policyType = *result
|
||||
}
|
||||
return policyType == PolicyTypeBind
|
||||
},
|
||||
}
|
||||
if err := validatePolicyDenormalization(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx = &denormCheckCtx{
|
||||
tx: tx,
|
||||
sourceStore: store.stores.identity,
|
||||
targetStore: store.stores.edgeService,
|
||||
policyStore: store,
|
||||
sourceCollection: store.identityCollection,
|
||||
targetCollection: store.serviceCollection,
|
||||
targetDenormCollection: store.stores.identity.dialServicesCollection,
|
||||
errorSink: errorSink,
|
||||
repair: fix,
|
||||
policyFilter: func(policyId []byte) bool {
|
||||
policyType := PolicyTypeInvalid
|
||||
if result := boltz.FieldToInt32(store.symbolPolicyType.Eval(tx, policyId)); result != nil {
|
||||
policyType = *result
|
||||
}
|
||||
return policyType == PolicyTypeDial
|
||||
},
|
||||
}
|
||||
|
||||
return validatePolicyDenormalization(ctx)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package persistence
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openziti/edge/eid"
|
||||
"github.com/openziti/fabric/controller/db"
|
||||
"github.com/openziti/foundation/util/errorz"
|
||||
"github.com/openziti/foundation/util/stringz"
|
||||
"go.etcd.io/bbolt"
|
||||
"sort"
|
||||
@@ -28,7 +30,7 @@ func (ctx *TestContext) testCreateServicePolicy(_ *testing.T) {
|
||||
|
||||
err := ctx.GetDb().View(func(tx *bbolt.Tx) error {
|
||||
ctx.ValidateBaseline(policy)
|
||||
ctx.Equal(0, len(ctx.stores.ServicePolicy.GetRelatedEntitiesIdList(tx, policy.Id, EntityTypeServices)))
|
||||
ctx.Equal(0, len(ctx.stores.ServicePolicy.GetRelatedEntitiesIdList(tx, policy.Id, db.EntityTypeServices)))
|
||||
ctx.Equal(0, len(ctx.stores.ServicePolicy.GetRelatedEntitiesIdList(tx, policy.Id, EntityTypeIdentities)))
|
||||
|
||||
testPolicy, err := ctx.stores.ServicePolicy.LoadOneByName(tx, policy.Name)
|
||||
@@ -207,7 +209,7 @@ func (ctx *TestContext) testServicePolicyRoleEvaluation(_ *testing.T) {
|
||||
policies := ctx.createServicePolicies(identityRoles, serviceRoles, identities, services, true)
|
||||
|
||||
for i := 0; i < 9; i++ {
|
||||
relatedServices := ctx.getRelatedIds(policies[i], EntityTypeServices)
|
||||
relatedServices := ctx.getRelatedIds(policies[i], db.EntityTypeServices)
|
||||
relatedIdentities := ctx.getRelatedIds(policies[i], EntityTypeIdentities)
|
||||
if i == 3 {
|
||||
ctx.Equal([]string{services[0].Id}, relatedServices)
|
||||
@@ -248,8 +250,7 @@ func (ctx *TestContext) testServicePolicyRoleEvaluation(_ *testing.T) {
|
||||
services = append(services, service)
|
||||
})
|
||||
|
||||
ctx.validateServicePolicyIdentities(identities, policies)
|
||||
ctx.validateServicePolicyServices(services, policies)
|
||||
ctx.validateServicePolicies(identities, services, policies)
|
||||
|
||||
for _, identity := range identities {
|
||||
ctx.RequireDelete(identity)
|
||||
@@ -278,8 +279,7 @@ func (ctx *TestContext) testServicePolicyRoleEvaluation(_ *testing.T) {
|
||||
services = append(services, service)
|
||||
})
|
||||
|
||||
ctx.validateServicePolicyIdentities(identities, policies)
|
||||
ctx.validateServicePolicyServices(services, policies)
|
||||
ctx.validateServicePolicies(identities, services, policies)
|
||||
|
||||
// ensure policies get cleaned up
|
||||
for _, policy := range policies {
|
||||
@@ -289,8 +289,7 @@ func (ctx *TestContext) testServicePolicyRoleEvaluation(_ *testing.T) {
|
||||
// test with policies created after identities/edge routers
|
||||
policies = ctx.createServicePolicies(identityRoles, serviceRoles, identities, services, true)
|
||||
|
||||
ctx.validateServicePolicyIdentities(identities, policies)
|
||||
ctx.validateServicePolicyServices(services, policies)
|
||||
ctx.validateServicePolicies(identities, services, policies)
|
||||
|
||||
for _, policy := range policies {
|
||||
ctx.RequireDelete(policy)
|
||||
@@ -299,8 +298,7 @@ func (ctx *TestContext) testServicePolicyRoleEvaluation(_ *testing.T) {
|
||||
// test with policies created after identities/edge routers and roles added after created
|
||||
policies = ctx.createServicePolicies(identityRoles, serviceRoles, identities, services, false)
|
||||
|
||||
ctx.validateServicePolicyIdentities(identities, policies)
|
||||
ctx.validateServicePolicyServices(services, policies)
|
||||
ctx.validateServicePolicies(identities, services, policies)
|
||||
|
||||
for _, identity := range identities {
|
||||
if len(identity.RoleAttributes) > 0 {
|
||||
@@ -326,8 +324,7 @@ func (ctx *TestContext) testServicePolicyRoleEvaluation(_ *testing.T) {
|
||||
ctx.RequireUpdate(policy)
|
||||
}
|
||||
|
||||
ctx.validateServicePolicyIdentities(identities, policies)
|
||||
ctx.validateServicePolicyServices(services, policies)
|
||||
ctx.validateServicePolicies(identities, services, policies)
|
||||
}
|
||||
|
||||
func (ctx *TestContext) createServicePolicies(identityRoles, serviceRoles []string, identities []*Identity, services []*EdgeService, oncreate bool) []*ServicePolicy {
|
||||
@@ -382,6 +379,12 @@ func (ctx *TestContext) createServicePolicies(identityRoles, serviceRoles []stri
|
||||
return policies
|
||||
}
|
||||
|
||||
func (ctx *TestContext) validateServicePolicies(identities []*Identity, services []*EdgeService, policies []*ServicePolicy) {
|
||||
ctx.validateServicePolicyIdentities(identities, policies)
|
||||
ctx.validateServicePolicyServices(services, policies)
|
||||
ctx.validateServicePolicyDenormalization()
|
||||
}
|
||||
|
||||
func (ctx *TestContext) validateServicePolicyIdentities(identities []*Identity, policies []*ServicePolicy) {
|
||||
for _, policy := range policies {
|
||||
count := 0
|
||||
@@ -407,7 +410,7 @@ func (ctx *TestContext) validateServicePolicyIdentities(identities []*Identity,
|
||||
func (ctx *TestContext) validateServicePolicyServices(services []*EdgeService, policies []*ServicePolicy) {
|
||||
for _, policy := range policies {
|
||||
count := 0
|
||||
relatedServices := ctx.getRelatedIds(policy, EntityTypeServices)
|
||||
relatedServices := ctx.getRelatedIds(policy, db.EntityTypeServices)
|
||||
for _, service := range services {
|
||||
relatedPolicies := ctx.getRelatedIds(service, EntityTypeServicePolicies)
|
||||
shouldContain := ctx.policyShouldMatch(policy.Semantic, policy.ServiceRoles, service, service.RoleAttributes)
|
||||
@@ -424,3 +427,13 @@ func (ctx *TestContext) validateServicePolicyServices(services []*EdgeService, p
|
||||
ctx.Equal(count, len(relatedServices))
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *TestContext) validateServicePolicyDenormalization() {
|
||||
errorHolder := &errorz.ErrorHolderImpl{}
|
||||
errorHolder.SetError(ctx.db.View(func(tx *bbolt.Tx) error {
|
||||
return ctx.stores.ServicePolicy.CheckIntegrity(tx, false, func(err error) {
|
||||
errorHolder.SetError(err)
|
||||
})
|
||||
}))
|
||||
ctx.NoError(errorHolder.GetError())
|
||||
}
|
||||
|
||||
@@ -159,12 +159,15 @@ func (ctx *TestContext) cleanupAll() {
|
||||
stores := []boltz.CrudStore{
|
||||
ctx.stores.Session,
|
||||
ctx.stores.ApiSession,
|
||||
ctx.stores.EdgeRouterPolicy,
|
||||
ctx.stores.Service,
|
||||
ctx.stores.EdgeService,
|
||||
ctx.stores.Identity,
|
||||
ctx.stores.EdgeRouter,
|
||||
ctx.stores.Config,
|
||||
ctx.stores.Identity,
|
||||
ctx.stores.EdgeRouterPolicy,
|
||||
ctx.stores.ServicePolicy,
|
||||
ctx.stores.ServiceEdgeRouterPolicy,
|
||||
}
|
||||
_ = ctx.GetDb().Update(func(tx *bbolt.Tx) error {
|
||||
mutateContext := boltz.NewMutateContext(tx)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package eid
|
||||
|
||||
import "github.com/teris-io/shortid"
|
||||
import (
|
||||
"github.com/teris-io/shortid"
|
||||
)
|
||||
|
||||
func New() string {
|
||||
id, _ := shortid.GetDefault().Generate()
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
@@ -31,10 +31,11 @@ require (
|
||||
github.com/michaelquigley/pfxlog v0.0.0-20190813191113-2be43bd0dccc
|
||||
github.com/miekg/dns v1.1.30
|
||||
github.com/mitchellh/mapstructure v1.3.3
|
||||
github.com/openziti/fabric v0.11.53
|
||||
github.com/openziti/foundation v0.11.16
|
||||
github.com/openziti/sdk-golang v0.13.26
|
||||
github.com/openziti/fabric v0.11.56
|
||||
github.com/openziti/foundation v0.11.15
|
||||
github.com/openziti/sdk-golang v0.13.24
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
github.com/spf13/cobra v0.0.7
|
||||
github.com/stretchr/testify v1.6.1
|
||||
|
||||
@@ -179,8 +179,6 @@ github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -284,14 +282,12 @@ github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/openziti/fabric v0.11.53 h1:OVT71Hmbgf5gqsO7fz4iwsyP/T/ZzUfgOD0Bp39z/3U=
|
||||
github.com/openziti/fabric v0.11.53/go.mod h1:d0EueCQD4CIC3TLAMdw0EoYb+GsmwPrZz3BwRgtU28c=
|
||||
github.com/openziti/foundation v0.11.11 h1:GPASO0aTtzuzwQbfvu1OMOPdAKzi/DTkxKJ0y2NTzXU=
|
||||
github.com/openziti/foundation v0.11.11/go.mod h1:Qdd7eCuTrpRwEeDbvYy89sFbEY4+J7OuX8UygxNBsvY=
|
||||
github.com/openziti/foundation v0.11.16 h1:8pWIEHGM6Stmc9QxW/R+saRzxkzDsTDlAcyO21qrh4k=
|
||||
github.com/openziti/foundation v0.11.16/go.mod h1:4Jgs3HGDiP1aVk5nNzXJqwJwWhrjOYy1F+4Xtxcgxvo=
|
||||
github.com/openziti/sdk-golang v0.13.26 h1:WOGdmkzqNTh/3dT1dU6Bg0yZ7IX3drAhQju3WO/+kJs=
|
||||
github.com/openziti/sdk-golang v0.13.26/go.mod h1:leIaRUZzYSt+AHnwXlzH/XSX0hOhcjjJ/rMDTkieV/8=
|
||||
github.com/openziti/fabric v0.11.56 h1:u76PWSwbIgATtlF3mp3J1ioDwCyr7geaHFr0mZaiqEk=
|
||||
github.com/openziti/fabric v0.11.56/go.mod h1:9hvRAwTCRJanUb+1iF/NQ6HFfull7maHa9tCqklsNxk=
|
||||
github.com/openziti/foundation v0.11.15 h1:+LpphupxZvABFT2lZoS+Nk5EPBe09aXE935Jyfb5sQ8=
|
||||
github.com/openziti/foundation v0.11.15/go.mod h1:4Jgs3HGDiP1aVk5nNzXJqwJwWhrjOYy1F+4Xtxcgxvo=
|
||||
github.com/openziti/sdk-golang v0.13.24 h1:gu9R7QaGwAjgBigbotZz8Qwe0jiCkCVSzMuBWNIc9FE=
|
||||
github.com/openziti/sdk-golang v0.13.24/go.mod h1:hPRLQHARp9OuRViYFsZ6icwn1LjudXQBJ/USswKQyTY=
|
||||
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6 h1:lNCW6THrCKBiJBpz8kbVGjC7MgdCGKwuvBgc7LoD6sw=
|
||||
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ v: 3
|
||||
# memory:
|
||||
# path: ctrl.memprof
|
||||
|
||||
db: testdata/ctrl.db
|
||||
db: testdata/${ZITI_TEST_DB}.db
|
||||
|
||||
identity:
|
||||
cert: testdata/ca/intermediate/certs/ctrl-client.cert.pem
|
||||
|
||||
@@ -323,6 +323,12 @@ func (request *authenticatedRequests) requireNewService(roleAttributes, configs
|
||||
return service
|
||||
}
|
||||
|
||||
func (request *authenticatedRequests) newServiceBulk(roleAttributes, configs []string) *service {
|
||||
service := request.testContext.newService(roleAttributes, configs)
|
||||
request.requireCreateEntity(service)
|
||||
return service
|
||||
}
|
||||
|
||||
func (request *authenticatedRequests) RequireNewServiceAccessibleToAll(terminatorStrategy string) *service {
|
||||
request.requireNewServicePolicy("Dial", s("#all"), s("#all"))
|
||||
request.requireNewServicePolicy("Bind", s("#all"), s("#all"))
|
||||
@@ -411,6 +417,16 @@ func (request *authenticatedRequests) requireCreateEntity(entity entity) string
|
||||
return id
|
||||
}
|
||||
|
||||
func (request *authenticatedRequests) createEntityBulk(entity entity) string {
|
||||
resp := request.createEntity(entity)
|
||||
if http.StatusCreated != resp.StatusCode() {
|
||||
panic(errors.Errorf("expected error code %v", resp.StatusCode()))
|
||||
}
|
||||
id := request.testContext.getEntityId(resp.Body())
|
||||
entity.setId(id)
|
||||
return id
|
||||
}
|
||||
|
||||
func (request *authenticatedRequests) requireDeleteEntity(entity entity) {
|
||||
resp := request.deleteEntityOfType(entity.getEntityType(), entity.getId())
|
||||
standardJsonResponseTests(resp, http.StatusOK, request.testContext.testing)
|
||||
|
||||
+17
-8
@@ -189,17 +189,26 @@ func (ctx *TestContext) NewClientComponents() (*resty.Client, *http.Client, *htt
|
||||
}
|
||||
|
||||
func (ctx *TestContext) StartServer() {
|
||||
ctx.StartServerFor("default", true)
|
||||
}
|
||||
|
||||
func (ctx *TestContext) StartServerFor(test string, clean bool) {
|
||||
log := pfxlog.Logger()
|
||||
_ = os.Mkdir("testdata", os.FileMode(0755))
|
||||
err := filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
|
||||
if err == nil {
|
||||
if !info.IsDir() && strings.HasPrefix(info.Name(), "ctrl.db") {
|
||||
pfxlog.Logger().Infof("removing test bolt file or backup: %v", path)
|
||||
err = os.Remove(path)
|
||||
if clean {
|
||||
err := filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
|
||||
if err == nil {
|
||||
if !info.IsDir() && strings.HasPrefix(info.Name(), test+".db") {
|
||||
pfxlog.Logger().Infof("removing test bolt file or backup: %v", path)
|
||||
err = os.Remove(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
return err
|
||||
})
|
||||
ctx.Req.NoError(err)
|
||||
}
|
||||
|
||||
err := os.Setenv("ZITI_TEST_DB", test)
|
||||
ctx.Req.NoError(err)
|
||||
|
||||
log.Info("loading config")
|
||||
|
||||
@@ -0,0 +1,552 @@
|
||||
// +build apitests,perftests
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/openziti/edge/controller/model"
|
||||
"github.com/openziti/edge/controller/persistence"
|
||||
"github.com/openziti/edge/eid"
|
||||
"github.com/openziti/fabric/controller/models"
|
||||
idloader "github.com/openziti/foundation/identity/identity"
|
||||
"github.com/openziti/foundation/util/errorz"
|
||||
"github.com/openziti/sdk-golang/ziti/config"
|
||||
"github.com/openziti/sdk-golang/ziti/edge"
|
||||
"github.com/openziti/sdk-golang/ziti/edge/api"
|
||||
"github.com/openziti/sdk-golang/ziti/sdkinfo"
|
||||
"github.com/rcrowley/go-metrics"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type modelPerf struct {
|
||||
*TestContext
|
||||
}
|
||||
|
||||
func Test_SpecLarge(t *testing.T) {
|
||||
spec := &perfScenarioSpec{
|
||||
name: "large",
|
||||
serviceCount: 200,
|
||||
identityCount: 100_000,
|
||||
edgeRouterCount: 500,
|
||||
servicePolicyCount: 250,
|
||||
edgeRouterPolicyCount: 250,
|
||||
serviceEdgeRouterPolicyCount: 100,
|
||||
}
|
||||
|
||||
p := &modelPerf{TestContext: NewTestContext(t)}
|
||||
p.runScenario(spec)
|
||||
}
|
||||
|
||||
func Test_SpecMedium(t *testing.T) {
|
||||
spec := &perfScenarioSpec{
|
||||
name: "medium",
|
||||
serviceCount: 100,
|
||||
identityCount: 5000,
|
||||
edgeRouterCount: 100,
|
||||
servicePolicyCount: 50,
|
||||
edgeRouterPolicyCount: 50,
|
||||
serviceEdgeRouterPolicyCount: 25,
|
||||
}
|
||||
|
||||
p := &modelPerf{TestContext: NewTestContext(t)}
|
||||
p.runScenario(spec)
|
||||
}
|
||||
|
||||
func Test_SpecSmall(t *testing.T) {
|
||||
spec := &perfScenarioSpec{
|
||||
name: "small",
|
||||
serviceCount: 20,
|
||||
identityCount: 100,
|
||||
edgeRouterCount: 10,
|
||||
servicePolicyCount: 10,
|
||||
edgeRouterPolicyCount: 10,
|
||||
serviceEdgeRouterPolicyCount: 10,
|
||||
}
|
||||
|
||||
p := &modelPerf{TestContext: NewTestContext(t)}
|
||||
p.runScenario(spec)
|
||||
}
|
||||
|
||||
func Test_SpecBaseline(t *testing.T) {
|
||||
spec := &perfScenarioSpec{
|
||||
name: "baseline",
|
||||
serviceCount: 1,
|
||||
identityCount: 1,
|
||||
edgeRouterCount: 1,
|
||||
servicePolicyCount: 1,
|
||||
edgeRouterPolicyCount: 1,
|
||||
serviceEdgeRouterPolicyCount: 1,
|
||||
}
|
||||
|
||||
p := &modelPerf{TestContext: NewTestContext(t)}
|
||||
p.runScenario(spec)
|
||||
}
|
||||
|
||||
type perfScenarioSpec struct {
|
||||
name string
|
||||
|
||||
serviceCount int
|
||||
identityCount int
|
||||
edgeRouterCount int
|
||||
|
||||
servicePolicyCount int
|
||||
edgeRouterPolicyCount int
|
||||
serviceEdgeRouterPolicyCount int
|
||||
|
||||
serviceAttrs [][]string
|
||||
serviceAttrSet []string
|
||||
identityAttrs [][]string
|
||||
identityAttrSet []string
|
||||
edgeRouterAttrs [][]string
|
||||
edgeRouterAttrSet []string
|
||||
|
||||
services []*model.Service
|
||||
identities []*model.Identity
|
||||
edgeRouters []*model.EdgeRouter
|
||||
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
func (spec *perfScenarioSpec) generateAllRoleAttributes() {
|
||||
spec.serviceAttrs, spec.serviceAttrSet = spec.generateRoleAttributes(spec.serviceCount)
|
||||
spec.identityAttrs, spec.identityAttrSet = spec.generateRoleAttributes(spec.identityCount)
|
||||
spec.edgeRouterAttrs, spec.edgeRouterAttrSet = spec.generateRoleAttributes(spec.edgeRouterCount)
|
||||
}
|
||||
|
||||
func (spec *perfScenarioSpec) generateRoleAttributes(count int) ([][]string, []string) {
|
||||
result := make([][]string, count)
|
||||
if count < 2 {
|
||||
return result, nil
|
||||
}
|
||||
var attrs []string
|
||||
|
||||
set := result[1:]
|
||||
|
||||
// assign 8 role attributes
|
||||
offset := 2
|
||||
index := 0
|
||||
|
||||
counts := []int{count / 2, count / 2, count / 3, count / 4, count / 5, count / 8, count / 10, count / 10}
|
||||
attr := eid.New()
|
||||
attrs = append(attrs, "#"+attr)
|
||||
for len(counts) > 0 {
|
||||
set[index] = append(set[index], attr)
|
||||
counts[0]--
|
||||
for len(counts) > 0 && counts[0] <= 0 {
|
||||
counts = counts[1:]
|
||||
if len(counts) > 0 && counts[0] > 0 {
|
||||
if len(counts) < 4 {
|
||||
offset++
|
||||
}
|
||||
attr = eid.New()
|
||||
attrs = append(attrs, "#"+attr)
|
||||
}
|
||||
}
|
||||
index = (index + offset) % len(set)
|
||||
}
|
||||
|
||||
return result, attrs
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) runScenario(spec *perfScenarioSpec) {
|
||||
shutdown := false
|
||||
defer func() {
|
||||
if !shutdown {
|
||||
ctx.Teardown()
|
||||
}
|
||||
}()
|
||||
|
||||
ctx.StartServer()
|
||||
ctx.RequireAdminLogin()
|
||||
|
||||
ctx.CreateEnrollAndStartEdgeRouter()
|
||||
|
||||
ctx.createScenario(spec)
|
||||
|
||||
stats := newPerfStats(ctx.TestContext, spec.config, spec.name, spec.services[0].Id, edge.SessionDial)
|
||||
stats.collectStats(100)
|
||||
|
||||
stats = newPerfStats(ctx.TestContext, spec.config, spec.name, spec.services[0].Id, edge.SessionDial)
|
||||
stats.collectStats(100)
|
||||
|
||||
ctx.Teardown()
|
||||
shutdown = true
|
||||
time.Sleep(time.Second)
|
||||
|
||||
stats.dumpStatsToStdOut()
|
||||
}
|
||||
|
||||
// For policies, want the following, using service policies as an example
|
||||
// a. 1 service policy which has 1 id and all services
|
||||
// b. 1 service policy which has all ids and 1 service
|
||||
// c. 1 identity with all policies (which must be the same as the one in line a)
|
||||
// d. 1 service with all policies (which must the same as the one in line b)
|
||||
//
|
||||
// randomly assign role attributes to 1/2, 1/4, 1/8, then do permutations of those with both anyof and allof
|
||||
// When more groups are needed, create more sets of 1/2, 1/4, 1/8 and 1/16
|
||||
func (ctx *modelPerf) createScenario(spec *perfScenarioSpec) {
|
||||
spec.generateAllRoleAttributes()
|
||||
ctx.createServices(spec)
|
||||
ctx.createIdentities(spec)
|
||||
ctx.createEdgeRouters(spec)
|
||||
ctx.createPolicies(spec)
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) createServices(spec *perfScenarioSpec) {
|
||||
serviceHandler := ctx.EdgeController.AppEnv.Handlers.EdgeService
|
||||
for i := 0; i < spec.serviceCount; i++ {
|
||||
id := eid.New()
|
||||
if i == 0 {
|
||||
id = "zzzzzzzzzzzzzz"
|
||||
}
|
||||
service := &model.Service{
|
||||
BaseEntity: models.BaseEntity{
|
||||
Id: id,
|
||||
},
|
||||
Name: id,
|
||||
RoleAttributes: spec.serviceAttrs[i],
|
||||
}
|
||||
_, err := serviceHandler.Create(service)
|
||||
ctx.Req.NoError(err)
|
||||
service.Id = id
|
||||
spec.services = append(spec.services, service)
|
||||
if (i+1)%100 == 0 {
|
||||
fmt.Printf("created %v services\n", i)
|
||||
}
|
||||
}
|
||||
fmt.Printf("finished creating %v services\n", spec.serviceCount)
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) createIdentities(spec *perfScenarioSpec) {
|
||||
identityHandler := ctx.EdgeController.AppEnv.Handlers.Identity
|
||||
for i := 0; i < spec.identityCount; i++ {
|
||||
id := eid.New()
|
||||
if i == 0 {
|
||||
id = "zzzzzzzzzzzzzz"
|
||||
}
|
||||
identity := &model.Identity{
|
||||
BaseEntity: models.BaseEntity{
|
||||
Id: id,
|
||||
},
|
||||
Name: id,
|
||||
IdentityTypeId: "User",
|
||||
IsDefaultAdmin: false,
|
||||
IsAdmin: false,
|
||||
RoleAttributes: spec.identityAttrs[i],
|
||||
}
|
||||
|
||||
enrollments := []*model.Enrollment{
|
||||
{
|
||||
BaseEntity: models.BaseEntity{},
|
||||
Method: persistence.MethodEnrollOtt,
|
||||
Token: uuid.New().String(),
|
||||
},
|
||||
}
|
||||
|
||||
_, _, err := identityHandler.CreateWithEnrollments(identity, enrollments)
|
||||
ctx.Req.NoError(err)
|
||||
spec.identities = append(spec.identities, identity)
|
||||
|
||||
if i == 0 {
|
||||
spec.config = ctx.EnrollIdentity(identity.Id)
|
||||
}
|
||||
if (i+1)%100 == 0 {
|
||||
fmt.Printf("created %v identities\n", i+1)
|
||||
}
|
||||
}
|
||||
fmt.Printf("finished creating %v identities\n", spec.identityCount)
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) createEdgeRouters(spec *perfScenarioSpec) {
|
||||
edgeRouterHandler := ctx.EdgeController.AppEnv.Handlers.EdgeRouter
|
||||
for i := 0; i < spec.edgeRouterCount; i++ {
|
||||
id := eid.New()
|
||||
if i == 0 {
|
||||
id = "zzzzzzzzzzzzzz"
|
||||
}
|
||||
edgeRouter := &model.EdgeRouter{
|
||||
BaseEntity: models.BaseEntity{Id: id},
|
||||
Name: id,
|
||||
RoleAttributes: spec.edgeRouterAttrs[i],
|
||||
IsVerified: false,
|
||||
}
|
||||
_, err := edgeRouterHandler.Create(edgeRouter)
|
||||
ctx.Req.NoError(err)
|
||||
spec.edgeRouters = append(spec.edgeRouters, edgeRouter)
|
||||
if (i+1)%100 == 0 {
|
||||
fmt.Printf("created %v edge routers\n", i+1)
|
||||
}
|
||||
}
|
||||
fmt.Printf("finished creating %v edge routers\n", spec.edgeRouterCount)
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) createPolicies(spec *perfScenarioSpec) {
|
||||
ctx.createServicePolicy("Dial", s("@"+spec.services[0].Id), s("#all"))
|
||||
ctx.createServicePolicy("Dial", s("#all"), s("@"+spec.identities[0].Id))
|
||||
|
||||
serviceRoles := ctx.firstNPermuations(spec.servicePolicyCount, spec.serviceAttrSet)
|
||||
identityRoles := ctx.firstNPermuations(spec.servicePolicyCount, spec.identityAttrSet)
|
||||
for i := 2; i < spec.servicePolicyCount; i++ {
|
||||
serviceRoles[i] = append(serviceRoles[i], "@"+spec.services[0].Id)
|
||||
identityRoles[i] = append(identityRoles[i], "@"+spec.identities[0].Id)
|
||||
ctx.createServicePolicy("Dial", serviceRoles[i], identityRoles[i])
|
||||
}
|
||||
|
||||
ctx.createEdgeRouterPolicy(s("@"+spec.edgeRouters[0].Id), s("#all"))
|
||||
ctx.createEdgeRouterPolicy(s("#all"), s("@"+spec.identities[0].Id))
|
||||
|
||||
edgeRouterRoles := ctx.firstNPermuations(spec.edgeRouterPolicyCount, spec.edgeRouterAttrSet)
|
||||
identityRoles = ctx.firstNPermuations(spec.edgeRouterPolicyCount, spec.identityAttrSet)
|
||||
for i := 2; i < spec.edgeRouterPolicyCount; i++ {
|
||||
edgeRouterRoles[i] = append(edgeRouterRoles[i], "@"+spec.edgeRouters[0].Id)
|
||||
identityRoles[i] = append(identityRoles[i], "@"+spec.identities[0].Id)
|
||||
ctx.createEdgeRouterPolicy(edgeRouterRoles[i], identityRoles[i])
|
||||
}
|
||||
|
||||
ctx.createServiceEdgeRouterPolicy(s("@"+spec.edgeRouters[0].Id), s("#all"))
|
||||
ctx.createServiceEdgeRouterPolicy(s("#all"), s("@"+spec.services[0].Id))
|
||||
|
||||
serviceRoles = ctx.firstNPermuations(spec.serviceEdgeRouterPolicyCount, spec.serviceAttrSet)
|
||||
edgeRouterRoles = ctx.firstNPermuations(spec.serviceEdgeRouterPolicyCount, spec.edgeRouterAttrSet)
|
||||
for i := 2; i < spec.serviceEdgeRouterPolicyCount; i++ {
|
||||
serviceRoles[i] = append(serviceRoles[i], "@"+spec.services[0].Id)
|
||||
edgeRouterRoles[i] = append(edgeRouterRoles[i], "@"+spec.edgeRouters[0].Id)
|
||||
ctx.createServiceEdgeRouterPolicy(edgeRouterRoles[i], serviceRoles[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) createServicePolicy(policyType string, identityRoles, serviceRoles []string) {
|
||||
policyHandler := ctx.EdgeController.AppEnv.Handlers.ServicePolicy
|
||||
id := eid.New()
|
||||
policy := &model.ServicePolicy{
|
||||
BaseEntity: models.BaseEntity{Id: id},
|
||||
Name: id,
|
||||
PolicyType: policyType,
|
||||
IdentityRoles: identityRoles,
|
||||
ServiceRoles: serviceRoles,
|
||||
}
|
||||
_, err := policyHandler.Create(policy)
|
||||
ctx.Req.NoError(err)
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) createEdgeRouterPolicy(identityRoles, edgeRouterRoles []string) {
|
||||
policyHandler := ctx.EdgeController.AppEnv.Handlers.EdgeRouterPolicy
|
||||
id := eid.New()
|
||||
policy := &model.EdgeRouterPolicy{
|
||||
BaseEntity: models.BaseEntity{Id: id},
|
||||
Name: id,
|
||||
IdentityRoles: identityRoles,
|
||||
EdgeRouterRoles: edgeRouterRoles,
|
||||
}
|
||||
_, err := policyHandler.Create(policy)
|
||||
ctx.Req.NoError(err)
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) createServiceEdgeRouterPolicy(edgeRouterRoles, serviceRoles []string) {
|
||||
policyHandler := ctx.EdgeController.AppEnv.Handlers.ServiceEdgeRouterPolicy
|
||||
id := eid.New()
|
||||
policy := &model.ServiceEdgeRouterPolicy{
|
||||
BaseEntity: models.BaseEntity{Id: id},
|
||||
Name: id,
|
||||
EdgeRouterRoles: edgeRouterRoles,
|
||||
ServiceRoles: serviceRoles,
|
||||
}
|
||||
_, err := policyHandler.Create(policy)
|
||||
ctx.Req.NoError(err)
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) firstNPermuations(n int, v []string) [][]string {
|
||||
var result [][]string
|
||||
ctx.permutations(v, func(strings []string) bool {
|
||||
result = append(result, strings)
|
||||
return len(result) < n
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) permutations(v []string, f func([]string) bool) {
|
||||
ctx.permutationWith([]string{}, v, f)
|
||||
}
|
||||
|
||||
func (ctx *modelPerf) permutationWith(base, v []string, f func([]string) bool) bool {
|
||||
for i := 0; i < len(v); i++ {
|
||||
var result []string
|
||||
if len(base) > 0 {
|
||||
result = append(result, base...)
|
||||
}
|
||||
result = append(result, v[i])
|
||||
if !f(result) {
|
||||
return false
|
||||
}
|
||||
if !ctx.permutationWith(result, v[i+1:], f) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func newHistogram() metrics.Histogram {
|
||||
return metrics.NewHistogram(metrics.NewExpDecaySample(128, 0.015))
|
||||
}
|
||||
|
||||
func newPerfStats(ctx *TestContext, config *config.Config, description string, serviceId string, sessionType edge.SessionType) *perfStats {
|
||||
zitiUrl, err := url.Parse(config.ZtAPI)
|
||||
ctx.Req.NoError(err)
|
||||
|
||||
id, err := idloader.LoadIdentity(config.ID)
|
||||
ctx.Req.NoError(err)
|
||||
|
||||
client, err := api.NewClient(zitiUrl, id.ClientTLSConfig())
|
||||
ctx.Req.NoError(err)
|
||||
|
||||
return &perfStats{
|
||||
TestContext: ctx,
|
||||
client: client,
|
||||
description: description,
|
||||
serviceId: serviceId,
|
||||
sessionType: sessionType,
|
||||
createApiSession: newHistogram(),
|
||||
refreshApiSession: newHistogram(),
|
||||
getServices: newHistogram(),
|
||||
createSession: newHistogram(),
|
||||
refreshSession: newHistogram(),
|
||||
}
|
||||
}
|
||||
|
||||
type perfStats struct {
|
||||
*TestContext
|
||||
description string
|
||||
serviceId string
|
||||
client api.Client
|
||||
sessionType edge.SessionType
|
||||
sessionId string
|
||||
createApiSession metrics.Histogram
|
||||
refreshApiSession metrics.Histogram
|
||||
getServices metrics.Histogram
|
||||
createSession metrics.Histogram
|
||||
refreshSession metrics.Histogram
|
||||
}
|
||||
|
||||
func (s *perfStats) dumpStatsToStdOut() {
|
||||
errWriter := &WriterWrapper{
|
||||
Writer: os.Stdout,
|
||||
}
|
||||
s.dumpStats(errWriter)
|
||||
s.Req.NoError(errWriter.GetError())
|
||||
}
|
||||
|
||||
func (s *perfStats) dumpStats(w ErrorWriter) {
|
||||
w.Println(s.description)
|
||||
w.Println("=======================================")
|
||||
s.logHistogram(w, "Create API Session", s.createApiSession)
|
||||
s.logHistogram(w, "Refresh API Session", s.refreshApiSession)
|
||||
s.logHistogram(w, "Get Services", s.getServices)
|
||||
s.logHistogram(w, "Create Session", s.createSession)
|
||||
s.logHistogram(w, "Refresh Session", s.refreshSession)
|
||||
}
|
||||
|
||||
func (s *perfStats) logHistogram(w ErrorWriter, name string, h metrics.Histogram) {
|
||||
w.Printf("%v:\n", name)
|
||||
w.Printf("\tMin : %vms\n", h.Min())
|
||||
w.Printf("\tMax : %vms\n", h.Max())
|
||||
w.Printf("\tMean : %vms\n", h.Mean())
|
||||
w.Printf("\t95th : %vms\n", h.Percentile(.95))
|
||||
}
|
||||
|
||||
func (s *perfStats) collectStats(iterations int) {
|
||||
s.repeat(iterations, s.timeCreateApiSession)
|
||||
s.repeat(iterations, s.timeRefreshApiSession)
|
||||
s.repeat(iterations, s.timeGetServices)
|
||||
s.repeat(iterations, s.timeCreateSession)
|
||||
s.repeat(iterations, s.timeRefreshSession)
|
||||
}
|
||||
|
||||
func (s *perfStats) repeat(n int, f func()) {
|
||||
for i := 0; i < n; i++ {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *perfStats) time(h metrics.Histogram, f func()) {
|
||||
start := time.Now()
|
||||
f()
|
||||
h.Update(time.Now().Sub(start).Milliseconds())
|
||||
}
|
||||
|
||||
func (s *perfStats) timeCreateApiSession() {
|
||||
s.time(s.createApiSession, func() {
|
||||
info, ok := sdkinfo.GetSdkInfo().(map[string]interface{})
|
||||
s.Req.True(ok)
|
||||
_, err := s.client.Login(info, []string{"all"})
|
||||
s.Req.NoError(err)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *perfStats) timeRefreshApiSession() {
|
||||
s.time(s.createApiSession, func() {
|
||||
_, err := s.client.Refresh()
|
||||
s.Req.NoError(err)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *perfStats) timeGetServices() {
|
||||
s.time(s.getServices, func() {
|
||||
_, err := s.client.GetServices()
|
||||
s.Req.NoError(err)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *perfStats) timeCreateSession() {
|
||||
s.time(s.createSession, func() {
|
||||
session, err := s.client.CreateSession(s.serviceId, s.sessionType)
|
||||
s.Req.NoError(err)
|
||||
s.sessionId = session.Id
|
||||
})
|
||||
}
|
||||
|
||||
func (s *perfStats) timeRefreshSession() {
|
||||
s.time(s.createSession, func() {
|
||||
_, err := s.client.RefreshSession(s.sessionId)
|
||||
s.Req.NoError(err)
|
||||
})
|
||||
}
|
||||
|
||||
type ErrorWriter interface {
|
||||
errorz.ErrorHolder
|
||||
Write([]byte) int
|
||||
Print(string)
|
||||
Println(string)
|
||||
Printf(s string, args ...interface{})
|
||||
}
|
||||
|
||||
type WriterWrapper struct {
|
||||
errorz.ErrorHolderImpl
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (w *WriterWrapper) Print(s string) {
|
||||
w.Write([]byte(s))
|
||||
}
|
||||
|
||||
func (w *WriterWrapper) Println(s string) {
|
||||
w.Write([]byte(s))
|
||||
w.Write([]byte("\n"))
|
||||
}
|
||||
|
||||
func (w *WriterWrapper) Printf(s string, args ...interface{}) {
|
||||
w.Write([]byte(fmt.Sprintf(s, args...)))
|
||||
}
|
||||
|
||||
func (w *WriterWrapper) Write(b []byte) int {
|
||||
if !w.HasError() {
|
||||
n, err := w.Writer.Write(b)
|
||||
w.SetError(err)
|
||||
return n
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
ctrl.db*
|
||||
*.db*
|
||||
edge-router/
|
||||
transit-router/
|
||||
transit-router/
|
||||
|
||||
Reference in New Issue
Block a user