рдлреНрд░реЗрдорд╡рд░реНрдХ рд░ SDK рдмрд┐рдирд╛ рдкрд╛рдЗрдердирдорд╛ Kubernetes рдЕрдкрд░реЗрдЯрд░

рдлреНрд░реЗрдорд╡рд░реНрдХ рд░ SDK рдмрд┐рдирд╛ рдкрд╛рдЗрдердирдорд╛ Kubernetes рдЕрдкрд░реЗрдЯрд░

Go рд╣рд╛рд▓ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдЩ рднрд╛рд╖рд╛рд╣рд░реВрдорд╛ рдПрдХрд╛рдзрд┐рдХрд╛рд░ рдЫ рдорд╛рдирд┐рд╕рд╣рд░реВрд▓реЗ Kubernetes рдХреЛ рд▓рд╛рдЧрд┐ рдХрдердирд╣рд░реВ рд▓реЗрдЦреНрди рд░реЛрдЬреНрдЫрдиреНред рдпрд╕рдХрд╛ рд▓рд╛рдЧрд┐ рд╡рд╕реНрддреБрдЧрдд рдХрд╛рд░рдгрд╣рд░реВ рдЫрдиреН, рдЬрд╕реНрддреИ:

  1. рддреНрдпрд╣рд╛рдБ Go рдорд╛ рдЕрдкрд░реЗрдЯрд░рд╣рд░реВ рд╡рд┐рдХрд╛рд╕ рдЧрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐ рдПрдХ рд╢рдХреНрддрд┐рд╢рд╛рд▓реА рдлреНрд░реЗрдорд╡рд░реНрдХ рдЫ - рдЕрдкрд░реЗрдЯрд░ SDK.
  2. Docker рд░ Kubernetes рдЬрд╕реНрддрд╛ рдЧреЗрдо рдкрд░рд┐рд╡рд░реНрддрди рдЧрд░реНрдиреЗ рдПрдкрд╣рд░реВ Go рдорд╛ рд▓реЗрдЦрд┐рдПрдХрд╛ рдЫрдиреНред Go рдорд╛ рдЖрдлреНрдиреЛ рдЕрдкрд░реЗрдЯрд░ рд▓реЗрдЦреНрдиреБ рднрдиреЗрдХреЛ рдЗрдХреЛрд╕рд┐рд╕реНрдЯрдорд╕рдБрдЧ рдЙрд╣реА рднрд╛рд╖рд╛ рдмреЛрд▓реНрдиреБ рд╣реЛред
  3. рдЧреЛ рдПрдкреНрд▓рд┐рдХреЗрд╕рдирд╣рд░реВрдХреЛ рдЙрдЪреНрдЪ рдкреНрд░рджрд░реНрд╢рди рд░ рдмрд╛рдХрд╕ рдмрд╛рд╣рд┐рд░ рдПрдХрд░реВрдкрддрд╛рд╕рдБрдЧ рдХрд╛рдо рдЧрд░реНрдирдХрд╛ рд▓рд╛рдЧрд┐ рд╕рд░рд▓ рдЙрдкрдХрд░рдгрд╣рд░реВред

NB: рдЦреИрд░, рдЧреЛ рдорд╛ рдЖрдлреНрдиреЛ рдмрдпрд╛рди рдХрд╕рд░реА рд▓реЗрдЦреНрдиреЗ, рд╣рд╛рдореА рдкрд╣рд┐рд▓реЗ рдиреИ рд╡рд░реНрдгрди рдЧрд░рд┐рдПрдХреЛ рдЫ рд╡рд┐рджреЗрд╢реА рд▓реЗрдЦрдХрд╣рд░реБ рджреНрд╡рд╛рд░рд╛ рд╣рд╛рдореНрд░реЛ рдЕрдиреБрд╡рд╛рджрд╣рд░реБ рдордзреНрдпреЗ рдПрдХ рдорд╛ред

