Skip to content

Commit

Permalink
Merge pull request #2037 from facchettos/runtime-class
Browse files Browse the repository at this point in the history
added runtime classes sync
  • Loading branch information
FabianKramm authored Aug 7, 2024
2 parents 6260c64 + fe98a15 commit 1657057
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 0 deletions.
1 change: 1 addition & 0 deletions chart/templates/_rbac.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
.Values.sync.toHost.volumeSnapshots.enabled
.Values.controlPlane.advanced.virtualScheduler.enabled
.Values.sync.fromHost.ingressClasses.enabled
.Values.sync.fromHost.runtimeClasses.enabled
(eq (toString .Values.sync.fromHost.storageClasses.enabled) "true")
(eq (toString .Values.sync.fromHost.csiNodes.enabled) "true")
(eq (toString .Values.sync.fromHost.csiDrivers.enabled) "true")
Expand Down
5 changes: 5 additions & 0 deletions chart/templates/clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ rules:
resources: ["ingressclasses"]
verbs: ["get", "watch", "list"]
{{- end }}
{{- if .Values.sync.fromHost.runtimeClasses.enabled }}
- apiGroups: ["nodes.k8s.io"]
resources: ["runtimeclasses"]
verbs: ["get", "watch", "list"]
{{- end }}
{{- if .Values.sync.toHost.storageClasses.enabled }}
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
Expand Down
4 changes: 4 additions & 0 deletions chart/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2701,6 +2701,10 @@
"$ref": "#/$defs/EnableSwitch",
"description": "IngressClasses defines if ingress classes should get synced from the host cluster to the virtual cluster, but not back."
},
"runtimeClasses": {
"$ref": "#/$defs/EnableSwitch",
"description": "RuntimeClasses defines if runtime classes should get synced from the host cluster to the virtual cluster, but not back."
},
"storageClasses": {
"$ref": "#/$defs/EnableAutoSwitch",
"description": "StorageClasses defines if storage classes should get synced from the host cluster to the virtual cluster, but not back. If auto, is automatically enabled when the virtual scheduler is enabled."
Expand Down
3 changes: 3 additions & 0 deletions chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ sync:
# IngressClasses defines if ingress classes should get synced from the host cluster to the virtual cluster, but not back.
ingressClasses:
enabled: false
# RuntimeClasses defines if runtime classes should get synced from the host cluster to the virtual cluster, but not back.
runtimeClasses:
enabled: false
# Nodes defines if nodes should get synced from the host cluster to the virtual cluster, but not back.
nodes:
# Enabled specifies if syncing real nodes should be enabled. If this is disabled, vCluster will create fake nodes instead.
Expand Down
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@ type SyncFromHost struct {
// IngressClasses defines if ingress classes should get synced from the host cluster to the virtual cluster, but not back.
IngressClasses EnableSwitch `json:"ingressClasses,omitempty"`

// RuntimeClasses defines if runtime classes should get synced from the host cluster to the virtual cluster, but not back.
RuntimeClasses EnableSwitch `json:"runtimeClasses,omitempty"`

// StorageClasses defines if storage classes should get synced from the host cluster to the virtual cluster, but not back. If auto, is automatically enabled when the virtual scheduler is enabled.
StorageClasses EnableAutoSwitch `json:"storageClasses,omitempty"`

Expand Down
2 changes: 2 additions & 0 deletions config/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ sync:
enabled: auto
ingressClasses:
enabled: false
runtimeClasses:
enabled: false
nodes:
enabled: false
syncBackChanges: false
Expand Down
2 changes: 2 additions & 0 deletions pkg/controllers/resources/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/loft-sh/vcluster/pkg/controllers/resources/poddisruptionbudgets"
"github.com/loft-sh/vcluster/pkg/controllers/resources/pods"
"github.com/loft-sh/vcluster/pkg/controllers/resources/priorityclasses"
"github.com/loft-sh/vcluster/pkg/controllers/resources/runtimeclasses"
"github.com/loft-sh/vcluster/pkg/controllers/resources/secrets"
"github.com/loft-sh/vcluster/pkg/controllers/resources/serviceaccounts"
"github.com/loft-sh/vcluster/pkg/controllers/resources/services"
Expand Down Expand Up @@ -50,6 +51,7 @@ func getSyncers(ctx *synccontext.RegisterContext) []BuildController {
isEnabled(ctx.Config.Sync.ToHost.PersistentVolumeClaims.Enabled, persistentvolumeclaims.New),
isEnabled(ctx.Config.Sync.ToHost.Ingresses.Enabled, ingresses.New),
isEnabled(ctx.Config.Sync.FromHost.IngressClasses.Enabled, ingressclasses.New),
isEnabled(ctx.Config.Sync.FromHost.RuntimeClasses.Enabled, runtimeclasses.New),
isEnabled(ctx.Config.Sync.ToHost.StorageClasses.Enabled, storageclasses.New),
isEnabled(ctx.Config.Sync.FromHost.StorageClasses.Enabled == "true", storageclasses.NewHostStorageClassSyncer),
isEnabled(ctx.Config.Sync.ToHost.PriorityClasses.Enabled, priorityclasses.New),
Expand Down
76 changes: 76 additions & 0 deletions pkg/controllers/resources/runtimeclasses/syncer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package runtimeclasses

import (
"fmt"

"github.com/loft-sh/vcluster/pkg/mappings/generic"
"github.com/loft-sh/vcluster/pkg/patcher"
"github.com/loft-sh/vcluster/pkg/syncer"
"github.com/loft-sh/vcluster/pkg/syncer/synccontext"
syncertypes "github.com/loft-sh/vcluster/pkg/syncer/types"
"github.com/loft-sh/vcluster/pkg/util/translate"
nodev1 "k8s.io/api/node/v1"
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func New(_ *synccontext.RegisterContext) (syncertypes.Object, error) {
mapper, err := generic.NewMirrorMapper(&nodev1.RuntimeClass{})
if err != nil {
return nil, err
}

return &runtimeClassSyncer{
Mapper: mapper,
}, nil
}

type runtimeClassSyncer struct {
synccontext.Mapper
}

func (i *runtimeClassSyncer) Name() string {
return "runtimeclass"
}

func (i *runtimeClassSyncer) Resource() client.Object {
return &nodev1.RuntimeClass{}
}

var _ syncertypes.Syncer = &runtimeClassSyncer{}

func (i *runtimeClassSyncer) Syncer() syncertypes.Sync[client.Object] {
return syncer.ToGenericSyncer[*nodev1.RuntimeClass](i)
}

func (i *runtimeClassSyncer) SyncToVirtual(ctx *synccontext.SyncContext, event *synccontext.SyncToVirtualEvent[*nodev1.RuntimeClass]) (ctrl.Result, error) {
vObj := translate.CopyObjectWithName(event.Host, types.NamespacedName{Name: event.Host.Name, Namespace: event.Host.Namespace}, false)
ctx.Log.Infof("create runtime class %s, because it does not exist in virtual cluster", vObj.Name)
return ctrl.Result{}, ctx.VirtualClient.Create(ctx, vObj)
}

func (i *runtimeClassSyncer) Sync(ctx *synccontext.SyncContext, event *synccontext.SyncEvent[*nodev1.RuntimeClass]) (_ ctrl.Result, retErr error) {
patch, err := patcher.NewSyncerPatcher(ctx, event.Host, event.Virtual)
if err != nil {
return ctrl.Result{}, fmt.Errorf("new syncer patcher: %w", err)
}
defer func() {
if err := patch.Patch(ctx, event.Host, event.Virtual); err != nil {
retErr = utilerrors.NewAggregate([]error{retErr, err})
}
}()

event.Virtual.Annotations = event.Host.Annotations
event.Virtual.Labels = event.Host.Labels
event.Virtual.Handler = event.Host.Handler
event.Virtual.Overhead = event.Host.Overhead
event.Virtual.Scheduling = event.Host.Scheduling
return ctrl.Result{}, nil
}

func (i *runtimeClassSyncer) SyncToHost(ctx *synccontext.SyncContext, event *synccontext.SyncToHostEvent[*nodev1.RuntimeClass]) (ctrl.Result, error) {
ctx.Log.Infof("delete virtual runtime class %s, because physical object is missing", event.Virtual.Name)
return ctrl.Result{}, ctx.VirtualClient.Delete(ctx, event.Virtual)
}
137 changes: 137 additions & 0 deletions pkg/controllers/resources/runtimeclasses/syncer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package runtimeclasses

import (
"testing"

"github.com/loft-sh/vcluster/pkg/syncer/synccontext"
syncertesting "github.com/loft-sh/vcluster/pkg/syncer/testing"
"github.com/loft-sh/vcluster/pkg/util/translate"
"gotest.tools/assert"
corev1 "k8s.io/api/core/v1"
nodev1 "k8s.io/api/node/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func TestSync(t *testing.T) {
vObjectMeta := metav1.ObjectMeta{
Name: "test-ingc",
Annotations: map[string]string{
translate.NameAnnotation: "test-runtimec",
translate.UIDAnnotation: "",
translate.KindAnnotation: nodev1.SchemeGroupVersion.WithKind("RuntimeClass").String(),
},
}

vObj := &nodev1.RuntimeClass{
ObjectMeta: vObjectMeta,
Scheduling: &nodev1.Scheduling{
NodeSelector: map[string]string{"stuff": "stuff"},
},
Handler: "somehandler",
Overhead: &nodev1.Overhead{
PodFixed: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("1")},
},
}

pObj := &nodev1.RuntimeClass{
ObjectMeta: metav1.ObjectMeta{
Name: vObjectMeta.Name,
Labels: map[string]string{
translate.MarkerLabel: translate.VClusterName,
},
Annotations: map[string]string{
translate.NameAnnotation: "test-runtimec",
translate.UIDAnnotation: "",
translate.KindAnnotation: nodev1.SchemeGroupVersion.WithKind("RuntimeClass").String(),
},
},
Scheduling: &nodev1.Scheduling{
NodeSelector: map[string]string{"stuff": "stuff"},
},
Handler: "somehandler",
Overhead: &nodev1.Overhead{
PodFixed: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("1")},
},
}

vObjUpdated := &nodev1.RuntimeClass{
ObjectMeta: vObjectMeta,
Scheduling: &nodev1.Scheduling{
NodeSelector: map[string]string{"stuff": "stuff2"},
},
Handler: "somehandler",
Overhead: &nodev1.Overhead{
PodFixed: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("1")},
},
}

pObjUpdated := &nodev1.RuntimeClass{
ObjectMeta: metav1.ObjectMeta{
Name: translate.Default.HostNameCluster(vObjectMeta.Name),
Labels: map[string]string{
translate.MarkerLabel: translate.VClusterName,
},
Annotations: map[string]string{
translate.NameAnnotation: "test-runtimec",
translate.UIDAnnotation: "",
translate.KindAnnotation: nodev1.SchemeGroupVersion.WithKind("RuntimeClass").String(),
},
},
Scheduling: &nodev1.Scheduling{
NodeSelector: map[string]string{"stuff": "stuff2"},
},
Handler: "somehandler",
Overhead: &nodev1.Overhead{
PodFixed: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("1")},
},
}

syncertesting.RunTests(t, []*syncertesting.SyncTest{
{
Name: "Import",
InitialVirtualState: []runtime.Object{},
InitialPhysicalState: []runtime.Object{pObj},
ExpectedVirtualState: map[schema.GroupVersionKind][]runtime.Object{
nodev1.SchemeGroupVersion.WithKind("RuntimeClass"): {vObj},
},
ExpectedPhysicalState: map[schema.GroupVersionKind][]runtime.Object{
nodev1.SchemeGroupVersion.WithKind("RuntimeClass"): {pObj},
},
Sync: func(ctx *synccontext.RegisterContext) {
syncCtx, syncer := syncertesting.FakeStartSyncer(t, ctx, New)
_, err := syncer.(*runtimeClassSyncer).SyncToVirtual(syncCtx, synccontext.NewSyncToVirtualEvent(pObj))
assert.NilError(t, err)
},
},
{
Name: "Delete virtual",
InitialVirtualState: []runtime.Object{vObj},
ExpectedVirtualState: map[schema.GroupVersionKind][]runtime.Object{},
ExpectedPhysicalState: map[schema.GroupVersionKind][]runtime.Object{},
Sync: func(ctx *synccontext.RegisterContext) {
syncCtx, syncer := syncertesting.FakeStartSyncer(t, ctx, New)
_, err := syncer.(*runtimeClassSyncer).SyncToHost(syncCtx, synccontext.NewSyncToHostEvent(vObj))
assert.NilError(t, err)
},
},
{
Name: "Sync",
InitialVirtualState: []runtime.Object{vObj},
InitialPhysicalState: []runtime.Object{pObjUpdated},
ExpectedVirtualState: map[schema.GroupVersionKind][]runtime.Object{
nodev1.SchemeGroupVersion.WithKind("RuntimeClass"): {vObjUpdated},
},
ExpectedPhysicalState: map[schema.GroupVersionKind][]runtime.Object{
nodev1.SchemeGroupVersion.WithKind("RuntimeClass"): {pObjUpdated},
},
Sync: func(ctx *synccontext.RegisterContext) {
syncCtx, syncer := syncertesting.FakeStartSyncer(t, ctx, New)
_, err := syncer.(*runtimeClassSyncer).Sync(syncCtx, synccontext.NewSyncEvent(pObjUpdated, vObj))
assert.NilError(t, err)
},
},
})
}

0 comments on commit 1657057

Please sign in to comment.