loft-sh / vcluster-sdk

Provides a toolset to create custom vcluster syncers
Apache License 2.0
25 stars 20 forks source link

Enchance vcluster SDK for easier syncing of controller managed resources #28

Open ishankhare07 opened 2 years ago

ishankhare07 commented 2 years ago

Problem Statement

The currently operation of vcluster SDK requires plugin developers to implement interfaces like Syncer and UpSyncer for their syncers and implement the bahavior of syncing of their particular CRDs.

While the directly SyncedDown root resources are relatively straight forward to implement for eg. Kservice from knative:


         ┌─────────────────────────────────────────────────────────────────┐
         │  apiVersion: serving.knative.dev/v1                             │
         │  kind: Service                                                  │
         │  metadata:                                                      │
         │    name: hello ◀────────────────────────────────────────────────┼───────┐
         │  spec:                                                          │       │
         │    containers:                                                  │       │
         │      - image: gcr.io/google-samples/hello-app:1.0               │       │
         │        name: ""                                                 │       │
         │    traffic:                                                     │       │
         │    - latestRevision: true                                       │       │
         │      percent: 100                                               │       │
         │                                                                 │       │
         │                                                                 │       │
         │                                                                 │       │
         │                                                                 │       │
         └─────────────────────────────────────────────────────────────────┘       │
                                                                                   ▼
                                                                                   Λ
                                                                                  ╱ ╲
                                                                                 ╱   ╲
                                                                                ╱     ╲
                                                                               ╱       ╲
                                                                              ╱         ╲
                                                                             ╱Plugin and ╲
                                                                            ▕ translator  ▏
                                                                             ╲           ╱
                                                                              ╲         ╱
     ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╲       ╱ ─ ─ ─
                                                                                ╲     ╱
                                                                                 ╲   ╱
                                                                                  ╲ ╱
         ┌────────────────────────────────────────────────────────────┐            V
         │  apiVersion: serving.knative.dev/v1                        │            ▲
         │  kind: Service                                             │            │
         │  metadata:                                                 │            │
         │    name: hello-x-default-x-vcluster-00001 ◀────────────────┼────────────┘
         │    namespace: vcluster                                     │
         │  spec:                                                     │
         │    containers:                                             │
         │      - image: gcr.io/google-samples/hello-app:1.0          │
         │        name: ""                                            │
         │    traffic:                                                │
         │    - latestRevision: true                                  │
         │      percent: 100                                          │
         │                                                            │
         │                                                            │
         │                                                            │
         │                                                            │
         │                                                            │
         └────────────────────────────────────────────────────────────┘

While this works fine because we can always deterministically calculate the name and namespace for the physical object and add annotations or labels on its creation to map with the corresponding virtual object. The problem quickly arises when a controller acts on one of these CRDs and a new CRD is created by the controller.

This new CRD only has the physical object in existence first and a virtual name has to be calculated and from the parent CRDs and accordingly synced to the virtual cluster. This can be demonstrated with the Config - Revision relationship of knative serving module:


              ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
                                                                 │
              │ ┌──────────────────────────────────────────────┐
                │Virtual Configuration                         │ │                             ┌───────────────────────────────────┐
              │ │=====================                         │                               │Virtual Revision                   │
                │Name: hello                                   │ │                             │=====================              │
              │ │Namespace: default                            │                               │Name: hello-00001                  │
                │                                              │ │                             │Namespace: default                 │
              │ │                                              │                               │                                   │
                │                                              │ │                             │                                   │
              │ │                                              │◀───────────────┐              └──────────────────▲────────────────┘
                │                                              │ │              │
              │ │                                              │                │                                 │
                │                                              │ │              │
              │ │                                              │                │                                 │
                └──────────────────────────────────────────────┘ │              │
              │                        ▲                                        │                                 │
                                       │                         │              │
              │                        │                                        │                                 │
                                       │                         │         Relates to
              │                        │                                        │                              UpSync
                                    UpSync                       │              │
              │                        │                                        │                                 │
      ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
              │                        │                                        │                                 │
                                       │                         │              │
              │                        │                                        │                                 │
                                       │                         │              │
              │                        │                                        │                                 │
                                       │                         │              │
              │                        │                                        │                                 │
                  ┌────────────────────────────────────────┐     │              │
              │   │Physical Configuration                  │                    │                                 │
                  │=====================                   │     │              │
              │   │Name: hello-x-default-x-vcluster        │                    └───────────────┬─────────────────┴────────────────────┐
                  │Namespace: vcluster                     │     │                              │Physical Revision                     │
              │   │                                        │                                    │=====================                 │
                  │status:                                 │─────┼───────────┐                  │Name: hello-x-default-x-vcluster-00001│
              │   │  latestRevName:                        │                 └─────────────────▶│Namespace: vcluster                   │
                  │hello-x-default-x-vcluster-00001        │     │   ┌ ─ ─ ─ ─ ─ ─ ─ ┐          │                                      │
              │   │                                        │            Controller              │                                      │
                  │                                        │     │   │    Created    │          └──────────────────────────────────────┘
              │   │                                        │          ─ ─ ─ ─ ─ ─ ─ ─
                  │                                        │     │
              │   │                                        │
                  │                                        │     │
              │   │                                        │
                  └────────────────────────────────────────┘     │
              │
                                                                 │
              │
                                                                 │
              │
                                                                 │
              │
               ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘

Here the syncing of Config objects is relatively straight forward but because the resulting revision objects a re controller created, they never carry forward the virtual-physical mapping labels. Hence we cannot easily determine a name for the Virtual Object (virtual revision) that the physical one to map to.

The typical pattern here is to:

  1. Select a related resource (config in this case for revision)
  2. Extract the right fields that give us informationg about the child resource (revision in this case)
  3. Apply some string transformation / concatanation / extraction if needed (we extract revision number)

Based on this we are able to to calculate a types.NamespacedName for the virtual object and record the right mappings for the same which can be reused further whenver reconcile happens and syncers need to step in for field updated.

Current Implementation

The current implementation for this pattern requires the plugin developer to do the following:

  1. Register an auxiliary Indexer between the related resources (pRevision -> vRevision in this case), by overriding the syncer.RegisterIndices method on the syncer object.
  2. Register an additional Watcher on the parent resource (config in this case) by overriding the syncer.ModifyController method on the syncer object.
  3. If a resource depends on multiple parent object (in our case revision might depend on config and also on routes) then those additional indices and watchers need to be registered in the above functions itself.

This design can become cumbersome and buggy very soon, hence we propose to abstract away these details in such a way that:

  1. They can be generically consumed by plugin developers with relative ease
  2. For not so generic or very specialised use cases that above mechanism should still be available for them to extend their plugin syncers.