рддрд░ рдХреЗ рд╣реБрдиреНрдЫ рдпрджрд┐ рддрдкрд╛рдИрд▓рд╛рдИ рд╕рдордпрдХреЛ рдЕрднрд╛рд╡рдорд╛ рдЧреЛ рд╕рд┐рдХреНрдирдмрд╛рдЯ рд░реЛрдХрд┐рдПрдХреЛ рдЫ рд╡рд╛, рд╕рд░рд▓ рд░реВрдкрдорд╛ рднрдиреНрдиреБрдкрд░реНрджрд╛, рдкреНрд░реЗрд░рдгрд╛рд▓реЗ? рд▓реЗрдЦрд▓реЗ рддрдкрд╛рдЗрдБ рдХрд╕рд░реА рдПрдХ рд╕рдмреИрднрдиреНрджрд╛ рд▓реЛрдХрдкреНрд░рд┐рдп рднрд╛рд╖рд╛рд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рд░рд╛рдореНрд░реЛ рдХрдерди рд▓реЗрдЦреНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ рднрдиреНрдиреЗ рдЙрджрд╛рд╣рд░рдг рдкреНрд░рджрд╛рди рдЧрд░реНрджрдЫ рдЬреБрди рд▓рдЧрднрдЧ рд╣рд░реЗрдХ DevOps рдЗрдиреНрдЬрд┐рдирд┐рдпрд░рд▓рд╛рдИ рдерд╛рд╣рд╛ рдЫ - рдЕрдЬрдЧрд░.

рднреЗрдЯреНрдиреБрд╣реЛрд╕реН: рдХрдкрд┐рдпрд░ - рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдЕрдкрд░реЗрдЯрд░!

рдЙрджрд╛рд╣рд░рдгрдХреЛ рд░реВрдкрдорд╛, рдХрдиреНрдлрд┐рдЧрдореНрдпрд╛рдк рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдЧрд░реНрди рдбрд┐рдЬрд╛рдЗрди рдЧрд░рд┐рдПрдХреЛ рдПрдЙрдЯрд╛ рд╕рд╛рдзрд╛рд░рдг рдХрдерди рд╡рд┐рдХрд╛рд╕ рдЧрд░реНрдиреЗ рд╡рд┐рдЪрд╛рд░ рдЧрд░реНрдиреБрд╣реЛрд╕реН рдЬрдм рдХрд┐ рдирдпрд╛рдБ рдиреЗрдорд╕реНрдкреЗрд╕ рджреЗрдЦрд╛ рдкрд░реНрджрдЫ рд╡рд╛ рдЬрдм рджреБрдИ рд╕рдВрд╕реНрдерд╛рд╣рд░реВ рдордзреНрдпреЗ рдПрдЙрдЯрд╛ рдкрд░рд┐рд╡рд░реНрддрди рд╣реБрдиреНрдЫ: рдХрдиреНрдлрд┐рдЧрдореНрдпрд╛рдк рд░ рд╕реЗрдХреНрд░реЗрдЯред рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рджреГрд╖реНрдЯрд┐рдХреЛрдгрдмрд╛рдЯ, рдЕрдкрд░реЗрдЯрд░ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рдирд╣рд░реВрдХреЛ рдмрд▓реНрдХ рдЕрджреНрдпрд╛рд╡рдзрд┐рдХ рдЧрд░реНрди (рдХрдиреНрдлрд┐рдЧрдореНрдпрд╛рдк рдЕрджреНрдпрд╛рд╡рдзрд┐рдХ рдЧрд░реЗрд░) рд╡рд╛ рдЧреЛрдкреНрдп рдбрд╛рдЯрд╛ рдЕрдкрдбреЗрдЯ рдЧрд░реНрдирдХрд╛ рд▓рд╛рдЧрд┐ рдЙрдкрдпреЛрдЧреА рд╣реБрди рд╕рдХреНрдЫ - рдЙрджрд╛рд╣рд░рдгрдХрд╛ рд▓рд╛рдЧрд┐, рдбрдХрд░ рд░рдЬрд┐рд╕реНрдЯреНрд░реАрд╕рдБрдЧ рдХрд╛рдо рдЧрд░реНрдиреЗ рдХреБрдЮреНрдЬреАрд╣рд░реВ (рдиреЗрдорд╕реНрдкреЗрд╕рдорд╛ рдЧреЛрдкреНрдп рдердкреНрджрд╛)ред

