Skip to content

Commit

Permalink
Merge pull request #375 from sdmodi/network-crd
Browse files Browse the repository at this point in the history
Add network CRDs to cloud-provider-gcp
  • Loading branch information
k8s-ci-robot committed Oct 19, 2022
2 parents 064029e + af5afe0 commit 43757f3
Show file tree
Hide file tree
Showing 20 changed files with 2,420 additions and 229 deletions.
32 changes: 32 additions & 0 deletions crd/apis/network/v1/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "v1",
srcs = [
"annotations.go",
"groupversion_info.go",
"network.go",
"network_types.go",
"networkinterface_types.go",
"zz_generated.deepcopy.go",
],
importpath = "k8s.io/cloud-provider-gcp/crd/apis/network/v1",
visibility = ["//visibility:public"],
deps = [
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:meta",
"//vendor/k8s.io/apimachinery/pkg/runtime",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema",
],
)

go_test(
name = "v1_test",
embed = [":v1"],
srcs = [
"annotations_test.go",
],
deps = [
"//vendor/github.com/google/go-cmp/cmp",
],
)

147 changes: 147 additions & 0 deletions crd/apis/network/v1/annotations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package v1

import (
"encoding/json"
"fmt"
)

// Annotation definitions.
const (
// DisableSourceValidationAnnotationKey is the annotation on pod to disable source IP validation on L2 interfaces.
// Useful when you want to assign new IPs onto the interface.
DisableSourceIPValidationAnnotationKey = "networking.gke.io/disable-source-ip-validation"
// DisableSourceValidationAnnotationValTrue is the value to disable source IP validation for the pod.
DisableSourceIPValidationAnnotationValTrue = "true"
// DefaultInterfaceAnnotationKey specifies the default route interface with interface name in pod.
// The IP of the gateway comes from network CRs.
DefaultInterfaceAnnotationKey = "networking.gke.io/default-interface"
// InterfaceAnnotationKey specifies interfaces for pod.
InterfaceAnnotationKey = "networking.gke.io/interfaces"
// NodeNetworkAnnotationKey is the key of the annotation which indicates the status of
// networks on the node.
NodeNetworkAnnotationKey = "networking.gke.io/network-status"
// PodIPsAnnotationKey is the key of the annotation which indicates additional pod IPs assigned to the pod.
PodIPsAnnotationKey = "networking.gke.io/pod-ips"
// NetworkAnnotationKey is the network annotation on NetworkPolicy object.
// Value for this key will be the network on which network policy should be enforced.
NetworkAnnotationKey = "networking.gke.io/network"
// NetworkInUseAnnotationKey is the annotation on Network object.
// It's used to indicate if the Network object is referenced by NetworkInterface/pod objects.
NetworkInUseAnnotationKey = "networking.gke.io/in-use"
// NetworkInUseAnnotationValTrue is the value to be set for NetworkInUseAnnotationKey to indicate
// the Network object is referenced by at least one NetworkInterface/pod object.
NetworkInUseAnnotationValTrue = "true"
// MultiNetworkAnnotationKey is the network annotation key used to hold network data per node, eg: PodCIDRs.
MultiNetworkAnnotationKey = "networking.gke.io/networks"
// AutoGenAnnotationKey is to indicate if the object is auto-generated.
AutoGenAnnotationKey = "networking.gke.io/auto-generated"
// AutoGenAnnotationValTrue is the value to be set for auto-generated objects.
AutoGenAnnotationValTrue = "true"
)

// InterfaceAnnotation is the value of the interface annotation.
// +kubebuilder:object:generate:=false
type InterfaceAnnotation []InterfaceRef

// InterfaceRef specifies the reference to network interface.
// All fields are mutual exclusive.
// Either Network or Interface field can be specified.
// +kubebuilder:object:generate:=false
type InterfaceRef struct {
// InterfaceName is the name of the interface in pod network namespace.
InterfaceName string `json:"interfaceName,omitempty"`
// Network refers to a network object within the cluster.
// When network is specified, NetworkInterface object is optionally generated with default configuration.
Network *string `json:"network,omitempty"`
// Interface reference the NetworkInterface object within the namespace.
Interface *string `json:"interface,omitempty"`
}

// ParseInterfaceAnnotation parses the given annotation.
func ParseInterfaceAnnotation(annotation string) (InterfaceAnnotation, error) {
ret := &InterfaceAnnotation{}
err := json.Unmarshal([]byte(annotation), ret)
return *ret, err
}

// MarshalAnnotation marshals any object into string using json.Marshal.
func MarshalAnnotation(a interface{}) (string, error) {
ret, err := json.Marshal(a)
if err != nil {
return "", fmt.Errorf("failed to marshal (%+v): %v", a, err)
}
return string(ret), nil
}

