Merge pull request #934 from XiShanYongYe-Chang/resource-explore-interface
Add interface design for Resource Explore Webhook
This commit is contained in:
commit
f7f53ee13f
|
@ -0,0 +1,76 @@
|
|||
package crdexplorer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
||||
"github.com/karmada-io/karmada/pkg/crdexplorer/customizedexplorer"
|
||||
"github.com/karmada-io/karmada/pkg/crdexplorer/defaultexplorer"
|
||||
"github.com/karmada-io/karmada/pkg/util/informermanager"
|
||||
)
|
||||
|
||||
// CustomResourceExplorer manages both default and customized webhooks to explore custom resource structure.
|
||||
type CustomResourceExplorer interface {
|
||||
// Start starts running the component and will never stop running until the context is closed or an error occurs.
|
||||
Start(ctx context.Context) (err error)
|
||||
// GetReplicas returns the desired replicas of the object as well as the requirements of each replica.
|
||||
GetReplicas(object runtime.Object) (replica int32, replicaRequires *workv1alpha2.ReplicaRequirements, err error)
|
||||
// GetHealthy tells if the object in healthy state.
|
||||
GetHealthy(object runtime.Object) (healthy bool, err error)
|
||||
|
||||
// other common method
|
||||
}
|
||||
|
||||
// NewCustomResourceExplore return a new CustomResourceExplorer object.
|
||||
func NewCustomResourceExplore(kubeconfig string, informer informermanager.SingleClusterInformerManager) CustomResourceExplorer {
|
||||
return &customResourceExplorerImpl{
|
||||
kubeconfig: kubeconfig,
|
||||
informer: informer,
|
||||
}
|
||||
}
|
||||
|
||||
type customResourceExplorerImpl struct {
|
||||
kubeconfig string
|
||||
informer informermanager.SingleClusterInformerManager
|
||||
|
||||
customizedExplorer *customizedexplorer.CustomizedExplorer
|
||||
defaultExplorer *defaultexplorer.DefaultExplorer
|
||||
}
|
||||
|
||||
// Start starts running the component and will never stop running until the context is closed or an error occurs.
|
||||
func (i *customResourceExplorerImpl) Start(ctx context.Context) (err error) {
|
||||
klog.Infof("Starting custom resource explorer.")
|
||||
|
||||
i.customizedExplorer, err = customizedexplorer.NewCustomizedExplorer(i.kubeconfig, i.informer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.defaultExplorer = defaultexplorer.NewDefaultExplorer()
|
||||
|
||||
i.informer.Start()
|
||||
<-ctx.Done()
|
||||
klog.Infof("Stopped as stopCh closed.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetReplicas returns the desired replicas of the object as well as the requirements of each replica.
|
||||
func (i *customResourceExplorerImpl) GetReplicas(object runtime.Object) (replica int32, replicaRequires *workv1alpha2.ReplicaRequirements, err error) {
|
||||
var hookMatched bool
|
||||
replica, replicaRequires, hookMatched, err = i.customizedExplorer.GetReplicas(context.TODO(), configv1alpha1.ExploreReplica, object)
|
||||
if hookMatched {
|
||||
return
|
||||
}
|
||||
|
||||
replica, replicaRequires, err = i.defaultExplorer.GetReplicas(object)
|
||||
return
|
||||
}
|
||||
|
||||
// GetHealthy tells if the object in healthy state.
|
||||
func (i *customResourceExplorerImpl) GetHealthy(object runtime.Object) (healthy bool, err error) {
|
||||
return false, nil
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package configmanager
|
||||
|
||||
import (
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
|
||||
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||
)
|
||||
|
||||
// WebhookAccessor provides a common interface to get webhook configuration.
|
||||
type WebhookAccessor interface {
|
||||
// GetUID gets a string that uniquely identifies the webhook.
|
||||
GetUID() string
|
||||
// GetConfigurationName gets the name of the webhook configuration that owns this webhook.
|
||||
GetConfigurationName() string
|
||||
// GetClientConfig gets the webhook ClientConfig field.
|
||||
GetClientConfig() admissionregistrationv1.WebhookClientConfig
|
||||
// GetRules gets the webhook Rules field.
|
||||
GetRules() []configv1alpha1.RuleWithOperations
|
||||
// GetFailurePolicy gets the webhook FailurePolicy field.
|
||||
GetFailurePolicy() *admissionregistrationv1.FailurePolicyType
|
||||
// GetTimeoutSeconds gets the webhook TimeoutSeconds field.
|
||||
GetTimeoutSeconds() *int32
|
||||
// GetExploreReviewVersions gets the webhook ExploreReviewVersions field.
|
||||
GetExploreReviewVersions() []string
|
||||
}
|
||||
|
||||
type resourceExploringAccessor struct {
|
||||
*configv1alpha1.ResourceExploringWebhook
|
||||
uid string
|
||||
configurationName string
|
||||
}
|
||||
|
||||
// NewResourceExploringAccessor create an accessor for webhook.
|
||||
func NewResourceExploringAccessor(uid, configurationName string, hook *configv1alpha1.ResourceExploringWebhook) WebhookAccessor {
|
||||
return &resourceExploringAccessor{uid: uid, configurationName: configurationName, ResourceExploringWebhook: hook}
|
||||
}
|
||||
|
||||
// GetUID gets a string that uniquely identifies the webhook.
|
||||
func (a *resourceExploringAccessor) GetUID() string {
|
||||
return a.uid
|
||||
}
|
||||
|
||||
// GetConfigurationName gets the name of the webhook configuration that owns this webhook.
|
||||
func (a *resourceExploringAccessor) GetConfigurationName() string {
|
||||
return a.configurationName
|
||||
}
|
||||
|
||||
// GetClientConfig gets the webhook ClientConfig field.
|
||||
func (a *resourceExploringAccessor) GetClientConfig() admissionregistrationv1.WebhookClientConfig {
|
||||
return a.ClientConfig
|
||||
}
|
||||
|
||||
// GetRules gets the webhook Rules field.
|
||||
func (a *resourceExploringAccessor) GetRules() []configv1alpha1.RuleWithOperations {
|
||||
return a.Rules
|
||||
}
|
||||
|
||||
// GetFailurePolicy gets the webhook FailurePolicy field.
|
||||
func (a *resourceExploringAccessor) GetFailurePolicy() *admissionregistrationv1.FailurePolicyType {
|
||||
return a.FailurePolicy
|
||||
}
|
||||
|
||||
// GetTimeoutSeconds gets the webhook TimeoutSeconds field.
|
||||
func (a *resourceExploringAccessor) GetTimeoutSeconds() *int32 {
|
||||
return a.TimeoutSeconds
|
||||
}
|
||||
|
||||
// GetExploreReviewVersions gets the webhook ExploreReviewVersions field.
|
||||
func (a *resourceExploringAccessor) GetExploreReviewVersions() []string {
|
||||
return a.ExploreReviewVersions
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package configmanager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||
"github.com/karmada-io/karmada/pkg/util/informermanager"
|
||||
)
|
||||
|
||||
var resourceExploringWebhookConfigurationsGVR = schema.GroupVersionResource{
|
||||
Group: configv1alpha1.GroupVersion.Group,
|
||||
Version: configv1alpha1.GroupVersion.Version,
|
||||
Resource: "resourceexploringwebhookconfigurations",
|
||||
}
|
||||
|
||||
// ConfigManager can list dynamic webhooks.
|
||||
type ConfigManager interface {
|
||||
HookAccessors() []WebhookAccessor
|
||||
HasSynced() bool
|
||||
}
|
||||
|
||||
// exploreConfigManager collect the resource explore webhook configuration.
|
||||
type exploreConfigManager struct {
|
||||
configuration *atomic.Value
|
||||
lister cache.GenericLister
|
||||
hasSynced func() bool
|
||||
initialConfigurationSynced *atomic.Value
|
||||
}
|
||||
|
||||
// HookAccessors return all configured resource explore webhook.
|
||||
func (m *exploreConfigManager) HookAccessors() []WebhookAccessor {
|
||||
return m.configuration.Load().([]WebhookAccessor)
|
||||
}
|
||||
|
||||
// HasSynced return true when the manager is synced with existing configuration.
|
||||
func (m *exploreConfigManager) HasSynced() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// NewExploreConfigManager return a new exploreConfigManager with resourceexploringwebhookconfigurations handlers.
|
||||
func NewExploreConfigManager(inform informermanager.SingleClusterInformerManager) ConfigManager {
|
||||
manager := &exploreConfigManager{
|
||||
configuration: &atomic.Value{},
|
||||
lister: inform.Lister(resourceExploringWebhookConfigurationsGVR),
|
||||
hasSynced: func() bool { return inform.IsInformerSynced(resourceExploringWebhookConfigurationsGVR) },
|
||||
initialConfigurationSynced: &atomic.Value{},
|
||||
}
|
||||
|
||||
manager.configuration.Store([]WebhookAccessor{})
|
||||
manager.initialConfigurationSynced.Store(false)
|
||||
|
||||
configHandlers := informermanager.NewHandlerOnEvents(
|
||||
func(_ interface{}) { manager.updateConfiguration() },
|
||||
func(_, _ interface{}) { manager.updateConfiguration() },
|
||||
func(_ interface{}) { manager.updateConfiguration() })
|
||||
inform.ForResource(resourceExploringWebhookConfigurationsGVR, configHandlers)
|
||||
|
||||
return manager
|
||||
}
|
||||
|
||||
func (m *exploreConfigManager) updateConfiguration() {
|
||||
//configurations, err := m.lister.List(labels.Everything())
|
||||
//if err != nil {
|
||||
// utilruntime.HandleError(fmt.Errorf("error updating configuration: %v", err))
|
||||
// return
|
||||
//}
|
||||
configurations := make([]configv1alpha1.ResourceExploringWebhookConfiguration, 0)
|
||||
|
||||
m.configuration.Store(mergeResourceExploreWebhookConfigurations(configurations))
|
||||
m.initialConfigurationSynced.Store(true)
|
||||
}
|
||||
|
||||
func mergeResourceExploreWebhookConfigurations(configurations []configv1alpha1.ResourceExploringWebhookConfiguration) []WebhookAccessor {
|
||||
sort.SliceStable(configurations, func(i, j int) bool {
|
||||
return configurations[i].Name < configurations[j].Name
|
||||
})
|
||||
|
||||
accessors := make([]WebhookAccessor, len(configurations))
|
||||
for _, config := range configurations {
|
||||
names := map[string]int{}
|
||||
for index, hook := range config.Webhooks {
|
||||
uid := fmt.Sprintf("%s/%s/%d", config.Name, hook.Name, names[hook.Name])
|
||||
names[hook.Name]++
|
||||
accessors = append(accessors, NewResourceExploringAccessor(uid, config.Name, &config.Webhooks[index]))
|
||||
}
|
||||
}
|
||||
|
||||
return accessors
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package customizedexplorer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
|
||||
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
||||
"github.com/karmada-io/karmada/pkg/crdexplorer/customizedexplorer/configmanager"
|
||||
"github.com/karmada-io/karmada/pkg/util/informermanager"
|
||||
)
|
||||
|
||||
// CustomizedExplorer explore custom resource with webhook configuration.
|
||||
type CustomizedExplorer struct {
|
||||
hookManager configmanager.ConfigManager
|
||||
configManager *webhookutil.ClientManager
|
||||
}
|
||||
|
||||
// NewCustomizedExplorer return a new CustomizedExplorer.
|
||||
func NewCustomizedExplorer(kubeconfig string, informer informermanager.SingleClusterInformerManager) (*CustomizedExplorer, error) {
|
||||
cm, err := webhookutil.NewClientManager(
|
||||
[]schema.GroupVersion{configv1alpha1.SchemeGroupVersion},
|
||||
configv1alpha1.AddToScheme,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authInfoResolver, err := webhookutil.NewDefaultAuthenticationInfoResolver(kubeconfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cm.SetAuthenticationInfoResolver(authInfoResolver)
|
||||
cm.SetServiceResolver(webhookutil.NewDefaultServiceResolver())
|
||||
|
||||
return &CustomizedExplorer{
|
||||
hookManager: configmanager.NewExploreConfigManager(informer),
|
||||
configManager: &cm,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetReplicas returns the desired replicas of the object as well as the requirements of each replica.
|
||||
// return matched value to indicate whether there is a matching hook.
|
||||
func (e *CustomizedExplorer) GetReplicas(ctx context.Context, operation configv1alpha1.OperationType,
|
||||
object runtime.Object) (replica int32, replicaRequires *workv1alpha2.ReplicaRequirements, matched bool, err error) {
|
||||
return 0, nil, false, err
|
||||
}
|
||||
|
||||
// GetHealthy tells if the object in healthy state.
|
||||
// return matched value to indicate whether there is a matching hook.
|
||||
func (e *CustomizedExplorer) GetHealthy(ctx context.Context, operation configv1alpha1.OperationType,
|
||||
object runtime.Object) (healthy, matched bool, err error) {
|
||||
return false, false, err
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package defaultexplorer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
||||
"github.com/karmada-io/karmada/pkg/util"
|
||||
)
|
||||
|
||||
// DefaultExplorer contains all default operation explorer factory
|
||||
// for exploring common resource.
|
||||
type DefaultExplorer struct {
|
||||
replicaHandlers map[schema.GroupVersionKind]replicaFactory
|
||||
packingHandlers map[schema.GroupVersionKind]packingFactory
|
||||
healthyHandlers map[schema.GroupVersionKind]healthyFactory
|
||||
}
|
||||
|
||||
// NewDefaultExplorer return a new DefaultExplorer.
|
||||
func NewDefaultExplorer() *DefaultExplorer {
|
||||
return &DefaultExplorer{
|
||||
replicaHandlers: getAllDefaultReplicaExplorer(),
|
||||
packingHandlers: getAllDefaultPackingExplorer(),
|
||||
healthyHandlers: getAllDefaultHealthyExplorer(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetReplicas returns the desired replicas of the object as well as the requirements of each replica.
|
||||
func (e *DefaultExplorer) GetReplicas(object runtime.Object) (int32, *workv1alpha2.ReplicaRequirements, error) {
|
||||
// judge object type, and then get correct kind.
|
||||
_, exist := e.replicaHandlers[appsv1.SchemeGroupVersion.WithKind(util.DeploymentKind)]
|
||||
if !exist {
|
||||
return 0, &workv1alpha2.ReplicaRequirements{}, fmt.Errorf("defalut explorer for operation %s not found", configv1alpha1.ExploreReplica)
|
||||
}
|
||||
return e.replicaHandlers[appsv1.SchemeGroupVersion.WithKind(util.DeploymentKind)](object)
|
||||
}
|
||||
|
||||
// GetHealthy tells if the object in healthy state.
|
||||
func (e *DefaultExplorer) GetHealthy(object runtime.Object) (bool, error) {
|
||||
// judge object type, and then get correct kind.
|
||||
_, exist := e.healthyHandlers[appsv1.SchemeGroupVersion.WithKind(util.DeploymentKind)]
|
||||
if !exist {
|
||||
return false, fmt.Errorf("defalut explorer for operation %s not found", configv1alpha1.ExploreHealthy)
|
||||
}
|
||||
return e.healthyHandlers[appsv1.SchemeGroupVersion.WithKind(util.DeploymentKind)](object)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package defaultexplorer
|
||||
|
||||
import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/karmada-io/karmada/pkg/util"
|
||||
)
|
||||
|
||||
// healthyFactory return default healthy factory that tells if the object in healthy state.
|
||||
type healthyFactory func(object runtime.Object) (bool, error)
|
||||
|
||||
func getAllDefaultHealthyExplorer() map[schema.GroupVersionKind]healthyFactory {
|
||||
explorers := make(map[schema.GroupVersionKind]healthyFactory)
|
||||
explorers[appsv1.SchemeGroupVersion.WithKind(util.DeploymentKind)] = deployHealthyExplorer
|
||||
explorers[batchv1.SchemeGroupVersion.WithKind(util.JobKind)] = jobHealthyExplorer
|
||||
return explorers
|
||||
}
|
||||
|
||||
func deployHealthyExplorer(object runtime.Object) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func jobHealthyExplorer(object runtime.Object) (bool, error) {
|
||||
return false, nil
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package defaultexplorer
|
||||
|
||||
import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/karmada-io/karmada/pkg/util"
|
||||
)
|
||||
|
||||
// packingFactory return default packing factory that can be used to
|
||||
// retain necessary field and packing from the input object.
|
||||
type packingFactory func(object runtime.Object) ([]byte, error)
|
||||
|
||||
func getAllDefaultPackingExplorer() map[schema.GroupVersionKind]packingFactory {
|
||||
explorers := make(map[schema.GroupVersionKind]packingFactory)
|
||||
explorers[appsv1.SchemeGroupVersion.WithKind(util.DeploymentKind)] = deployPackingExplorer
|
||||
explorers[batchv1.SchemeGroupVersion.WithKind(util.JobKind)] = jobPackingExplorer
|
||||
return explorers
|
||||
}
|
||||
|
||||
func deployPackingExplorer(object runtime.Object) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func jobPackingExplorer(object runtime.Object) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package defaultexplorer
|
||||
|
||||
import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
||||
"github.com/karmada-io/karmada/pkg/util"
|
||||
)
|
||||
|
||||
// replicaFactory return default replica factory that can be used to obtain replica
|
||||
// and requirements by each replica from the input object.
|
||||
type replicaFactory func(object runtime.Object) (int32, *workv1alpha2.ReplicaRequirements, error)
|
||||
|
||||
func getAllDefaultReplicaExplorer() map[schema.GroupVersionKind]replicaFactory {
|
||||
explorers := make(map[schema.GroupVersionKind]replicaFactory)
|
||||
explorers[appsv1.SchemeGroupVersion.WithKind(util.DeploymentKind)] = deployReplicaExplorer
|
||||
explorers[batchv1.SchemeGroupVersion.WithKind(util.JobKind)] = jobReplicaExplorer
|
||||
return explorers
|
||||
}
|
||||
|
||||
func deployReplicaExplorer(object runtime.Object) (int32, *workv1alpha2.ReplicaRequirements, error) {
|
||||
return 0, &workv1alpha2.ReplicaRequirements{}, nil
|
||||
}
|
||||
|
||||
func jobReplicaExplorer(object runtime.Object) (int32, *workv1alpha2.ReplicaRequirements, error) {
|
||||
return 0, &workv1alpha2.ReplicaRequirements{}, nil
|
||||
}
|
Loading…
Reference in New Issue