Skip to content

Commit eb75981

Browse files
committed
create policy cache
1 parent 7eb851c commit eb75981

File tree

6 files changed

+288
-22
lines changed

6 files changed

+288
-22
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package cache
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
7+
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
8+
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
9+
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
10+
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
11+
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
12+
13+
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
14+
policyregistry "github.com/openshift/origin/pkg/authorization/registry/policy"
15+
bindingregistry "github.com/openshift/origin/pkg/authorization/registry/policybinding"
16+
)
17+
18+
// PolicyCache maintains a cache of PolicyRules
19+
type PolicyCache struct {
20+
policyBindingIndexer cache.Indexer
21+
policyIndexer cache.Indexer
22+
23+
bindingRegistry bindingregistry.Registry
24+
policyRegistry policyregistry.Registry
25+
26+
keyFunc cache.KeyFunc
27+
}
28+
29+
// TODO: Eliminate listWatch when this merges upstream: https://github.com/GoogleCloudPlatform/kubernetes/pull/4453
30+
type listFunc func() (runtime.Object, error)
31+
type watchFunc func(resourceVersion string) (watch.Interface, error)
32+
type listWatch struct {
33+
listFunc listFunc
34+
watchFunc watchFunc
35+
}
36+
37+
func (lw *listWatch) List() (runtime.Object, error) {
38+
return lw.listFunc()
39+
}
40+
41+
func (lw *listWatch) Watch(resourceVersion string) (watch.Interface, error) {
42+
return lw.watchFunc(resourceVersion)
43+
}
44+
45+
// NewPolicyCache creates a new PolicyCache. You cannot use a normal client, because you don't want policy guarding the policy from the authorizer
46+
func NewPolicyCache(bindingRegistry bindingregistry.Registry, policyRegistry policyregistry.Registry) *PolicyCache {
47+
result := &PolicyCache{
48+
policyIndexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{"namespace": cache.MetaNamespaceIndexFunc}),
49+
policyBindingIndexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{"namespace": cache.MetaNamespaceIndexFunc}),
50+
51+
keyFunc: cache.MetaNamespaceKeyFunc,
52+
53+
bindingRegistry: bindingRegistry,
54+
policyRegistry: policyRegistry,
55+
}
56+
return result
57+
}
58+
59+
// Run begins watching and synchronizing the cache
60+
func (c *PolicyCache) Run() {
61+
policyBindingReflector, policyReflector := c.configureReflectors()
62+
63+
policyBindingReflector.Run()
64+
policyReflector.Run()
65+
}
66+
67+
// RunUntil starts a watch and handles watch events. Will restart the watch if it is closed.
68+
// RunUntil starts a goroutine and returns immediately. It will exit when stopCh is closed.
69+
func (c *PolicyCache) RunUntil(bindingStopCh <-chan struct{}, policyStopCh <-chan struct{}) {
70+
policyBindingReflector, policyReflector := c.configureReflectors()
71+
72+
policyBindingReflector.RunUntil(bindingStopCh)
73+
policyReflector.RunUntil(policyStopCh)
74+
}
75+
76+
func (c *PolicyCache) configureReflectors() (*cache.Reflector, *cache.Reflector) {
77+
ctx := kapi.WithNamespace(kapi.NewContext(), kapi.NamespaceAll)
78+
79+
policyBindingReflector := cache.NewReflector(
80+
&listWatch{
81+
listFunc: func() (runtime.Object, error) {
82+
return c.bindingRegistry.ListPolicyBindings(ctx, labels.Everything(), labels.Everything())
83+
},
84+
watchFunc: func(resourceVersion string) (watch.Interface, error) {
85+
return c.bindingRegistry.WatchPolicyBindings(ctx, labels.Everything(), labels.Everything(), resourceVersion)
86+
},
87+
},
88+
&authorizationapi.PolicyBinding{},
89+
c.policyBindingIndexer,
90+
)
91+
92+
policyReflector := cache.NewReflector(
93+
&listWatch{
94+
listFunc: func() (runtime.Object, error) {
95+
return c.policyRegistry.ListPolicies(ctx, labels.Everything(), labels.Everything())
96+
},
97+
watchFunc: func(resourceVersion string) (watch.Interface, error) {
98+
return c.policyRegistry.WatchPolicies(ctx, labels.Everything(), labels.Everything(), resourceVersion)
99+
},
100+
},
101+
&authorizationapi.Policy{},
102+
c.policyIndexer,
103+
)
104+
105+
return policyBindingReflector, policyReflector
106+
}
107+
108+
// GetPolicy retrieves a specific policy. It conforms to rulevalidation.PolicyGetter.
109+
func (c *PolicyCache) GetPolicy(ctx kapi.Context, name string) (*authorizationapi.Policy, error) {
110+
namespace, exists := kapi.NamespaceFrom(ctx)
111+
if !exists {
112+
return nil, errors.New("no namespace found")
113+
}
114+
115+
keyObj := &authorizationapi.Policy{ObjectMeta: kapi.ObjectMeta{Namespace: namespace, Name: name}}
116+
key, _ := c.keyFunc(keyObj)
117+
118+
policy, exists, err := c.policyIndexer.GetByKey(key)
119+
if err != nil {
120+
return nil, err
121+
}
122+
if !exists {
123+
return nil, fmt.Errorf("%v not found", key)
124+
}
125+
126+
return policy.(*authorizationapi.Policy), nil
127+
}
128+
129+
// ListPolicyBindings obtains list of policyBindings that match a selector. It conforms to rulevalidation.BindingLister
130+
func (c *PolicyCache) ListPolicyBindings(ctx kapi.Context, labels, fields labels.Selector) (*authorizationapi.PolicyBindingList, error) {
131+
namespace, exists := kapi.NamespaceFrom(ctx)
132+
if !exists {
133+
return nil, errors.New("no namespace found")
134+
}
135+
136+
bindings, err := c.policyBindingIndexer.Index("namespace", &authorizationapi.PolicyBinding{ObjectMeta: kapi.ObjectMeta{Namespace: namespace}})
137+
if err != nil {
138+
return nil, err
139+
}
140+
141+
ret := &authorizationapi.PolicyBindingList{
142+
Items: make([]authorizationapi.PolicyBinding, 0, len(bindings)),
143+
}
144+
for i := range bindings {
145+
ret.Items = append(ret.Items, *bindings[i].(*authorizationapi.PolicyBinding))
146+
}
147+
148+
return ret, nil
149+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package cache
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
8+
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
9+
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
10+
11+
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
12+
testregistry "github.com/openshift/origin/pkg/authorization/registry/test"
13+
)
14+
15+
func TestPolicyGet(t *testing.T) {
16+
policyStop := make(chan struct{})
17+
bindingStop := make(chan struct{})
18+
defer close(policyStop)
19+
defer close(bindingStop)
20+
21+
policyRegistry := testregistry.NewPolicyRegistry(testPolicies(), nil)
22+
bindingRegistry := testregistry.NewPolicyBindingRegistry(testBindings(), nil)
23+
24+
policyCache := NewPolicyCache(bindingRegistry, policyRegistry)
25+
policyCache.RunUntil(bindingStop, policyStop)
26+
27+
testStop := make(chan struct{})
28+
29+
util.Until(func() {
30+
ctx := kapi.WithNamespace(kapi.NewContext(), "mallet")
31+
policy, policyErr := policyCache.GetPolicy(ctx, authorizationapi.PolicyName)
32+
33+
bindings, bindingErr := policyCache.ListPolicyBindings(ctx, labels.Everything(), labels.Everything())
34+
if (policyErr == nil) && (bindingErr == nil) && (policy != nil) && (len(bindings.Items) == 1) {
35+
close(testStop)
36+
}
37+
38+
}, 1*time.Millisecond, testStop)
39+
}
40+
41+
func testPolicies() []authorizationapi.Policy {
42+
return []authorizationapi.Policy{
43+
{
44+
ObjectMeta: kapi.ObjectMeta{
45+
Name: authorizationapi.PolicyName,
46+
Namespace: "mallet",
47+
},
48+
Roles: map[string]authorizationapi.Role{},
49+
}}
50+
}
51+
func testBindings() []authorizationapi.PolicyBinding {
52+
return []authorizationapi.PolicyBinding{
53+
{
54+
ObjectMeta: kapi.ObjectMeta{
55+
Name: "mallet",
56+
Namespace: "mallet",
57+
},
58+
RoleBindings: map[string]authorizationapi.RoleBinding{
59+
"projectAdmins": {
60+
ObjectMeta: kapi.ObjectMeta{
61+
Name: "projectAdmins",
62+
Namespace: "mallet",
63+
},
64+
RoleRef: kapi.ObjectReference{
65+
Name: "admin",
66+
Namespace: "mallet",
67+
},
68+
Users: util.NewStringSet("Matthew"),
69+
},
70+
"viewers": {
71+
ObjectMeta: kapi.ObjectMeta{
72+
Name: "viewers",
73+
Namespace: "mallet",
74+
},
75+
RoleRef: kapi.ObjectReference{
76+
Name: "view",
77+
Namespace: "mallet",
78+
},
79+
Users: util.NewStringSet("Victor"),
80+
},
81+
"editors": {
82+
ObjectMeta: kapi.ObjectMeta{
83+
Name: "editors",
84+
Namespace: "mallet",
85+
},
86+
RoleRef: kapi.ObjectReference{
87+
Name: "edit",
88+
Namespace: "mallet",
89+
},
90+
Users: util.NewStringSet("Edgar"),
91+
},
92+
},
93+
},
94+
}
95+
}

pkg/authorization/registry/test/policy.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,21 @@ func (r *PolicyRegistry) ListPolicies(ctx kapi.Context, labels, fields klabels.S
3434
}
3535

3636
namespace := kapi.NamespaceValue(ctx)
37-
if len(namespace) == 0 {
38-
return nil, errors.New("invalid request. Namespace parameter required.")
39-
}
40-
4137
list := make([]authorizationapi.Policy, 0)
42-
if namespacedPolicies, ok := r.Policies[namespace]; ok {
43-
for _, curr := range namespacedPolicies {
44-
list = append(list, curr)
38+
39+
if namespace == kapi.NamespaceAll {
40+
for _, curr := range r.Policies {
41+
for _, policy := range curr {
42+
list = append(list, policy)
43+
}
4544
}
4645

46+
} else {
47+
if namespacedPolicies, ok := r.Policies[namespace]; ok {
48+
for _, curr := range namespacedPolicies {
49+
list = append(list, curr)
50+
}
51+
}
4752
}
4853

4954
return &authorizationapi.PolicyList{

pkg/authorization/registry/test/policybinding.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,21 @@ func (r *PolicyBindingRegistry) ListPolicyBindings(ctx kapi.Context, labels, fie
3434
}
3535

3636
namespace := kapi.NamespaceValue(ctx)
37-
if len(namespace) == 0 {
38-
return nil, errors.New("invalid request. Namespace parameter required.")
39-
}
40-
4137
list := make([]authorizationapi.PolicyBinding, 0)
42-
if namespacedBindings, ok := r.PolicyBindings[namespace]; ok {
43-
for _, curr := range namespacedBindings {
44-
list = append(list, curr)
38+
39+
if namespace == kapi.NamespaceAll {
40+
for _, curr := range r.PolicyBindings {
41+
for _, binding := range curr {
42+
list = append(list, binding)
43+
}
4544
}
4645

46+
} else {
47+
if namespacedBindings, ok := r.PolicyBindings[namespace]; ok {
48+
for _, curr := range namespacedBindings {
49+
list = append(list, curr)
50+
}
51+
}
4752
}
4853

4954
return &authorizationapi.PolicyBindingList{

pkg/cmd/server/origin/master.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import (
7575

7676
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
7777
"github.com/openshift/origin/pkg/authorization/authorizer"
78+
policycache "github.com/openshift/origin/pkg/authorization/cache"
7879
authorizationetcd "github.com/openshift/origin/pkg/authorization/registry/etcd"
7980
policyregistry "github.com/openshift/origin/pkg/authorization/registry/policy"
8081
policybindingregistry "github.com/openshift/origin/pkg/authorization/registry/policybinding"
@@ -115,6 +116,7 @@ type MasterConfig struct {
115116
AuthorizationAttributeBuilder authorizer.AuthorizationAttributeBuilder
116117
MasterAuthorizationNamespace string
117118

119+
PolicyCache *policycache.PolicyCache
118120
ProjectAuthorizationCache *projectauth.AuthorizationCache
119121

120122
// Map requests to contexts
@@ -559,6 +561,11 @@ func (c *MasterConfig) RunProjectAuthorizationCache() {
559561
c.ProjectAuthorizationCache.Run(period)
560562
}
561563

564+
// RunPolicyCache starts the policy cache
565+
func (c *MasterConfig) RunPolicyCache() {
566+
c.PolicyCache.Run()
567+
}
568+
562569
// RunAssetServer starts the asset server for the OpenShift UI.
563570
func (c *MasterConfig) RunAssetServer() {
564571
// TODO use version.Get().GitCommit as an etag cache header

pkg/cmd/server/start.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"github.com/openshift/origin/pkg/auth/authenticator/request/unionrequest"
3939
"github.com/openshift/origin/pkg/auth/authenticator/request/x509request"
4040
"github.com/openshift/origin/pkg/authorization/authorizer"
41+
policycache "github.com/openshift/origin/pkg/authorization/cache"
4142
authorizationetcd "github.com/openshift/origin/pkg/authorization/registry/etcd"
4243
"github.com/openshift/origin/pkg/authorization/rulevalidation"
4344
projectauth "github.com/openshift/origin/pkg/project/auth"
@@ -367,11 +368,9 @@ func start(cfg *config, args []string) error {
367368

368369
EtcdHelper: etcdHelper,
369370

370-
AdmissionControl: admit.NewAlwaysAdmit(),
371-
Authorizer: newAuthorizer(etcdHelper, masterAuthorizationNamespace),
372-
AuthorizationAttributeBuilder: newAuthorizationAttributeBuilder(requestContextMapper),
373-
MasterAuthorizationNamespace: masterAuthorizationNamespace,
374-
RequestContextMapper: requestContextMapper,
371+
AdmissionControl: admit.NewAlwaysAdmit(),
372+
MasterAuthorizationNamespace: masterAuthorizationNamespace,
373+
RequestContextMapper: requestContextMapper,
375374

376375
ImageFor: imageResolverFn,
377376
}
@@ -502,6 +501,13 @@ func start(cfg *config, args []string) error {
502501

503502
osmaster.BuildClients()
504503

504+
authorizationEtcd := authorizationetcd.New(etcdHelper)
505+
osmaster.PolicyCache = policycache.NewPolicyCache(authorizationEtcd, authorizationEtcd)
506+
osmaster.Authorizer = newAuthorizer(osmaster.PolicyCache, masterAuthorizationNamespace)
507+
osmaster.AuthorizationAttributeBuilder = newAuthorizationAttributeBuilder(requestContextMapper)
508+
// the policy cache must start before you attempt to start any other components
509+
osmaster.RunPolicyCache()
510+
505511
osmaster.ProjectAuthorizationCache = projectauth.NewAuthorizationCache(
506512
projectauth.NewReviewer(osmaster.PolicyClient()),
507513
osmaster.KubeClient().Namespaces(),
@@ -653,9 +659,8 @@ func start(cfg *config, args []string) error {
653659
return nil
654660
}
655661

656-
func newAuthorizer(etcdHelper tools.EtcdHelper, masterAuthorizationNamespace string) authorizer.Authorizer {
657-
authorizationEtcd := authorizationetcd.New(etcdHelper)
658-
authorizer := authorizer.NewAuthorizer(masterAuthorizationNamespace, rulevalidation.NewDefaultRuleResolver(authorizationEtcd, authorizationEtcd))
662+
func newAuthorizer(policyCache *policycache.PolicyCache, masterAuthorizationNamespace string) authorizer.Authorizer {
663+
authorizer := authorizer.NewAuthorizer(masterAuthorizationNamespace, rulevalidation.NewDefaultRuleResolver(policyCache, policyCache))
659664
return authorizer
660665
}
661666

0 commit comments

Comments
 (0)