diff --git a/deploy/cluster-manager/config/crds/0000_01_operator.open-cluster-management.io_clustermanagers.crd.yaml b/deploy/cluster-manager/config/crds/0000_01_operator.open-cluster-management.io_clustermanagers.crd.yaml index eba39a2f6..6b7d246c3 100644 --- a/deploy/cluster-manager/config/crds/0000_01_operator.open-cluster-management.io_clustermanagers.crd.yaml +++ b/deploy/cluster-manager/config/crds/0000_01_operator.open-cluster-management.io_clustermanagers.crd.yaml @@ -37,14 +37,21 @@ spec: that govern registration and work distribution for attached Klusterlets. type: object properties: + placementImagePullSpec: + description: PlacementImagePullSpec represents the desired image configuration + of placement controller/webhook installed on hub. + type: string + default: quay.io/open-cluster-management/placement registrationImagePullSpec: description: RegistrationImagePullSpec represents the desired image of registration controller/webhook installed on hub. type: string + default: quay.io/open-cluster-management/registration workImagePullSpec: description: WorkImagePullSpec represents the desired image configuration of work controller/webhook installed on hub. type: string + default: quay.io/open-cluster-management/work status: description: Status represents the current status of controllers that govern the lifecycle of managed clusters. diff --git a/deploy/cluster-manager/olm-catalog/cluster-manager/manifests/operator.open-cluster-management.io_clustermanagers.yaml b/deploy/cluster-manager/olm-catalog/cluster-manager/manifests/operator.open-cluster-management.io_clustermanagers.yaml index fc19c14b6..5a4922f55 100644 --- a/deploy/cluster-manager/olm-catalog/cluster-manager/manifests/operator.open-cluster-management.io_clustermanagers.yaml +++ b/deploy/cluster-manager/olm-catalog/cluster-manager/manifests/operator.open-cluster-management.io_clustermanagers.yaml @@ -28,10 +28,16 @@ spec: spec: description: Spec represents a desired deployment configuration of controllers that govern registration and work distribution for attached Klusterlets. properties: + placementImagePullSpec: + default: quay.io/open-cluster-management/placement + description: PlacementImagePullSpec represents the desired image configuration of placement controller/webhook installed on hub. + type: string registrationImagePullSpec: + default: quay.io/open-cluster-management/registration description: RegistrationImagePullSpec represents the desired image of registration controller/webhook installed on hub. type: string workImagePullSpec: + default: quay.io/open-cluster-management/work description: WorkImagePullSpec represents the desired image configuration of work controller/webhook installed on hub. type: string type: object diff --git a/go.mod b/go.mod index ad21bcd2e..856c67216 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/go-bindata/go-bindata v3.1.2+incompatible github.com/onsi/ginkgo v1.14.0 github.com/onsi/gomega v1.10.1 - github.com/open-cluster-management/api v0.0.0-20210409125704-06f2aec1a73f + github.com/open-cluster-management/api v0.0.0-20210511122802-f38973154cbd github.com/openshift/api v0.0.0-20210331193751-3acddb19d360 github.com/openshift/build-machinery-go v0.0.0-20210209125900-0da259a2c359 github.com/openshift/client-go v0.0.0-20210331195552-cf6c2669e01f diff --git a/go.sum b/go.sum index cff797bb1..d456cb085 100644 --- a/go.sum +++ b/go.sum @@ -418,8 +418,8 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/open-cluster-management/api v0.0.0-20210409125704-06f2aec1a73f h1:s6z3k0jV0ccoYDPJWMSqVNevO1UoQLYb8f7dYFALSNk= -github.com/open-cluster-management/api v0.0.0-20210409125704-06f2aec1a73f/go.mod h1:ot+A1DWq+v1IV+e1S7nhIteYAmNByFgtazvzpoeAfRQ= +github.com/open-cluster-management/api v0.0.0-20210511122802-f38973154cbd h1:/cigzPZOt3UYfDv5Nc7jvc1ydZDXESmi1/2P60HR3kc= +github.com/open-cluster-management/api v0.0.0-20210511122802-f38973154cbd/go.mod h1:ot+A1DWq+v1IV+e1S7nhIteYAmNByFgtazvzpoeAfRQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/openshift/api v0.0.0-20210331162552-3e31249e6a55/go.mod h1:dZ4kytOo3svxJHNYd0J55hwe/6IQG5gAUHUE0F3Jkio= diff --git a/manifests/klusterlet/0000_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml b/manifests/klusterlet/0000_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml index 11fa847a4..eebbe280f 100644 --- a/manifests/klusterlet/0000_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml +++ b/manifests/klusterlet/0000_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml @@ -19,7 +19,8 @@ spec: claims ClusterClaims with well known names include, 1. id.k8s.io, it contains a unique identifier for the cluster. 2. clusterset.k8s.io, it contains an identifier that relates the cluster to the ClusterSet in which it - belongs. + belongs. ClusterClaims created on a managed cluster will be collected and + saved into the status of the corresponding ManagedCluster on hub. type: object properties: apiVersion: diff --git a/manifests/klusterlet/0001_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml b/manifests/klusterlet/0001_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml index 4ab960857..4027c7a94 100644 --- a/manifests/klusterlet/0001_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml +++ b/manifests/klusterlet/0001_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml @@ -17,6 +17,8 @@ spec: claims ClusterClaims with well known names include, 1. id.k8s.io, it contains a unique identifier for the cluster. 2. clusterset.k8s.io, it contains an identifier that relates the cluster to the ClusterSet in which it belongs. + ClusterClaims created on a managed cluster will be collected and saved into + the status of the corresponding ManagedCluster on hub. type: object properties: apiVersion: diff --git a/pkg/operators/klusterlet/bindata/bindata.go b/pkg/operators/klusterlet/bindata/bindata.go index 07afe9b51..06b27546f 100644 --- a/pkg/operators/klusterlet/bindata/bindata.go +++ b/pkg/operators/klusterlet/bindata/bindata.go @@ -211,7 +211,8 @@ spec: claims ClusterClaims with well known names include, 1. id.k8s.io, it contains a unique identifier for the cluster. 2. clusterset.k8s.io, it contains an identifier that relates the cluster to the ClusterSet in which it - belongs. + belongs. ClusterClaims created on a managed cluster will be collected and + saved into the status of the corresponding ManagedCluster on hub. type: object properties: apiVersion: @@ -402,6 +403,8 @@ spec: claims ClusterClaims with well known names include, 1. id.k8s.io, it contains a unique identifier for the cluster. 2. clusterset.k8s.io, it contains an identifier that relates the cluster to the ClusterSet in which it belongs. + ClusterClaims created on a managed cluster will be collected and saved into + the status of the corresponding ManagedCluster on hub. type: object properties: apiVersion: diff --git a/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/cluster_client.go b/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/cluster_client.go index f51bf3c89..aa835a2d4 100644 --- a/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/cluster_client.go +++ b/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/cluster_client.go @@ -13,6 +13,8 @@ type ClusterV1alpha1Interface interface { ClusterClaimsGetter ManagedClusterSetsGetter ManagedClusterSetBindingsGetter + PlacementsGetter + PlacementDecisionsGetter } // ClusterV1alpha1Client is used to interact with features provided by the cluster.open-cluster-management.io group. @@ -32,6 +34,14 @@ func (c *ClusterV1alpha1Client) ManagedClusterSetBindings(namespace string) Mana return newManagedClusterSetBindings(c, namespace) } +func (c *ClusterV1alpha1Client) Placements(namespace string) PlacementInterface { + return newPlacements(c, namespace) +} + +func (c *ClusterV1alpha1Client) PlacementDecisions(namespace string) PlacementDecisionInterface { + return newPlacementDecisions(c, namespace) +} + // NewForConfig creates a new ClusterV1alpha1Client for the given config. func NewForConfig(c *rest.Config) (*ClusterV1alpha1Client, error) { config := *c diff --git a/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/generated_expansion.go b/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/generated_expansion.go index 51c4f094d..457dafc99 100644 --- a/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/generated_expansion.go +++ b/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/generated_expansion.go @@ -7,3 +7,7 @@ type ClusterClaimExpansion interface{} type ManagedClusterSetExpansion interface{} type ManagedClusterSetBindingExpansion interface{} + +type PlacementExpansion interface{} + +type PlacementDecisionExpansion interface{} diff --git a/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/placement.go b/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/placement.go new file mode 100644 index 000000000..4cc87e4f3 --- /dev/null +++ b/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/placement.go @@ -0,0 +1,179 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + scheme "github.com/open-cluster-management/api/client/cluster/clientset/versioned/scheme" + v1alpha1 "github.com/open-cluster-management/api/cluster/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PlacementsGetter has a method to return a PlacementInterface. +// A group's client should implement this interface. +type PlacementsGetter interface { + Placements(namespace string) PlacementInterface +} + +// PlacementInterface has methods to work with Placement resources. +type PlacementInterface interface { + Create(ctx context.Context, placement *v1alpha1.Placement, opts v1.CreateOptions) (*v1alpha1.Placement, error) + Update(ctx context.Context, placement *v1alpha1.Placement, opts v1.UpdateOptions) (*v1alpha1.Placement, error) + UpdateStatus(ctx context.Context, placement *v1alpha1.Placement, opts v1.UpdateOptions) (*v1alpha1.Placement, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.Placement, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.PlacementList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Placement, err error) + PlacementExpansion +} + +// placements implements PlacementInterface +type placements struct { + client rest.Interface + ns string +} + +// newPlacements returns a Placements +func newPlacements(c *ClusterV1alpha1Client, namespace string) *placements { + return &placements{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the placement, and returns the corresponding placement object, and an error if there is any. +func (c *placements) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Placement, err error) { + result = &v1alpha1.Placement{} + err = c.client.Get(). + Namespace(c.ns). + Resource("placements"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Placements that match those selectors. +func (c *placements) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PlacementList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PlacementList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("placements"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested placements. +func (c *placements) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("placements"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a placement and creates it. Returns the server's representation of the placement, and an error, if there is any. +func (c *placements) Create(ctx context.Context, placement *v1alpha1.Placement, opts v1.CreateOptions) (result *v1alpha1.Placement, err error) { + result = &v1alpha1.Placement{} + err = c.client.Post(). + Namespace(c.ns). + Resource("placements"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(placement). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a placement and updates it. Returns the server's representation of the placement, and an error, if there is any. +func (c *placements) Update(ctx context.Context, placement *v1alpha1.Placement, opts v1.UpdateOptions) (result *v1alpha1.Placement, err error) { + result = &v1alpha1.Placement{} + err = c.client.Put(). + Namespace(c.ns). + Resource("placements"). + Name(placement.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(placement). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *placements) UpdateStatus(ctx context.Context, placement *v1alpha1.Placement, opts v1.UpdateOptions) (result *v1alpha1.Placement, err error) { + result = &v1alpha1.Placement{} + err = c.client.Put(). + Namespace(c.ns). + Resource("placements"). + Name(placement.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(placement). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the placement and deletes it. Returns an error if one occurs. +func (c *placements) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("placements"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *placements) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("placements"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched placement. +func (c *placements) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Placement, err error) { + result = &v1alpha1.Placement{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("placements"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/placementdecision.go b/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/placementdecision.go new file mode 100644 index 000000000..627760d5d --- /dev/null +++ b/vendor/github.com/open-cluster-management/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/placementdecision.go @@ -0,0 +1,179 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + scheme "github.com/open-cluster-management/api/client/cluster/clientset/versioned/scheme" + v1alpha1 "github.com/open-cluster-management/api/cluster/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PlacementDecisionsGetter has a method to return a PlacementDecisionInterface. +// A group's client should implement this interface. +type PlacementDecisionsGetter interface { + PlacementDecisions(namespace string) PlacementDecisionInterface +} + +// PlacementDecisionInterface has methods to work with PlacementDecision resources. +type PlacementDecisionInterface interface { + Create(ctx context.Context, placementDecision *v1alpha1.PlacementDecision, opts v1.CreateOptions) (*v1alpha1.PlacementDecision, error) + Update(ctx context.Context, placementDecision *v1alpha1.PlacementDecision, opts v1.UpdateOptions) (*v1alpha1.PlacementDecision, error) + UpdateStatus(ctx context.Context, placementDecision *v1alpha1.PlacementDecision, opts v1.UpdateOptions) (*v1alpha1.PlacementDecision, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.PlacementDecision, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.PlacementDecisionList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PlacementDecision, err error) + PlacementDecisionExpansion +} + +// placementDecisions implements PlacementDecisionInterface +type placementDecisions struct { + client rest.Interface + ns string +} + +// newPlacementDecisions returns a PlacementDecisions +func newPlacementDecisions(c *ClusterV1alpha1Client, namespace string) *placementDecisions { + return &placementDecisions{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the placementDecision, and returns the corresponding placementDecision object, and an error if there is any. +func (c *placementDecisions) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.PlacementDecision, err error) { + result = &v1alpha1.PlacementDecision{} + err = c.client.Get(). + Namespace(c.ns). + Resource("placementdecisions"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of PlacementDecisions that match those selectors. +func (c *placementDecisions) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PlacementDecisionList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PlacementDecisionList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("placementdecisions"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested placementDecisions. +func (c *placementDecisions) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("placementdecisions"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a placementDecision and creates it. Returns the server's representation of the placementDecision, and an error, if there is any. +func (c *placementDecisions) Create(ctx context.Context, placementDecision *v1alpha1.PlacementDecision, opts v1.CreateOptions) (result *v1alpha1.PlacementDecision, err error) { + result = &v1alpha1.PlacementDecision{} + err = c.client.Post(). + Namespace(c.ns). + Resource("placementdecisions"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(placementDecision). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a placementDecision and updates it. Returns the server's representation of the placementDecision, and an error, if there is any. +func (c *placementDecisions) Update(ctx context.Context, placementDecision *v1alpha1.PlacementDecision, opts v1.UpdateOptions) (result *v1alpha1.PlacementDecision, err error) { + result = &v1alpha1.PlacementDecision{} + err = c.client.Put(). + Namespace(c.ns). + Resource("placementdecisions"). + Name(placementDecision.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(placementDecision). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *placementDecisions) UpdateStatus(ctx context.Context, placementDecision *v1alpha1.PlacementDecision, opts v1.UpdateOptions) (result *v1alpha1.PlacementDecision, err error) { + result = &v1alpha1.PlacementDecision{} + err = c.client.Put(). + Namespace(c.ns). + Resource("placementdecisions"). + Name(placementDecision.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(placementDecision). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the placementDecision and deletes it. Returns an error if one occurs. +func (c *placementDecisions) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("placementdecisions"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *placementDecisions) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("placementdecisions"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched placementDecision. +func (c *placementDecisions) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PlacementDecision, err error) { + result = &v1alpha1.PlacementDecision{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("placementdecisions"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0000_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0000_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml index 11fa847a4..eebbe280f 100644 --- a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0000_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml +++ b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0000_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml @@ -19,7 +19,8 @@ spec: claims ClusterClaims with well known names include, 1. id.k8s.io, it contains a unique identifier for the cluster. 2. clusterset.k8s.io, it contains an identifier that relates the cluster to the ClusterSet in which it - belongs. + belongs. ClusterClaims created on a managed cluster will be collected and + saved into the status of the corresponding ManagedCluster on hub. type: object properties: apiVersion: diff --git a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0000_03_clusters.open-cluster-management.io_placements.crd.yaml b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0000_03_clusters.open-cluster-management.io_placements.crd.yaml new file mode 100644 index 000000000..619a15a89 --- /dev/null +++ b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0000_03_clusters.open-cluster-management.io_placements.crd.yaml @@ -0,0 +1,274 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: placements.cluster.open-cluster-management.io +spec: + group: cluster.open-cluster-management.io + names: + kind: Placement + listKind: PlacementList + plural: placements + singular: placement + scope: Namespaced + preserveUnknownFields: false + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: "Placement defines a rule to select a set of ManagedClusters + from the ManagedClusterSets bound to the placement namespace. \n Here is + how the placement policy combines with other selection methods to determine + a matching list of ManagedClusters: 1) Kubernetes clusters are registered + with hub as cluster-scoped ManagedClusters; 2) ManagedClusters are organized + into cluster-scoped ManagedClusterSets; 3) ManagedClusterSets are bound + to workload namespaces; 4) Namespace-scoped Placements specify a slice of + ManagedClusterSets which select a working set of potential ManagedClusters; + 5) Then Placements subselect from that working set using label/claim selection. + \n No ManagedCluster will be selected if no ManagedClusterSet is bound to + the placement namespace. User is able to bind a ManagedClusterSet to a namespace + by creating a ManagedClusterSetBinding in that namespace if they have a + RBAC rule to CREATE on the virtual subresource of `managedclustersets/bind`. + \n A slice of PlacementDecisions with label cluster.open-cluster-management.io/placement={placement + name} will be created to represent the ManagedClusters selected by this + placement. \n If a ManagedCluster is selected and added into the PlacementDecisions, + other components may apply workload on it; once it is removed from the PlacementDecisions, + the workload applied on this ManagedCluster should be evicted accordingly." + type: object + required: + - spec + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the attributes of Placement. + type: object + properties: + clusterSets: + description: ClusterSets represent the ManagedClusterSets from which + the ManagedClusters are selected. If the slice is empty, ManagedClusters + will be selected from the ManagedClusterSets bound to the placement + namespace, otherwise ManagedClusters will be selected from the intersection + of this slice and the ManagedClusterSets bound to the placement + namespace. + type: array + items: + type: string + numberOfClusters: + description: NumberOfClusters represents the desired number of ManagedClusters + to be selected which meet the placement requirements. 1) If not + specified, all ManagedClusters which meet the placement requirements + (including ClusterSets, and Predicates) will be selected; 2) + Otherwise if the nubmer of ManagedClusters meet the placement requirements + is larger than NumberOfClusters, a random subset with desired + number of ManagedClusters will be selected; 3) If the nubmer of + ManagedClusters meet the placement requirements is equal to NumberOfClusters, all + of them will be selected; 4) If the nubmer of ManagedClusters meet + the placement requirements is less than NumberOfClusters, all + of them will be selected, and the status of condition `PlacementConditionSatisfied` + will be set to false; + type: integer + format: int32 + predicates: + description: Predicates represent a slice of predicates to select + ManagedClusters. The predicates are ORed. + type: array + items: + description: ClusterPredicate represents a predicate to select ManagedClusters. + type: object + properties: + requiredClusterSelector: + description: RequiredClusterSelector represents a selector of + ManagedClusters by label and claim. If specified, 1) Any ManagedCluster, + which does not match the selector, should not be selected + by this ClusterPredicate; 2) If a selected ManagedCluster + (of this ClusterPredicate) ceases to match the selector (e.g. + due to an update) of any ClusterPredicate, it will be eventually + removed from the placement decisions; 3) If a ManagedCluster + (not selected previously) starts to match the selector, it + will either be selected or at least has a chance to be + selected (when NumberOfClusters is specified); + type: object + properties: + claimSelector: + description: ClaimSelector represents a selector of ManagedClusters + by clusterClaims in status + type: object + properties: + matchExpressions: + description: matchExpressions is a list of cluster claim + selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + type: array + items: + type: string + labelSelector: + description: LabelSelector represents a selector of ManagedClusters + by label + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + additionalProperties: + type: string + status: + description: Status represents the current status of the Placement + type: object + properties: + conditions: + description: Conditions contains the different condition statuses + for this Placement. + type: array + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: + \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // +listMapKey=type + \ Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" + type: object + required: + - lastTransitionTime + - message + - reason + - status + - type + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + type: string + format: date-time + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + type: string + maxLength: 32768 + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + type: integer + format: int64 + minimum: 0 + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + type: string + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + status: + description: status of the condition, one of True, False, Unknown. + type: string + enum: + - "True" + - "False" + - Unknown + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + type: string + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + numberOfSelectedClusters: + description: NumberOfSelectedClusters represents the number of selected + ManagedClusters + type: integer + format: int32 + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml new file mode 100644 index 000000000..fc75289ea --- /dev/null +++ b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml @@ -0,0 +1,76 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: placementdecisions.cluster.open-cluster-management.io +spec: + group: cluster.open-cluster-management.io + names: + kind: PlacementDecision + listKind: PlacementDecisionList + plural: placementdecisions + singular: placementdecision + scope: Namespaced + preserveUnknownFields: false + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: "PlacementDecision indicates a decision from a placement PlacementDecision + should has a label cluster.open-cluster-management.io/placement={placement + name} to reference a certain placement. \n If a placement has spec.numberOfClusters + specified, the total number of decisions contained in status.decisions of + PlacementDecisions should always be NumberOfClusters; otherwise, the total + number of decisions should be the number of ManagedClusters which match + the placement requirements. \n Some of the decisions might be empty when + there are no enough ManagedClusters meet the placement requirements." + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + status: + description: Status represents the current status of the PlacementDecision + type: object + required: + - decisions + properties: + decisions: + description: Decisions is a slice of decisions according to a placement + The number of decisions should not be larger than 100 + type: array + items: + description: ClusterDecision represents a decision from a placement + An empty ClusterDecision indicates it is not scheduled yet. + type: object + required: + - clusterName + - reason + properties: + clusterName: + description: ClusterName is the name of the ManagedCluster. + If it is not empty, its value should be unique cross all placement + decisions for the Placement. + type: string + reason: + description: Reason represents the reason why the ManagedCluster + is selected. + type: string + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0001_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0001_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml index 4ab960857..4027c7a94 100644 --- a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0001_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml +++ b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/0001_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml @@ -17,6 +17,8 @@ spec: claims ClusterClaims with well known names include, 1. id.k8s.io, it contains a unique identifier for the cluster. 2. clusterset.k8s.io, it contains an identifier that relates the cluster to the ClusterSet in which it belongs. + ClusterClaims created on a managed cluster will be collected and saved into + the status of the corresponding ManagedCluster on hub. type: object properties: apiVersion: diff --git a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/register.go b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/register.go index 69a6abb54..3defb57d6 100644 --- a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/register.go +++ b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/register.go @@ -36,6 +36,10 @@ func addKnownTypes(scheme *runtime.Scheme) error { &ManagedClusterSetBindingList{}, &ClusterClaim{}, &ClusterClaimList{}, + &Placement{}, + &PlacementList{}, + &PlacementDecision{}, + &PlacementDecisionList{}, ) metav1.AddToGroupVersion(scheme, GroupVersion) return nil diff --git a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/types.go b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/types.go index bf89c9941..b30754c09 100644 --- a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/types.go +++ b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/types.go @@ -115,6 +115,8 @@ type ManagedClusterSetBindingList struct { // 1. id.k8s.io, it contains a unique identifier for the cluster. // 2. clusterset.k8s.io, it contains an identifier that relates the cluster // to the ClusterSet in which it belongs. +// ClusterClaims created on a managed cluster will be collected and saved into +// the status of the corresponding ManagedCluster on hub. type ClusterClaim struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -157,3 +159,200 @@ var ReservedClusterClaimNames = [...]string{ // product name, like OpenShift, Anthos, EKS and GKE "product.open-cluster-management.io", } + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:scope="Namespaced" +// +kubebuilder:subresource:status + +// Placement defines a rule to select a set of ManagedClusters from the ManagedClusterSets bound +// to the placement namespace. +// +// Here is how the placement policy combines with other selection methods to determine a matching +// list of ManagedClusters: +// 1) Kubernetes clusters are registered with hub as cluster-scoped ManagedClusters; +// 2) ManagedClusters are organized into cluster-scoped ManagedClusterSets; +// 3) ManagedClusterSets are bound to workload namespaces; +// 4) Namespace-scoped Placements specify a slice of ManagedClusterSets which select a working set +// of potential ManagedClusters; +// 5) Then Placements subselect from that working set using label/claim selection. +// +// No ManagedCluster will be selected if no ManagedClusterSet is bound to the placement +// namespace. User is able to bind a ManagedClusterSet to a namespace by creating a +// ManagedClusterSetBinding in that namespace if they have a RBAC rule to CREATE on the virtual +// subresource of `managedclustersets/bind`. +// +// A slice of PlacementDecisions with label cluster.open-cluster-management.io/placement={placement name} +// will be created to represent the ManagedClusters selected by this placement. +// +// If a ManagedCluster is selected and added into the PlacementDecisions, other components may +// apply workload on it; once it is removed from the PlacementDecisions, the workload applied on +// this ManagedCluster should be evicted accordingly. +type Placement struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the attributes of Placement. + // +kubebuilder:validation:Required + // +required + Spec PlacementSpec `json:"spec"` + + // Status represents the current status of the Placement + // +optional + Status PlacementStatus `json:"status,omitempty"` +} + +// PlacementSpec defines the attributes of Placement. +// An empty PlacementSpec selects all ManagedClusters from the ManagedClusterSets bound to +// the placement namespace. The containing fields are ANDed. +type PlacementSpec struct { + // ClusterSets represent the ManagedClusterSets from which the ManagedClusters are selected. + // If the slice is empty, ManagedClusters will be selected from the ManagedClusterSets bound to the placement + // namespace, otherwise ManagedClusters will be selected from the intersection of this slice and the + // ManagedClusterSets bound to the placement namespace. + // +optional + ClusterSets []string `json:"clusterSets,omitempty"` + + // NumberOfClusters represents the desired number of ManagedClusters to be selected which meet the + // placement requirements. + // 1) If not specified, all ManagedClusters which meet the placement requirements (including ClusterSets, + // and Predicates) will be selected; + // 2) Otherwise if the nubmer of ManagedClusters meet the placement requirements is larger than + // NumberOfClusters, a random subset with desired number of ManagedClusters will be selected; + // 3) If the nubmer of ManagedClusters meet the placement requirements is equal to NumberOfClusters, + // all of them will be selected; + // 4) If the nubmer of ManagedClusters meet the placement requirements is less than NumberOfClusters, + // all of them will be selected, and the status of condition `PlacementConditionSatisfied` will be + // set to false; + // +optional + NumberOfClusters *int32 `json:"numberOfClusters,omitempty"` + + // Predicates represent a slice of predicates to select ManagedClusters. The predicates are ORed. + // +optional + Predicates []ClusterPredicate `json:"predicates,omitempty"` +} + +// ClusterPredicate represents a predicate to select ManagedClusters. +type ClusterPredicate struct { + // RequiredClusterSelector represents a selector of ManagedClusters by label and claim. If specified, + // 1) Any ManagedCluster, which does not match the selector, should not be selected by this ClusterPredicate; + // 2) If a selected ManagedCluster (of this ClusterPredicate) ceases to match the selector (e.g. due to + // an update) of any ClusterPredicate, it will be eventually removed from the placement decisions; + // 3) If a ManagedCluster (not selected previously) starts to match the selector, it will either + // be selected or at least has a chance to be selected (when NumberOfClusters is specified); + // +optional + RequiredClusterSelector ClusterSelector `json:"requiredClusterSelector,omitempty"` +} + +// ClusterSelector represents the AND of the containing selectors. An empty cluster selector matches all objects. +// A null cluster selector matches no objects. +type ClusterSelector struct { + // LabelSelector represents a selector of ManagedClusters by label + // +optional + LabelSelector metav1.LabelSelector `json:"labelSelector,omitempty"` + + // ClaimSelector represents a selector of ManagedClusters by clusterClaims in status + // +optional + ClaimSelector ClusterClaimSelector `json:"claimSelector,omitempty"` +} + +// ClusterClaimSelector is a claim query over a set of ManagedClusters. An empty cluster claim +// selector matches all objects. A null cluster claim selector matches no objects. +type ClusterClaimSelector struct { + // matchExpressions is a list of cluster claim selector requirements. The requirements are ANDed. + // +optional + MatchExpressions []metav1.LabelSelectorRequirement `json:"matchExpressions,omitempty"` +} + +type PlacementStatus struct { + // NumberOfSelectedClusters represents the number of selected ManagedClusters + // +optional + NumberOfSelectedClusters int32 `json:"numberOfSelectedClusters"` + + // Conditions contains the different condition statuses for this Placement. + // +optional + Conditions []metav1.Condition `json:"conditions"` +} + +const ( + // PlacementConditionSatisfied means Placement requirements are satisfied. + // A placement is not satisfied only if there is empty ClusterDecision in the status.decisions + // of PlacementDecisions. + PlacementConditionSatisfied string = "PlacementSatisfied" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PlacementList is a collection of Placements. +type PlacementList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + // Items is a list of Placements. + Items []Placement `json:"items"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:scope="Namespaced" +// +kubebuilder:subresource:status + +// PlacementDecision indicates a decision from a placement +// PlacementDecision should has a label cluster.open-cluster-management.io/placement={placement name} +// to reference a certain placement. +// +// If a placement has spec.numberOfClusters specified, the total number of decisions contained in +// status.decisions of PlacementDecisions should always be NumberOfClusters; otherwise, the total +// number of decisions should be the number of ManagedClusters which match the placement requirements. +// +// Some of the decisions might be empty when there are no enough ManagedClusters meet the placement +// requirements. +type PlacementDecision struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Status represents the current status of the PlacementDecision + // +optional + Status PlacementDecisionStatus `json:"status,omitempty"` +} + +// PlacementDecisionStatus represents the current status of the PlacementDecision. +type PlacementDecisionStatus struct { + // Decisions is a slice of decisions according to a placement + // The number of decisions should not be larger than 100 + // +kubebuilder:validation:Required + // +required + Decisions []ClusterDecision `json:"decisions"` +} + +// ClusterDecision represents a decision from a placement +// An empty ClusterDecision indicates it is not scheduled yet. +type ClusterDecision struct { + // ClusterName is the name of the ManagedCluster. If it is not empty, its value should be unique cross all + // placement decisions for the Placement. + // +kubebuilder:validation:Required + // +required + ClusterName string `json:"clusterName"` + + // Reason represents the reason why the ManagedCluster is selected. + // +kubebuilder:validation:Required + // +required + Reason string `json:"reason"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterDecisionList is a collection of PlacementDecision. +type PlacementDecisionList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + // Items is a list of PlacementDecision. + Items []PlacementDecision `json:"items"` +} diff --git a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/zz_generated.deepcopy.go index 864ad7de3..7344a96e6 100644 --- a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/zz_generated.deepcopy.go +++ b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/zz_generated.deepcopy.go @@ -69,6 +69,29 @@ func (in *ClusterClaimList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterClaimSelector) DeepCopyInto(out *ClusterClaimSelector) { + *out = *in + if in.MatchExpressions != nil { + in, out := &in.MatchExpressions, &out.MatchExpressions + *out = make([]v1.LabelSelectorRequirement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClaimSelector. +func (in *ClusterClaimSelector) DeepCopy() *ClusterClaimSelector { + if in == nil { + return nil + } + out := new(ClusterClaimSelector) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterClaimSpec) DeepCopyInto(out *ClusterClaimSpec) { *out = *in @@ -85,6 +108,57 @@ func (in *ClusterClaimSpec) DeepCopy() *ClusterClaimSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterDecision) DeepCopyInto(out *ClusterDecision) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterDecision. +func (in *ClusterDecision) DeepCopy() *ClusterDecision { + if in == nil { + return nil + } + out := new(ClusterDecision) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterPredicate) DeepCopyInto(out *ClusterPredicate) { + *out = *in + in.RequiredClusterSelector.DeepCopyInto(&out.RequiredClusterSelector) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPredicate. +func (in *ClusterPredicate) DeepCopy() *ClusterPredicate { + if in == nil { + return nil + } + out := new(ClusterPredicate) + in.DeepCopyInto(out) + return out +} + +// 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 + in.LabelSelector.DeepCopyInto(&out.LabelSelector) + in.ClaimSelector.DeepCopyInto(&out.ClaimSelector) + 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 *ManagedClusterSet) DeepCopyInto(out *ManagedClusterSet) { *out = *in @@ -260,3 +334,201 @@ func (in *ManagedClusterSetStatus) DeepCopy() *ManagedClusterSetStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Placement) DeepCopyInto(out *Placement) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Placement. +func (in *Placement) DeepCopy() *Placement { + if in == nil { + return nil + } + out := new(Placement) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Placement) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementDecision) DeepCopyInto(out *PlacementDecision) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementDecision. +func (in *PlacementDecision) DeepCopy() *PlacementDecision { + if in == nil { + return nil + } + out := new(PlacementDecision) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PlacementDecision) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementDecisionList) DeepCopyInto(out *PlacementDecisionList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PlacementDecision, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementDecisionList. +func (in *PlacementDecisionList) DeepCopy() *PlacementDecisionList { + if in == nil { + return nil + } + out := new(PlacementDecisionList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PlacementDecisionList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementDecisionStatus) DeepCopyInto(out *PlacementDecisionStatus) { + *out = *in + if in.Decisions != nil { + in, out := &in.Decisions, &out.Decisions + *out = make([]ClusterDecision, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementDecisionStatus. +func (in *PlacementDecisionStatus) DeepCopy() *PlacementDecisionStatus { + if in == nil { + return nil + } + out := new(PlacementDecisionStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementList) DeepCopyInto(out *PlacementList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Placement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementList. +func (in *PlacementList) DeepCopy() *PlacementList { + if in == nil { + return nil + } + out := new(PlacementList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PlacementList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementSpec) DeepCopyInto(out *PlacementSpec) { + *out = *in + if in.ClusterSets != nil { + in, out := &in.ClusterSets, &out.ClusterSets + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.NumberOfClusters != nil { + in, out := &in.NumberOfClusters, &out.NumberOfClusters + *out = new(int32) + **out = **in + } + if in.Predicates != nil { + in, out := &in.Predicates, &out.Predicates + *out = make([]ClusterPredicate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementSpec. +func (in *PlacementSpec) DeepCopy() *PlacementSpec { + if in == nil { + return nil + } + out := new(PlacementSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementStatus) DeepCopyInto(out *PlacementStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementStatus. +func (in *PlacementStatus) DeepCopy() *PlacementStatus { + if in == nil { + return nil + } + out := new(PlacementStatus) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/zz_generated.swagger_doc_generated.go b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/zz_generated.swagger_doc_generated.go index b0f04ac0e..79653cb61 100644 --- a/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/zz_generated.swagger_doc_generated.go +++ b/vendor/github.com/open-cluster-management/api/cluster/v1alpha1/zz_generated.swagger_doc_generated.go @@ -12,7 +12,7 @@ package v1alpha1 // AUTO-GENERATED FUNCTIONS START HERE var map_ClusterClaim = map[string]string{ - "": "ClusterClaim represents cluster information that a managed cluster claims ClusterClaims with well known names include,\n 1. id.k8s.io, it contains a unique identifier for the cluster.\n 2. clusterset.k8s.io, it contains an identifier that relates the cluster\n to the ClusterSet in which it belongs.", + "": "ClusterClaim represents cluster information that a managed cluster claims ClusterClaims with well known names include,\n 1. id.k8s.io, it contains a unique identifier for the cluster.\n 2. clusterset.k8s.io, it contains an identifier that relates the cluster\n to the ClusterSet in which it belongs.\nClusterClaims created on a managed cluster will be collected and saved into the status of the corresponding ManagedCluster on hub.", "spec": "Spec defines the attributes of the ClusterClaim.", } @@ -30,6 +30,15 @@ func (ClusterClaimList) SwaggerDoc() map[string]string { return map_ClusterClaimList } +var map_ClusterClaimSelector = map[string]string{ + "": "ClusterClaimSelector is a claim query over a set of ManagedClusters. An empty cluster claim selector matches all objects. A null cluster claim selector matches no objects.", + "matchExpressions": "matchExpressions is a list of cluster claim selector requirements. The requirements are ANDed.", +} + +func (ClusterClaimSelector) SwaggerDoc() map[string]string { + return map_ClusterClaimSelector +} + var map_ClusterClaimSpec = map[string]string{ "value": "Value is a claim-dependent string", } @@ -38,6 +47,35 @@ func (ClusterClaimSpec) SwaggerDoc() map[string]string { return map_ClusterClaimSpec } +var map_ClusterDecision = map[string]string{ + "": "ClusterDecision represents a decision from a placement An empty ClusterDecision indicates it is not scheduled yet.", + "clusterName": "ClusterName is the name of the ManagedCluster. If it is not empty, its value should be unique cross all placement decisions for the Placement.", + "reason": "Reason represents the reason why the ManagedCluster is selected.", +} + +func (ClusterDecision) SwaggerDoc() map[string]string { + return map_ClusterDecision +} + +var map_ClusterPredicate = map[string]string{ + "": "ClusterPredicate represents a predicate to select ManagedClusters.", + "requiredClusterSelector": "RequiredClusterSelector represents a selector of ManagedClusters by label and claim. If specified, 1) Any ManagedCluster, which does not match the selector, should not be selected by this ClusterPredicate; 2) If a selected ManagedCluster (of this ClusterPredicate) ceases to match the selector (e.g. due to\n an update) of any ClusterPredicate, it will be eventually removed from the placement decisions;\n3) If a ManagedCluster (not selected previously) starts to match the selector, it will either\n be selected or at least has a chance to be selected (when NumberOfClusters is specified);", +} + +func (ClusterPredicate) SwaggerDoc() map[string]string { + return map_ClusterPredicate +} + +var map_ClusterSelector = map[string]string{ + "": "ClusterSelector represents the AND of the containing selectors. An empty cluster selector matches all objects. A null cluster selector matches no objects.", + "labelSelector": "LabelSelector represents a selector of ManagedClusters by label", + "claimSelector": "ClaimSelector represents a selector of ManagedClusters by clusterClaims in status", +} + +func (ClusterSelector) SwaggerDoc() map[string]string { + return map_ClusterSelector +} + var map_ManagedClusterSet = map[string]string{ "": "ManagedClusterSet defines a group of ManagedClusters that user's workload can run on. A workload can be defined to deployed on a ManagedClusterSet, which mean:\n 1. The workload can run on any ManagedCluster in the ManagedClusterSet\n 2. The workload cannot run on any ManagedCluster outside the ManagedClusterSet\n 3. The service exposed by the workload can be shared in any ManagedCluster in the ManagedClusterSet\n\nIn order to assign a ManagedCluster to a certian ManagedClusterSet, add a label with name `cluster.open-cluster-management.io/clusterset` on the ManagedCluster to refers to the ManagedClusterSet. User is not allow to add/remove this label on a ManagedCluster unless they have a RBAC rule to CREATE on a virtual subresource of managedclustersets/join. In order to update this label, user must have the permission on both the old and new ManagedClusterSet.", "spec": "Spec defines the attributes of the ManagedClusterSet", @@ -103,4 +141,72 @@ func (ManagedClusterSetStatus) SwaggerDoc() map[string]string { return map_ManagedClusterSetStatus } +var map_Placement = map[string]string{ + "": "Placement defines a rule to select a set of ManagedClusters from the ManagedClusterSets bound to the placement namespace.\n\nHere is how the placement policy combines with other selection methods to determine a matching list of ManagedClusters: 1) Kubernetes clusters are registered with hub as cluster-scoped ManagedClusters; 2) ManagedClusters are organized into cluster-scoped ManagedClusterSets; 3) ManagedClusterSets are bound to workload namespaces; 4) Namespace-scoped Placements specify a slice of ManagedClusterSets which select a working set\n of potential ManagedClusters;\n5) Then Placements subselect from that working set using label/claim selection.\n\nNo ManagedCluster will be selected if no ManagedClusterSet is bound to the placement namespace. User is able to bind a ManagedClusterSet to a namespace by creating a ManagedClusterSetBinding in that namespace if they have a RBAC rule to CREATE on the virtual subresource of `managedclustersets/bind`.\n\nA slice of PlacementDecisions with label cluster.open-cluster-management.io/placement={placement name} will be created to represent the ManagedClusters selected by this placement.\n\nIf a ManagedCluster is selected and added into the PlacementDecisions, other components may apply workload on it; once it is removed from the PlacementDecisions, the workload applied on this ManagedCluster should be evicted accordingly.", + "spec": "Spec defines the attributes of Placement.", + "status": "Status represents the current status of the Placement", +} + +func (Placement) SwaggerDoc() map[string]string { + return map_Placement +} + +var map_PlacementDecision = map[string]string{ + "": "PlacementDecision indicates a decision from a placement PlacementDecision should has a label cluster.open-cluster-management.io/placement={placement name} to reference a certain placement.\n\nIf a placement has spec.numberOfClusters specified, the total number of decisions contained in status.decisions of PlacementDecisions should always be NumberOfClusters; otherwise, the total number of decisions should be the number of ManagedClusters which match the placement requirements.\n\nSome of the decisions might be empty when there are no enough ManagedClusters meet the placement requirements.", + "status": "Status represents the current status of the PlacementDecision", +} + +func (PlacementDecision) SwaggerDoc() map[string]string { + return map_PlacementDecision +} + +var map_PlacementDecisionList = map[string]string{ + "": "ClusterDecisionList is a collection of PlacementDecision.", + "metadata": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "items": "Items is a list of PlacementDecision.", +} + +func (PlacementDecisionList) SwaggerDoc() map[string]string { + return map_PlacementDecisionList +} + +var map_PlacementDecisionStatus = map[string]string{ + "": "PlacementDecisionStatus represents the current status of the PlacementDecision.", + "decisions": "Decisions is a slice of decisions according to a placement The number of decisions should not be larger than 100", +} + +func (PlacementDecisionStatus) SwaggerDoc() map[string]string { + return map_PlacementDecisionStatus +} + +var map_PlacementList = map[string]string{ + "": "PlacementList is a collection of Placements.", + "metadata": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "items": "Items is a list of Placements.", +} + +func (PlacementList) SwaggerDoc() map[string]string { + return map_PlacementList +} + +var map_PlacementSpec = map[string]string{ + "": "PlacementSpec defines the attributes of Placement. An empty PlacementSpec selects all ManagedClusters from the ManagedClusterSets bound to the placement namespace. The containing fields are ANDed.", + "clusterSets": "ClusterSets represent the ManagedClusterSets from which the ManagedClusters are selected. If the slice is empty, ManagedClusters will be selected from the ManagedClusterSets bound to the placement namespace, otherwise ManagedClusters will be selected from the intersection of this slice and the ManagedClusterSets bound to the placement namespace.", + "numberOfClusters": "NumberOfClusters represents the desired number of ManagedClusters to be selected which meet the placement requirements. 1) If not specified, all ManagedClusters which meet the placement requirements (including ClusterSets,\n and Predicates) will be selected;\n2) Otherwise if the nubmer of ManagedClusters meet the placement requirements is larger than\n NumberOfClusters, a random subset with desired number of ManagedClusters will be selected;\n3) If the nubmer of ManagedClusters meet the placement requirements is equal to NumberOfClusters,\n all of them will be selected;\n4) If the nubmer of ManagedClusters meet the placement requirements is less than NumberOfClusters,\n all of them will be selected, and the status of condition `PlacementConditionSatisfied` will be\n set to false;", + "predicates": "Predicates represent a slice of predicates to select ManagedClusters. The predicates are ORed.", +} + +func (PlacementSpec) SwaggerDoc() map[string]string { + return map_PlacementSpec +} + +var map_PlacementStatus = map[string]string{ + "numberOfSelectedClusters": "NumberOfSelectedClusters represents the number of selected ManagedClusters", + "conditions": "Conditions contains the different condition statuses for this Placement.", +} + +func (PlacementStatus) SwaggerDoc() map[string]string { + return map_PlacementStatus +} + // AUTO-GENERATED FUNCTIONS END HERE diff --git a/vendor/github.com/open-cluster-management/api/operator/v1/0000_01_operator.open-cluster-management.io_clustermanagers.crd.yaml b/vendor/github.com/open-cluster-management/api/operator/v1/0000_01_operator.open-cluster-management.io_clustermanagers.crd.yaml index eba39a2f6..6b7d246c3 100644 --- a/vendor/github.com/open-cluster-management/api/operator/v1/0000_01_operator.open-cluster-management.io_clustermanagers.crd.yaml +++ b/vendor/github.com/open-cluster-management/api/operator/v1/0000_01_operator.open-cluster-management.io_clustermanagers.crd.yaml @@ -37,14 +37,21 @@ spec: that govern registration and work distribution for attached Klusterlets. type: object properties: + placementImagePullSpec: + description: PlacementImagePullSpec represents the desired image configuration + of placement controller/webhook installed on hub. + type: string + default: quay.io/open-cluster-management/placement registrationImagePullSpec: description: RegistrationImagePullSpec represents the desired image of registration controller/webhook installed on hub. type: string + default: quay.io/open-cluster-management/registration workImagePullSpec: description: WorkImagePullSpec represents the desired image configuration of work controller/webhook installed on hub. type: string + default: quay.io/open-cluster-management/work status: description: Status represents the current status of controllers that govern the lifecycle of managed clusters. diff --git a/vendor/github.com/open-cluster-management/api/operator/v1/types.go b/vendor/github.com/open-cluster-management/api/operator/v1/types.go index e12f7f875..e01ca4ef5 100644 --- a/vendor/github.com/open-cluster-management/api/operator/v1/types.go +++ b/vendor/github.com/open-cluster-management/api/operator/v1/types.go @@ -27,12 +27,19 @@ type ClusterManager struct { // ClusterManagerSpec represents a desired deployment configuration of controllers that govern registration and work distribution for attached Klusterlets. type ClusterManagerSpec struct { // RegistrationImagePullSpec represents the desired image of registration controller/webhook installed on hub. - // +required - RegistrationImagePullSpec string `json:"registrationImagePullSpec"` + // +optional + // +kubebuilder:default=quay.io/open-cluster-management/registration + RegistrationImagePullSpec string `json:"registrationImagePullSpec,omitempty"` // WorkImagePullSpec represents the desired image configuration of work controller/webhook installed on hub. - // +required + // +optional + // +kubebuilder:default=quay.io/open-cluster-management/work WorkImagePullSpec string `json:"workImagePullSpec,omitempty"` + + // PlacementImagePullSpec represents the desired image configuration of placement controller/webhook installed on hub. + // +optional + // +kubebuilder:default=quay.io/open-cluster-management/placement + PlacementImagePullSpec string `json:"placementImagePullSpec,omitempty"` } // ClusterManagerStatus represents the current status of the registration and work distribution controllers running on the hub. diff --git a/vendor/github.com/open-cluster-management/api/operator/v1/zz_generated.swagger_doc_generated.go b/vendor/github.com/open-cluster-management/api/operator/v1/zz_generated.swagger_doc_generated.go index 60d2d791c..7853d59bc 100644 --- a/vendor/github.com/open-cluster-management/api/operator/v1/zz_generated.swagger_doc_generated.go +++ b/vendor/github.com/open-cluster-management/api/operator/v1/zz_generated.swagger_doc_generated.go @@ -35,6 +35,7 @@ var map_ClusterManagerSpec = map[string]string{ "": "ClusterManagerSpec represents a desired deployment configuration of controllers that govern registration and work distribution for attached Klusterlets.", "registrationImagePullSpec": "RegistrationImagePullSpec represents the desired image of registration controller/webhook installed on hub.", "workImagePullSpec": "WorkImagePullSpec represents the desired image configuration of work controller/webhook installed on hub.", + "placementImagePullSpec": "PlacementImagePullSpec represents the desired image configuration of placement controller/webhook installed on hub.", } func (ClusterManagerSpec) SwaggerDoc() map[string]string { diff --git a/vendor/modules.txt b/vendor/modules.txt index ea9250cbd..09eefd395 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -138,7 +138,7 @@ github.com/onsi/gomega/matchers/support/goraph/edge github.com/onsi/gomega/matchers/support/goraph/node github.com/onsi/gomega/matchers/support/goraph/util github.com/onsi/gomega/types -# github.com/open-cluster-management/api v0.0.0-20210409125704-06f2aec1a73f +# github.com/open-cluster-management/api v0.0.0-20210511122802-f38973154cbd ## explicit github.com/open-cluster-management/api/addon/v1alpha1 github.com/open-cluster-management/api/client/addon/clientset/versioned