// NodeNetworkAnnotation is the value of the network status annotation.
// +kubebuilder:object:generate:=false
type NodeNetworkAnnotation []NodeNetworkStatus

// PodIPsAnnotation is the value of the pod IPs annotation.
// +kubebuilder:object:generate:=false
type PodIPsAnnotation []PodIP

// MultiNetworkAnnotation is the value of networks annotation.
// +kubebuilder:object:generate:=false
type MultiNetworkAnnotation []NodeNetwork

// NodeNetworkStatus specifies the status of a network.
// +kubebuilder:object:generate:=false
type NodeNetworkStatus struct {
// Name specifies the name of the network.
Name string `json:"name,omitempty"`

// IPv4Subnet is the Node internal IPv4 subnet for the network.
IPv4Subnet string `json:"ipv4-subnet,omitempty"`

// IPv6Subnet is the Node internal IPv6 subnet for the network.
IPv6Subnet string `json:"ipv6-subnet,omitempty"`
}

// PodIP specifies the additional pod IPs assigned to the pod.
// This will eventually be merged into the `podIPs` field in PodStatus, so the fields must remain compatible.
// +kubebuilder:object:generate:=false
type PodIP struct {
// NetworkName refers to the network object associated with this IP.
NetworkName string `json:"networkName"`

// IP is an IP address (IPv4 or IPv6) assigned to the pod.
IP string `json:"ip"`
}

// NodeNetwork specifies network data on a node.
// +kubebuilder:object:generate:=false
type NodeNetwork struct {
// Name specifies the name of the network.
Name string `json:"name"`
// Cidrs denotes the IPv4/IPv6 ranges of the network.
Cidrs []string `json:"cidrs"`
// Scope specifies if the network is local to a node or global across a node pool.
Scope string `json:"scope"`
}

// ParseNodeNetworkAnnotation parses the given annotation to NodeNetworkAnnotation.
func ParseNodeNetworkAnnotation(annotation string) (NodeNetworkAnnotation, error) {
ret := &NodeNetworkAnnotation{}
err := json.Unmarshal([]byte(annotation), ret)
return *ret, err
}

// ParsePodIPsAnnotation parses the given annotation to PodIPsAnnotation.
func ParsePodIPsAnnotation(annotation string) (PodIPsAnnotation, error) {
ret := &PodIPsAnnotation{}
err := json.Unmarshal([]byte(annotation), ret)
return *ret, err
}

// ParseMultiNetworkAnnotation parses given annotation to MultiNetworkAnnotation.
func ParseMultiNetworkAnnotation(annotation string) (MultiNetworkAnnotation, error) {
ret := &MultiNetworkAnnotation{}
err := json.Unmarshal([]byte(annotation), ret)
return *ret, err
}

// MarshalNodeNetworkAnnotation marshals a NodeNetworkAnnotation into string.
func MarshalNodeNetworkAnnotation(a NodeNetworkAnnotation) (string, error) {
return MarshalAnnotation(a)
}
173 changes: 173 additions & 0 deletions crd/apis/network/v1/annotations_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package v1

import (
"testing"

"github.com/google/go-cmp/cmp"
)

func TestPodIPsAnnotation(t *testing.T) {
tests := []struct {
name string
input PodIPsAnnotation
expected string
}{
{
name: "nil",
input: nil,
expected: "null",
},
{
name: "empty list",
input: PodIPsAnnotation{},
expected: "[]",
},
{
name: "single pod IP",
input: PodIPsAnnotation{
{NetworkName: "network-a", IP: "198.51.100.0"},
},
expected: `[{"networkName":"network-a","ip":"198.51.100.0"}]`,
},
{
name: "missing network",
input: PodIPsAnnotation{
{IP: "198.51.100.0"},
},
expected: `[{"networkName":"","ip":"198.51.100.0"}]`,
},
{
name: "multiple pod IPs",
input: PodIPsAnnotation{
{NetworkName: "network-a", IP: "198.51.100.0"},
{NetworkName: "network-b", IP: "2001:db8::"},
},
expected: `[{"networkName":"network-a","ip":"198.51.100.0"},{"networkName":"network-b","ip":"2001:db8::"}]`,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
marshalled, err := MarshalAnnotation(tc.input)
if err != nil {
t.Fatalf("MarshalAnnotation(%+v) failed with error: %v", tc.input, err)
}
if marshalled != tc.expected {
t.Fatalf("MarshalAnnotation(%+v) returns %q but want %q", tc.input, marshalled, tc.expected)
}

parsed, err := ParsePodIPsAnnotation(marshalled)
if err != nil {
t.Fatalf("ParsePodIPsAnnotation(%s) failed with error: %v", marshalled, err)
}

if diff := cmp.Diff(parsed, tc.input); diff != "" {
t.Fatalf("ParsePodIPsAnnotation(%s) returns diff: (-got +want): %s", marshalled, diff)
}
})
}
}

func TestNodeNetworkAnnotation(t *testing.T) {
tests := []struct {
name string
input NodeNetworkAnnotation
expected string
}{
{
name: "nil",
input: nil,
expected: "null",
},
{
name: "empty list",
input: NodeNetworkAnnotation{},
expected: "[]",
},
{
name: "list with items",
input: NodeNetworkAnnotation{
{Name: "network-a"},
{Name: "network-b"},
},
expected: `[{"name":"network-a"},{"name":"network-b"}]`,
},
{
name: "list with items with subnets",
input: NodeNetworkAnnotation{
{Name: "network-a", IPv4Subnet: "198.51.100.0/24", IPv6Subnet: "2001:db8::/32"},
{Name: "network-b", IPv4Subnet: "198.52.100.0/24", IPv6Subnet: "2001:db9::/32"},
},
expected: `[{"name":"network-a","ipv4-subnet":"198.51.100.0/24","ipv6-subnet":"2001:db8::/32"},{"name":"network-b","ipv4-subnet":"198.52.100.0/24","ipv6-subnet":"2001:db9::/32"}]`,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
marshalled, err := MarshalAnnotation(tc.input)
if err != nil {
t.Fatalf("MarshalAnnotation(%+v) failed with error: %v", tc.input, err)
}
if marshalled != tc.expected {
t.Fatalf("MarshalAnnotation(%+v) returns %q but want %q", tc.input, marshalled, tc.expected)
}

parsed, err := ParseNodeNetworkAnnotation(marshalled)
if err != nil {
t.Fatalf("ParseNodeNetworkAnnotation(%s) failed with error: %v", marshalled, err)
}

if diff := cmp.Diff(parsed, tc.input); diff != "" {
t.Fatalf("ParseNodeNetworkAnnotation(%s) returns diff: (-got +want): %s", marshalled, diff)
}
})
}
}

func TestParseMultiNetworkAnnotation(t *testing.T) {
tests := []struct {
name string
input MultiNetworkAnnotation
expected string
}{
{
name: "nil",
input: nil,
expected: "null",
},
{
name: "empty list",
input: MultiNetworkAnnotation{},
expected: "[]",
},
{
name: "list with items",
input: MultiNetworkAnnotation{
{Name: "network-a", Cidrs: []string{"1.1.1.1/21"}, Scope: "host-local"},
{Name: "network-b", Cidrs: []string{"2.2.2.2/12"}, Scope: "global"},
},
expected: `[{"name":"network-a","cidrs":["1.1.1.1/21"],"scope":"host-local"},{"name":"network-b","cidrs":["2.2.2.2/12"],"scope":"global"}]`,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
marshalled, err := MarshalAnnotation(tc.input)
if err != nil {
t.Fatalf("MarshalAnnotation(%+v) failed with error: %v", tc.input, err)
}
if marshalled != tc.expected {
t.Fatalf("MarshalAnnotation(%+v) returns %q but want %q", tc.input, marshalled, tc.expected)
}

parsed, err := ParseMultiNetworkAnnotation(marshalled)
if err != nil {
t.Fatalf("ParseMultiNetworkAnnotation(%s) failed with error: %v", marshalled, err)
}

if diff := cmp.Diff(parsed, tc.input); diff != "" {
t.Fatalf("ParseMultiNetworkAnnotation(%s) returns diff: (-got +want): %s", marshalled, diff)
}
})
}
}
42 changes: 42 additions & 0 deletions crd/apis/network/v1/groupversion_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Package v1 contains API Schema definitions for the networking v1 API group
// +kubebuilder:object:generate=true
// +groupName=networking.gke.io

package v1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)

var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "networking.gke.io", Version: "v1"}

schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = schemeBuilder.AddToScheme
)

// Kind names for the Network objects.
const (
KindNetwork = "Network"
KindNetworkInterface = "NetworkInterface"
)

// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(GroupVersion,
&Network{},
&NetworkList{},
&NetworkInterface{},
&NetworkInterfaceList{},
)
metav1.AddToGroupVersion(scheme, GroupVersion)
return nil
}

// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return GroupVersion.WithKind(kind).GroupKind()
}

0 comments on commit 43757f3

Please sign in to comment.