From 03ae034ef29ca8e644247a3bb5053aa1c27ec926 Mon Sep 17 00:00:00 2001 From: Miguel Duarte Barroso Date: Tue, 10 Sep 2024 14:30:30 +0200 Subject: [PATCH] crd: add new attributes to the spec This commits adds attributes to the spec: - spec.IPRequests: user can specify the IP address for an interface - status.Conditions: controller can report success / errors while reserving IP addresses for a workload - status.OwnerPod: controller reports which pod owns the allocation The first two conditions are used to support a feature where the user indicates ahead of time which IP address they want their VMs to have, while the latter can be used to provide information to the user, and to ensure that a claim in use cannot be used by another workload. Signed-off-by: Miguel Duarte Barroso --- artifacts/k8s.cni.cncf.io_ipamclaims.yaml | 80 +++++++++++++++++++ cmd/example/main.go | 2 +- pkg/crd/ipamclaims/v1alpha1/types.go | 12 ++- .../v1alpha1/zz_generated.deepcopy.go | 17 +++- 4 files changed, 107 insertions(+), 4 deletions(-) diff --git a/artifacts/k8s.cni.cncf.io_ipamclaims.yaml b/artifacts/k8s.cni.cncf.io_ipamclaims.yaml index 6d17746..8c6674d 100644 --- a/artifacts/k8s.cni.cncf.io_ipamclaims.yaml +++ b/artifacts/k8s.cni.cncf.io_ipamclaims.yaml @@ -37,6 +37,11 @@ spec: description: The pod interface name for which this allocation was created type: string + ipRequests: + description: The IPs requested by the user + items: + type: string + type: array network: description: The network name for which this persistent allocation was created @@ -46,15 +51,90 @@ spec: - network type: object status: + description: IPAMClaimStatus contains the observed status of the IPAMClaim. properties: + conditions: + description: Conditions contains details for one aspect of the current + state of this API Resource + 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, + \n 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 }" + 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 ips: description: The list of IP addresses (v4, v6) that were allocated for the pod interface items: type: string type: array + ownerPod: + description: The name of the pod holding the IPAMClaim + type: string required: - ips + - ownerPod type: object type: object served: true diff --git a/cmd/example/main.go b/cmd/example/main.go index ddee48e..774b567 100644 --- a/cmd/example/main.go +++ b/cmd/example/main.go @@ -61,7 +61,7 @@ func main() { ) }() - ipamClaim.Status.IPs = []string{"winner", "winner", "chicken", "dinner"} + ipamClaim.Status.IPs = []v1alpha1.CIDR{"winner", "winner", "chicken", "dinner"} _, err = exampleClient.K8sV1alpha1().IPAMClaims("default").UpdateStatus( context.Background(), ipamClaim, diff --git a/pkg/crd/ipamclaims/v1alpha1/types.go b/pkg/crd/ipamclaims/v1alpha1/types.go index ca94219..ad76b4f 100644 --- a/pkg/crd/ipamclaims/v1alpha1/types.go +++ b/pkg/crd/ipamclaims/v1alpha1/types.go @@ -33,11 +33,19 @@ type IPAMClaimSpec struct { Network string `json:"network"` // The pod interface name for which this allocation was created Interface string `json:"interface"` + // The IPs requested by the user + // +optional + IPRequests []CIDR `json:"ipRequests,omitempty"` } +// IPAMClaimStatus contains the observed status of the IPAMClaim. type IPAMClaimStatus struct { // The list of IP addresses (v4, v6) that were allocated for the pod interface - IPs []string `json:"ips"` + IPs []CIDR `json:"ips"` + // The name of the pod holding the IPAMClaim + OwnerPod string `json:"ownerPod"` + // Conditions contains details for one aspect of the current state of this API Resource + Conditions []metav1.Condition `json:"conditions,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -47,3 +55,5 @@ type IPAMClaimList struct { metav1.ListMeta `json:"metadata,omitempty"` Items []IPAMClaim `json:"items"` } + +type CIDR string diff --git a/pkg/crd/ipamclaims/v1alpha1/zz_generated.deepcopy.go b/pkg/crd/ipamclaims/v1alpha1/zz_generated.deepcopy.go index 737efd7..dde6627 100644 --- a/pkg/crd/ipamclaims/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/crd/ipamclaims/v1alpha1/zz_generated.deepcopy.go @@ -5,6 +5,7 @@ package v1alpha1 import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -13,7 +14,7 @@ func (in *IPAMClaim) DeepCopyInto(out *IPAMClaim) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) } @@ -70,6 +71,11 @@ func (in *IPAMClaimList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IPAMClaimSpec) DeepCopyInto(out *IPAMClaimSpec) { *out = *in + if in.IPRequests != nil { + in, out := &in.IPRequests, &out.IPRequests + *out = make([]CIDR, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPAMClaimSpec. @@ -87,9 +93,16 @@ func (in *IPAMClaimStatus) DeepCopyInto(out *IPAMClaimStatus) { *out = *in if in.IPs != nil { in, out := &in.IPs, &out.IPs - *out = make([]string, len(*in)) + *out = make([]CIDR, len(*in)) copy(*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]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPAMClaimStatus.