Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions pkg/apis/config/v1alpha1/resourceinterpretercustomization_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ type CustomizationRules struct {
// +optional
HealthInterpretation *HealthInterpretation `json:"healthInterpretation,omitempty"`

// SchedulingResultInterpretation describes the rules for Karmada to customize
// the scheduling result for distributing replicas among clusters.
// This allows customizing how remainders in replica distribution are handled
// or syncing replica changes from member clusters back to the control plane.
// +optional
SchedulingResultInterpretation *SchedulingResultInterpretation `json:"schedulingResultInterpretation,omitempty"`

// DependencyInterpretation describes the rules for Karmada to analyze the
// dependent resources.
// Karmada provides built-in rules for several standard Kubernetes types, see:
Expand Down Expand Up @@ -370,6 +377,36 @@ type HealthInterpretation struct {
LuaScript string `json:"luaScript"`
}

// SchedulingResultInterpretation holds the rules for customizing the scheduling result
// for distributing replicas among clusters.
type SchedulingResultInterpretation struct {
// LuaScript holds the Lua script that is used to customize the scheduling result.
// The script should implement a function as follows:
//
// ```
// luaScript: >
// function InterpretSchedulingResult(obj, schedulingResult)
// -- obj: the resource object being scheduled
// -- schedulingResult: array of TargetCluster with name and replicas
// -- Return modified schedulingResult array
// return schedulingResult
// end
// ```
//
// The content of the LuaScript needs to be a whole function including both
// declaration and implementation.
//
// The parameters will be supplied by the system:
// - obj: the resource object being scheduled
// - schedulingResult: array of TargetCluster objects with name and replicas fields
//
// The returned value should be an array of TargetCluster objects representing
// the customized scheduling result.
//
// +required
LuaScript string `json:"luaScript"`
}

// DependencyInterpretation holds the rules for interpreting the dependent resources
// of a specific resources.
type DependencyInterpretation struct {
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/config/v1alpha1/resourceinterpreterwebhook_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ const (
// Only necessary for those resource types that have dependencies resources and expect the dependencies be propagated
// together, like Deployment depends on ConfigMap/Secret.
InterpreterOperationInterpretDependency InterpreterOperation = "InterpretDependency"

// InterpreterOperationInterpretSchedulingResult indicates that karmada wants to customize the scheduling result.
// This allows customizing how replicas are distributed among clusters, especially for handling remainders
// or when member clusters have modified replica counts that should be reflected back to the control plane.
InterpreterOperationInterpretSchedulingResult InterpreterOperation = "InterpretSchedulingResult"
)

// Rule is a tuple of APIGroups, APIVersion, and Kinds.
Expand Down
32 changes: 4 additions & 28 deletions pkg/apis/config/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type LuaScriptAccessor interface {
GetStatusAggregationLuaScript() string
GetHealthInterpretationLuaScript() string
GetDependencyInterpretationLuaScripts() []string
GetSchedulingResultInterpretationLuaScript() string
}

// CustomAccessor provides a common interface to get custom interpreter configuration.
Expand All @@ -45,14 +46,15 @@ type CustomAccessor interface {
}

type resourceCustomAccessor struct {
retention *configv1alpha1.LocalValueRetention
replicaResource *configv1alpha1.ReplicaResourceRequirement
componentResource *configv1alpha1.ComponentResourceRequirement
replicaRevision *configv1alpha1.ReplicaRevision
statusReflection *configv1alpha1.StatusReflection
statusAggregation *configv1alpha1.StatusAggregation
healthInterpretation *configv1alpha1.HealthInterpretation
dependencyInterpretations []*configv1alpha1.DependencyInterpretation
retention *configv1alpha1.LocalValueRetention
replicaResource *configv1alpha1.ReplicaResourceRequirement
componentResource *configv1alpha1.ComponentResourceRequirement
replicaRevision *configv1alpha1.ReplicaRevision
statusReflection *configv1alpha1.StatusReflection
statusAggregation *configv1alpha1.StatusAggregation
healthInterpretation *configv1alpha1.HealthInterpretation
schedulingResultInterpretation *configv1alpha1.SchedulingResultInterpretation
dependencyInterpretations []*configv1alpha1.DependencyInterpretation
}

// NewResourceCustomAccessor creates an accessor for resource interpreter customization.
Expand Down Expand Up @@ -83,6 +85,9 @@ func (a *resourceCustomAccessor) Merge(rules configv1alpha1.CustomizationRules)
if rules.HealthInterpretation != nil {
a.setHealthInterpretation(rules.HealthInterpretation)
}
if rules.SchedulingResultInterpretation != nil {
a.setSchedulingResultInterpretation(rules.SchedulingResultInterpretation)
}
if rules.DependencyInterpretation != nil {
a.appendDependencyInterpretation(rules.DependencyInterpretation)
}
Expand Down Expand Up @@ -137,6 +142,13 @@ func (a *resourceCustomAccessor) GetHealthInterpretationLuaScript() string {
return a.healthInterpretation.LuaScript
}

func (a *resourceCustomAccessor) GetSchedulingResultInterpretationLuaScript() string {
if a.schedulingResultInterpretation == nil {
return ""
}
return a.schedulingResultInterpretation.LuaScript
}

func (a *resourceCustomAccessor) GetDependencyInterpretationLuaScripts() []string {
if a.dependencyInterpretations == nil {
return nil
Expand Down Expand Up @@ -228,6 +240,17 @@ func (a *resourceCustomAccessor) setHealthInterpretation(healthInterpretation *c
}
}

func (a *resourceCustomAccessor) setSchedulingResultInterpretation(schedulingResultInterpretation *configv1alpha1.SchedulingResultInterpretation) {
if a.schedulingResultInterpretation == nil {
a.schedulingResultInterpretation = schedulingResultInterpretation
return
}

if schedulingResultInterpretation.LuaScript != "" && a.schedulingResultInterpretation.LuaScript == "" {
a.schedulingResultInterpretation.LuaScript = schedulingResultInterpretation.LuaScript
}
}

func (a *resourceCustomAccessor) appendDependencyInterpretation(dependencyInterpretation *configv1alpha1.DependencyInterpretation) {
a.dependencyInterpretations = append(a.dependencyInterpretations, dependencyInterpretation)
}
19 changes: 19 additions & 0 deletions pkg/resourceinterpreter/customized/declarative/configurable.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,25 @@ func (c *ConfigurableInterpreter) InterpretHealth(object *unstructured.Unstructu
return
}

// InterpretSchedulingResult allows customizing the scheduling result for distributing replicas among clusters.
func (c *ConfigurableInterpreter) InterpretSchedulingResult(object *unstructured.Unstructured, schedulingResult []workv1alpha2.TargetCluster) (result []workv1alpha2.TargetCluster, enabled bool, err error) {
klog.V(4).Infof("Interpret scheduling result for object: %v %s/%s with configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())

accessor, enabled := c.getCustomAccessor(object.GroupVersionKind())
if !enabled {
return
}

script := accessor.GetSchedulingResultInterpretationLuaScript()
if len(script) == 0 {
enabled = false
return
}

result, err = c.luaVM.InterpretSchedulingResult(object, schedulingResult, script)
return
}

func (c *ConfigurableInterpreter) getCustomAccessor(kind schema.GroupVersionKind) (configmanager.CustomAccessor, bool) {
if !c.configManager.HasSynced() {
klog.Errorf("not yet ready to handle request")
Expand Down
15 changes: 15 additions & 0 deletions pkg/resourceinterpreter/customized/declarative/luavm/lua.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,21 @@ func (vm *VM) InterpretHealth(object *unstructured.Unstructured, script string)
return health, nil
}

// InterpretSchedulingResult customizes the scheduling result by lua script.
func (vm *VM) InterpretSchedulingResult(object *unstructured.Unstructured, schedulingResult []workv1alpha2.TargetCluster, script string) ([]workv1alpha2.TargetCluster, error) {
results, err := vm.RunScript(script, "InterpretSchedulingResult", 1, object, schedulingResult)
if err != nil {
return nil, err
}

var result []workv1alpha2.TargetCluster
result, err = ConvertLuaResultToTargetClusters(results[0])
if err != nil {
return nil, err
}
return result, nil
}

// ReflectStatus returns the status of the object by lua.
func (vm *VM) ReflectStatus(object *unstructured.Unstructured, script string) (status *runtime.RawExtension, err error) {
results, err := vm.RunScript(script, "ReflectStatus", 1, object)
Expand Down
Loading
Loading