Namespace
represents the sandbox environment of a team, and we wanted to restrict access to them so that teams could only play in their sandboxes. RoleBinding
to specific Namespace
and ClusterRole
with the right to edit. The YAML view will look like this: --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: kubernetes-team-1 namespace: team-1 subjects: - kind: Group name: kubernetes-team-1 apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: edit apiGroup: rbac.authorization.k8s.io
RoleBinding
manually, but after overcoming the mark in a hundred namespaces, this becomes a tedious task. This is where the Kubernetes operators help - they automate the creation of Kubernetes resources based on changes in resources. In our case, we want to create a RoleBinding
when creating a Namespace
.main
function, which performs the required setup to start the operator and then invokes the operator’s action: func main() { // STDOUT log.SetOutput(os.Stdout) sigs := make(chan os.Signal, 1) // stop := make(chan struct{}) // - // SIGTERM sigs signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) // Goroutines WaitGroup, // wg := &sync.WaitGroup{} runOutsideCluster := flag.Bool("run-outside-cluster", false, "Set this flag when running outside of the cluster.") flag.Parse() // clientset Kubernetes clientset, err := newClientSet(*runOutsideCluster) if err != nil { panic(err.Error()) } controller.NewNamespaceController(clientset).Run(stop, wg) <-sigs // ( ) log.Printf("Shutting down...") close(stop) // goroutines wg.Wait() // , }
WaitGroup
to correctly stop all goroutines before terminating the application.clientset
.NamespaceController
in which all our logic will be located.NamespaceController
mentioned: // NamespaceController Kubernetes API // RoleBinding namespace. type NamespaceController struct { namespaceInformer cache.SharedIndexInformer kclient *kubernetes.Clientset } // NewNamespaceController NewNamespaceController func NewNamespaceController(kclient *kubernetes.Clientset) *NamespaceController { namespaceWatcher := &NamespaceController{} // Namespaces namespaceInformer := cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { return kclient.Core().Namespaces().List(options) }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { return kclient.Core().Namespaces().Watch(options) }, }, &v1.Namespace{}, 3*time.Minute, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, ) namespaceInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: namespaceWatcher.createRoleBinding, }) namespaceWatcher.kclient = kclient namespaceWatcher.namespaceInformer = namespaceInformer return namespaceWatcher }
SharedIndexInformer
, which will effectively (using the cache) wait for changes in namespaces (for more information about informers, see the article “ How does the Kubernetes scheduler actually work? ” - approx. Transl. ) . After that, we connect the EventHandler
to the informer, so that when the namespace is added, the createRoleBinding
function is createRoleBinding
.createRoleBinding
function: func (c *NamespaceController) createRoleBinding(obj interface{}) { namespaceObj := obj.(*v1.Namespace) namespaceName := namespaceObj.Name roleBinding := &v1beta1.RoleBinding{ TypeMeta: metav1.TypeMeta{ Kind: "RoleBinding", APIVersion: "rbac.authorization.k8s.io/v1beta1", }, ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("ad-kubernetes-%s", namespaceName), Namespace: namespaceName, }, Subjects: []v1beta1.Subject{ v1beta1.Subject{ Kind: "Group", Name: fmt.Sprintf("ad-kubernetes-%s", namespaceName), }, }, RoleRef: v1beta1.RoleRef{ APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: "edit", }, } _, err := c.kclient.Rbac().RoleBindings(namespaceName).Create(roleBinding) if err != nil { log.Println(fmt.Sprintf("Failed to create Role Binding: %s", err.Error())) } else { log.Println(fmt.Sprintf("Created AD RoleBinding for Namespace: %s", roleBinding.Name)) } }
obj
and convert it to a Namespace
object. Then we define the RoleBinding
based on the YAML file mentioned at the beginning, using the provided Namespace
object and creating the RoleBinding
. Finally, we log whether the creation was successful.Run
: // Run // . func (c *NamespaceController) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) { // , defer wg.Done() // wait group, .. goroutine wg.Add(1) // goroutine go c.namespaceInformer.Run(stopCh) // - <-stopCh }
WaitGroup
to run goroutine and then call the namespaceInformer
that was previously defined. When the stop signal arrives, it will terminate the function, tell WaitGroup
that it is no longer running, and this function will complete its work.RoleBinding
when Namespace
appears in the Kubernetes cluster is ready.Source: https://habr.com/ru/post/337698/
All Articles