feat(karmada-search): Implement SearchRegistry reconcile logic

Co-authored-by: liys87x <liyasong1987x@gmail.com>
Signed-off-by: huntsman_ly <huntsman_ly@sina.com>
This commit is contained in:
huntsman_ly 2022-05-21 16:11:11 +08:00
parent 58c0bc4887
commit de186ea043
No known key found for this signature in database
GPG Key ID: 8A80BC8F22FF8E64
9 changed files with 435 additions and 370 deletions

View File

@ -90,6 +90,16 @@ func (o *Options) Run(ctx context.Context) error {
return nil
})
server.GenericAPIServer.AddPostStartHookOrDie("start-karmada-search-controller", func(context genericapiserver.PostStartHookContext) error {
// start ResourceRegistry controller
ctl, err := search.NewController(restConfig, search.CachedResourceHandler())
if err != nil {
return err
}
ctl.Start(context.StopCh)
return nil
})
return server.GenericAPIServer.PrepareRun().Run(ctx.Done())
}

View File

@ -1,8 +1,9 @@
package search
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
)
// +genclient
@ -24,9 +25,9 @@ type ResourceRegistry struct {
// ResourceRegistrySpec defines the desired state of ResourceRegistry.
type ResourceRegistrySpec struct {
// ClusterSelectors represents the filter to select clusters.
// TargetCluster is the cluster that the resource registry is targeting.
// +required
ClusterSelectors []ClusterSelector
TargetCluster *policyv1alpha1.ClusterAffinity `json:"targetCluster"`
// ResourceSelectors used to select resources.
// +required
@ -38,42 +39,15 @@ type ResourceRegistrySpec struct {
StatusUpdatePeriodSeconds uint32
}
// ClusterSelector represents the filter to select clusters.
type ClusterSelector struct {
// LabelSelector is a filter to select member clusters by labels.
// If non-nil and non-empty, only the clusters match this filter will be selected.
// +optional
LabelSelector *metav1.LabelSelector
// FieldSelector is a filter to select member clusters by fields.
// If non-nil and non-empty, only the clusters match this filter will be selected.
// +optional
FieldSelector *FieldSelector
// ClusterNames is the list of clusters to be selected.
// +optional
ClusterNames []string
// ExcludedClusters is the list of clusters to be ignored.
// +optional
ExcludeClusters []string
}
// FieldSelector is a field filter.
type FieldSelector struct {
// A list of field selector requirements.
MatchExpressions []corev1.NodeSelectorRequirement
}
// ResourceSelector the resources will be selected.
type ResourceSelector struct {
// APIVersion represents the API version of the target resources.
// +required
APIVersion string
// Kind represents the Kind of the target resources.
// Kind represents the kind of the target resources.
// +required
Kind string
Kind string `json:"kind"`
// Namespace of the target resource.
// Default is empty, which means all namespaces.

View File

@ -1,17 +1,18 @@
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
)
const (
// ResourceKindResourceRegistry is the name of the resource registry
ResourceKindResourceRegistry = "ResourceRegistry"
// ResourceSingularResourceRegistry is singular name of ResourceRegistry.
ResourceSingularResourceRegistry = "resourceRegistry"
ResourceSingularResourceRegistry = "resourceregistry"
// ResourcePluralResourceRegistry is plural name of ResourceRegistry.
ResourcePluralResourceRegistry = "resourceRegistries"
ResourcePluralResourceRegistry = "resourceregistries"
// ResourceNamespaceScopedResourceRegistry is the scope of the ResourceRegistry
ResourceNamespaceScopedResourceRegistry = false
)
@ -35,9 +36,9 @@ type ResourceRegistry struct {
// ResourceRegistrySpec defines the desired state of ResourceRegistry.
type ResourceRegistrySpec struct {
// ClusterSelectors represents the filter to select clusters.
// TargetCluster is the cluster that the resource registry is targeting.
// +required
ClusterSelectors []ClusterSelector `json:"clusterSelectors"`
TargetCluster *policyv1alpha1.ClusterAffinity `json:"targetCluster"`
// ResourceSelectors used to select resources.
// +required
@ -49,40 +50,13 @@ type ResourceRegistrySpec struct {
StatusUpdatePeriodSeconds uint32 `json:"statusUpdatePeriodSeconds,omitempty"`
}
// ClusterSelector represents the filter to select clusters.
type ClusterSelector struct {
// LabelSelector is a filter to select member clusters by labels.
// If non-nil and non-empty, only the clusters match this filter will be selected.
// +optional
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
// FieldSelector is a filter to select member clusters by fields.
// If non-nil and non-empty, only the clusters match this filter will be selected.
// +optional
FieldSelector *FieldSelector `json:"fieldSelector,omitempty"`
// ClusterNames is the list of clusters to be selected.
// +optional
ClusterNames []string `json:"clusterNames,omitempty"`
// ExcludedClusters is the list of clusters to be ignored.
// +optional
ExcludeClusters []string `json:"exclude,omitempty"`
}
// FieldSelector is a field filter.
type FieldSelector struct {
// A list of field selector requirements.
MatchExpressions []corev1.NodeSelectorRequirement `json:"matchExpressions,omitempty"`
}
// ResourceSelector the resources will be selected.
type ResourceSelector struct {
// APIVersion represents the API version of the target resources.
// +required
APIVersion string `json:"apiVersion"`
// Kind represents the Kind of the target resources.
// Kind represents the kind of the target resources.
// +required
Kind string `json:"kind"`

View File

@ -8,8 +8,8 @@ package v1alpha1
import (
unsafe "unsafe"
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
search "github.com/karmada-io/karmada/pkg/apis/search"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
@ -22,26 +22,6 @@ func init() {
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*ClusterSelector)(nil), (*search.ClusterSelector)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ClusterSelector_To_search_ClusterSelector(a.(*ClusterSelector), b.(*search.ClusterSelector), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*search.ClusterSelector)(nil), (*ClusterSelector)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_search_ClusterSelector_To_v1alpha1_ClusterSelector(a.(*search.ClusterSelector), b.(*ClusterSelector), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*FieldSelector)(nil), (*search.FieldSelector)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_FieldSelector_To_search_FieldSelector(a.(*FieldSelector), b.(*search.FieldSelector), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*search.FieldSelector)(nil), (*FieldSelector)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_search_FieldSelector_To_v1alpha1_FieldSelector(a.(*search.FieldSelector), b.(*FieldSelector), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*ResourceRegistry)(nil), (*search.ResourceRegistry)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ResourceRegistry_To_search_ResourceRegistry(a.(*ResourceRegistry), b.(*search.ResourceRegistry), scope)
}); err != nil {
@ -105,52 +85,6 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil
}
func autoConvert_v1alpha1_ClusterSelector_To_search_ClusterSelector(in *ClusterSelector, out *search.ClusterSelector, s conversion.Scope) error {
out.LabelSelector = (*v1.LabelSelector)(unsafe.Pointer(in.LabelSelector))
out.FieldSelector = (*search.FieldSelector)(unsafe.Pointer(in.FieldSelector))
out.ClusterNames = *(*[]string)(unsafe.Pointer(&in.ClusterNames))
out.ExcludeClusters = *(*[]string)(unsafe.Pointer(&in.ExcludeClusters))
return nil
}
// Convert_v1alpha1_ClusterSelector_To_search_ClusterSelector is an autogenerated conversion function.
func Convert_v1alpha1_ClusterSelector_To_search_ClusterSelector(in *ClusterSelector, out *search.ClusterSelector, s conversion.Scope) error {
return autoConvert_v1alpha1_ClusterSelector_To_search_ClusterSelector(in, out, s)
}
func autoConvert_search_ClusterSelector_To_v1alpha1_ClusterSelector(in *search.ClusterSelector, out *ClusterSelector, s conversion.Scope) error {
out.LabelSelector = (*v1.LabelSelector)(unsafe.Pointer(in.LabelSelector))
out.FieldSelector = (*FieldSelector)(unsafe.Pointer(in.FieldSelector))
out.ClusterNames = *(*[]string)(unsafe.Pointer(&in.ClusterNames))
out.ExcludeClusters = *(*[]string)(unsafe.Pointer(&in.ExcludeClusters))
return nil
}
// Convert_search_ClusterSelector_To_v1alpha1_ClusterSelector is an autogenerated conversion function.
func Convert_search_ClusterSelector_To_v1alpha1_ClusterSelector(in *search.ClusterSelector, out *ClusterSelector, s conversion.Scope) error {
return autoConvert_search_ClusterSelector_To_v1alpha1_ClusterSelector(in, out, s)
}
func autoConvert_v1alpha1_FieldSelector_To_search_FieldSelector(in *FieldSelector, out *search.FieldSelector, s conversion.Scope) error {
out.MatchExpressions = *(*[]corev1.NodeSelectorRequirement)(unsafe.Pointer(&in.MatchExpressions))
return nil
}
// Convert_v1alpha1_FieldSelector_To_search_FieldSelector is an autogenerated conversion function.
func Convert_v1alpha1_FieldSelector_To_search_FieldSelector(in *FieldSelector, out *search.FieldSelector, s conversion.Scope) error {
return autoConvert_v1alpha1_FieldSelector_To_search_FieldSelector(in, out, s)
}
func autoConvert_search_FieldSelector_To_v1alpha1_FieldSelector(in *search.FieldSelector, out *FieldSelector, s conversion.Scope) error {
out.MatchExpressions = *(*[]corev1.NodeSelectorRequirement)(unsafe.Pointer(&in.MatchExpressions))
return nil
}
// Convert_search_FieldSelector_To_v1alpha1_FieldSelector is an autogenerated conversion function.
func Convert_search_FieldSelector_To_v1alpha1_FieldSelector(in *search.FieldSelector, out *FieldSelector, s conversion.Scope) error {
return autoConvert_search_FieldSelector_To_v1alpha1_FieldSelector(in, out, s)
}
func autoConvert_v1alpha1_ResourceRegistry_To_search_ResourceRegistry(in *ResourceRegistry, out *search.ResourceRegistry, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1alpha1_ResourceRegistrySpec_To_search_ResourceRegistrySpec(&in.Spec, &out.Spec, s); err != nil {
@ -206,7 +140,7 @@ func Convert_search_ResourceRegistryList_To_v1alpha1_ResourceRegistryList(in *se
}
func autoConvert_v1alpha1_ResourceRegistrySpec_To_search_ResourceRegistrySpec(in *ResourceRegistrySpec, out *search.ResourceRegistrySpec, s conversion.Scope) error {
out.ClusterSelectors = *(*[]search.ClusterSelector)(unsafe.Pointer(&in.ClusterSelectors))
out.TargetCluster = (*policyv1alpha1.ClusterAffinity)(unsafe.Pointer(in.TargetCluster))
out.ResourceSelectors = *(*[]search.ResourceSelector)(unsafe.Pointer(&in.ResourceSelectors))
out.StatusUpdatePeriodSeconds = in.StatusUpdatePeriodSeconds
return nil
@ -218,7 +152,7 @@ func Convert_v1alpha1_ResourceRegistrySpec_To_search_ResourceRegistrySpec(in *Re
}
func autoConvert_search_ResourceRegistrySpec_To_v1alpha1_ResourceRegistrySpec(in *search.ResourceRegistrySpec, out *ResourceRegistrySpec, s conversion.Scope) error {
out.ClusterSelectors = *(*[]ClusterSelector)(unsafe.Pointer(&in.ClusterSelectors))
out.TargetCluster = (*policyv1alpha1.ClusterAffinity)(unsafe.Pointer(in.TargetCluster))
out.ResourceSelectors = *(*[]ResourceSelector)(unsafe.Pointer(&in.ResourceSelectors))
out.StatusUpdatePeriodSeconds = in.StatusUpdatePeriodSeconds
return nil

View File

@ -6,70 +6,11 @@
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterSelector) DeepCopyInto(out *ClusterSelector) {
*out = *in
if in.LabelSelector != nil {
in, out := &in.LabelSelector, &out.LabelSelector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
if in.FieldSelector != nil {
in, out := &in.FieldSelector, &out.FieldSelector
*out = new(FieldSelector)
(*in).DeepCopyInto(*out)
}
if in.ClusterNames != nil {
in, out := &in.ClusterNames, &out.ClusterNames
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ExcludeClusters != nil {
in, out := &in.ExcludeClusters, &out.ExcludeClusters
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSelector.
func (in *ClusterSelector) DeepCopy() *ClusterSelector {
if in == nil {
return nil
}
out := new(ClusterSelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FieldSelector) DeepCopyInto(out *FieldSelector) {
*out = *in
if in.MatchExpressions != nil {
in, out := &in.MatchExpressions, &out.MatchExpressions
*out = make([]corev1.NodeSelectorRequirement, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FieldSelector.
func (in *FieldSelector) DeepCopy() *FieldSelector {
if in == nil {
return nil
}
out := new(FieldSelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceRegistry) DeepCopyInto(out *ResourceRegistry) {
*out = *in
@ -134,12 +75,10 @@ func (in *ResourceRegistryList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceRegistrySpec) DeepCopyInto(out *ResourceRegistrySpec) {
*out = *in
if in.ClusterSelectors != nil {
in, out := &in.ClusterSelectors, &out.ClusterSelectors
*out = make([]ClusterSelector, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
if in.TargetCluster != nil {
in, out := &in.TargetCluster, &out.TargetCluster
*out = new(policyv1alpha1.ClusterAffinity)
(*in).DeepCopyInto(*out)
}
if in.ResourceSelectors != nil {
in, out := &in.ResourceSelectors, &out.ResourceSelectors

View File

@ -6,70 +6,11 @@
package search
import (
corev1 "k8s.io/api/core/v1"
v1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterSelector) DeepCopyInto(out *ClusterSelector) {
*out = *in
if in.LabelSelector != nil {
in, out := &in.LabelSelector, &out.LabelSelector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
if in.FieldSelector != nil {
in, out := &in.FieldSelector, &out.FieldSelector
*out = new(FieldSelector)
(*in).DeepCopyInto(*out)
}
if in.ClusterNames != nil {
in, out := &in.ClusterNames, &out.ClusterNames
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ExcludeClusters != nil {
in, out := &in.ExcludeClusters, &out.ExcludeClusters
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSelector.
func (in *ClusterSelector) DeepCopy() *ClusterSelector {
if in == nil {
return nil
}
out := new(ClusterSelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FieldSelector) DeepCopyInto(out *FieldSelector) {
*out = *in
if in.MatchExpressions != nil {
in, out := &in.MatchExpressions, &out.MatchExpressions
*out = make([]corev1.NodeSelectorRequirement, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FieldSelector.
func (in *FieldSelector) DeepCopy() *FieldSelector {
if in == nil {
return nil
}
out := new(FieldSelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceRegistry) DeepCopyInto(out *ResourceRegistry) {
*out = *in
@ -134,12 +75,10 @@ func (in *ResourceRegistryList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceRegistrySpec) DeepCopyInto(out *ResourceRegistrySpec) {
*out = *in
if in.ClusterSelectors != nil {
in, out := &in.ClusterSelectors, &out.ClusterSelectors
*out = make([]ClusterSelector, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
if in.TargetCluster != nil {
in, out := &in.TargetCluster, &out.TargetCluster
*out = new(v1alpha1.ClusterAffinity)
(*in).DeepCopyInto(*out)
}
if in.ResourceSelectors != nil {
in, out := &in.ResourceSelectors, &out.ResourceSelectors

View File

@ -69,8 +69,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.SpreadConstraint": schema_pkg_apis_policy_v1alpha1_SpreadConstraint(ref),
"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.StaticClusterAssignment": schema_pkg_apis_policy_v1alpha1_StaticClusterAssignment(ref),
"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.StaticClusterWeight": schema_pkg_apis_policy_v1alpha1_StaticClusterWeight(ref),
"github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.ClusterSelector": schema_pkg_apis_search_v1alpha1_ClusterSelector(ref),
"github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.FieldSelector": schema_pkg_apis_search_v1alpha1_FieldSelector(ref),
"github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.ResourceRegistry": schema_pkg_apis_search_v1alpha1_ResourceRegistry(ref),
"github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.ResourceRegistryList": schema_pkg_apis_search_v1alpha1_ResourceRegistryList(ref),
"github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.ResourceRegistrySpec": schema_pkg_apis_search_v1alpha1_ResourceRegistrySpec(ref),
@ -2979,92 +2977,6 @@ func schema_pkg_apis_policy_v1alpha1_StaticClusterWeight(ref common.ReferenceCal
}
}
func schema_pkg_apis_search_v1alpha1_ClusterSelector(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ClusterSelector represents the filter to select clusters.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"labelSelector": {
SchemaProps: spec.SchemaProps{
Description: "LabelSelector is a filter to select member clusters by labels. If non-nil and non-empty, only the clusters match this filter will be selected.",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"),
},
},
"fieldSelector": {
SchemaProps: spec.SchemaProps{
Description: "FieldSelector is a filter to select member clusters by fields. If non-nil and non-empty, only the clusters match this filter will be selected.",
Ref: ref("github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.FieldSelector"),
},
},
"clusterNames": {
SchemaProps: spec.SchemaProps{
Description: "ClusterNames is the list of clusters to be selected.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
"exclude": {
SchemaProps: spec.SchemaProps{
Description: "ExcludedClusters is the list of clusters to be ignored.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
},
},
},
Dependencies: []string{
"github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.FieldSelector", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"},
}
}
func schema_pkg_apis_search_v1alpha1_FieldSelector(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "FieldSelector is a field filter.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"matchExpressions": {
SchemaProps: spec.SchemaProps{
Description: "A list of field selector requirements.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/core/v1.NodeSelectorRequirement"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/core/v1.NodeSelectorRequirement"},
}
}
func schema_pkg_apis_search_v1alpha1_ResourceRegistry(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@ -3171,18 +3083,10 @@ func schema_pkg_apis_search_v1alpha1_ResourceRegistrySpec(ref common.ReferenceCa
Description: "ResourceRegistrySpec defines the desired state of ResourceRegistry.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"clusterSelectors": {
"targetCluster": {
SchemaProps: spec.SchemaProps{
Description: "ClusterSelectors represents the filter to select clusters.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.ClusterSelector"),
},
},
},
Description: "TargetCluster is the cluster that the resource registry is targeting.",
Ref: ref("github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ClusterAffinity"),
},
},
"resourceSelectors": {
@ -3207,11 +3111,11 @@ func schema_pkg_apis_search_v1alpha1_ResourceRegistrySpec(ref common.ReferenceCa
},
},
},
Required: []string{"clusterSelectors", "resourceSelectors"},
Required: []string{"targetCluster", "resourceSelectors"},
},
},
Dependencies: []string{
"github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.ClusterSelector", "github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.ResourceSelector"},
"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ClusterAffinity", "github.com/karmada-io/karmada/pkg/apis/search/v1alpha1.ResourceSelector"},
}
}
@ -3261,7 +3165,7 @@ func schema_pkg_apis_search_v1alpha1_ResourceSelector(ref common.ReferenceCallba
},
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind represents the Kind of the target resources.",
Description: "Kind represents the kind of the target resources.",
Default: "",
Type: []string{"string"},
Format: "",

View File

@ -105,8 +105,8 @@ func (c completedConfig) New(kubeClient kubernetes.Interface) (*APIServer, error
searchREST := searchstorage.NewSearchREST(kubeClient)
v1alpha1search := map[string]rest.Storage{}
v1alpha1search["resourceRegistry"] = resourceRegistryStorage.ResourceRegistry
v1alpha1search["resourceRegistry/status"] = resourceRegistryStorage.Status
v1alpha1search["resourceregistries"] = resourceRegistryStorage.ResourceRegistry
v1alpha1search["resourceregistries/status"] = resourceRegistryStorage.Status
v1alpha1search["search"] = searchREST
apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1search

391
pkg/search/controller.go Normal file
View File

@ -0,0 +1,391 @@
package search
import (
"sync"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
clusterV1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
"github.com/karmada-io/karmada/pkg/apis/search/v1alpha1"
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
informerfactory "github.com/karmada-io/karmada/pkg/generated/informers/externalversions"
clusterlister "github.com/karmada-io/karmada/pkg/generated/listers/cluster/v1alpha1"
"github.com/karmada-io/karmada/pkg/util"
"github.com/karmada-io/karmada/pkg/util/gclient"
"github.com/karmada-io/karmada/pkg/util/informermanager"
"github.com/karmada-io/karmada/pkg/util/restmapper"
)
type clusterRegistry struct {
registries map[string]struct{}
resources map[schema.GroupVersionResource]struct{}
}
func (c *clusterRegistry) unregistry() bool {
return len(c.registries) == 0
}
// Controller ResourceRegistry controller
type Controller struct {
restConfig *rest.Config
restMapper meta.RESTMapper
informerFactory informerfactory.SharedInformerFactory
clusterLister clusterlister.ClusterLister
queue workqueue.RateLimitingInterface
clusterRegistry sync.Map
resourceHandler cache.ResourceEventHandler
InformerManager informermanager.MultiClusterInformerManager
}
// NewController returns a new ResourceRegistry controller
func NewController(restConfig *rest.Config, rh cache.ResourceEventHandler) (*Controller, error) {
karmadaClient := karmadaclientset.NewForConfigOrDie(restConfig)
factory := informerfactory.NewSharedInformerFactory(karmadaClient, 0)
clusterLister := factory.Cluster().V1alpha1().Clusters().Lister()
restMapper, err := apiutil.NewDynamicRESTMapper(restConfig)
if err != nil {
klog.Errorf("Failed to create REST mapper: %v", err)
return nil, err
}
queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
c := &Controller{
restConfig: restConfig,
informerFactory: factory,
clusterLister: clusterLister,
queue: queue,
restMapper: restMapper,
resourceHandler: rh,
InformerManager: informermanager.GetInstance(),
}
c.addAllEventHandlers()
return c, nil
}
// addAllEventHandlers adds all event handlers to the informer
func (c *Controller) addAllEventHandlers() {
clusterInformer := c.informerFactory.Cluster().V1alpha1().Clusters().Informer()
clusterInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: c.addCluster,
UpdateFunc: c.updateCluster,
DeleteFunc: c.deleteCluster,
})
resourceRegistryInformer := c.informerFactory.Search().V1alpha1().ResourceRegistries().Informer()
resourceRegistryInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: c.addResourceRegistry,
UpdateFunc: c.updateResourceRegistry,
DeleteFunc: c.deleteResourceRegistry,
})
}
// Start the controller
func (c *Controller) Start(stopCh <-chan struct{}) {
klog.Infof("Starting karmada search controller")
defer runtime.HandleCrash()
c.informerFactory.Start(stopCh)
c.informerFactory.WaitForCacheSync(stopCh)
go wait.Until(c.worker, time.Second, stopCh)
go func() {
<-stopCh
informermanager.StopInstance()
klog.Infof("Shutting down karmada search controller")
}()
}
// worker processes the queue of resourceRegistry objects.
func (c *Controller) worker() {
for c.cacheNext() {
}
}
// cacheNext processes the next cluster object in the queue.
func (c *Controller) cacheNext() bool {
// Wait until there is a new item in the working queue
key, shutdown := c.queue.Get()
if shutdown {
klog.Errorf("Fail to pop item from queue")
return false
}
// Tell the queue that we are done with processing this key. This unblocks the key for other workers
// This allows safe parallel processing because two pods with the same key are never processed in
// parallel.
defer c.queue.Done(key)
err := c.doCacheCluster(key.(string))
// Handle the error if something went wrong during the execution of the business logic
c.handleErr(err, key)
return true
}
// handleErr checks if an error happened and makes sure we will retry later.
func (c *Controller) handleErr(err error, key interface{}) {
if err == nil {
c.queue.Forget(key)
return
}
klog.Errorf("Error cache memeber cluster %v, %v", key, err)
c.queue.AddRateLimited(key)
}
// doCacheCluster processes the resourceRegistry object
// TODO: update status
func (c *Controller) doCacheCluster(cluster string) error {
// STEP0: stop informer manager for the cluster which is not referenced by any `SearchRegistry` object.
v, ok := c.clusterRegistry.Load(cluster)
if !ok {
klog.Infof("Cluster %s is not registered", cluster)
return nil
}
cr := v.(clusterRegistry)
if cr.unregistry() {
klog.Infof("try to stop cluster informer %s", cluster)
c.InformerManager.Stop(cluster)
return nil
}
// STEP1: stop informer manager for the cluster which does not exist anymore.
cls, err := c.clusterLister.Get(cluster)
if err != nil {
if apierrors.IsNotFound(err) {
klog.Infof("try to stop cluster informer %s", cluster)
c.InformerManager.Stop(cluster)
return nil
}
return err
}
if !cls.DeletionTimestamp.IsZero() {
klog.Infof("try to stop cluster informer %s", cluster)
c.InformerManager.Stop(cluster)
return nil
}
// STEP2: added/updated cluster, builds an informer manager for a specific cluster.
if !c.InformerManager.IsManagerExist(cluster) {
klog.Info("try to build informer manager for cluster ", cluster)
controlPlaneClient := gclient.NewForConfigOrDie(c.restConfig)
clusterDynamicClient, err := util.NewClusterDynamicClientSet(cluster, controlPlaneClient)
if err != nil {
return err
}
_ = c.InformerManager.ForCluster(cluster, clusterDynamicClient.DynamicClientSet, 0)
}
if c.resourceHandler != nil {
sci := c.InformerManager.GetSingleClusterManager(cluster)
for gvr := range cr.resources {
klog.Infof("try to start informer for %s, %v", cluster, gvr)
// TODO: gvr exists check
sci.ForResource(gvr, c.resourceHandler)
}
sci.Start()
_ = sci.WaitForCacheSync()
}
return nil
}
// addResourceRegistry parse the resourceRegistry object and add Cluster to the queue
func (c *Controller) addResourceRegistry(obj interface{}) {
rr := obj.(*v1alpha1.ResourceRegistry)
resources := c.getResources(rr.Spec.ResourceSelectors)
for _, cluster := range c.getClusters(*rr.Spec.TargetCluster) {
v, _ := c.clusterRegistry.LoadOrStore(cluster, clusterRegistry{
resources: make(map[schema.GroupVersionResource]struct{}),
registries: make(map[string]struct{})})
cr := v.(clusterRegistry)
for _, r := range resources {
cr.resources[r] = struct{}{}
}
cr.registries[rr.GetName()] = struct{}{}
c.clusterRegistry.Store(cluster, cr)
c.queue.Add(cluster)
}
}
// updateResourceRegistry parse the resourceRegistry object and add (added/deleted) Cluster to the queue
func (c *Controller) updateResourceRegistry(oldObj, newObj interface{}) {
oldRR := oldObj.(*v1alpha1.ResourceRegistry)
newRR := newObj.(*v1alpha1.ResourceRegistry)
// TODO: stop resource informers if it is not in the new resource registry
resources := c.getResources(newRR.Spec.ResourceSelectors)
clusters := c.getClusters(*newRR.Spec.TargetCluster)
clusterSets := make(map[string]struct{})
for _, cls := range clusters {
v, _ := c.clusterRegistry.LoadOrStore(cls, clusterRegistry{
resources: make(map[schema.GroupVersionResource]struct{}),
registries: make(map[string]struct{})})
cr := v.(clusterRegistry)
for _, r := range resources {
cr.resources[r] = struct{}{}
}
cr.registries[newRR.GetName()] = struct{}{}
c.clusterRegistry.Store(cls, cr)
clusterSets[cls] = struct{}{}
c.queue.Add(cls)
}
for _, cls := range c.getClusters(*oldRR.Spec.TargetCluster) {
if _, ok := clusterSets[cls]; ok {
continue
}
v, ok := c.clusterRegistry.Load(cls)
if !ok {
continue
}
cr := v.(clusterRegistry)
delete(cr.registries, oldRR.GetName())
c.clusterRegistry.Store(cls, cr)
c.queue.Add(cls)
}
}
// deleteResourceRegistry parse the resourceRegistry object and add deleted Cluster to the queue
func (c *Controller) deleteResourceRegistry(obj interface{}) {
rr := obj.(*v1alpha1.ResourceRegistry)
for _, cluster := range c.getClusters(*rr.Spec.TargetCluster) {
v, ok := c.clusterRegistry.Load(cluster)
if !ok {
return
}
cr := v.(clusterRegistry)
delete(cr.registries, rr.GetName())
c.clusterRegistry.Store(cluster, cr)
c.queue.Add(cluster)
}
}
// addCluster adds a cluster object to the queue if needed
func (c *Controller) addCluster(obj interface{}) {
cluster, ok := obj.(*clusterV1alpha1.Cluster)
if !ok {
klog.Errorf("cannot convert to *clusterV1alpha1.Cluster: %v", obj)
return
}
_, ok = c.clusterRegistry.Load(cluster.GetName())
if ok {
// unregistered cluster, do nothing.
return
}
c.queue.Add(cluster.GetName())
}
// updateCluster TODO: rebuild informer if Cluster.Spec is changed
func (c *Controller) updateCluster(oldObj, newObj interface{}) {}
// deleteCluster set cluster to not exists
func (c *Controller) deleteCluster(obj interface{}) {
cluster, ok := obj.(*clusterV1alpha1.Cluster)
if !ok {
klog.Errorf("cannot convert to *clusterV1alpha1.Cluster: %v", obj)
return
}
_, ok = c.clusterRegistry.Load(cluster.GetName())
if !ok {
// unregistered cluster, do nothing.
return
}
c.queue.Add(cluster.GetName())
}
// getClusterAndResource returns the cluster and resources from the resourceRegistry object
func (c *Controller) getClusters(affinity policyv1alpha1.ClusterAffinity) []string {
clusters := make([]string, 0)
lst, err := c.clusterLister.List(labels.Everything())
if err != nil {
klog.Errorf("failed to list clusters: %v", err)
return clusters
}
for _, cls := range lst {
if util.ClusterMatches(cls, affinity) {
clusters = append(clusters, cls.GetName())
}
}
return clusters
}
// getClusterAndResource returns the cluster and resources from the resourceRegistry object
func (c *Controller) getResources(selectors []v1alpha1.ResourceSelector) []schema.GroupVersionResource {
resources := make([]schema.GroupVersionResource, 0)
for _, rs := range selectors {
gvr, err := restmapper.GetGroupVersionResource(
c.restMapper, schema.FromAPIVersionAndKind(rs.APIVersion, rs.Kind),
)
if err != nil {
klog.Errorf("failed to get gvr: %v", err)
continue
}
resources = append(resources, gvr)
}
return resources
}
// CachedResourceHandler is the default handler for resource events
func CachedResourceHandler() cache.ResourceEventHandler {
return &cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
us, ok := obj.(*unstructured.Unstructured)
if !ok {
klog.Errorf("cannot convert to Unstructured: %v", obj)
}
klog.V(4).Infof("add resource %s, %s, %s, %s", us.GetAPIVersion(), us.GetKind(), us.GetNamespace(), us.GetName())
},
UpdateFunc: func(oldObj, newObj interface{}) {
us, ok := newObj.(*unstructured.Unstructured)
if !ok {
klog.Errorf("cannot convert to Unstructured: %v", newObj)
}
klog.V(4).Infof("update resource %s, %s, %s, %s", us.GetAPIVersion(), us.GetKind(), us.GetNamespace(), us.GetName())
},
DeleteFunc: func(obj interface{}) {
us, ok := obj.(*unstructured.Unstructured)
if !ok {
klog.Errorf("cannot convert to Unstructured: %v", obj)
}
klog.V(4).Infof("delete resource %s, %s, %s, %s", us.GetAPIVersion(), us.GetKind(), us.GetNamespace(), us.GetName())
},
}
}