diff --git a/api/v1/checkpointschedule_types.go b/api/v1/checkpointschedule_types.go new file mode 100644 index 00000000..8313c1cc --- /dev/null +++ b/api/v1/checkpointschedule_types.go @@ -0,0 +1,83 @@ +/* +Copyright 2026. + +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 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +type CheckpointIntent string + +const ( + Backup CheckpointIntent = "Backup" + PreEviction CheckpointIntent = "PreEviction" + ResourcePressure CheckpointIntent = "ResourcePressure" + Manual CheckpointIntent = "Manual" +) + +type CheckpointScheduleSpec struct { + Namespace string `json:"namespace"` + Selector metav1.LabelSelector `json:"selector"` + ContainerNames []string `json:"containerNames,omitempty"` + Intent CheckpointIntent `json:"intent"` + Triggers TriggersSpec `json:"triggers"` +} + +type TriggersSpec struct { + Schedule string `json:"schedule,omitempty"` + ResourceThreshold *ResourceThresholdSpec `json:"resourceThreshold,omitempty"` + OnKubernetesEvents []string `json:"onKubernetesEvents,omitempty"` + OnAnnotation bool `json:"onAnnotation,omitempty"` +} + +type ResourceThresholdSpec struct { + CPUPercent *int `json:"cpuPercent,omitempty"` + MemoryPercent *int `json:"memoryPercent,omitempty"` + PollIntervalSeconds *int `json:"pollIntervalSeconds,omitempty"` +} + +// CheckpointScheduleStatus defines the observed state of CheckpointSchedule +type CheckpointScheduleStatus struct { + LastCheckpointTime *metav1.Time `json:"lastCheckpointTime,omitempty"` + CheckpointsCreated int `json:"checkpointsCreated,omitempty"` + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// CheckpointSchedule is the Schema for the checkpointschedules API +type CheckpointSchedule struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CheckpointScheduleSpec `json:"spec,omitempty"` + Status CheckpointScheduleStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// CheckpointScheduleList contains a list of CheckpointSchedule +type CheckpointScheduleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CheckpointSchedule `json:"items"` +} + +func init() { + SchemeBuilder.Register(&CheckpointSchedule{}, &CheckpointScheduleList{}) +} diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 0b6bf35c..43051180 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1 import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -135,6 +136,113 @@ func (in *CheckpointRestoreOperatorStatus) DeepCopy() *CheckpointRestoreOperator return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheckpointSchedule) DeepCopyInto(out *CheckpointSchedule) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheckpointSchedule. +func (in *CheckpointSchedule) DeepCopy() *CheckpointSchedule { + if in == nil { + return nil + } + out := new(CheckpointSchedule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CheckpointSchedule) 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 *CheckpointScheduleList) DeepCopyInto(out *CheckpointScheduleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CheckpointSchedule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheckpointScheduleList. +func (in *CheckpointScheduleList) DeepCopy() *CheckpointScheduleList { + if in == nil { + return nil + } + out := new(CheckpointScheduleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CheckpointScheduleList) 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 *CheckpointScheduleSpec) DeepCopyInto(out *CheckpointScheduleSpec) { + *out = *in + in.Selector.DeepCopyInto(&out.Selector) + if in.ContainerNames != nil { + in, out := &in.ContainerNames, &out.ContainerNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Triggers.DeepCopyInto(&out.Triggers) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheckpointScheduleSpec. +func (in *CheckpointScheduleSpec) DeepCopy() *CheckpointScheduleSpec { + if in == nil { + return nil + } + out := new(CheckpointScheduleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheckpointScheduleStatus) DeepCopyInto(out *CheckpointScheduleStatus) { + *out = *in + if in.LastCheckpointTime != nil { + in, out := &in.LastCheckpointTime, &out.LastCheckpointTime + *out = (*in).DeepCopy() + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheckpointScheduleStatus. +func (in *CheckpointScheduleStatus) DeepCopy() *CheckpointScheduleStatus { + if in == nil { + return nil + } + out := new(CheckpointScheduleStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ContainerPolicySpec) DeepCopyInto(out *ContainerPolicySpec) { *out = *in @@ -294,3 +402,58 @@ func (in *PodPolicySpec) DeepCopy() *PodPolicySpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceThresholdSpec) DeepCopyInto(out *ResourceThresholdSpec) { + *out = *in + if in.CPUPercent != nil { + in, out := &in.CPUPercent, &out.CPUPercent + *out = new(int) + **out = **in + } + if in.MemoryPercent != nil { + in, out := &in.MemoryPercent, &out.MemoryPercent + *out = new(int) + **out = **in + } + if in.PollIntervalSeconds != nil { + in, out := &in.PollIntervalSeconds, &out.PollIntervalSeconds + *out = new(int) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceThresholdSpec. +func (in *ResourceThresholdSpec) DeepCopy() *ResourceThresholdSpec { + if in == nil { + return nil + } + out := new(ResourceThresholdSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggersSpec) DeepCopyInto(out *TriggersSpec) { + *out = *in + if in.ResourceThreshold != nil { + in, out := &in.ResourceThreshold, &out.ResourceThreshold + *out = new(ResourceThresholdSpec) + (*in).DeepCopyInto(*out) + } + if in.OnKubernetesEvents != nil { + in, out := &in.OnKubernetesEvents, &out.OnKubernetesEvents + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggersSpec. +func (in *TriggersSpec) DeepCopy() *TriggersSpec { + if in == nil { + return nil + } + out := new(TriggersSpec) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/criu.org_checkpointrestoreoperators.yaml b/config/crd/bases/criu.org_checkpointrestoreoperators.yaml index a235af18..64848a01 100644 --- a/config/crd/bases/criu.org_checkpointrestoreoperators.yaml +++ b/config/crd/bases/criu.org_checkpointrestoreoperators.yaml @@ -53,11 +53,19 @@ spec: container: type: string maxCheckpointSize: - type: integer + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true maxCheckpoints: type: integer maxTotalSize: - type: integer + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true namespace: type: string pod: @@ -69,7 +77,11 @@ spec: globalPolicy: properties: maxCheckpointSize: - type: integer + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true maxCheckpointsPerContainer: type: integer maxCheckpointsPerNamespace: @@ -77,11 +89,23 @@ spec: maxCheckpointsPerPod: type: integer maxTotalSizePerContainer: - type: integer + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true maxTotalSizePerNamespace: - type: integer + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true maxTotalSizePerPod: - type: integer + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true retainOrphan: type: boolean type: object @@ -89,11 +113,19 @@ spec: items: properties: maxCheckpointSize: - type: integer + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true maxCheckpoints: type: integer maxTotalSize: - type: integer + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true namespace: type: string retainOrphan: @@ -104,11 +136,19 @@ spec: items: properties: maxCheckpointSize: - type: integer + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true maxCheckpoints: type: integer maxTotalSize: - type: integer + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true namespace: type: string pod: diff --git a/config/crd/bases/criu.org_checkpointschedules.yaml b/config/crd/bases/criu.org_checkpointschedules.yaml new file mode 100644 index 00000000..48d7a7d7 --- /dev/null +++ b/config/crd/bases/criu.org_checkpointschedules.yaml @@ -0,0 +1,207 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: checkpointschedules.criu.org +spec: + group: criu.org + names: + kind: CheckpointSchedule + listKind: CheckpointScheduleList + plural: checkpointschedules + singular: checkpointschedule + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: CheckpointSchedule is the Schema for the checkpointschedules + API + 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: + properties: + containerNames: + items: + type: string + type: array + intent: + type: string + namespace: + type: string + selector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + 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. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + 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 + type: object + x-kubernetes-map-type: atomic + triggers: + properties: + onAnnotation: + type: boolean + onKubernetesEvents: + items: + type: string + type: array + resourceThreshold: + properties: + cpuPercent: + type: integer + memoryPercent: + type: integer + pollIntervalSeconds: + type: integer + type: object + schedule: + type: string + type: object + required: + - intent + - namespace + - selector + - triggers + type: object + status: + description: CheckpointScheduleStatus defines the observed state of CheckpointSchedule + properties: + checkpointsCreated: + type: integer + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + 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. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + 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. + format: int64 + minimum: 0 + type: integer + 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. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + 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) + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastCheckpointTime: + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {}