
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