ПишСм ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π° для Kubernetes Π½Π° Golang

ΠŸΡ€ΠΈΠΌ. ΠΏΠ΅Ρ€Π΅Π².: ΠžΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Ρ‹ (operators) β€” это Π²ΡΠΏΠΎΠΌΠΎΠ³Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ ПО для Kubernetes, ΠΏΡ€ΠΈΠ·Π²Π°Π½Π½ΠΎΠ΅ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ Ρ€ΡƒΡ‚ΠΈΠ½Π½Ρ‹Ρ… дСйствий Π½Π°Π΄ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°ΠΌΠΈ кластСра ΠΏΡ€ΠΈ ΠΎΠΏΡ€Π΅Π΄Π΅Π»Ρ‘Π½Π½Ρ‹Ρ… событиях. ΠœΡ‹ ΡƒΠΆΠ΅ писали ΠΎΠ± ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π°Ρ… Π² этой ΡΡ‚Π°Ρ‚ΡŒΠ΅, Π³Π΄Π΅ рассказывали ΠΎΠ± ΠΎΡΠ½ΠΎΠ²ΠΎΠΏΠΎΠ»Π°Π³Π°ΡŽΡ‰ΠΈΡ… идСях ΠΈ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠ°Ρ… ΠΈΡ… Ρ€Π°Π±ΠΎΡ‚Ρ‹. Но Ссли Ρ‚ΠΎΡ‚ ΠΌΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π» Π±Ρ‹Π» скорСС взглядом со стороны эксплуатации Π³ΠΎΡ‚ΠΎΠ²Ρ‹Ρ… ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² для Kubernetes, Ρ‚ΠΎ ΠΏΡ€Π΅Π΄Π»Π°Π³Π°Π΅ΠΌΡ‹ΠΉ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄ Π½ΠΎΠ²ΠΎΠΉ ΡΡ‚Π°Ρ‚ΡŒΠΈ β€” это ΡƒΠΆΠ΅ Π²ΠΈΠ΄Π΅Π½ΠΈΠ΅ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°/DevOps-ΠΈΠ½ΠΆΠ΅Π½Π΅Ρ€Π°, ΠΎΠ·Π°Π΄Π°Ρ‡Π΅Π½Π½ΠΎΠ³ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠ΅ΠΉ Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π°.

ПишСм ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π° для Kubernetes Π½Π° Golang

Π­Ρ‚ΠΎΡ‚ пост с ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠΌ ΠΈΠ· Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΉ ΠΆΠΈΠ·Π½ΠΈ я Ρ€Π΅ΡˆΠΈΠ» Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ послС своих ΠΏΠΎΠΏΡ‹Ρ‚ΠΎΠΊ Π½Π°ΠΉΡ‚ΠΈ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΡŽ ΠΏΠΎ созданию ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π° для Kubernetes, ΠΏΡ€ΠΎΡˆΠ΅Π΄ΡˆΠΈΡ… Ρ‡Π΅Ρ€Π΅Π· ΠΈΠ·ΡƒΡ‡Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ΄Π°.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ описан, Ρ‚Π°ΠΊΠΎΠ²: Π² нашСм кластСрС Kubernetes ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Namespace прСдставляСт ΠΎΠΊΡ€ΡƒΠΆΠ΅Π½ΠΈΠ΅-пСсочницу ΠΊΠ°ΠΊΠΎΠΉ-Ρ‚ΠΎ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹, ΠΈ ΠΌΡ‹ Ρ…ΠΎΡ‚Π΅Π»ΠΈ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΡ‚ΡŒ доступ ΠΊ Π½ΠΈΠΌ Ρ‚Π°ΠΊ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ ΠΌΠΎΠ³Π»ΠΈ ΠΈΠ³Ρ€Π°Ρ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² своих пСсочницах.

Π”ΠΎΡΡ‚ΠΈΡ‡ΡŒ ΠΆΠ΅Π»Π°Π΅ΠΌΠΎΠ³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ΠΌ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŽ Π³Ρ€ΡƒΠΏΠΏΡ‹, Ρƒ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Π΅ΡΡ‚ΡŒ RoleBinding ΠΊ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΌ Namespace ΠΈ ClusterRole с ΠΏΡ€Π°Π²ΠΎΠΌ Π½Π° Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅. YAML-прСдставлСниС Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π³Π»ΡΠ΄Π΅Ρ‚ΡŒ Ρ‚Π°ΠΊ:

---
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.yaml, Π² raw)

Π‘ΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ Ρ‚Π°ΠΊΠΎΠΉ RoleBinding ΠΌΠΎΠΆΠ½ΠΎ ΠΈ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ, Π½ΠΎ послС прСодолСния ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΊΠΈ Π² ΡΠΎΡ‚Π½ΡŽ пространств ΠΈΠΌΡ‘Π½ это становится ΡƒΡ‚ΠΎΠΌΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌ занятиСм. Как Ρ€Π°Π· здСсь ΠΏΠΎΠΌΠΎΠ³Π°ΡŽΡ‚ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Ρ‹ Kubernetes β€” ΠΎΠ½ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ созданиС рСсурсов Kubernetes, ΠΎΡΠ½ΠΎΠ²Ρ‹Π²Π°ΡΡΡŒ Π½Π° измСнСниях Π² рСсурсах. Π’ нашСм случаС ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ RoleBinding ΠΏΡ€ΠΈ создании Namespace.

ΠŸΠ΅Ρ€Π²Ρ‹ΠΌ Π΄Π΅Π»ΠΎΠΌ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ main, которая выполняСт Ρ‚Ρ€Π΅Π±ΡƒΠ΅ΠΌΡƒΡŽ настройку для запуска ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π° ΠΈ Π·Π°Ρ‚Π΅ΠΌ Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ дСйствиС ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π°:

(ΠŸΡ€ΠΈΠΌ. ΠΏΠ΅Ρ€Π΅Π².: здСсь ΠΈ Π΄Π°Π»Π΅Π΅ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ Π² ΠΊΠΎΠ΄Π΅ ΠΏΠ΅Ρ€Π΅Π²Π΅Π΄Π΅Π½Ρ‹ Π½Π° русский язык. ΠšΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ, отступы исправлСны Π½Π° ΠΏΡ€ΠΎΠ±Π΅Π»Ρ‹ вмСсто [Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡƒΠ΅ΠΌΡ‹Ρ… Π² Go] Ρ‚Π°Π±ΠΎΠ² ΠΈΡΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ с Ρ†Π΅Π»ΡŒΡŽ Π»ΡƒΡ‡ΡˆΠ΅ΠΉ читаСмости Π² Ρ€Π°ΠΌΠΊΠ°Ρ… вёрстки Π₯Π°Π±Ρ€Π°. ПослС ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ листинга ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½Ρ‹ ссылки Π½Π° ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π» Π½Π° GitHub, Π³Π΄Π΅ сохранСны англоязычныС ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ ΠΈ Ρ‚Π°Π±Ρ‹.)

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()   // ОТидаСм, Ρ‡Ρ‚ΠΎ всС остановлСно
}

(main.go, Π² raw)

ΠœΡ‹ Π΄Π΅Π»Π°Π΅ΠΌ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π΅:

  1. НастраиваСм ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹Ρ… сигналов ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΉ систСмы, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎΠ΅ (graceful) Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π°.
  2. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ WaitGroup, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ ΠΎΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ всС goroutines ΠΏΠ΅Ρ€Π΅Π΄ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ΠΌ Ρ€Π°Π±ΠΎΡ‚Ρ‹ прилоТСния.
  3. ΠŸΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²Π»ΡΠ΅ΠΌ доступ ΠΊ кластСру созданиСм clientset.
  4. ЗапускаСм NamespaceController, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π±ΡƒΠ΄Π΅Ρ‚ располоТСна вся наша Π»ΠΎΠ³ΠΈΠΊΠ°.

Π’Π΅ΠΏΠ΅Ρ€ΡŒ Π½ΡƒΠΆΠ½Π° основа для Π»ΠΎΠ³ΠΈΠΊΠΈ, ΠΈ Π² нашСм случаС это упомянутый NamespaceController:

// 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
}

(controller.go, Π² raw)

Π—Π΄Π΅ΡΡŒ ΠΌΡ‹ настраиваСм SharedIndexInformer, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ эффСктивно (ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ кэш) ΠΎΠΆΠΈΠ΄Π°Ρ‚ΡŒ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ Π² пространствах ΠΈΠΌΡ‘Π½ (ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½Π΅Π΅ ΠΎΠ± informers Ρ‡ΠΈΡ‚Π°ΠΉΡ‚Π΅ Π² ΡΡ‚Π°Ρ‚ΡŒΠ΅ «Как Π½Π° самом Π΄Π΅Π»Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ ΠΏΠ»Π°Π½ΠΈΡ€ΠΎΠ²Ρ‰ΠΈΠΊ Kubernetes?Β» β€” ΠΏΡ€ΠΈΠΌ. ΠΏΠ΅Ρ€Π΅Π².). ПослС этого ΠΌΡ‹ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌ EventHandler ΠΊ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ΅Ρ€Ρƒ, благодаря Ρ‡Π΅ΠΌΡƒ ΠΏΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ пространства ΠΈΠΌΡ‘Π½ (Namespace) вызываСтся функция createRoleBinding.

Π‘Π»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ шаг β€” ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ эту Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ createRoleBinding:

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))
  }
}

(controller.go, Π² raw)

ΠœΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ пространство ΠΈΠΌΡ‘Π½ ΠΊΠ°ΠΊ obj ΠΈ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΡƒΠ΅ΠΌ Π΅Π³ΠΎ Π² ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ Namespace. Π—Π°Ρ‚Π΅ΠΌ опрСдСляСм RoleBinding, ΠΎΡΠ½ΠΎΠ²Ρ‹Π²Π°ΡΡΡŒ Π½Π° упомянутом Π² Π½Π°Ρ‡Π°Π»Π΅ YAML-Ρ„Π°ΠΉΠ»Π΅, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ прСдоставлСнный ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ Namespace ΠΈ создавая RoleBinding. НаконСц, Π»ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ, ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ Π»ΠΈ ΠΏΡ€ΠΎΡˆΠ»ΠΎ созданиС.

ПослСдняя функция, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ, β€” 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
}

(controller.go, Π² raw)

Π—Π΄Π΅ΡΡŒ ΠΌΡ‹ Π³ΠΎΠ²ΠΎΡ€ΠΈΠΌ WaitGroup, Ρ‡Ρ‚ΠΎ запустим goroutine ΠΈ Π·Π°Ρ‚Π΅ΠΌ Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌ namespaceInformer, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±Ρ‹Π» ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΎΠΏΡ€Π΅Π΄Π΅Π»Ρ‘Π½. Когда поступит сигнал остановки, ΠΎΠ½ Π·Π°Π²Π΅Ρ€ΡˆΠΈΡ‚ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ, сообщит WaitGroup, Ρ‡Ρ‚ΠΎ большС Π½Π΅ выполняСтся, ΠΈ эта функция Π·Π°Π²Π΅Ρ€ΡˆΠΈΡ‚ свою Ρ€Π°Π±ΠΎΡ‚Ρƒ.

Π˜Π½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ сборкС ΠΈ запускС этого ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π° Π² кластСрС Kubernetes ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΉΡ‚ΠΈ Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ Π½Π° GitHub.

На этом ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ создаёт RoleBinding ΠΏΡ€ΠΈ появлСнии Namespace Π² кластСрС Kubernetes, Π³ΠΎΡ‚ΠΎΠ².

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com