рд░ рддреНрдпрд╕реИрд▓реЗ, рд░рд╛рдореНрд░реЛ рдЕрдкрд░реЗрдЯрд░ рдХреЗ рд╣реБрдиреБрдкрд░реНрдЫ:

  1. рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рдЕрдкрд░реЗрдЯрд░ рд╕рдВрдЧ рдЕрдиреНрддрд░рдХреНрд░рд┐рдпрд╛ рдЧрд░рд┐рдиреНрдЫ рдЕрдиреБрдХреВрд▓рди рд╕реНрд░реЛрдд рдкрд░рд┐рднрд╛рд╖рд╛рд╣рд░реВ (рдпрд╕рдкрдЫрд┐ CRD рднрдирд┐рдиреНрдЫ)ред
  2. рдЕрдкрд░реЗрдЯрд░ рдХрдиреНрдлрд┐рдЧрд░ рдЧрд░реНрди рд╕рдХрд┐рдиреНрдЫред рдпреЛ рдЧрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐, рд╣рд╛рдореА рдХрдорд╛рдгреНрдб рд▓рд╛рдЗрди рдлреНрд▓реНрдпрд╛рдЧрд╣рд░реВ рд░ рд╡рд╛рддрд╛рд╡рд░рдг рдЪрд░рд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдиреЗрдЫреМрдВред
  3. рдбрдХрд░ рдХрдиреНрдЯреЗрдирд░ рд░ рд╣реЗрд▓реНрдо рдЪрд╛рд░реНрдЯрдХреЛ рдирд┐рд░реНрдорд╛рдг рдбрд┐рдЬрд╛рдЗрди рдЧрд░рд┐рдПрдХреЛ рдЫ рддрд╛рдХрд┐ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛рд╣рд░реВрд▓реЗ рд╕рдЬрд┐рд▓реИрд╕рдБрдЧ (рд╢рд╛рдмреНрджрд┐рдХ рд░реВрдкрдорд╛ рдПрдЙрдЯрд╛ рдЖрджреЗрд╢рдХреЛ рд╕рд╛рде) рдЕрдкрд░реЗрдЯрд░рд▓рд╛рдИ рддрд┐рдиреАрд╣рд░реВрдХреЛ рдХреБрдмрд░реНрдиреЗрдЯреНрд╕ рдХреНрд▓рд╕реНрдЯрд░рдорд╛ рд╕реНрдерд╛рдкрдирд╛ рдЧрд░реНрди рд╕рдХреВрдиреНред

рд╕реАрдЖрд░рдбреА

рддреНрдпрд╕рдХрд╛рд░рдг рдЕрдкрд░реЗрдЯрд░рд▓рд╛рдИ рдерд╛рд╣рд╛ рдЫ рдХреБрди рд╕реНрд░реЛрддрд╣рд░реВ рд░ рдХрд╣рд╛рдБ рд╣реЗрд░реНрдиреЗ, рд╣рд╛рдореАрд▓реЗ рдЙрд╕рдХреЛ рд▓рд╛рдЧрд┐ рдирд┐рдпрдо рд╕реЗрдЯ рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫред рдкреНрд░рддреНрдпреЗрдХ рдирд┐рдпрдорд▓рд╛рдИ рдПрдХрд▓ CRD рд╡рд╕реНрддреБрдХреЛ рд░реВрдкрдорд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдЧрд░рд┐рдиреЗрдЫред рдпреЛ CRD рдХреЗ рдХреНрд╖реЗрддреНрд░рд╣рд░реВ рд╣реБрдиреБрдкрд░реНрдЫ?

  1. рд╕реНрд░реЛрдд рдкреНрд░рдХрд╛рд░, рдЬреБрди рд╣рд╛рдореАрд▓реЗ рдЦреЛрдЬреНрдиреЗрдЫреМрдВ (рдХрдиреНрдлрд┐рдЧрдореНрдпрд╛рдк рд╡рд╛ рдЧреЛрдкреНрдп)ред
  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

