From 527d56693484aa63b377c1864fb739748904013c Mon Sep 17 00:00:00 2001 From: Tamal Saha Date: Mon, 29 Jan 2024 22:52:18 -0800 Subject: [PATCH] Add PlacementPolicy api Signed-off-by: Tamal Saha --- apis/apps/v1/placementpolicy_types.go | 99 ++++++++++ apis/apps/v1/zz_generated.deepcopy.go | 172 ++++++++++++++++++ .../versioned/typed/apps/v1/apps_client.go | 5 + .../typed/apps/v1/fake/fake_apps_client.go | 4 + .../apps/v1/fake/fake_placementpolicy.go | 121 ++++++++++++ .../typed/apps/v1/generated_expansion.go | 2 + .../typed/apps/v1/placementpolicy.go | 168 +++++++++++++++++ .../externalversions/apps/v1/interface.go | 7 + .../apps/v1/placementpolicy.go | 89 +++++++++ client/informers/externalversions/generic.go | 2 + client/listers/apps/v1/expansion_generated.go | 4 + client/listers/apps/v1/placementpolicy.go | 68 +++++++ ...ps.k8s.appscode.com_placementpolicies.yaml | 95 ++++++++++ crds/apps.k8s.appscode.com_placements.yaml | 95 ++++++++++ hack/samples/placement-spot.yaml | 49 +++++ 15 files changed, 980 insertions(+) create mode 100644 apis/apps/v1/placementpolicy_types.go create mode 100644 client/clientset/versioned/typed/apps/v1/fake/fake_placementpolicy.go create mode 100644 client/clientset/versioned/typed/apps/v1/placementpolicy.go create mode 100644 client/informers/externalversions/apps/v1/placementpolicy.go create mode 100644 client/listers/apps/v1/placementpolicy.go create mode 100644 crds/apps.k8s.appscode.com_placementpolicies.yaml create mode 100644 crds/apps.k8s.appscode.com_placements.yaml create mode 100644 hack/samples/placement-spot.yaml diff --git a/apis/apps/v1/placementpolicy_types.go b/apis/apps/v1/placementpolicy_types.go new file mode 100644 index 00000000..783dfebf --- /dev/null +++ b/apis/apps/v1/placementpolicy_types.go @@ -0,0 +1,99 @@ +/* +Copyright AppsCode Inc. and Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +genclient:nonNamespaced +// +genclient:skipVerbs=updateStatus +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:scope=Cluster + +// PlacementPolicy represents a set of pods with consistent identities. +// Identities are defined as: +// - Network: A single stable DNS and hostname. +// - Storage: As many VolumeClaims as requested. +// +// The PlacementPolicy guarantees that a given network identity will always +// map to the same storage identity. +type PlacementPolicy struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired identities of pods in this set. + // +optional + Spec PlacementPolicySpec `json:"spec,omitempty"` +} + +// A PlacementPolicySpec is the specification of a PlacementPolicy. +type PlacementPolicySpec struct { + // +optional + ZoneSpreadConstraint *ZoneSpreadConstraint `json:"zoneSpreadConstraint,omitempty"` + + // +optional + NodeSpreadConstraint *NodeSpreadConstraint `json:"nodeSpreadConstraint,omitempty"` + + // +optional + NodeAffinity []NodeAffinityRules `json:"nodeAffinity,omitempty"` +} + +type ZoneSpreadConstraint struct { + Zones []string `json:"zones"` + MaxSkew int32 `json:"maxSkew"` + WhenUnsatisfiable v1.UnsatisfiableConstraintAction `json:"whenUnsatisfiable"` +} + +type NodeSpreadConstraint struct { + MaxSkew int32 `json:"maxSkew"` + WhenUnsatisfiable v1.UnsatisfiableConstraintAction `json:"whenUnsatisfiable"` +} + +type NodeAffinityRules struct { + TopologyKey string `json:"topologyKey"` + Domains []TopologyDomain `json:"domains"` + WhenUnsatisfiable v1.UnsatisfiableConstraintAction `json:"whenUnsatisfiable"` +} + +type TopologyDomain struct { + Domain string `json:"domain"` + Replicas string `json:"replicas"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PlacementPolicyList is a collection of PlacementPolicys. +type PlacementPolicyList struct { + metav1.TypeMeta `json:",inline"` + // Standard list's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + // Items is the list of stateful sets. + Items []PlacementPolicy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&PlacementPolicy{}, &PlacementPolicyList{}) +} diff --git a/apis/apps/v1/zz_generated.deepcopy.go b/apis/apps/v1/zz_generated.deepcopy.go index cb69bbd4..1dd32ce6 100644 --- a/apis/apps/v1/zz_generated.deepcopy.go +++ b/apis/apps/v1/zz_generated.deepcopy.go @@ -28,6 +28,136 @@ import ( 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 *NodeAffinityRules) DeepCopyInto(out *NodeAffinityRules) { + *out = *in + if in.Domains != nil { + in, out := &in.Domains, &out.Domains + *out = make([]TopologyDomain, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeAffinityRules. +func (in *NodeAffinityRules) DeepCopy() *NodeAffinityRules { + if in == nil { + return nil + } + out := new(NodeAffinityRules) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeSpreadConstraint) DeepCopyInto(out *NodeSpreadConstraint) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeSpreadConstraint. +func (in *NodeSpreadConstraint) DeepCopy() *NodeSpreadConstraint { + if in == nil { + return nil + } + out := new(NodeSpreadConstraint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementPolicy) DeepCopyInto(out *PlacementPolicy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementPolicy. +func (in *PlacementPolicy) DeepCopy() *PlacementPolicy { + if in == nil { + return nil + } + out := new(PlacementPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PlacementPolicy) 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 *PlacementPolicyList) DeepCopyInto(out *PlacementPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PlacementPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementPolicyList. +func (in *PlacementPolicyList) DeepCopy() *PlacementPolicyList { + if in == nil { + return nil + } + out := new(PlacementPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PlacementPolicyList) 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 *PlacementPolicySpec) DeepCopyInto(out *PlacementPolicySpec) { + *out = *in + if in.ZoneSpreadConstraint != nil { + in, out := &in.ZoneSpreadConstraint, &out.ZoneSpreadConstraint + *out = new(ZoneSpreadConstraint) + (*in).DeepCopyInto(*out) + } + if in.NodeSpreadConstraint != nil { + in, out := &in.NodeSpreadConstraint, &out.NodeSpreadConstraint + *out = new(NodeSpreadConstraint) + **out = **in + } + if in.NodeAffinity != nil { + in, out := &in.NodeAffinity, &out.NodeAffinity + *out = make([]NodeAffinityRules, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementPolicySpec. +func (in *PlacementPolicySpec) DeepCopy() *PlacementPolicySpec { + if in == nil { + return nil + } + out := new(PlacementPolicySpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PodTemplateSpec) DeepCopyInto(out *PodTemplateSpec) { *out = *in @@ -144,6 +274,11 @@ func (in *StatefulSetSpec) DeepCopyInto(out *StatefulSetSpec) { *out = new(appsv1.StatefulSetOrdinals) **out = **in } + if in.PodPlacementPolicy != nil { + in, out := &in.PodPlacementPolicy, &out.PodPlacementPolicy + *out = new(corev1.LocalObjectReference) + **out = **in + } return } @@ -156,3 +291,40 @@ func (in *StatefulSetSpec) DeepCopy() *StatefulSetSpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TopologyDomain) DeepCopyInto(out *TopologyDomain) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologyDomain. +func (in *TopologyDomain) DeepCopy() *TopologyDomain { + if in == nil { + return nil + } + out := new(TopologyDomain) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ZoneSpreadConstraint) DeepCopyInto(out *ZoneSpreadConstraint) { + *out = *in + if in.Zones != nil { + in, out := &in.Zones, &out.Zones + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZoneSpreadConstraint. +func (in *ZoneSpreadConstraint) DeepCopy() *ZoneSpreadConstraint { + if in == nil { + return nil + } + out := new(ZoneSpreadConstraint) + in.DeepCopyInto(out) + return out +} diff --git a/client/clientset/versioned/typed/apps/v1/apps_client.go b/client/clientset/versioned/typed/apps/v1/apps_client.go index c4283f90..e9f1b16a 100644 --- a/client/clientset/versioned/typed/apps/v1/apps_client.go +++ b/client/clientset/versioned/typed/apps/v1/apps_client.go @@ -28,6 +28,7 @@ import ( type AppsV1Interface interface { RESTClient() rest.Interface + PlacementPoliciesGetter StatefulSetsGetter } @@ -36,6 +37,10 @@ type AppsV1Client struct { restClient rest.Interface } +func (c *AppsV1Client) PlacementPolicies() PlacementPolicyInterface { + return newPlacementPolicies(c) +} + func (c *AppsV1Client) StatefulSets(namespace string) StatefulSetInterface { return newStatefulSets(c, namespace) } diff --git a/client/clientset/versioned/typed/apps/v1/fake/fake_apps_client.go b/client/clientset/versioned/typed/apps/v1/fake/fake_apps_client.go index 325975b9..22ee81b4 100644 --- a/client/clientset/versioned/typed/apps/v1/fake/fake_apps_client.go +++ b/client/clientset/versioned/typed/apps/v1/fake/fake_apps_client.go @@ -28,6 +28,10 @@ type FakeAppsV1 struct { *testing.Fake } +func (c *FakeAppsV1) PlacementPolicies() v1.PlacementPolicyInterface { + return &FakePlacementPolicies{c} +} + func (c *FakeAppsV1) StatefulSets(namespace string) v1.StatefulSetInterface { return &FakeStatefulSets{c, namespace} } diff --git a/client/clientset/versioned/typed/apps/v1/fake/fake_placementpolicy.go b/client/clientset/versioned/typed/apps/v1/fake/fake_placementpolicy.go new file mode 100644 index 00000000..d6e1864a --- /dev/null +++ b/client/clientset/versioned/typed/apps/v1/fake/fake_placementpolicy.go @@ -0,0 +1,121 @@ +/* +Copyright AppsCode Inc. and Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1 "kubeops.dev/statefulset/apis/apps/v1" +) + +// FakePlacementPolicies implements PlacementPolicyInterface +type FakePlacementPolicies struct { + Fake *FakeAppsV1 +} + +var placementpoliciesResource = v1.SchemeGroupVersion.WithResource("placementpolicies") + +var placementpoliciesKind = v1.SchemeGroupVersion.WithKind("PlacementPolicy") + +// Get takes name of the placementPolicy, and returns the corresponding placementPolicy object, and an error if there is any. +func (c *FakePlacementPolicies) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.PlacementPolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(placementpoliciesResource, name), &v1.PlacementPolicy{}) + if obj == nil { + return nil, err + } + return obj.(*v1.PlacementPolicy), err +} + +// List takes label and field selectors, and returns the list of PlacementPolicies that match those selectors. +func (c *FakePlacementPolicies) List(ctx context.Context, opts metav1.ListOptions) (result *v1.PlacementPolicyList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(placementpoliciesResource, placementpoliciesKind, opts), &v1.PlacementPolicyList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1.PlacementPolicyList{ListMeta: obj.(*v1.PlacementPolicyList).ListMeta} + for _, item := range obj.(*v1.PlacementPolicyList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested placementPolicies. +func (c *FakePlacementPolicies) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(placementpoliciesResource, opts)) +} + +// Create takes the representation of a placementPolicy and creates it. Returns the server's representation of the placementPolicy, and an error, if there is any. +func (c *FakePlacementPolicies) Create(ctx context.Context, placementPolicy *v1.PlacementPolicy, opts metav1.CreateOptions) (result *v1.PlacementPolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(placementpoliciesResource, placementPolicy), &v1.PlacementPolicy{}) + if obj == nil { + return nil, err + } + return obj.(*v1.PlacementPolicy), err +} + +// Update takes the representation of a placementPolicy and updates it. Returns the server's representation of the placementPolicy, and an error, if there is any. +func (c *FakePlacementPolicies) Update(ctx context.Context, placementPolicy *v1.PlacementPolicy, opts metav1.UpdateOptions) (result *v1.PlacementPolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(placementpoliciesResource, placementPolicy), &v1.PlacementPolicy{}) + if obj == nil { + return nil, err + } + return obj.(*v1.PlacementPolicy), err +} + +// Delete takes name of the placementPolicy and deletes it. Returns an error if one occurs. +func (c *FakePlacementPolicies) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteActionWithOptions(placementpoliciesResource, name, opts), &v1.PlacementPolicy{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakePlacementPolicies) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(placementpoliciesResource, listOpts) + + _, err := c.Fake.Invokes(action, &v1.PlacementPolicyList{}) + return err +} + +// Patch applies the patch and returns the patched placementPolicy. +func (c *FakePlacementPolicies) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.PlacementPolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(placementpoliciesResource, name, pt, data, subresources...), &v1.PlacementPolicy{}) + if obj == nil { + return nil, err + } + return obj.(*v1.PlacementPolicy), err +} diff --git a/client/clientset/versioned/typed/apps/v1/generated_expansion.go b/client/clientset/versioned/typed/apps/v1/generated_expansion.go index 2562a3c9..2bde990f 100644 --- a/client/clientset/versioned/typed/apps/v1/generated_expansion.go +++ b/client/clientset/versioned/typed/apps/v1/generated_expansion.go @@ -18,4 +18,6 @@ limitations under the License. package v1 +type PlacementPolicyExpansion interface{} + type StatefulSetExpansion interface{} diff --git a/client/clientset/versioned/typed/apps/v1/placementpolicy.go b/client/clientset/versioned/typed/apps/v1/placementpolicy.go new file mode 100644 index 00000000..a49ee3b5 --- /dev/null +++ b/client/clientset/versioned/typed/apps/v1/placementpolicy.go @@ -0,0 +1,168 @@ +/* +Copyright AppsCode Inc. and Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + "context" + "time" + + metav1 "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" + v1 "kubeops.dev/statefulset/apis/apps/v1" + scheme "kubeops.dev/statefulset/client/clientset/versioned/scheme" +) + +// PlacementPoliciesGetter has a method to return a PlacementPolicyInterface. +// A group's client should implement this interface. +type PlacementPoliciesGetter interface { + PlacementPolicies() PlacementPolicyInterface +} + +// PlacementPolicyInterface has methods to work with PlacementPolicy resources. +type PlacementPolicyInterface interface { + Create(ctx context.Context, placementPolicy *v1.PlacementPolicy, opts metav1.CreateOptions) (*v1.PlacementPolicy, error) + Update(ctx context.Context, placementPolicy *v1.PlacementPolicy, opts metav1.UpdateOptions) (*v1.PlacementPolicy, error) + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error + Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.PlacementPolicy, error) + List(ctx context.Context, opts metav1.ListOptions) (*v1.PlacementPolicyList, error) + Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.PlacementPolicy, err error) + PlacementPolicyExpansion +} + +// placementPolicies implements PlacementPolicyInterface +type placementPolicies struct { + client rest.Interface +} + +// newPlacementPolicies returns a PlacementPolicies +func newPlacementPolicies(c *AppsV1Client) *placementPolicies { + return &placementPolicies{ + client: c.RESTClient(), + } +} + +// Get takes name of the placementPolicy, and returns the corresponding placementPolicy object, and an error if there is any. +func (c *placementPolicies) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.PlacementPolicy, err error) { + result = &v1.PlacementPolicy{} + err = c.client.Get(). + Resource("placementpolicies"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of PlacementPolicies that match those selectors. +func (c *placementPolicies) List(ctx context.Context, opts metav1.ListOptions) (result *v1.PlacementPolicyList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1.PlacementPolicyList{} + err = c.client.Get(). + Resource("placementpolicies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested placementPolicies. +func (c *placementPolicies) Watch(ctx context.Context, opts metav1.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(). + Resource("placementpolicies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a placementPolicy and creates it. Returns the server's representation of the placementPolicy, and an error, if there is any. +func (c *placementPolicies) Create(ctx context.Context, placementPolicy *v1.PlacementPolicy, opts metav1.CreateOptions) (result *v1.PlacementPolicy, err error) { + result = &v1.PlacementPolicy{} + err = c.client.Post(). + Resource("placementpolicies"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(placementPolicy). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a placementPolicy and updates it. Returns the server's representation of the placementPolicy, and an error, if there is any. +func (c *placementPolicies) Update(ctx context.Context, placementPolicy *v1.PlacementPolicy, opts metav1.UpdateOptions) (result *v1.PlacementPolicy, err error) { + result = &v1.PlacementPolicy{} + err = c.client.Put(). + Resource("placementpolicies"). + Name(placementPolicy.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(placementPolicy). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the placementPolicy and deletes it. Returns an error if one occurs. +func (c *placementPolicies) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + return c.client.Delete(). + Resource("placementpolicies"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *placementPolicies) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("placementpolicies"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched placementPolicy. +func (c *placementPolicies) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.PlacementPolicy, err error) { + result = &v1.PlacementPolicy{} + err = c.client.Patch(pt). + Resource("placementpolicies"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/client/informers/externalversions/apps/v1/interface.go b/client/informers/externalversions/apps/v1/interface.go index 0eba1efb..0c7e56f4 100644 --- a/client/informers/externalversions/apps/v1/interface.go +++ b/client/informers/externalversions/apps/v1/interface.go @@ -24,6 +24,8 @@ import ( // Interface provides access to all the informers in this group version. type Interface interface { + // PlacementPolicies returns a PlacementPolicyInformer. + PlacementPolicies() PlacementPolicyInformer // StatefulSets returns a StatefulSetInformer. StatefulSets() StatefulSetInformer } @@ -39,6 +41,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} } +// PlacementPolicies returns a PlacementPolicyInformer. +func (v *version) PlacementPolicies() PlacementPolicyInformer { + return &placementPolicyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + // StatefulSets returns a StatefulSetInformer. func (v *version) StatefulSets() StatefulSetInformer { return &statefulSetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/client/informers/externalversions/apps/v1/placementpolicy.go b/client/informers/externalversions/apps/v1/placementpolicy.go new file mode 100644 index 00000000..37f4a9e8 --- /dev/null +++ b/client/informers/externalversions/apps/v1/placementpolicy.go @@ -0,0 +1,89 @@ +/* +Copyright AppsCode Inc. and Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1 + +import ( + "context" + time "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + appsv1 "kubeops.dev/statefulset/apis/apps/v1" + versioned "kubeops.dev/statefulset/client/clientset/versioned" + internalinterfaces "kubeops.dev/statefulset/client/informers/externalversions/internalinterfaces" + v1 "kubeops.dev/statefulset/client/listers/apps/v1" +) + +// PlacementPolicyInformer provides access to a shared informer and lister for +// PlacementPolicies. +type PlacementPolicyInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1.PlacementPolicyLister +} + +type placementPolicyInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewPlacementPolicyInformer constructs a new informer for PlacementPolicy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPlacementPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPlacementPolicyInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredPlacementPolicyInformer constructs a new informer for PlacementPolicy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPlacementPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AppsV1().PlacementPolicies().List(context.TODO(), options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AppsV1().PlacementPolicies().Watch(context.TODO(), options) + }, + }, + &appsv1.PlacementPolicy{}, + resyncPeriod, + indexers, + ) +} + +func (f *placementPolicyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPlacementPolicyInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *placementPolicyInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&appsv1.PlacementPolicy{}, f.defaultInformer) +} + +func (f *placementPolicyInformer) Lister() v1.PlacementPolicyLister { + return v1.NewPlacementPolicyLister(f.Informer().GetIndexer()) +} diff --git a/client/informers/externalversions/generic.go b/client/informers/externalversions/generic.go index 688b1e11..77017efd 100644 --- a/client/informers/externalversions/generic.go +++ b/client/informers/externalversions/generic.go @@ -53,6 +53,8 @@ func (f *genericInformer) Lister() cache.GenericLister { func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { // Group=apps.k8s.appscode.com, Version=v1 + case v1.SchemeGroupVersion.WithResource("placementpolicies"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Apps().V1().PlacementPolicies().Informer()}, nil case v1.SchemeGroupVersion.WithResource("statefulsets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Apps().V1().StatefulSets().Informer()}, nil diff --git a/client/listers/apps/v1/expansion_generated.go b/client/listers/apps/v1/expansion_generated.go index 06cfbb5d..3f69d6f7 100644 --- a/client/listers/apps/v1/expansion_generated.go +++ b/client/listers/apps/v1/expansion_generated.go @@ -17,3 +17,7 @@ limitations under the License. // Code generated by lister-gen. DO NOT EDIT. package v1 + +// PlacementPolicyListerExpansion allows custom methods to be added to +// PlacementPolicyLister. +type PlacementPolicyListerExpansion interface{} diff --git a/client/listers/apps/v1/placementpolicy.go b/client/listers/apps/v1/placementpolicy.go new file mode 100644 index 00000000..3fb10dad --- /dev/null +++ b/client/listers/apps/v1/placementpolicy.go @@ -0,0 +1,68 @@ +/* +Copyright AppsCode Inc. and Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1 "kubeops.dev/statefulset/apis/apps/v1" +) + +// PlacementPolicyLister helps list PlacementPolicies. +// All objects returned here must be treated as read-only. +type PlacementPolicyLister interface { + // List lists all PlacementPolicies in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1.PlacementPolicy, err error) + // Get retrieves the PlacementPolicy from the index for a given name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1.PlacementPolicy, error) + PlacementPolicyListerExpansion +} + +// placementPolicyLister implements the PlacementPolicyLister interface. +type placementPolicyLister struct { + indexer cache.Indexer +} + +// NewPlacementPolicyLister returns a new PlacementPolicyLister. +func NewPlacementPolicyLister(indexer cache.Indexer) PlacementPolicyLister { + return &placementPolicyLister{indexer: indexer} +} + +// List lists all PlacementPolicies in the indexer. +func (s *placementPolicyLister) List(selector labels.Selector) (ret []*v1.PlacementPolicy, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1.PlacementPolicy)) + }) + return ret, err +} + +// Get retrieves the PlacementPolicy from the index for a given name. +func (s *placementPolicyLister) Get(name string) (*v1.PlacementPolicy, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1.Resource("placementpolicy"), name) + } + return obj.(*v1.PlacementPolicy), nil +} diff --git a/crds/apps.k8s.appscode.com_placementpolicies.yaml b/crds/apps.k8s.appscode.com_placementpolicies.yaml new file mode 100644 index 00000000..ee92502d --- /dev/null +++ b/crds/apps.k8s.appscode.com_placementpolicies.yaml @@ -0,0 +1,95 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: placementpolicies.apps.k8s.appscode.com +spec: + group: apps.k8s.appscode.com + names: + kind: PlacementPolicy + listKind: PlacementPolicyList + plural: placementpolicies + singular: placementpolicy + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: "PlacementPolicy represents a set of pods with consistent identities. + Identities are defined as: - Network: A single stable DNS and hostname. + - Storage: As many VolumeClaims as requested. \n The PlacementPolicy guarantees + that a given network identity will always map to the same storage identity." + 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 desired identities of pods in this set. + properties: + nodeAffinity: + items: + properties: + domains: + items: + properties: + domain: + type: string + replicas: + type: string + required: + - domain + - replicas + type: object + type: array + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - domains + - topologyKey + - whenUnsatisfiable + type: object + type: array + nodeSpreadConstraint: + properties: + maxSkew: + format: int32 + type: integer + whenUnsatisfiable: + type: string + required: + - maxSkew + - whenUnsatisfiable + type: object + zoneSpreadConstraint: + properties: + maxSkew: + format: int32 + type: integer + whenUnsatisfiable: + type: string + zones: + items: + type: string + type: array + required: + - maxSkew + - whenUnsatisfiable + - zones + type: object + type: object + type: object + served: true + storage: true diff --git a/crds/apps.k8s.appscode.com_placements.yaml b/crds/apps.k8s.appscode.com_placements.yaml new file mode 100644 index 00000000..ea09728f --- /dev/null +++ b/crds/apps.k8s.appscode.com_placements.yaml @@ -0,0 +1,95 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: placements.apps.k8s.appscode.com +spec: + group: apps.k8s.appscode.com + names: + kind: Placement + listKind: PlacementList + plural: placements + singular: placement + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: "Placement represents a set of pods with consistent identities. + Identities are defined as: - Network: A single stable DNS and hostname. + - Storage: As many VolumeClaims as requested. \n The Placement guarantees + that a given network identity will always map to the same storage identity." + 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 desired identities of pods in this set. + properties: + nodeAffinity: + items: + properties: + domains: + items: + properties: + domain: + type: string + replicas: + type: string + required: + - domain + - replicas + type: object + type: array + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - domains + - topologyKey + - whenUnsatisfiable + type: object + type: array + nodeSpreadConstraint: + properties: + maxSkew: + format: int32 + type: integer + whenUnsatisfiable: + type: string + required: + - maxSkew + - whenUnsatisfiable + type: object + zoneSpreadConstraint: + properties: + maxSkew: + format: int32 + type: integer + whenUnsatisfiable: + type: string + zones: + items: + type: string + type: array + required: + - maxSkew + - whenUnsatisfiable + - zones + type: object + type: object + type: object + served: true + storage: true diff --git a/hack/samples/placement-spot.yaml b/hack/samples/placement-spot.yaml new file mode 100644 index 00000000..8dbf6e91 --- /dev/null +++ b/hack/samples/placement-spot.yaml @@ -0,0 +1,49 @@ +apiVersion: apps.k8s.appscode.com/v1 +kind: PlacementPolicy +metadata: + name: one-ondemand +spec: + zoneSpreadConstraint: + maxSkew: 1 + whenUnsatisfiable: DoNotSchedule + zones: + - us-east-2a + - us-east-2b + - us-east-2c + nodeSpreadConstraint: + maxSkew: 1 + whenUnsatisfiable: DoNotSchedule + nodeAffinity: + - topologyKey: karpenter.sh/capacity-type + whenUnsatisfiable: DoNotSchedule + domains: + - domain: on-demand + replicas: "1" + - domain: spot + replicas: "" # empty means rest of replicas + +--- + +apiVersion: apps.k8s.appscode.com/v1 +kind: PlacementPolicy +metadata: + name: minority-ondemand +spec: + zoneSpreadConstraint: + maxSkew: 1 + whenUnsatisfiable: DoNotSchedule + zones: + - us-east-2a + - us-east-2b + - us-east-2c + nodeSpreadConstraint: + maxSkew: 1 + whenUnsatisfiable: DoNotSchedule + nodeAffinity: + - topologyKey: karpenter.sh/capacity-type + whenUnsatisfiable: DoNotSchedule + domains: + - domain: on-demand + replicas: "obj.replicas/2" + - domain: spot + replicas: "" # empty means rest of replicas