Kubernetes Operator ืื™ืŸ Python ืึธืŸ ืคืจืึทืžืขื•ื•ืึธืจืงืก ืื•ืŸ SDK

Kubernetes Operator ืื™ืŸ Python ืึธืŸ ืคืจืึทืžืขื•ื•ืึธืจืงืก ืื•ืŸ SDK

Go ื“ืขืจื•ื•ื™ื™ึทืœ ื”ืื˜ ืึท ืžืึธื ืึธืคึผืึธืœ ืื•ื™ืฃ ื“ื™ ืคึผืจืึธื’ืจืึทืžืžื™ื ื’ ืฉืคึผืจืึทื›ืŸ ืžืขื ื˜ืฉืŸ ืงืœื™ื™ึทื‘ืŸ ืฆื• ืฉืจื™ื™ึทื‘ืŸ ืกื˜ื™ื™ื˜ืžืึทื ืฅ ืคึฟืึทืจ Kubernetes. ืขืก ื–ืขื ืขืŸ ืึธื‘ื™ืขืงื˜ื™ื•ื• ืกื™ื‘ื•ืช ืคึฟืึทืจ ื“ืขื, ืึทื–ืึท ื•ื•ื™:

  1. ืขืก ืื™ื– ืึท ืฉื˜ืึทืจืง ืคืจื™ื™ืžื•ื•ืขืจืง ืคึฟืึทืจ ื“ืขื•ื•ืขืœืึธืคึผื™ื ื’ ืึธืคึผืขืจื™ื™ื˜ืขืจื– ืื™ืŸ Go - ืึธืคึผืขืจืึทื˜ืึธืจ ืกื“ืง.
  2. ืฉืคึผื™ืœ-ื˜ืฉืึทื ื’ื™ื ื’ ืึทืคึผืœืึทืงื™ื™ืฉืึทื ื– ื•ื•ื™ Docker ืื•ืŸ Kubernetes ื–ืขื ืขืŸ ื’ืขืฉืจื™ื‘ืŸ ืื™ืŸ Go. ืฉืจื™ื™ื‘ืŸ ื“ื™ื™ืŸ ืึธืคึผืขืจืึทื˜ืึธืจ ืื™ืŸ Go ืžื™ื˜ืœ ืจืขื“ืŸ ื“ื™ ื–ืขืœื‘ืข ืฉืคึผืจืึทืš ืžื™ื˜ ื“ื™ ื™ืงืึธื•ืกื™ืกื˜ืึทื.
  3. ื”ื•ื™ืš ืคืึธืจืฉื˜ืขืœื•ื ื’ ืคื•ืŸ Go ืึทืคึผืœืึทืงื™ื™ืฉืึทื ื– ืื•ืŸ ืคึผืฉื•ื˜ ืžื›ืฉื™ืจื™ื ืคึฟืึทืจ ืืจื‘ืขื˜ืŸ ืžื™ื˜ ืงืึทื ืงืขืจืึทื ืกื™ ืื•ื™ืก ืคื•ืŸ ื“ื™ ืงืขืกื˜ืœ.

NB: ื“ื•ืจืš ื“ืขื ื•ื•ืขื’, ื•ื•ื™ ืฆื• ืฉืจื™ื™ึทื‘ืŸ ื“ื™ื™ืŸ ืื™ื™ื’ืขื ืข ื“ืขืจืงืœืขืจื•ื ื’ ืื™ืŸ Go, ืžื™ืจ ืฉื•ื™ืŸ ื“ื™ืกืงืจื™ื™ื‘ื“ ืื™ืŸ ืื™ื™ื ืข ืคึฟื•ืŸ ืื•ื ื“ื–ืขืจืข ืื™ื‘ืขืจื–ืขืฆื•ื ื’ืขืŸ ืคึฟื•ืŸ ืื•ื™ืกืœืขื ื“ื™ืฉืข ืžื—ื‘ืจื™ื.

ืึธื‘ืขืจ ื•ื•ืึธืก ืื•ื™ื‘ ืื™ืจ ื–ืขื ื˜ ืคึผืจื™ื•ื•ืขื ื˜ื™ื“ ืคื•ืŸ ืœืขืจื ืขืŸ ื’ื™ื™ืŸ ื“ื•ืจืš ืคืขืœืŸ ืคื•ืŸ ืฆื™ื™ื˜ ืึธื“ืขืจ, ืคืฉื•ื˜, ืžืึธื•ื˜ืึทื•ื•ื™ื™ืฉืึทืŸ? ื“ืขืจ ืึทืจื˜ื™ืงืœ ื’ื™ื˜ ืึท ื‘ื™ื™ืฉืคึผื™ืœ ืคื•ืŸ ื•ื•ื™ ืื™ืจ ืงืขื ืขืŸ ืฉืจื™ื™ึทื‘ืŸ ืึท ื’ื•ื˜ืข ื“ืขืจืงืœืขืจื•ื ื’ ืžื™ื˜ ืื™ื™ื ืขืจ ืคื•ืŸ ื“ื™ ืžืขืจืกื˜ ืคืึธืœืงืก ืฉืคึผืจืึทื›ืŸ ื•ื•ืึธืก ื›ึผืžืขื˜ ื™ืขื“ืขืจ ื“ืขื•ื•ืึธืคึผืก ื™ื ื–ืฉืขื ื™ืจ ื•ื•ื™ื™ืกื˜ - ืคึผื™ื˜ื”ืึธืŸ.

ื˜ืจืขืคืŸ: ืงืึทืคึผื™ืขืจ - ืงืึธืคึผื™ืข ืึธืคึผืขืจืึทื˜ืึธืจ!

ื•ื•ื™ ืึท ื‘ื™ื™ืฉืคึผื™ืœ, ื‘ืึทื˜ืจืึทื›ื˜ืŸ ื“ืขื•ื•ืขืœืึธืคึผื™ื ื’ ืึท ืคึผืฉื•ื˜ ื•ื™ืกื–ืึธื’ื•ื ื’ ื“ื™ื–ื™ื™ื ื“ ืฆื• ื ืึธื›ืžืึทื›ืŸ ืึท ืงืึธื ืคื™ื’ืžืึทืคึผ, ืึธื“ืขืจ ื•ื•ืขืŸ ืึท ื ื™ื™ึทืข ื ืึธืžืขืŸ ืื™ื– ืืจื•ื™ืก ืึธื“ืขืจ ื•ื•ืขืŸ ืื™ื™ื ืขืจ ืคื•ืŸ ืฆื•ื•ื™ื™ ืขื ื˜ื™ื˜ื™ื– ืขื ื“ืขืจื•ื ื’ืขืŸ: ืงืึธื ืคื™ื’ืžืึทืคึผ ืื•ืŸ ืกืขืงืจืขื˜. ืคึฟื•ืŸ ืึท ืคึผืจืึทืงื˜ื™ืฉ ืคื•ื ื˜ ืคื•ืŸ ืžื™ื™ื ื•ื ื’, ื“ืขืจ ืึธืคึผืขืจืึทื˜ืึธืจ ืงืขื ืขืŸ ื–ื™ื™ืŸ ื ื•ืฆื™ืง ืคึฟืึทืจ ืคืึทืจื ืขื ืึทืคึผื“ื™ื™ื˜ื™ื ื’ ืคื•ืŸ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทื ื– (ื“ื•ืจืš ืึทืคึผื“ื™ื™ื˜ื™ื ื’ ื“ื™ ConfigMap) ืึธื“ืขืจ ืคึฟืึทืจ ืึทืคึผื“ื™ื™ื˜ื™ื ื’ ืกื•ื“ ื“ืึทื˜ืŸ - ืคึฟืึทืจ ื‘ื™ื™ึทืฉืคึผื™ืœ, ืฉืœื™ืกืœืขืŸ ืคึฟืึทืจ ืืจื‘ืขื˜ืŸ ืžื™ื˜ ื“ื™ ื“ืึธืงืงืขืจ ืจืขื’ื™ืกื˜ืจื™ (ื•ื•ืขืŸ ืึทื“ื™ื ื’ ืกืขืงืจืขื˜ ืฆื• ื“ื™ ื ืึทืžืขืกืคึผืึทืกืข).

ืื–ื•ื™, ื•ื•ืึธืก ืึท ื’ื•ื˜ ืึธืคึผืขืจืึทื˜ืึธืจ ื–ืึธืœ ื”ืึธื‘ืŸ:

  1. ื™ื ื˜ืขืจืึทืงืฉืึทืŸ ืžื™ื˜ ื“ืขืจ ืึธืคึผืขืจืึทื˜ืึธืจ ืื™ื– ื’ืขืคื™ืจื˜ ืื•ื™ืก ื ื™ืฆืŸ ืžื ื”ื’ ืจื™ืกืึธืจืก ื“ืขืคึฟื™ื ื™ืฆื™ืขืก (ื“ืขืจื ืึธืš ืจื™ืคืขืจื“ ืฆื• ื•ื•ื™ CRD).
  2. ื“ืขืจ ืึธืคึผืขืจืึทื˜ืึธืจ ืงืขื ืขืŸ ื–ื™ื™ืŸ ืงืึทื ืคื™ื’ื™ืขืจื“. ืฆื• ื˜ืึธืŸ ื“ืึธืก, ืžื™ืจ ื•ื•ืขืœืŸ ื ื•ืฆืŸ ื‘ืึทืคึฟืขืœืŸ ืฉื•ืจื” ืคืœืึทื’ืก ืื•ืŸ ืกื•ื•ื™ื•ื•ืข ื•ื•ืขืจื™ืึทื‘ืึทืœื–.
  3. ื“ื™ ื‘ื•ื™ืขืŸ ืคื•ืŸ ื“ื™ ื“ืึธืงืงืขืจ ืงืึทื ื˜ื™ื™ื ืขืจ ืื•ืŸ ื”ืขืœื ื˜ืฉืึทืจื˜ ืื™ื– ื“ื™ื–ื™ื™ื ื“ ืึทื–ื•ื™ ืึทื– ื™ื•ื–ืขืจื– ืงืขื ืขืŸ ืœื™ื™ื›ื˜ (ืžืžืฉ ืžื™ื˜ ืื™ื™ืŸ ื‘ืึทืคึฟืขืœ) ื™ื ืกื˜ืึทืœื™ืจืŸ ื“ื™ ืึธืคึผืขืจืึทื˜ืึธืจ ืื™ืŸ ื–ื™ื™ืขืจ Kubernetes ืงื ื•ื™ืœ.

ืงืจื“

ื›ึผื“ื™ ื“ืขืจ ืึธืคึผืขืจืึทื˜ืึธืจ ื–ืึธืœ ื•ื•ื™ืกืŸ ื•ื•ืึธืก ืจืขืกื•ืจืกืŸ ืฆื• ืงื•ืงืŸ ืคึฟืึทืจ ืื•ืŸ ื•ื•ื• ืฆื• ืงื•ืงืŸ, ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืฉื˜ืขืœืŸ ืึท ื”ืขืจืฉืŸ ืคึฟืึทืจ ืื™ื. ื™ืขื“ืขืจ ื”ืขืจืฉืŸ ื•ื•ืขื˜ ื–ื™ื™ืŸ ืจืขืคึผืจื™ื–ืขื ื˜ื™ื“ ื•ื•ื™ ืึท ืื™ื™ืŸ CRD ื›ื™ื™ืคืขืฅ. ื•ื•ืึธืก ืคืขืœื“ืขืจ ื–ืึธืœ ื“ืขื CRD ื”ืึธื‘ืŸ?

  1. ืžื™ื˜ืœ ื˜ื™ืคึผ, ื•ื•ืึธืก ืžื™ืจ ื•ื•ืขืœืŸ ืงื•ืงืŸ ืคึฟืึทืจ (ConfigMap ืึธื“ืขืจ Secret).
  2. ืจืฉื™ืžื” ืคื•ืŸ ื ืึธืžืขืŸ ืกืคึผื™ื™ืกืึทื–, ืื™ ืŸ ืฐืขืœื› ืŸ ื“ ื™ ืžื™ื˜ืœืข ืŸ ื–ืืœ ืŸ ื–ื™ ืš ื’ืขืคื™ื ืข ืŸ .
  3. ืกืขืœืขืงื˜ืึธืจ, ื“ื•ืจืš ื•ื•ืึธืก ืžื™ืจ ื•ื•ืขืœืŸ ื–ื•ื›ืŸ ืคึฟืึทืจ ืจืขืกื•ืจืกืŸ ืื™ืŸ ื“ื™ ื ืึทืžืขืกืคึผืึทืกืข.

ืœืึธืžื™ืจ ื‘ืึทืฉืจื™ื™ึทื‘ืŸ ื“ื™ CRD:

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: copyrator.flant.com
spec:
  group: flant.com
  versions:
  - name: v1
    served: true
    storage: true
  scope: Namespaced
  names:
    plural: copyrators
    singular: copyrator
    kind: CopyratorRule
    shortNames:
    - copyr
  validation:
    openAPIV3Schema:
      type: object
      properties:
        ruleType:
          type: string
        namespaces:
          type: array
          items:
            type: string
        selector:
          type: string

ืื•ืŸ ืžื™ืจ ื•ื•ืขืœืŸ ืžืึทื›ืŸ ืขืก ื’ืœื™ื™ืš ืคึผืฉื•ื˜ ื”ืขืจืฉืŸ - ืฆื• ื–ื•ื›ืŸ ืื™ืŸ ื“ื™ ื ืึธืžืขืŸ ืคึผืœืึทืฅ ืžื™ื˜ ื“ืขื ื ืึธืžืขืŸ default ืึทืœืข ืงืึธื ืคื™ื’ืžืึทืคึผ ืžื™ื˜ ืœืึทื‘ืขืœืก ื•ื•ื™ copyrator: "true":

apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
  name: main-rule
  labels:
    module: copyrator
ruleType: configmap
selector:
  copyrator: "true"
namespace: default

ื’ืจื™ื™ื˜! ืื™ืฆื˜ ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืขืคืขืก ื‘ืึทืงื•ืžืขืŸ ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ื•ื•ืขื’ืŸ ืื•ื ื“ื–ืขืจ ื”ืขืจืฉืŸ. ืœืึธื–ืŸ ืžื™ืจ ืžืึทื›ืŸ ืึท ืจืขื–ืขืจื•ื•ืึทืฆื™ืข ื’ืœื™ื™ืš ืึทื– ืžื™ืจ ื•ื•ืขืœืŸ ื ื™ืฉื˜ ืฉืจื™ื™ึทื‘ืŸ ืจื™ืงื•ื•ืขืก ืฆื• ื“ื™ ืงื ื•ื™ืœ ืึทืคึผื™ ืกืขืจื•ื•ื™ืจืขืจ ื–ื™ืš. ืฆื• ื˜ืึธืŸ ื“ืึธืก, ืžื™ืจ ื•ื•ืขืœืŸ ื ื•ืฆืŸ ืึท ืคืึทืจื˜ื™ืง ืคึผื™ื˜ื”ืึธืŸ ื‘ื™ื‘ืœื™ืึธื˜ืขืง kubernetes-client:

import kubernetes
from contextlib import suppress


CRD_GROUP = 'flant.com'
CRD_VERSION = 'v1'
CRD_PLURAL = 'copyrators'


def load_crd(namespace, name):
    client = kubernetes.client.ApiClient()
    custom_api = kubernetes.client.CustomObjectsApi(client)

    with suppress(kubernetes.client.api_client.ApiException):
        crd = custom_api.get_namespaced_custom_object(
            CRD_GROUP,
            CRD_VERSION,
            namespace,
            CRD_PLURAL,
            name,
        )
    return {x: crd[x] for x in ('ruleType', 'selector', 'namespace')}

ื•ื•ื™ ืึท ืจืขื–ื•ืœื˜ืึทื˜ ืคื•ืŸ ืคืœื™ืกื ื“ื™ืง ื“ืขื ืงืึธื“, ืžื™ืจ ื‘ืึทืงื•ืžืขืŸ ื“ื™ ืคืืœื’ืขื ื“ืข:

{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}

ื’ืจื•ื™ืก: ืžื™ืจ ื’ืขืจืื˜ืŸ ืฆื• ื‘ืึทืงื•ืžืขืŸ ืึท ื”ืขืจืฉืŸ ืคึฟืึทืจ ื“ื™ ืึธืคึผืขืจืึทื˜ืึธืจ. ืื•ืŸ ืจื•ื‘ึฟ ื™ืžืคึผืึธืจื˜ืึทื ื˜ืœื™, ืžื™ืจ ื”ืึธื‘ืŸ ื’ืขื˜ืืŸ ื•ื•ืึธืก ืื™ื– ื’ืขืจื•ืคืŸ ื“ื™ ืงื•ื‘ืขืจื ืขื˜ืขืก ื•ื•ืขื’.

ืกื•ื•ื™ื•ื•ืข ื•ื•ืขืจื™ืึทื‘ืึทืœื– ืึธื“ืขืจ ืคืœืึทื’ืก? ืžื™ืจ ื ืขืžืขืŸ ืึทืœืฅ!

ื–ืืœ ืก ืžืึทืš ืื•ื™ืฃ ืฆื• ื“ื™ ื”ื•ื™ืคึผื˜ ืึธืคึผืขืจืึทื˜ืึธืจ ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ. ืขืก ื–ืขื ืขืŸ ืฆื•ื•ื™ื™ ื™ืงืขืจื“ื™ืง ืึทืคึผืจืึธื•ื˜ืฉื™ื– ืฆื• ืงืึทื ืคื™ื’ื™ืขืจ ืึทืคึผืœืึทืงื™ื™ืฉืึทื ื–:

  1. ื ื•ืฆืŸ ื‘ืึทืคึฟืขืœืŸ ืฉื•ืจื” ืึธืคึผืฆื™ืขืก;
  2. ื ื•ืฆืŸ ืกื•ื•ื™ื•ื•ืข ื•ื•ืขืจื™ืึทื‘ืึทืœื–.

ื‘ืึทืคึฟืขืœืŸ ืฉื•ืจื” ืึธืคึผืฆื™ืขืก ืœืึธื–ืŸ ืื™ืจ ืฆื• ืœื™ื™ืขื ืขืŸ ืกืขื˜ื˜ื™ื ื’ืก ืžืขืจ ืคืœืขืงืกืึทื‘ืึทืœ, ืžื™ื˜ ื“ืึทื˜ืŸ ื˜ื™ืคึผ ืฉื˜ื™ืฆืŸ ืื•ืŸ ื•ื•ืึทืœืึทื“ื™ื™ืฉืึทืŸ. ื“ื™ ื ืึธืจืžืึทืœ ื‘ื™ื‘ืœื™ืึธื˜ืขืง ืคื•ืŸ Python ื”ืื˜ ืึท ืžืึธื“ื•ืœืข argparser, ื•ื•ืึธืก ืžื™ืจ ื•ื•ืขืœืŸ ื ื•ืฆืŸ. ื“ืขื˜ืึทื™ืœืก ืื•ืŸ ื‘ื™ื™ืฉืคื™ืœืŸ ืคื•ืŸ ื–ื™ื™ึทืŸ ืงื™ื™ืคึผืึทื‘ื™ืœืึทื˜ื™ื– ื–ืขื ืขืŸ ื‘ื ื™ืžืฆื ืื™ืŸ ื‘ืึทืึทืžื˜ืขืจ ื“ืึทืงื™ื•ืžืขื ื˜ื™ื™ืฉืึทืŸ.

ืคึฟืึทืจ ืื•ื ื“ื–ืขืจ ืคืึทืœ, ื“ืึธืก ืื™ื– ื•ื•ื™ ืึท ื‘ื™ื™ืฉืคึผื™ืœ ืคื•ืŸ ื‘ืึทืฉื˜ืขื˜ื™ืงืŸ ืœื™ื™ืขื ืขืŸ ื‘ืึทืคึฟืขืœืŸ ืฉื•ืจื” ืคืœืึทื’ืก:

   parser = ArgumentParser(
        description='Copyrator - copy operator.',
        prog='copyrator'
    )
    parser.add_argument(
        '--namespace',
        type=str,
        default=getenv('NAMESPACE', 'default'),
        help='Operator Namespace'
    )
    parser.add_argument(
        '--rule-name',
        type=str,
        default=getenv('RULE_NAME', 'main-rule'),
        help='CRD Name'
    )
    args = parser.parse_args()

ืื•ื™ืฃ ื“ื™ ืื ื“ืขืจืข ื”ืึทื ื˜, ืžื™ื˜ ื™ื ื•ื•ื™ื™ืจืึทื ืžืขื ืึทืœ ื•ื•ืขืจื™ืึทื‘ืึทืœื– ืื™ืŸ Kubernetes, ืื™ืจ ืงืขื ืขืŸ ืœื™ื™ื›ื˜ ืึทืจื™ื‘ืขืจืคื™ืจืŸ ืกืขืจื•ื•ื™ืก ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ื•ื•ืขื’ืŸ ื“ื™ ืคึผืึธื“ ืื™ืŸ ื“ืขื ืงืึทื ื˜ื™ื™ื ืขืจ. ืคึฟืึทืจ ื‘ื™ื™ึทืฉืคึผื™ืœ, ืžื™ืจ ืงืขื ืขืŸ ื‘ืึทืงื•ืžืขืŸ ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ื•ื•ืขื’ืŸ ื“ื™ ื ืึทืžืขืกืคึผืึทืกืข ืื™ืŸ ื•ื•ืึธืก ื“ื™ ืคึผืึธื“ ืื™ื– ืคืœื™ืกื ื“ื™ืง ืžื™ื˜ ื“ื™ ืคืืœื’ืขื ื“ืข ืงืึทื ืกื˜ืจืึทืงืฉืึทืŸ:

env:
- name: NAMESPACE
  valueFrom:
     fieldRef:
         fieldPath: metadata.namespace 

ืึธืคึผืขืจืึทื˜ืึธืจ ืœืึธื’ื™ืง

ืฆื• ืคึฟืึทืจืฉื˜ื™ื™ืŸ ื•ื•ื™ ืฆื• ื‘ืึทื–ื•ื ื“ืขืจ ืžืขื˜ื”ืึธื“ืก ืคึฟืึทืจ ืืจื‘ืขื˜ืŸ ืžื™ื˜ ConfigMap ืื•ืŸ Secret, ืžื™ืจ ื•ื•ืขืœืŸ ื ื•ืฆืŸ ืกืคึผืขืฆื™ืขืœ ืžืึทืคึผืก. ื“ืขืจื ืึธืš ืžื™ืจ ืงืขื ืขืŸ ืคึฟืึทืจืฉื˜ื™ื™ืŸ ื•ื•ืึธืก ืžืขื˜ื”ืึธื“ืก ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืฉืคึผื•ืจ ืื•ืŸ ืฉืึทืคึฟืŸ ื“ื™ ื›ื™ื™ืคืขืฅ:

LIST_TYPES_MAP = {
    'configmap': 'list_namespaced_config_map',
    'secret': 'list_namespaced_secret',
}

CREATE_TYPES_MAP = {
    'configmap': 'create_namespaced_config_map',
    'secret': 'create_namespaced_secret',
}

ื•ื•ื™ื™ึทื˜ืขืจ, ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื‘ืึทืงื•ืžืขืŸ ื’ืขืฉืขืขื ื™ืฉืŸ ืคื•ืŸ ื“ื™ ืึทืคึผื™ ืกืขืจื•ื•ืขืจ. ื–ืืœ ืื•ื ื“ื– ื™ื ืกื˜ืจื•ืžืขื ื˜ ืขืก ื•ื•ื™ ื’ื™ื™ื˜:

def handle(specs):
    kubernetes.config.load_incluster_config()
    v1 = kubernetes.client.CoreV1Api()

    # ะŸะพะปัƒั‡ะฐะตะผ ะผะตั‚ะพะด ะดะปั ัะปะตะถะตะฝะธั ะทะฐ ะพะฑัŠะตะบั‚ะฐะผะธ
    method = getattr(v1, LIST_TYPES_MAP[specs['ruleType']])
    func = partial(method, specs['namespace'])

    w = kubernetes.watch.Watch()
    for event in w.stream(func, _request_timeout=60):
        handle_event(v1, specs, event)

ื ืึธืš ื‘ืืงื•ืžืขืŸ ื“ื™ ื’ืขืฉืขืขื ื™ืฉ, ืžื™ืจ ืžืึทืš ืื•ื™ืฃ ืฆื• ื“ื™ ื”ื•ื™ืคึผื˜ ืœืึธื’ื™ืง ืคื•ืŸ ืคึผืจืึทืกืขืกื™ื ื’ ืขืก:

# ะขะธะฟั‹ ัะพะฑั‹ั‚ะธะน, ะฝะฐ ะบะพั‚ะพั€ั‹ะต ะฑัƒะดะตะผ ั€ะตะฐะณะธั€ะพะฒะฐั‚ัŒ
ALLOWED_EVENT_TYPES = {'ADDED', 'UPDATED'}


def handle_event(v1, specs, event):
    if event['type'] not in ALLOWED_EVENT_TYPES:
        return

    object_ = event['object']
    labels = object_['metadata'].get('labels', {})

    # ะ˜ั‰ะตะผ ัะพะฒะฟะฐะดะตะฝะธั ะฟะพ selector'ัƒ
    for key, value in specs['selector'].items():
        if labels.get(key) != value:
            return
    # ะŸะพะปัƒั‡ะฐะตะผ ะฐะบั‚ะธะฒะฝั‹ะต namespace'ั‹
    namespaces = map(
        lambda x: x.metadata.name,
        filter(
            lambda x: x.status.phase == 'Active',
            v1.list_namespace().items
        )
    )
    for namespace in namespaces:
        # ะžั‡ะธั‰ะฐะตะผ ะผะตั‚ะฐะดะฐะฝะฝั‹ะต, ัƒัั‚ะฐะฝะฐะฒะปะธะฒะฐะตะผ namespace
        object_['metadata'] = {
            'labels': object_['metadata']['labels'],
            'namespace': namespace,
            'name': object_['metadata']['name'],
        }
        # ะ’ั‹ะทั‹ะฒะฐะตะผ ะผะตั‚ะพะด ัะพะทะดะฐะฝะธั/ะพะฑะฝะพะฒะปะตะฝะธั ะพะฑัŠะตะบั‚ะฐ
        methodcaller(
            CREATE_TYPES_MAP[specs['ruleType']],
            namespace,
            object_
        )(v1)

ื“ื™ ื”ื•ื™ืคึผื˜ ืœืึธื’ื™ืง ืื™ื– ื’ืจื™ื™ื˜! ืื™ืฆื˜ ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืคึผืขืงืœ ืึทืœืข ื“ืขื ืื™ืŸ ืื™ื™ืŸ ืคึผื™ื˜ื”ืึธืŸ ืคึผืขืงืœ. ืžื™ืจ ืฆื•ื’ืจื™ื™ื˜ืŸ ื“ื™ ื˜ืขืงืข setup.py, ืฉืจื™ื™ึทื‘ืŸ ืžืขื˜ืึท ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ื•ื•ืขื’ืŸ ื“ืขื ืคึผืจื•ื™ืขืงื˜ ื“ืึธืจื˜:

from sys import version_info

from setuptools import find_packages, setup

if version_info[:2] < (3, 5):
    raise RuntimeError(
        'Unsupported python version %s.' % '.'.join(version_info)
    )


_NAME = 'copyrator'
setup(
    name=_NAME,
    version='0.0.1',
    packages=find_packages(),
    classifiers=[
        'Development Status :: 3 - Alpha',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
    ],
    author='Flant',
    author_email='[email protected]',
    include_package_data=True,
    install_requires=[
        'kubernetes==9.0.0',
    ],
    entry_points={
        'console_scripts': [
            '{0} = {0}.cli:main'.format(_NAME),
        ]
    }
)

NB: ื“ืขืจ kubernetes ืงืœื™ืขื ื˜ ืคึฟืึทืจ ืคึผื™ื˜ื”ืึธืŸ ื”ืื˜ ื–ื™ื™ืŸ ืื™ื™ื’ืขื ืข ื•ื•ืขืจืกื™ืข. ืžืขืจ ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ื•ื•ืขื’ืŸ ืงืึทืžืคึผืึทื˜ืึทื‘ื™ืœืึทื˜ื™ ืฆื•ื•ื™ืฉืŸ ืงืœื™ืขื ื˜ ื•ื•ืขืจืกื™ืขืก ืื•ืŸ Kubernetes ื•ื•ืขืจืกื™ืขืก ืงืขื ืขืŸ ื–ื™ื™ืŸ ื’ืขืคึฟื•ื ืขืŸ ืื™ืŸ ืงืึทืžืคึผืึทื˜ืึทื‘ื™ืœืึทื˜ื™ ืžืึทื˜ืจื™ืฅ.

ืื™ืฆื˜ ืื•ื ื“ื–ืขืจ ืคึผืจื•ื™ืขืงื˜ ืงื•ืงื˜ ื•ื•ื™ ื“ืึธืก:

copyrator
โ”œโ”€โ”€ copyrator
โ”‚   โ”œโ”€โ”€ cli.py # ะ›ะพะณะธะบะฐ ั€ะฐะฑะพั‚ั‹ ั ะบะพะผะฐะฝะดะฝะพะน ัั‚ั€ะพะบะพะน
โ”‚   โ”œโ”€โ”€ constant.py # ะšะพะฝัั‚ะฐะฝั‚ั‹, ะบะพั‚ะพั€ั‹ะต ะผั‹ ะฟั€ะธะฒะพะดะธะปะธ ะฒั‹ัˆะต
โ”‚   โ”œโ”€โ”€ load_crd.py # ะ›ะพะณะธะบะฐ ะทะฐะณั€ัƒะทะบะธ CRD
โ”‚   โ””โ”€โ”€ operator.py # ะžัะฝะพะฒะฝะฐั ะปะพะณะธะบะฐ ั€ะฐะฑะพั‚ั‹ ะพะฟะตั€ะฐั‚ะพั€ะฐ
โ””โ”€โ”€ setup.py # ะžั„ะพั€ะผะปะตะฝะธะต ะฟะฐะบะตั‚ะฐ

ื“ืึธืงืขืจ ืื•ืŸ ื”ืขืœื

ื“ื™ ื“ืึธืงืขืจืคื™ืœืข ื•ื•ืขื˜ ื–ื™ื™ืŸ ื™ื ืงืจืขื“ืึทื‘ืœื™ ืคึผืฉื•ื˜: ื ืขืžืขืŸ ื“ื™ ื‘ืึทื–ืข ืคึผื™ื˜ื”ืึธืŸ-ืึทืœืคึผื™ื™ืŸ ื‘ื™ืœื“ ืื•ืŸ ื™ื ืกื˜ืึทืœื™ืจืŸ ืื•ื ื“ื–ืขืจ ืคึผืขืงืœ. ืœืึธืžื™ืจ ืึธืคึผืœื™ื™ื’ืŸ ื–ื™ื™ืŸ ืึทืคึผื˜ืึทืžืึทื–ื™ื™ืฉืึทืŸ ื‘ื™ื– ื‘ืขืกืขืจ ืฆื™ื™ื˜:

FROM python:3.7.3-alpine3.9

ADD . /app

RUN pip3 install /app

ENTRYPOINT ["copyrator"]

ื“ื™ืคึผืœื•ื™ืžืึทื ื˜ ืคึฟืึทืจ ื“ื™ ืึธืคึผืขืจืึทื˜ืึธืจ ืื™ื– ืื•ื™ืš ื–ื™ื™ืขืจ ืคึผืฉื•ื˜:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Chart.Name }}
spec:
  selector:
    matchLabels:
      name: {{ .Chart.Name }}
  template:
    metadata:
      labels:
        name: {{ .Chart.Name }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: privaterepo.yourcompany.com/copyrator:latest
        imagePullPolicy: Always
        args: ["--rule-type", "main-rule"]
        env:
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
      serviceAccountName: {{ .Chart.Name }}-acc

ืฆื•ื ืกื•ืฃ, ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืฉืึทืคึฟืŸ ืึท ืฆื•ื ืขืžืขืŸ ืจืึธืœืข ืคึฟืึทืจ ื“ืขืจ ืึธืคึผืขืจืึทื˜ืึธืจ ืžื™ื˜ ื“ื™ ื ื™ื™ื˜ื™ืง ืจืขื›ื˜:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Chart.Name }}-acc

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: {{ .Chart.Name }}
rules:
  - apiGroups: [""]
    resources: ["namespaces"]
    verbs: ["get", "watch", "list"]
  - apiGroups: [""]
    resources: ["secrets", "configmaps"]
    verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: {{ .Chart.Name }}
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: {{ .Chart.Name }}
subjects:
- kind: ServiceAccount
  name: {{ .Chart.Name }}

ื’ืึทื ืฅ

ืึทื–ื•ื™, ืึธืŸ ืžื•ืจื, ื˜ื™ื™ึทื ืข ืึธื“ืขืจ ืœืขืจื ืขืŸ Go, ืžื™ืจ ื–ืขื ืขืŸ ื‘ื™ื›ื•ืœืช ืฆื• ื‘ื•ื™ืขืŸ ืื•ื ื“ื–ืขืจ ืื™ื™ื’ืขื ืข ืึธืคึผืขืจืึทื˜ืึธืจ ืคึฟืึทืจ Kubernetes ืื™ืŸ ืคึผื™ื˜ื”ืึธืŸ. ืคื•ืŸ ืงื•ืจืก, ืขืก ื ืึธืš ื”ืื˜ ืคึผืœืึทืฅ ืฆื• ื•ื•ืึทืงืกืŸ: ืื™ืŸ ื“ืขืจ ืฆื•ืงื•ื ืคึฟื˜ ืขืก ื•ื•ืขื˜ ืงืขื ืขืŸ ืฆื• ืคึผืจืึธืฆืขืก ืงื™ื™ืคืœ ื›ึผืœืœื™ื, ืึทืจื‘ืขื˜ ืื™ืŸ ืงื™ื™ืคืœ ืคึฟืขื“ืขื, ื™ื ื“ื™ืคึผืขื ื“ืึทื ื˜ืœื™ ืžืึธื ื™ื˜ืึธืจ ืขื ื“ืขืจื•ื ื’ืขืŸ ืื™ืŸ ื“ื™ ืงืจื“ืก ...

ืฆื• ื’ืขื‘ืŸ ืื™ืจ ืึท ื ืขืขื ื˜ืขืจ ืงื•ืง ืื™ืŸ ื“ืขื ืงืึธื“, ืžื™ืจ ื”ืึธื‘ืŸ ืขืก ืื™ืŸ ืฆื™ื‘ื•ืจ ืจื™ืคึผืึทื–ืึทื˜ืึธืจื™. ืื•ื™ื‘ ืื™ืจ ื•ื•ื™ืœื˜ ื‘ื™ื™ืฉืคื™ืœืŸ ืคื•ืŸ ืžืขืจ ืขืจื ืกื˜ ืึธืคึผืขืจื™ื™ื˜ืขืจื– ื™ืžืคึผืœืึทืžืขื ืึทื“ ื ื™ืฆืŸ Python, ืื™ืจ ืงืขื ืขืŸ ื•ื•ืขื ื“ืŸ ื“ื™ื™ืŸ ื•ืคืžืขืจืงื–ืึทืžืงื™ื™ื˜ ืฆื• ืฆื•ื•ื™ื™ ืึธืคึผืขืจื™ื™ื˜ืขืจื– ืคึฟืึทืจ ื“ื™ืคึผืœื•ื™ื™ื ื’ mongodb (ะฟะตั€ะฒั‹ะน ะธ ืฆื•ื•ื™ื™ื˜).

ืคึผืก ืื•ืŸ ืื•ื™ื‘ ืื™ืจ ื–ืขื ื˜ ืฆื• ืคื•ื™ืœ ืฆื• ื”ืึทื ื“ืœืขืŸ ืžื™ื˜ Kubernetes events ืึธื“ืขืจ ืื™ืจ ื–ืขื ื˜ ืคืฉื•ื˜ ืžืขืจ ืฆื•ื’ืขื•ื•ื•ื™ื ื˜ ืฆื• ื ื•ืฆืŸ Bash, ืื•ื ื“ื–ืขืจ ืงืึธืœืขื’ืขืก ื”ืึธื‘ืŸ ืฆื•ื’ืขื’ืจื™ื™ื˜ ืึท ืคืึทืจื˜ื™ืง ืœื™ื™ื–ื•ื ื’ ืื™ืŸ ื“ื™ ืคืึธืจืขื ืฉืึธืœ-ืึธืคึผืขืจืึทื˜ืึธืจ (ืžื™ืจ ืžื•ื“ื™ืข ืื™ืŸ ืืคืจื™ืœ).

ืคึผืคึผืก

ืœื™ื™ืขื ืขืŸ ืื•ื™ืš ืื•ื™ืฃ ืื•ื ื“ื–ืขืจ ื‘ืœืึธื’:

ืžืงื•ืจ: www.habr.com

ืœื™ื™ื’ืŸ ืึท ื‘ืึทืžืขืจืงื•ื ื’