Proposed Redesign


            ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
                                                               │
            │ ┌──────────────────────────────────────────────┐
              │Virtual Configuration                         │ │                             ┌───────────────────────────────────┐
            │ │=====================                         │                               │Virtual Revision                   │
              │Name: hello                                   │ │                             │=====================              │
            │ │Namespace: default                            │                               │Name: hello-00001                  │
              │                                              │ │                             │Namespace: default                 │
            │ │Virtual Configuration                         │                               │                                   │
              │=====================                         │ │                             │                                   │
            │ │Name:                                         │◀────────────────┐             └──────────────────▲────────────────┘
              │hello-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│ │               │                                │
            │ │aaaaaaaaaaaaaaa                               │                 │                                │
              │Namespace: default                            │ │               │                                │
            │ │                                              │                 │                                │
              └──────────────────────────────────────────────┘ │               │                                │
            │                         ▲                                        │                                │
                                      │                        │               │                                │
            │                         │                                        │                                │
                                      │                        │               │                                │
            │                         │                                        │                                │
                                      │   UpSync               │               │                                │ UpSync
            │                         │                                        │                                │
    ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴┐─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
            │                          │                                       │                                │
                                       │                       │               │                                │
            │                          │                                       │                                │
                                       │                       │               │                                │
            │                          │                                       │                           .────┼──────.
                                       │                       │               │                      _.──'     │       `───.
            │                          │                                       │                   ,─'          │            '─.
                     ┌───────────────────────────────────┐     │               └──────────────────'             │               `.
            │        │Physical Configuration             │                                     ,'               │                 `.
                     │=====================              │     │                             ,'                 │                   `.
            │        │Name: hello-x-default-x-vcluster   │                                  ╱ ┌─────────────────┴────────────────────┐╲
                     │Namespace: vcluster                │     │                           ╱  │Physical Revision                     │ ╲
            │        │                                   │                                ╱   │=====================                 │  ╲
                     │status:                            │─────┼───────────┐             ╱    │Name: hello-x-default-x-vcluster-00001│   ╲
            │        │  latestRevName:                   │                 └────────────╳────▶│Namespace: vcluster                   │    ╲
                     │hello-x-default-x-vcluster-00001   │     │   ┌ ─ ─ ─ ─ ─ ─ ─ ┐   ;      │                                      │     :
            │        │                                   │            Controller       │      │                                      │     │
                     │                                   │     │   │    Created    │   ;      └──────────────────────────────────────┘     :
            │        │------------                       │          ─ ─ ─ ─ ─ ─ ─ ─   ;                                                     :
                     │Name: hellosdfcwdfcwecfew-<hash>   │     │                      │                                                     │
            │        │                                   │                            │                                                     │
                     │                                   │     │                      │       ┌──────────────────────────────────────┐      │
            │        │                                   │                            │       │Physical Revision                     │      │
                     └───────────────────────────────────┘     │                      :       │=====================                 │      ;
            │                                                                          :      │Name:                                 │     ;
                                                               │                       │      │hello-aaaaaaaaaaaaaaaaaaaf9a4eb3e10a40│     │
            │                                                                          :      │2b8d8e0ef009070064f-00001             │     ;
                                                               │                        ╲     │Namespace: vcluster                   │    ╱
            │                                                                            ╲    │                                      │   ╱
                                                               │                          ╲   └──────────────────────────────────────┘  ╱
            │                                                                              ╲                                           ╱
             ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘                            ╲                                         ╱
                                                                                             ╲                                       ╱
                                                                                              `.                                   ,'
                                                                                                `.                               ,'
                                                                                                  `.                           ,'
                                                                                                    '─.                     ,─'
                                                                                                       `──.             _.─'
                                                                                                           `───────────'

                    ╔═════════════════════════╗
                    ║       Abstraction       ║
                    ╠═════════════════════════╩───────────────────────────────────────────────────────────────────────┐
                    │                                                                                                 │
                    │   ┌──────────────────────────────────────┐                                                      │
                    │   │                                      │                                                      │
                    │   │               Watcher                │◀═════╗                                               │
                    │   │                                      │      ║                                               │
                    │   └──────────────────────────────────────┘      ║                                               │
                    │                                                 ║                                               │
                    │                                                 ║                                               │
                    │                                                 ║                                               │
                    ├─── ──── ──── ──── ──── ──── ──── ──── ──── ──── ╬─── ──── ──── ──── ──── ──── ──── ──── ──── ───┤
                    │                                                 ║                                               │
                    │                                                 ║                                               │
                    │                                                 ║                                               │
                    │                                                 ║                                               │
                    │                                                 ▼                                               │
                    │  ┌──────────────────────────────────────┐       Λ                                               │
                    │  │                                      │      ╱ ╲                                              │
                    │  │               Indexer                │     ╱   ╲                                             │
                    │  │                                      │    ╱     ╲                                            │
                    │  └──────────────────────────────────────┘   ╱       ╲                                           │
                    │                                            ╱         ╲                                          │
                    │                                           ╱           ╲                                         │
                    │                                          ╱             ╲                                        │
                    │                                         ╱     Mapper    ╲                                       │
                    │                     ╔═════════════════▶▕       Func      ▏════════════════════╗                 │
                    │                     ║                   ╲               ╱                     ║                 │
                    │                     ║                    ╲             ╱                      ║                 │
                    │                     ║                     ╲           ╱                       ║                 │
                    │                     ║                      ╲         ╱                        ║                 │
                    │                     ║                       ╲       ╱                         ║                 │
                    │                     ║                        ╲     ╱                          ║                 │
                    │                     ║                         ╲   ╱                           ║                 │
                    │                     ║                          ╲ ╱                            ▼                 │
                    │     ┌──────────────────────────────┐            V             ┌──────────────────────────────┐  │
                    │     │                              │                          │                              │  │
                    │     │      Physical Revision       │                          │    Virtual Configuration     │  │
                    │     │                              │                          │                              │  │
                    │     └──────────────────────────────┘                          └──────────────────────────────┘  │
                    │                                                                                                 │
                    │                                                                                                 │
                    ├─────────────────────────────────────────────────────────────────────────────────────────────────┤
                    │┌──────────────────────────┐                                                                     │
                    ││         Addition         │                                                                     │
                    │└──────────────────────────┘                                                                     │
                    │                                                                                                 │
                    │                                                                                                 │
                    │        ┌──────────────────────────────────────────────────────────────────────────────┐         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │      ┌───────────────────┐                        ┌───────────────────┐      │         │
                    │        │      │ Virtual Revision  │                        │ Physical Revision │      │         │
                    │        │      │       Name        │───────────────────────▶│       Name        │      │         │
                    │        │      └───────────────────┘                        └───────────────────┘      │         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │                                                                              │         │
                    │        │                         ┌──────────────────────────┐                         │         │
                    │        │                         │        NameCache         │                         │         │
                    │        └─────────────────────────┴──────────────────────────┴─────────────────────────┘         │
                    │                                                                                                 │
                    │                                                                                                 │
                    │                                                                                                 │
                    │                                                                                                 │
                    │                                                                                                 │
                    │                                                                                                 │
                    │                                                                                                 │
                    │                                                                                                 │
                    └─────────────────────────────────────────────────────────────────────────────────────────────────┘

The way we propose to do this is to have a MapperConfig structure in the translator / syncer defined as:

type MapperConfig struct {
    ExtraIndices  IndexFunc
    ExtraWatchers []Watchers
}

type IndexFunc func(ctx *context.RegisterContext) error

type Watchers func(ctx *context.RegisterContext, builder *builder.Builder) (*builder.Builder, error)

With this added our asbtraction boils down to simply calling the method:

func (r *revisionSyncer) AddReverseMapper(
    ctx *context.RegisterContext,
    obj client.Object,
    indexName string,
    mapper MapperFunc,
    enqueuer Enqueuer) error

Along with this, an additional nameCache of the form nameCache map[types.NamespacedName]types.NamespacedName is proposed that can hold the calculated reverse mappings and be subsequently reused:

    // register this in the namecache
            r.nameCache[key] = types.NamespacedName{
                Namespace: pObj.GetNamespace(),
                Name:      pObj.GetName(),
            }
...
...
...
func (r *revisionSyncer) VirtualToPhysical(req types.NamespacedName, vObj client.Object) types.NamespacedName {
    // lookup in the nameCache
    physicalName := r.nameCache[req]

The user still needs to define:

  1. The MapperFunc
  2. The Enqueuer func

As in the previous implmentation as well but now they don't need to register indices and watchers themselves. For comparision, the code would now become something like:

func (r *revisionSyncer) Init(ctx *context.RegisterContext) error {
    ...
    ...
    r.AddReverseMapper(ctx,
        &ksvcv1.Configuration{},
        IndexByConfiguration,
        func(rawObj client.Object) []string {
            return filterRevisionFromConfiguration(ctx.TargetNamespace, rawObj)
        },
        func(obj client.Object) []reconcile.Request {
            return mapconfigs(ctx, obj)
        },
    )

    r.AddReverseMapper(ctx,
        &ksvcv1.Route{},
        IndexByConfiguration,
        func(rawObj client.Object) []string {
            return filterRouteFromConfiguration(ctx.TargetNamespace, rawObj)
        },
        func(obj client.Object) []reconcile.Request {
            return mapconfigs(ctx, obj)
        },
    )

    // we register 2 parents here

Compared to the previous approach of registering indices and watchers:

func (r *revisionSyncer) RegisterIndices(ctx *context.RegisterContext) error {
    err := ctx.VirtualManager.GetFieldIndexer().IndexField(ctx.Context, &ksvcv1.Configuration{}, IndexByConfiguration, func(rawObj client.Object) []string {
        return revisionNamesFromConfiguration(ctx.TargetNamespace, rawObj.(*ksvcv1.Configuration))
    })

    err := ctx.VirtualManager.GetFieldIndexer().IndexField(ctx.Context, &ksvcv1.Route{}, IndexByRoute, func(rawObj client.Object) []string {
        return revisionNamesFromRoute(ctx.TargetNamespace, rawObj.(*ksvcv1.Route))
    })

    if err != nil {
        return err
    }

    return r.NamespacedTranslator.RegisterIndices(ctx)
}

func (r *revisionSyncer) ModifyController(ctx *context.RegisterContext, builder *builder.Builder) (*builder.Builder, error) {
    builder = builder.Watches(&source.Kind{
        Type: &ksvcv1.Configuration{},
    }, handler.EnqueueRequestsFromMapFunc(func(obj client.Object) []reconcile.Request {
        return mapconfigs(ctx, obj)
    }))

    builder = builder.Watches(&source.Kind{
        Type: &ksvcv1.Route{},
    }, handler.EnqueueRequestsFromMapFunc(func(obj client.Object) []reconcile.Request {
        return maproutes(ctx, obj)
    }))

    return builder, nil
}
ishankhare07 commented 2 years ago

These above changes require slight modification in the vcluster-sdk core codebase itself. These would roughly be:

  1. This will tap into the correct indices and register them - plugin.go

    for _, s := range m.syncers {
        // Experimental!!
        // check if syncer.MapperConfig
        syncerReverseMapper, ok := s.(syncer.ReverseMapper)
        if ok {
            reverseMapperConfig := syncerReverseMapper.GetReverseMapper()
            reverseMapperConfig.ExtraIndices(m.context)
        }
        // ------------------------ end of experimental ------------------------ //
    
        indexRegisterer, ok := s.(syncer.IndicesRegisterer)
        if ok {
            err := indexRegisterer.RegisterIndices(m.context)
            if err != nil {
                return errors.Wrapf(err, "register indices for %s syncer", s.Name())
            }
        }
    }
  2. This will tap and register the required watchers - syncer.go
    mapperConfig, ok := r.syncer.(ReverseMapper)
    if ok {
        for _, watcher := range mapperConfig.GetWatchers() {
            controller, err = watcher(ctx, controller)
            if err != nil {
                return err
            }
        }
    }
  3. An additional Interface the base implementation for which can possibly be implemented by NamespacedTranslator itself and hence can remain out of concern of the plugin developer - types.go
    // Experimental!!!
    type ReverseMapper interface {
      GetReverseMapper() MapperConfig
      GetWatchers() []Watchers
    }