karmada/pkg/resourceinterpreter/configurableinterpreter/configmanager/manager.go

123 lines
4.2 KiB
Go

package configmanager
import (
"fmt"
"sort"
"sync/atomic"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
"github.com/karmada-io/karmada/pkg/util/fedinformer"
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
"github.com/karmada-io/karmada/pkg/util/helper"
)
var resourceInterpreterCustomizationsGVR = schema.GroupVersionResource{
Group: configv1alpha1.GroupVersion.Group,
Version: configv1alpha1.GroupVersion.Version,
Resource: "resourceinterpretercustomizations",
}
// ConfigManager can list custom resource interpreter.
type ConfigManager interface {
LuaScriptAccessors() map[schema.GroupVersionKind]CustomAccessor
HasSynced() bool
LoadConfig(customizations []*configv1alpha1.ResourceInterpreterCustomization)
}
// interpreterConfigManager collects the resource interpreter customization.
type interpreterConfigManager struct {
initialSynced atomic.Bool
lister cache.GenericLister
configuration atomic.Value
}
// LuaScriptAccessors returns all cached configurations.
func (configManager *interpreterConfigManager) LuaScriptAccessors() map[schema.GroupVersionKind]CustomAccessor {
return configManager.configuration.Load().(map[schema.GroupVersionKind]CustomAccessor)
}
// HasSynced returns true when the cache is synced.
func (configManager *interpreterConfigManager) HasSynced() bool {
if configManager.initialSynced.Load() {
return true
}
if configuration, err := configManager.lister.List(labels.Everything()); err == nil && len(configuration) == 0 {
// the empty list we initially stored is valid to use.
// Setting initialSynced to true, so subsequent checks
// would be able to take the fast path on the atomic boolean in a
// cluster without any customization configured.
configManager.initialSynced.Store(true)
// the informer has synced, and we don't have any items
return true
}
return false
}
// NewInterpreterConfigManager watches ResourceInterpreterCustomization and organizes
// the configurations in the cache.
func NewInterpreterConfigManager(informer genericmanager.SingleClusterInformerManager) ConfigManager {
manager := &interpreterConfigManager{}
manager.configuration.Store(make(map[schema.GroupVersionKind]CustomAccessor))
// In interpret command, rules are not loaded from server, so we don't start informer for it.
if informer != nil {
manager.lister = informer.Lister(resourceInterpreterCustomizationsGVR)
configHandlers := fedinformer.NewHandlerOnEvents(
func(_ interface{}) { manager.updateConfiguration() },
func(_, _ interface{}) { manager.updateConfiguration() },
func(_ interface{}) { manager.updateConfiguration() })
informer.ForResource(resourceInterpreterCustomizationsGVR, configHandlers)
}
return manager
}
func (configManager *interpreterConfigManager) updateConfiguration() {
configurations, err := configManager.lister.List(labels.Everything())
if err != nil {
utilruntime.HandleError(fmt.Errorf("error updating configuration: %v", err))
return
}
configs := make([]*configv1alpha1.ResourceInterpreterCustomization, len(configurations))
for index, c := range configurations {
config := &configv1alpha1.ResourceInterpreterCustomization{}
if err = helper.ConvertToTypedObject(c, config); err != nil {
klog.Errorf("Failed to transform ResourceInterpreterCustomization: %w", err)
return
}
configs[index] = config
}
configManager.LoadConfig(configs)
}
func (configManager *interpreterConfigManager) LoadConfig(configs []*configv1alpha1.ResourceInterpreterCustomization) {
sort.Slice(configs, func(i, j int) bool {
return configs[i].Name < configs[j].Name
})
accessors := make(map[schema.GroupVersionKind]CustomAccessor)
for _, config := range configs {
key := schema.FromAPIVersionAndKind(config.Spec.Target.APIVersion, config.Spec.Target.Kind)
var accessor CustomAccessor
var ok bool
if accessor, ok = accessors[key]; !ok {
accessor = NewResourceCustomAccessor()
}
accessor.Merge(config.Spec.Customizations)
accessors[key] = accessor
}
configManager.configuration.Store(accessors)
configManager.initialSynced.Store(true)
}