Pengfei Ni 681f75ddd0 Add LICENSE for files
This PR adds license for all files. It also adds a script
hack/verify-boilerplate.sh for checking whether license is
set correctly.

Change-Id: Ib691187f3128f6787510aa914d5c0e01e8e1b22f
Signed-off-by: Pengfei Ni <feiskyer@gmail.com>
2017-07-28 16:09:52 +08:00

296 lines
7.4 KiB
Go

/*
Copyright (c) 2017 OpenStack Foundation.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rbacmanager
import (
"fmt"
"time"
crv1 "git.openstack.org/openstack/stackube/pkg/apis/v1"
"git.openstack.org/openstack/stackube/pkg/auth-controller/rbacmanager/rbac"
crdClient "git.openstack.org/openstack/stackube/pkg/kubecrd"
"git.openstack.org/openstack/stackube/pkg/util"
"github.com/golang/glog"
"k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
)
const (
resyncPeriod = 5 * time.Minute
)
type Controller struct {
kclient *kubernetes.Clientset
nsInf cache.SharedIndexInformer
queue workqueue.RateLimitingInterface
kubeCRDClient *crdClient.CRDClient
userCIDR string
userGateway string
}
// New creates a new RBAC controller.
func NewRBACController(kubeClient *kubernetes.Clientset, kubeCRDClient *crdClient.CRDClient, userCIDR string,
userGateway string) (*Controller, error) {
o := &Controller{
kclient: kubeClient,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "rbacmanager"),
kubeCRDClient: kubeCRDClient,
userCIDR: userCIDR,
userGateway: userGateway,
}
o.nsInf = cache.NewSharedIndexInformer(
cache.NewListWatchFromClient(o.kclient.Core().RESTClient(), "namespaces", v1.NamespaceAll, fields.Everything()),
&v1.Namespace{}, resyncPeriod, cache.Indexers{},
)
o.nsInf.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: o.handleNamespaceAdd,
DeleteFunc: o.handleNamespaceDelete,
UpdateFunc: o.handleNamespaceUpdate,
})
return o, nil
}
// Run the controller.
func (c *Controller) Run(stopc <-chan struct{}) error {
defer c.queue.ShutDown()
glog.V(4).Info("Starting rbac manager")
go c.worker()
go c.nsInf.Run(stopc)
<-stopc
return nil
}
func (c *Controller) keyFunc(obj interface{}) (string, bool) {
k, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
if err != nil {
glog.Errorf("Creating key failed: %v", err)
return k, false
}
return k, true
}
// enqueue adds a key to the queue. If obj is a key already it gets added directly.
// Otherwise, the key is extracted via keyFunc.
func (c *Controller) enqueue(obj interface{}) {
if obj == nil {
return
}
key, ok := obj.(string)
if !ok {
key, ok = c.keyFunc(obj)
if !ok {
return
}
}
c.queue.Add(key)
}
// worker runs a worker thread that just dequeues items, processes them, and marks them done.
// It enforces that the syncHandler is never invoked concurrently with the same key.
func (c *Controller) worker() {
for c.processNextWorkItem() {
}
}
func (c *Controller) processNextWorkItem() bool {
key, quit := c.queue.Get()
if quit {
return false
}
defer c.queue.Done(key)
err := c.sync(key.(string))
if err == nil {
c.queue.Forget(key)
return true
}
utilruntime.HandleError(fmt.Errorf("Sync %q failed: %v", key, err))
c.queue.AddRateLimited(key)
return true
}
func (c *Controller) handleNamespaceAdd(obj interface{}) {
key, ok := c.keyFunc(obj)
if !ok {
return
}
// check if this is a system reserved namespace
if util.IsSystemNamespace(key) {
if err := c.initSystemReservedTenantNetwork(); err != nil {
glog.Error(err)
return
}
} else {
if err := c.createNetworkForTenant(key); err != nil {
glog.Error(err)
return
}
}
glog.V(4).Infof("Added namespace %s", key)
c.enqueue(key)
}
// createNetworkForTenant automatically create network for given non-system tenant
func (c *Controller) createNetworkForTenant(namespace string) error {
network := &crv1.Network{
ObjectMeta: metav1.ObjectMeta{
// use the namespace name as network
Name: namespace,
Namespace: namespace,
},
Spec: crv1.NetworkSpec{
CIDR: c.userCIDR,
Gateway: c.userGateway,
},
}
// network controller will always check if Tenant is ready so we will not wait here
if err := c.kubeCRDClient.AddNetwork(network); err != nil {
return err
}
return nil
}
// initSystemReservedTenantNetwork automatically create tenant network for system namespace
func (c *Controller) initSystemReservedTenantNetwork() error {
tenant := &crv1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: util.SystemTenant,
// always add tenant to system namespace
Namespace: util.SystemTenant,
},
Spec: crv1.TenantSpec{
UserName: util.SystemTenant,
Password: util.SystemPassword,
},
}
if err := c.kubeCRDClient.AddTenant(tenant); err != nil {
return err
}
// NOTE(harry): we do not support update Network, so although configurable,
// user can not update CIDR by changing the configuration, unless manually delete
// that system network. We may need to document this.
network := &crv1.Network{
ObjectMeta: metav1.ObjectMeta{
Name: util.SystemNetwork,
Namespace: util.SystemTenant,
},
Spec: crv1.NetworkSpec{
CIDR: c.userCIDR,
Gateway: c.userGateway,
},
}
// network controller will always check if Tenant is ready so we will not wait here
if err := c.kubeCRDClient.AddNetwork(network); err != nil {
return err
}
return nil
}
func (c *Controller) handleNamespaceDelete(obj interface{}) {
key, ok := c.keyFunc(obj)
if !ok {
return
}
glog.V(4).Infof("Deleted namespace %s", key)
c.enqueue(key)
}
func (c *Controller) handleNamespaceUpdate(old, cur interface{}) {
oldns := old.(*v1.Namespace)
curns := cur.(*v1.Namespace)
if oldns.ResourceVersion == curns.ResourceVersion {
return
}
key, ok := c.keyFunc(cur)
if !ok {
return
}
glog.V(4).Infof("Updated namespace %s", key)
c.enqueue(key)
}
func (c *Controller) sync(key string) error {
obj, exists, err := c.nsInf.GetIndexer().GetByKey(key)
if err != nil {
return err
}
if !exists {
return nil
}
ns := obj.(*v1.Namespace)
glog.V(4).Infof("Sync RBAC %s", key)
err = c.syncRbac(ns)
if err != nil {
return err
}
return nil
}
func (c *Controller) syncRbac(ns *v1.Namespace) error {
if ns.DeletionTimestamp != nil {
return nil
}
tenant, ok := ns.Labels["tenant"]
if !ok {
return nil
}
rbacClient := c.kclient.Rbac()
// Create role for tenant
role := rbac.GenerateRoleByNamespace(ns.Name)
_, err := rbacClient.Roles(ns.Name).Create(role)
if err != nil && !apierrors.IsAlreadyExists(err) {
glog.Errorf("Failed create default-role in namespace %s for tenant %s: %v", ns.Name, tenant, err)
return err
}
glog.V(4).Infof("Created default-role in namespace %s for tenant %s", ns.Name, tenant)
// Create rolebinding for tenant
roleBinding := rbac.GenerateRoleBinding(ns.Name, tenant)
_, err = rbacClient.RoleBindings(ns.Name).Create(roleBinding)
if err != nil && !apierrors.IsAlreadyExists(err) {
glog.Errorf("Failed create %s-rolebindings in namespace %s for tenant %s: %v", tenant, ns.Name, tenant, err)
return err
}
glog.V(4).Infof("Created %s-rolebindings in namespace %s for tenant %s", tenant, ns.Name, tenant)
return nil
}