Listings Müller/Kubernetes-Operator in Go

Listing 1: Watches in Go
svcWatcher, err :=
    clientset.CoreV1().Services("").Watch(metav1.ListOptions{})
if err != nil {
    panic(err.Error())
}
for {
    select {
      case evt := <-svcWatcher.ResultChan():
          ...
}

--------

Listing 2: Informers in Go mit Callbacks
factory := informers.NewSharedInformerFactory(client, time.Second*30)
svcInformer = factory.Core().V1().Services().Informer()
svcInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
    AddFunc:    addServiceHandler,
    UpdateFunc: updateServiceHandler,
    DeleteFunc: deleteServiceHandler,
})
go cd.svcInformer.Run(wait.NeverStop)

--------
 
Listing 3: YAML-Datei mit CustomResourceDefinition
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: configurationdistributionrules.k8s.tideland.dev
spec:
  group: k8s.tideland.dev
  versions:
  - name: v1alpha1
    served: true
    storage: true
  scope: Namespaced
  names:
    plural: configurationdistributionrules
    singular: configurationdistributionrule
    kind: ConfigurationDistributionRule
    shortNames:
    - cdr
    - codisrule
    - rule
  validation:
    openAPIV3Schema:
      type: object
      properties:
        mode:
          type: string
          pattern: '^(configmap)|(secret)|(both)$'
        namespaces:
          type: array
          items:
            type: string
        selector:
          type: string
          pattern: '^\w$'

--------

Listing 4: Strukturen für Spezifikation und Regel
type ConfigurationDistributionRuleSpec struct {
    Mode       string   `json:"mode"`
    Selector   string   `json:"selector"`
    Namespaces []string `json:"namespaces"`
}

type ConfigurationDistributionRule struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`

    Spec ConfigurationDistributionRuleSpec `json:"spec"`
}

--------

Listing 5: Aktualisierung des Schemas
var (
    // SchemeGroupVersion describes the CRD.
    SchemeGroupVersion = schema.GroupVersion{
      Group:   "k8s.tideland.dev",
      Version: "v1alpha1",
  }

    // SchemeBuilder creates a scheme builder for the known types.
    SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)

    // AddToScheme points to a function to create the known types.
    AddToScheme = SchemeBuilder.AddToScheme
)

// addKnownTypes adds our new types.
func addKnownTypes(scheme *runtime.Scheme) error {
    scheme.AddKnownTypes(SchemeGroupVersion,
        &ConfigurationDistributionRule{},
        &ConfigurationDistributionRuleList{},
    )

    metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
    return nil
}

--------

Listing 6: REST-Client „client-go“ nutzen
func (ri *ruleInterface) Create(
    rule *ConfigurationDistributionRule, 
    opts metav1.CreateOptions,
) (*ConfigurationDistributionRule, error) {
    result := ConfigurationDistributionRule{}
    err := ri.restClient.
        Post().
        Namespace(ri.namespace).
        Resource("configurationdistributionrules").
        Body(rule).
        VersionedParams(&opts, scheme.ParameterCodec).
        Do().
        Into(&result)
    return &result, err
} 

--------

Listing 7: Beobachtung sich verändernder Regeln
type RuleInformer interface {
    Informer() cache.SharedIndexInformer
    Lister() RuleLister
}

func (ri *ruleInformer) Informer() cache.SharedIndexInformer {
    return cache.NewSharedIndexInformer(
        &cache.ListWatch{
            ListFunc: func(opts metav1.ListOptions) (result runtime.Object, err error) {
                return ri.rif.List(opts)
            },
            WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
                return ri.rif.Watch(opts)
            },
        },
        &ConfigurationDistributionRule{},
        30*time.Second,
        cache.Indexers{},
    )
}

func (ri *ruleInformer) Lister() RuleLister {
    return &ruleLister{
        rif: ri.rif,
    }
} 

--------

Listing 8: Lesender Zugriff auf Regeln
type RuleLister interface {
    List(selector labels.Selector) ([]*ConfigurationDistributionRule, error)
    Get(name string) (*ConfigurationDistributionRule, error)
}

func (rl *ruleLister) List(selector labels.Selector) ([]*ConfigurationDistributionRule, error) {
    cdrl, err := rl.rif.List(metav1.ListOptions{
        LabelSelector: selector.String(),
    })
    if err != nil {
        return nil, err
    }
    list := make([]*ConfigurationDistributionRule, len(cdrl.Items))
    for i, rule := range cdrl.Items {
        list[i] = &rule
    }
    return list, nil
}

func (rl *ruleLister) Get(name string) (*ConfigurationDistributionRule, error) {
    return rl.rif.Get(name, metav1.GetOptions{})
}

--------

Listing 9: Hauptfunktion des ConfigurationDistributor
// Run executes the configuration distributor.
func (cd *ConfigurationDistributor) Run(ctx context.Context) {
    cd.ruleInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
        AddFunc:    cd.addRuleHandler,
        UpdateFunc: cd.updateRuleHandler,
        DeleteFunc: cd.deleteRuleHandler,
    })
    cd.cmInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
        AddFunc:    cd.addConfigMapHandler,
        UpdateFunc: cd.updateConfigMapHandler,
    })

  ...

    go cd.ruleInformer.Run(wait.NeverStop)
    go cd.cmInformer.Run(wait.NeverStop)
    go cd.scrtInformer.Run(wait.NeverStop)
    go cd.nsInformer.Run(wait.NeverStop)
    
    select {
    case <-ctx.Done():
        // Work is done.
    }
}

--------

Listing 10: Callback für hinzugefügte ConfigMap
func (cd *ConfigurationDistributor) addConfigMapHandler(
    obj interface{},
) {
    if cd.rule == nil {
        return
    }
    if cd.rule.Spec.Mode != "configmap" && 
        cd.rule.Spec.Mode != "both" {
        return
    }
    cm := obj.(*corev1.ConfigMap)
    if cm.GetNamespace() != cd.namespace {
        return
    }
    if cd.rule.Spec.Selector != "" {
        if cm.GetLabels()["rule"] != cd.rule.Spec.Selector {
            return
        }
    }
    cd.applyConfigMap(cm, true)
}

--------

Listing 11: Ressourcenbeschreibung für Kubernetes
apiVersion: k8s.tideland.dev/v1alpha1
kind: ConfigurationDistributionRule
metadata:
    name: rule-codis-test
    namespace: ns-codis-test
spec:
    mode: both
    selector: test
    namespaces:
      - default
      - another-test