рддрдпрд╛рд░! рдЕрдм рд╣рд╛рдореАрд▓реЗ рд╣рд╛рдореНрд░реЛ рдирд┐рдпрдордХреЛ рдмрд╛рд░реЗрдорд╛ рдЬрд╛рдирдХрд╛рд░реА рд▓рд┐рди рдЖрд╡рд╢реНрдпрдХ рдЫред рдорд▓рд╛рдИ рддреБрд░реБрдиреНрддреИ рд░рд┐рдЬрд░реНрднреЗрд╕рди рдЧрд░реНрди рджрд┐рдиреБрд╣реЛрд╕реН рдХрд┐ рд╣рд╛рдореА рдХреНрд▓рд╕реНрдЯрд░ API рд╕рд░реНрднрд░рдорд╛ рдЕрдиреБрд░реЛрдзрд╣рд░реВ рд▓реЗрдЦреНрдиреЗ рдЫреИрдиреМрдВред рдпреЛ рдЧрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐, рд╣рд╛рдореА рддрдпрд╛рд░-рдирд┐рд░реНрдорд┐рдд рдкрд╛рдЗрдерди рдкреБрд╕реНрддрдХрд╛рд▓рдп рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдиреЗрдЫреМрдВ kubernetes-рдЧреНрд░рд╛рд╣рдХ:

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. рд╡рд╛рддрд╛рд╡рд░рдг рдЪрд░ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдиреБрд╣реЛрд╕реНред

рдХрдорд╛рдгреНрдб рд▓рд╛рдЗрди рд╡рд┐рдХрд▓реНрдкрд╣рд░реВрд▓реЗ рддрдкрд╛рдИрдВрд▓рд╛рдИ рдбреЗрдЯрд╛ рдкреНрд░рдХрд╛рд░ рд╕рдорд░реНрдерди рд░ рдкреНрд░рдорд╛рдгреАрдХрд░рдгрдХреЛ рд╕рд╛рде рд╕реЗрдЯрд┐рдЩрд╣рд░реВ рдердк рд▓рдЪрд┐рд▓реЛ рд░реВрдкрдорд╛ рдкрдвреНрди рдЕрдиреБрдорддрд┐ рджрд┐рдиреНрдЫред рдкрд╛рдЗрдердирдХреЛ рдорд╛рдирдХ рдкреБрд╕реНрддрдХрд╛рд▓рдпрдорд╛ рдореЛрдбреНрдпреБрд▓ рдЫ 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 

рдЕрдкрд░реЗрдЯрд░ рддрд░реНрдХ

рдХрдиреНрдлрд┐рдЧрдореНрдпрд╛рдк рд░ рд╕реЗрдХреНрд░реЗрдЯрд╕рдБрдЧ рдХрд╛рдо рдЧрд░реНрдиреЗ рд╡рд┐рдзрд┐рд╣рд░реВ рдХрд╕рд░реА рдЕрд▓рдЧ рдЧрд░реНрдиреЗ рднрдиреЗрд░ рдмреБрдЭреНрди, рд╣рд╛рдореА рд╡рд┐рд╢реЗрд╖ рдирдХреНрд╕рд╛рд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдиреЗрдЫреМрдВред рддреНрдпрд╕реЛрднрдП рд╣рд╛рдореАрд▓реЗ рд╡рд╕реНрддреБрд▓рд╛рдИ рдЯреНрд░реНрдпрд╛рдХ рдЧрд░реНрди рд░ рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдкрд░реНрдиреЗ рд╡рд┐рдзрд┐рд╣рд░реВ рдмреБрдЭреНрди рд╕рдХреНрдЫреМрдВ:

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

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

рдЕрд░реНрдХреЛ, рддрдкрд╛рдИрдВрд▓реЗ API рд╕рд░реНрднрд░рдмрд╛рдЯ рдШрдЯрдирд╛рд╣рд░реВ рдкреНрд░рд╛рдкреНрдд рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫред рдпрд╕рд▓рд╛рдИ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЧрд░реМрдВ:

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

рдкрд░рд┐рдгрд╛рдо

