From f1159ef19156055ec96af91b8911f62877c5c8eb Mon Sep 17 00:00:00 2001 From: Miguel Duarte Barroso Date: Thu, 28 Sep 2023 17:47:52 +0200 Subject: [PATCH] api: add IPAMClaimReference to network-selection-elements Also add some unit tests to assert users cannot both request a particular IP address and reference an IPAMClaim object at the same time. Signed-off-by: Miguel Duarte Barroso --- pkg/apis/k8s.cni.cncf.io/v1/types.go | 21 ++++++++ pkg/apis/k8s.cni.cncf.io/v1/types_test.go | 60 +++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 pkg/apis/k8s.cni.cncf.io/v1/types_test.go diff --git a/pkg/apis/k8s.cni.cncf.io/v1/types.go b/pkg/apis/k8s.cni.cncf.io/v1/types.go index 2b81d0482..7e202ed8d 100644 --- a/pkg/apis/k8s.cni.cncf.io/v1/types.go +++ b/pkg/apis/k8s.cni.cncf.io/v1/types.go @@ -1,6 +1,8 @@ package v1 import ( + "encoding/json" + "errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "net" ) @@ -162,6 +164,23 @@ type NetworkSelectionElement struct { CNIArgs *map[string]interface{} `json:"cni-args,omitempty"` // GatewayRequest contains default route IP address for the pod GatewayRequest []net.IP `json:"default-route,omitempty"` + // IPAMClaimReference container the IPAMClaim name where the IPs for this + // attachment will be located. + IPAMClaimReference string `json:"ipam-claim-reference,omitempty"` +} + +func (nse *NetworkSelectionElement) UnmarshalJSON(b []byte) error { + type networkSelectionElement NetworkSelectionElement + + var netSelectionElement networkSelectionElement + if err := json.Unmarshal(b, &netSelectionElement); err != nil { + return err + } + if len(netSelectionElement.IPRequest) > 0 && netSelectionElement.IPAMClaimReference != "" { + return TooManyIPSources + } + *nse = NetworkSelectionElement(netSelectionElement) + return nil } const ( @@ -178,3 +197,5 @@ type NoK8sNetworkError struct { } func (e *NoK8sNetworkError) Error() string { return string(e.Message) } + +var TooManyIPSources = errors.New("cannot provide a static IP and a reference of an IPAM claim in the same network selection element") diff --git a/pkg/apis/k8s.cni.cncf.io/v1/types_test.go b/pkg/apis/k8s.cni.cncf.io/v1/types_test.go new file mode 100644 index 000000000..d0c246fd7 --- /dev/null +++ b/pkg/apis/k8s.cni.cncf.io/v1/types_test.go @@ -0,0 +1,60 @@ +package v1 + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "testing" +) + +type testCase struct { + description string + input string + expectedOutput NetworkSelectionElement + expectedError error +} + +func TestNetworkSelectionElementUnmarshaller(t *testing.T) { + + testCases := []testCase{ + { + description: "ip request + IPAMClaims", + input: "{\"name\":\"yo!\",\"ips\":[\"asd\"],\"ipam-claim-reference\":\"woop\"}", + expectedError: TooManyIPSources, + }, + { + description: "successfully deserialize a simple struct", + input: "{\"name\":\"yo!\",\"ips\":[\"an IP\"]}", + expectedOutput: NetworkSelectionElement{ + Name: "yo!", + IPRequest: []string{"an IP"}, + }, + expectedError: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + if err := run(tc); err != nil { + t.Errorf("failed test %q: %v", tc.description, err) + } + }) + } +} + +func run(tc testCase) error { + inputBytes := []byte(tc.input) + + var nse NetworkSelectionElement + err := json.Unmarshal(inputBytes, &nse) + if tc.expectedError != nil { + if !errors.Is(err, tc.expectedError) { + return fmt.Errorf("unexpected error: %v. Expected error: %v", err, tc.expectedError) + } + } + if !reflect.DeepEqual(nse, tc.expectedOutput) { + return fmt.Errorf("parsed object is wrong: %v. Expected object: %v", nse, tc.expectedOutput) + } + return nil +}