рдпрд╕рд░реА, рдмрд┐рдирд╛ рдбрд░, рдирд┐рдиреНрджрд╛, рд╡рд╛ рд╕рд┐рдХреНрдиреЗ рдЧреЛ, рд╣рд╛рдореАрд▓реЗ рдкрд╛рдЗрдердирдорд╛ Kubernetes рдХреЛ рд▓рд╛рдЧрд┐ рд╣рд╛рдореНрд░реЛ рдЖрдлреНрдиреИ рдЕрдкрд░реЗрдЯрд░ рдирд┐рд░реНрдорд╛рдг рдЧрд░реНрди рд╕рдХреНрд╖рдо рднрдпреМрдВред рдирд┐рд╕реНрд╕рдиреНрджреЗрд╣, рдпрд╕рдорд╛ рдЕрдЭреИ рдмрдвреНрдирдХреЛ рд▓рд╛рдЧрд┐ рдард╛рдЙрдБ рдЫ: рднрд╡рд┐рд╖реНрдпрдорд╛ рдпрд╕рд▓реЗ рдзреЗрд░реИ рдирд┐рдпрдорд╣рд░реВ рдкреНрд░рд╢реЛрдзрди рдЧрд░реНрди, рдмрд╣реБ рдереНрд░реЗрдбрд╣рд░реВрдорд╛ рдХрд╛рдо рдЧрд░реНрди, рд╕реНрд╡рддрдиреНрддреНрд░ рд░реВрдкрдорд╛ рдпрд╕рдХреЛ CRDs рдорд╛ рдкрд░рд┐рд╡рд░реНрддрдирд╣рд░реВ рдирд┐рдЧрд░рд╛рдиреА рдЧрд░реНрди рд╕рдХреНрд╖рдо рд╣реБрдиреЗрдЫред

рддрдкрд╛рдИрдВрд▓рд╛рдИ рдХреЛрдбрдХреЛ рдирдЬрд┐рдХрдмрд╛рдЯ рд╣реЗрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐, рд╣рд╛рдореАрд▓реЗ рдпрд╕рд▓рд╛рдИ рд░рд╛рдЦреЗрдХрд╛ рдЫреМрдВ рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рднрдгреНрдбрд╛рд░ред рдпрджрд┐ рддрдкрд╛рдЗрдБ рдкрд╛рдЗрдерди рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЧрд░рд┐рдПрдХрд╛ рдердк рдЧрдореНрднреАрд░ рдЕрдкрд░реЗрдЯрд░рд╣рд░реВрдХреЛ рдЙрджрд╛рд╣рд░рдгрд╣рд░реВ рдЪрд╛рд╣рдиреБрд╣реБрдиреНрдЫ рднрдиреЗ, рддрдкрд╛рдЗрдБ рдЖрдлреНрдиреЛ рдзреНрдпрд╛рди рджреБрдИрд╡рдЯрд╛ рдЕрдкрд░реЗрдЯрд░рд╣рд░реВрд▓рд╛рдИ рдореЛрдВрдЧреЛрдбрдм (рдкрд╣рд┐рд▓реЛ ╨╕ рджреЛрд╕реНрд░реЛ).

PS рд░ рдпрджрд┐ рддрдкрд╛рдЗрдБ Kubernetes рдШрдЯрдирд╛рд╣рд░реВрд╕рдБрдЧ рд╡реНрдпрд╡рд╣рд╛рд░ рдЧрд░реНрди рдзреЗрд░реИ рдЕрд▓реНрдЫреА рд╣реБрдиреБрд╣реБрдиреНрдЫ рд╡рд╛ рддрдкрд╛рдЗрдБ рдХреЗрд╡рд▓ Bash рдкреНрд░рдпреЛрдЧ рдЧрд░реНрди рдЕрднреНрдпрд╕реНрдд рд╣реБрдиреБрд╣реБрдиреНрдЫ рднрдиреЗ, рд╣рд╛рдореНрд░рд╛ рд╕рд╣рдХрд░реНрдореАрд╣рд░реВрд▓реЗ рдлрд╛рд░рдордорд╛ рддрдпрд╛рд░ рд╕рдорд╛рдзрд╛рди рддрдпрд╛рд░ рдЧрд░реЗрдХрд╛ рдЫрдиреНред рд╢реЗрд▓ рдЕрдкрд░реЗрдЯрд░ (рд╣рд╛рдореА рдШреЛрд╖рдгрд╛ рдЧрд░рд┐рдпреЛ рдпреЛ рдЕрдкреНрд░рд┐рд▓рдорд╛)ред

рдкреАрдкреАрдПрд╕

рд╣рд╛рдореНрд░реЛ рдмреНрд▓рдЧрдорд╛ рдкрдирд┐ рдкрдвреНрдиреБрд╣реЛрд╕реН:

рд╕реНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдердкреНрди