From 02a916032f87824fdf06bb33b8bb20fdf9f7e7dc Mon Sep 17 00:00:00 2001 From: mmamczur Date: Thu, 11 May 2023 16:34:52 +0200 Subject: [PATCH 1/2] Vendor changes for multinetwork related testing sources. --- .../network/clientset/versioned/fake/BUILD | 30 ++++ .../versioned/fake/clientset_generated.go | 89 +++++++++++ .../network/clientset/versioned/fake/doc.go | 20 +++ .../clientset/versioned/fake/register.go | 58 +++++++ .../versioned/typed/network/v1/fake/BUILD | 26 ++++ .../versioned/typed/network/v1/fake/doc.go | 20 +++ .../typed/network/v1/fake/fake_network.go | 133 ++++++++++++++++ .../network/v1/fake/fake_network_client.go | 52 +++++++ .../network/v1/fake/fake_networkinterface.go | 142 ++++++++++++++++++ .../v1/fake/fake_networkinterfacelist.go | 49 ++++++ .../typed/network/v1/fake/fake_networklist.go | 47 ++++++ .../typed/network/v1alpha1/fake/BUILD | 28 ++++ .../typed/network/v1alpha1/fake/doc.go | 20 +++ .../v1alpha1/fake/fake_gkenetworkparamset.go | 133 ++++++++++++++++ .../fake/fake_gkenetworkparamsetlist.go | 47 ++++++ .../network/v1alpha1/fake/fake_network.go | 133 ++++++++++++++++ .../v1alpha1/fake/fake_network_client.go | 60 ++++++++ .../v1alpha1/fake/fake_networkinterface.go | 142 ++++++++++++++++++ .../fake/fake_networkinterfacelist.go | 49 ++++++ .../network/v1alpha1/fake/fake_networklist.go | 47 ++++++ 20 files changed, 1325 insertions(+) create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/BUILD create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/clientset_generated.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/doc.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/register.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/BUILD create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/doc.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_network.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_network_client.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networkinterface.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networkinterfacelist.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networklist.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/BUILD create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/doc.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_gkenetworkparamset.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_gkenetworkparamsetlist.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_network.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_network_client.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networkinterface.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networkinterfacelist.go create mode 100644 vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networklist.go diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/BUILD b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/BUILD new file mode 100644 index 0000000000..a971f005e6 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/BUILD @@ -0,0 +1,30 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "fake", + srcs = [ + "clientset_generated.go", + "doc.go", + "register.go", + ], + importpath = "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake", + 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", + "//vendor/k8s.io/apimachinery/pkg/runtime/serializer", + "//vendor/k8s.io/apimachinery/pkg/util/runtime", + "//vendor/k8s.io/apimachinery/pkg/watch", + "//vendor/k8s.io/client-go/discovery", + "//vendor/k8s.io/client-go/discovery/fake", + "//vendor/k8s.io/client-go/testing", + "//vendor/k8s.io/cloud-provider-gcp/crd/apis/network/v1:network", + "//vendor/k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1", + "//vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned", + "//vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1:network", + "//vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake", + "//vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1", + "//vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake", + ], +) diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/clientset_generated.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 0000000000..43b70b3058 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,89 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" + clientset "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned" + networkingv1 "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1" + fakenetworkingv1 "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake" + networkingv1alpha1 "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1" + fakenetworkingv1alpha1 "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{tracker: o} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery + tracker testing.ObjectTracker +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +func (c *Clientset) Tracker() testing.ObjectTracker { + return c.tracker +} + +var _ clientset.Interface = &Clientset{} + +// NetworkingV1alpha1 retrieves the NetworkingV1alpha1Client +func (c *Clientset) NetworkingV1alpha1() networkingv1alpha1.NetworkingV1alpha1Interface { + return &fakenetworkingv1alpha1.FakeNetworkingV1alpha1{Fake: &c.Fake} +} + +// NetworkingV1 retrieves the NetworkingV1Client +func (c *Clientset) NetworkingV1() networkingv1.NetworkingV1Interface { + return &fakenetworkingv1.FakeNetworkingV1{Fake: &c.Fake} +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/doc.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/doc.go new file mode 100644 index 0000000000..50d0812d7f --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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. + +// This package has the automatically generated fake clientset. +package fake diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/register.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/register.go new file mode 100644 index 0000000000..26726b8d9f --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake/register.go @@ -0,0 +1,58 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + networkingv1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1" + networkingv1alpha1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) + +var localSchemeBuilder = runtime.SchemeBuilder{ + networkingv1alpha1.AddToScheme, + networkingv1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/BUILD b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/BUILD new file mode 100644 index 0000000000..39fe2a3bbb --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/BUILD @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "fake", + srcs = [ + "doc.go", + "fake_network.go", + "fake_network_client.go", + "fake_networkinterface.go", + "fake_networkinterfacelist.go", + "fake_networklist.go", + ], + importpath = "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake", + visibility = ["//visibility:public"], + deps = [ + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:meta", + "//vendor/k8s.io/apimachinery/pkg/labels", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema", + "//vendor/k8s.io/apimachinery/pkg/types", + "//vendor/k8s.io/apimachinery/pkg/watch", + "//vendor/k8s.io/client-go/rest", + "//vendor/k8s.io/client-go/testing", + "//vendor/k8s.io/cloud-provider-gcp/crd/apis/network/v1:network", + "//vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1:network", + ], +) diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/doc.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/doc.go new file mode 100644 index 0000000000..1672f9cfa1 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 has the automatically generated clients. +package fake diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_network.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_network.go new file mode 100644 index 0000000000..29a26c60f7 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_network.go @@ -0,0 +1,133 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + networkv1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1" +) + +// FakeNetworks implements NetworkInterface +type FakeNetworks struct { + Fake *FakeNetworkingV1 +} + +var networksResource = schema.GroupVersionResource{Group: "networking.gke.io", Version: "v1", Resource: "networks"} + +var networksKind = schema.GroupVersionKind{Group: "networking.gke.io", Version: "v1", Kind: "Network"} + +// Get takes name of the network, and returns the corresponding network object, and an error if there is any. +func (c *FakeNetworks) Get(ctx context.Context, name string, options v1.GetOptions) (result *networkv1.Network, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(networksResource, name), &networkv1.Network{}) + if obj == nil { + return nil, err + } + return obj.(*networkv1.Network), err +} + +// List takes label and field selectors, and returns the list of Networks that match those selectors. +func (c *FakeNetworks) List(ctx context.Context, opts v1.ListOptions) (result *networkv1.NetworkList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(networksResource, networksKind, opts), &networkv1.NetworkList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &networkv1.NetworkList{ListMeta: obj.(*networkv1.NetworkList).ListMeta} + for _, item := range obj.(*networkv1.NetworkList).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 networks. +func (c *FakeNetworks) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(networksResource, opts)) +} + +// Create takes the representation of a network and creates it. Returns the server's representation of the network, and an error, if there is any. +func (c *FakeNetworks) Create(ctx context.Context, network *networkv1.Network, opts v1.CreateOptions) (result *networkv1.Network, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(networksResource, network), &networkv1.Network{}) + if obj == nil { + return nil, err + } + return obj.(*networkv1.Network), err +} + +// Update takes the representation of a network and updates it. Returns the server's representation of the network, and an error, if there is any. +func (c *FakeNetworks) Update(ctx context.Context, network *networkv1.Network, opts v1.UpdateOptions) (result *networkv1.Network, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(networksResource, network), &networkv1.Network{}) + if obj == nil { + return nil, err + } + return obj.(*networkv1.Network), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeNetworks) UpdateStatus(ctx context.Context, network *networkv1.Network, opts v1.UpdateOptions) (*networkv1.Network, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(networksResource, "status", network), &networkv1.Network{}) + if obj == nil { + return nil, err + } + return obj.(*networkv1.Network), err +} + +// Delete takes name of the network and deletes it. Returns an error if one occurs. +func (c *FakeNetworks) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(networksResource, name), &networkv1.Network{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeNetworks) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(networksResource, listOpts) + + _, err := c.Fake.Invokes(action, &networkv1.NetworkList{}) + return err +} + +// Patch applies the patch and returns the patched network. +func (c *FakeNetworks) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *networkv1.Network, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(networksResource, name, pt, data, subresources...), &networkv1.Network{}) + if obj == nil { + return nil, err + } + return obj.(*networkv1.Network), err +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_network_client.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_network_client.go new file mode 100644 index 0000000000..293c61735c --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_network_client.go @@ -0,0 +1,52 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" + v1 "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1" +) + +type FakeNetworkingV1 struct { + *testing.Fake +} + +func (c *FakeNetworkingV1) Networks() v1.NetworkInterface { + return &FakeNetworks{c} +} + +func (c *FakeNetworkingV1) NetworkInterfaces(namespace string) v1.NetworkInterfaceInterface { + return &FakeNetworkInterfaces{c, namespace} +} + +func (c *FakeNetworkingV1) NetworkInterfaceLists(namespace string) v1.NetworkInterfaceListInterface { + return &FakeNetworkInterfaceLists{c, namespace} +} + +func (c *FakeNetworkingV1) NetworkLists() v1.NetworkListInterface { + return &FakeNetworkLists{c} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeNetworkingV1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networkinterface.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networkinterface.go new file mode 100644 index 0000000000..f39d5e3e75 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networkinterface.go @@ -0,0 +1,142 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + networkv1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1" +) + +// FakeNetworkInterfaces implements NetworkInterfaceInterface +type FakeNetworkInterfaces struct { + Fake *FakeNetworkingV1 + ns string +} + +var networkinterfacesResource = schema.GroupVersionResource{Group: "networking.gke.io", Version: "v1", Resource: "networkinterfaces"} + +var networkinterfacesKind = schema.GroupVersionKind{Group: "networking.gke.io", Version: "v1", Kind: "NetworkInterface"} + +// Get takes name of the networkInterface, and returns the corresponding networkInterface object, and an error if there is any. +func (c *FakeNetworkInterfaces) Get(ctx context.Context, name string, options v1.GetOptions) (result *networkv1.NetworkInterface, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(networkinterfacesResource, c.ns, name), &networkv1.NetworkInterface{}) + + if obj == nil { + return nil, err + } + return obj.(*networkv1.NetworkInterface), err +} + +// List takes label and field selectors, and returns the list of NetworkInterfaces that match those selectors. +func (c *FakeNetworkInterfaces) List(ctx context.Context, opts v1.ListOptions) (result *networkv1.NetworkInterfaceList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(networkinterfacesResource, networkinterfacesKind, c.ns, opts), &networkv1.NetworkInterfaceList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &networkv1.NetworkInterfaceList{ListMeta: obj.(*networkv1.NetworkInterfaceList).ListMeta} + for _, item := range obj.(*networkv1.NetworkInterfaceList).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 networkInterfaces. +func (c *FakeNetworkInterfaces) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(networkinterfacesResource, c.ns, opts)) + +} + +// Create takes the representation of a networkInterface and creates it. Returns the server's representation of the networkInterface, and an error, if there is any. +func (c *FakeNetworkInterfaces) Create(ctx context.Context, networkInterface *networkv1.NetworkInterface, opts v1.CreateOptions) (result *networkv1.NetworkInterface, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(networkinterfacesResource, c.ns, networkInterface), &networkv1.NetworkInterface{}) + + if obj == nil { + return nil, err + } + return obj.(*networkv1.NetworkInterface), err +} + +// Update takes the representation of a networkInterface and updates it. Returns the server's representation of the networkInterface, and an error, if there is any. +func (c *FakeNetworkInterfaces) Update(ctx context.Context, networkInterface *networkv1.NetworkInterface, opts v1.UpdateOptions) (result *networkv1.NetworkInterface, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(networkinterfacesResource, c.ns, networkInterface), &networkv1.NetworkInterface{}) + + if obj == nil { + return nil, err + } + return obj.(*networkv1.NetworkInterface), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeNetworkInterfaces) UpdateStatus(ctx context.Context, networkInterface *networkv1.NetworkInterface, opts v1.UpdateOptions) (*networkv1.NetworkInterface, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(networkinterfacesResource, "status", c.ns, networkInterface), &networkv1.NetworkInterface{}) + + if obj == nil { + return nil, err + } + return obj.(*networkv1.NetworkInterface), err +} + +// Delete takes name of the networkInterface and deletes it. Returns an error if one occurs. +func (c *FakeNetworkInterfaces) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(networkinterfacesResource, c.ns, name), &networkv1.NetworkInterface{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeNetworkInterfaces) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(networkinterfacesResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &networkv1.NetworkInterfaceList{}) + return err +} + +// Patch applies the patch and returns the patched networkInterface. +func (c *FakeNetworkInterfaces) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *networkv1.NetworkInterface, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(networkinterfacesResource, c.ns, name, pt, data, subresources...), &networkv1.NetworkInterface{}) + + if obj == nil { + return nil, err + } + return obj.(*networkv1.NetworkInterface), err +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networkinterfacelist.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networkinterfacelist.go new file mode 100644 index 0000000000..940cfa7522 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networkinterfacelist.go @@ -0,0 +1,49 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + testing "k8s.io/client-go/testing" + networkv1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1" +) + +// FakeNetworkInterfaceLists implements NetworkInterfaceListInterface +type FakeNetworkInterfaceLists struct { + Fake *FakeNetworkingV1 + ns string +} + +var networkinterfacelistsResource = schema.GroupVersionResource{Group: "networking.gke.io", Version: "v1", Resource: "networkinterfacelists"} + +var networkinterfacelistsKind = schema.GroupVersionKind{Group: "networking.gke.io", Version: "v1", Kind: "NetworkInterfaceList"} + +// Get takes name of the networkInterfaceList, and returns the corresponding networkInterfaceList object, and an error if there is any. +func (c *FakeNetworkInterfaceLists) Get(ctx context.Context, name string, options v1.GetOptions) (result *networkv1.NetworkInterfaceList, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(networkinterfacelistsResource, c.ns, name), &networkv1.NetworkInterfaceList{}) + + if obj == nil { + return nil, err + } + return obj.(*networkv1.NetworkInterfaceList), err +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networklist.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networklist.go new file mode 100644 index 0000000000..adf9b5767e --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1/fake/fake_networklist.go @@ -0,0 +1,47 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + testing "k8s.io/client-go/testing" + networkv1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1" +) + +// FakeNetworkLists implements NetworkListInterface +type FakeNetworkLists struct { + Fake *FakeNetworkingV1 +} + +var networklistsResource = schema.GroupVersionResource{Group: "networking.gke.io", Version: "v1", Resource: "networklists"} + +var networklistsKind = schema.GroupVersionKind{Group: "networking.gke.io", Version: "v1", Kind: "NetworkList"} + +// Get takes name of the networkList, and returns the corresponding networkList object, and an error if there is any. +func (c *FakeNetworkLists) Get(ctx context.Context, name string, options v1.GetOptions) (result *networkv1.NetworkList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(networklistsResource, name), &networkv1.NetworkList{}) + if obj == nil { + return nil, err + } + return obj.(*networkv1.NetworkList), err +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/BUILD b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/BUILD new file mode 100644 index 0000000000..c396b2742c --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/BUILD @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "fake", + srcs = [ + "doc.go", + "fake_gkenetworkparamset.go", + "fake_gkenetworkparamsetlist.go", + "fake_network.go", + "fake_network_client.go", + "fake_networkinterface.go", + "fake_networkinterfacelist.go", + "fake_networklist.go", + ], + importpath = "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake", + visibility = ["//visibility:public"], + deps = [ + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:meta", + "//vendor/k8s.io/apimachinery/pkg/labels", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema", + "//vendor/k8s.io/apimachinery/pkg/types", + "//vendor/k8s.io/apimachinery/pkg/watch", + "//vendor/k8s.io/client-go/rest", + "//vendor/k8s.io/client-go/testing", + "//vendor/k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1", + "//vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1", + ], +) diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/doc.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/doc.go new file mode 100644 index 0000000000..1672f9cfa1 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 has the automatically generated clients. +package fake diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_gkenetworkparamset.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_gkenetworkparamset.go new file mode 100644 index 0000000000..f062e4f35f --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_gkenetworkparamset.go @@ -0,0 +1,133 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1" +) + +// FakeGKENetworkParamSets implements GKENetworkParamSetInterface +type FakeGKENetworkParamSets struct { + Fake *FakeNetworkingV1alpha1 +} + +var gkenetworkparamsetsResource = schema.GroupVersionResource{Group: "networking.gke.io", Version: "v1alpha1", Resource: "gkenetworkparamsets"} + +var gkenetworkparamsetsKind = schema.GroupVersionKind{Group: "networking.gke.io", Version: "v1alpha1", Kind: "GKENetworkParamSet"} + +// Get takes name of the gKENetworkParamSet, and returns the corresponding gKENetworkParamSet object, and an error if there is any. +func (c *FakeGKENetworkParamSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.GKENetworkParamSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(gkenetworkparamsetsResource, name), &v1alpha1.GKENetworkParamSet{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.GKENetworkParamSet), err +} + +// List takes label and field selectors, and returns the list of GKENetworkParamSets that match those selectors. +func (c *FakeGKENetworkParamSets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.GKENetworkParamSetList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(gkenetworkparamsetsResource, gkenetworkparamsetsKind, opts), &v1alpha1.GKENetworkParamSetList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.GKENetworkParamSetList{ListMeta: obj.(*v1alpha1.GKENetworkParamSetList).ListMeta} + for _, item := range obj.(*v1alpha1.GKENetworkParamSetList).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 gKENetworkParamSets. +func (c *FakeGKENetworkParamSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(gkenetworkparamsetsResource, opts)) +} + +// Create takes the representation of a gKENetworkParamSet and creates it. Returns the server's representation of the gKENetworkParamSet, and an error, if there is any. +func (c *FakeGKENetworkParamSets) Create(ctx context.Context, gKENetworkParamSet *v1alpha1.GKENetworkParamSet, opts v1.CreateOptions) (result *v1alpha1.GKENetworkParamSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(gkenetworkparamsetsResource, gKENetworkParamSet), &v1alpha1.GKENetworkParamSet{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.GKENetworkParamSet), err +} + +// Update takes the representation of a gKENetworkParamSet and updates it. Returns the server's representation of the gKENetworkParamSet, and an error, if there is any. +func (c *FakeGKENetworkParamSets) Update(ctx context.Context, gKENetworkParamSet *v1alpha1.GKENetworkParamSet, opts v1.UpdateOptions) (result *v1alpha1.GKENetworkParamSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(gkenetworkparamsetsResource, gKENetworkParamSet), &v1alpha1.GKENetworkParamSet{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.GKENetworkParamSet), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeGKENetworkParamSets) UpdateStatus(ctx context.Context, gKENetworkParamSet *v1alpha1.GKENetworkParamSet, opts v1.UpdateOptions) (*v1alpha1.GKENetworkParamSet, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(gkenetworkparamsetsResource, "status", gKENetworkParamSet), &v1alpha1.GKENetworkParamSet{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.GKENetworkParamSet), err +} + +// Delete takes name of the gKENetworkParamSet and deletes it. Returns an error if one occurs. +func (c *FakeGKENetworkParamSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(gkenetworkparamsetsResource, name), &v1alpha1.GKENetworkParamSet{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeGKENetworkParamSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(gkenetworkparamsetsResource, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.GKENetworkParamSetList{}) + return err +} + +// Patch applies the patch and returns the patched gKENetworkParamSet. +func (c *FakeGKENetworkParamSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.GKENetworkParamSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(gkenetworkparamsetsResource, name, pt, data, subresources...), &v1alpha1.GKENetworkParamSet{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.GKENetworkParamSet), err +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_gkenetworkparamsetlist.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_gkenetworkparamsetlist.go new file mode 100644 index 0000000000..dc34adf5ea --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_gkenetworkparamsetlist.go @@ -0,0 +1,47 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + testing "k8s.io/client-go/testing" + v1alpha1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1" +) + +// FakeGKENetworkParamSetLists implements GKENetworkParamSetListInterface +type FakeGKENetworkParamSetLists struct { + Fake *FakeNetworkingV1alpha1 +} + +var gkenetworkparamsetlistsResource = schema.GroupVersionResource{Group: "networking.gke.io", Version: "v1alpha1", Resource: "gkenetworkparamsetlists"} + +var gkenetworkparamsetlistsKind = schema.GroupVersionKind{Group: "networking.gke.io", Version: "v1alpha1", Kind: "GKENetworkParamSetList"} + +// Get takes name of the gKENetworkParamSetList, and returns the corresponding gKENetworkParamSetList object, and an error if there is any. +func (c *FakeGKENetworkParamSetLists) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.GKENetworkParamSetList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(gkenetworkparamsetlistsResource, name), &v1alpha1.GKENetworkParamSetList{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.GKENetworkParamSetList), err +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_network.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_network.go new file mode 100644 index 0000000000..32dbd1d6d4 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_network.go @@ -0,0 +1,133 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1" +) + +// FakeNetworks implements NetworkInterface +type FakeNetworks struct { + Fake *FakeNetworkingV1alpha1 +} + +var networksResource = schema.GroupVersionResource{Group: "networking.gke.io", Version: "v1alpha1", Resource: "networks"} + +var networksKind = schema.GroupVersionKind{Group: "networking.gke.io", Version: "v1alpha1", Kind: "Network"} + +// Get takes name of the network, and returns the corresponding network object, and an error if there is any. +func (c *FakeNetworks) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Network, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(networksResource, name), &v1alpha1.Network{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Network), err +} + +// List takes label and field selectors, and returns the list of Networks that match those selectors. +func (c *FakeNetworks) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.NetworkList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(networksResource, networksKind, opts), &v1alpha1.NetworkList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.NetworkList{ListMeta: obj.(*v1alpha1.NetworkList).ListMeta} + for _, item := range obj.(*v1alpha1.NetworkList).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 networks. +func (c *FakeNetworks) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(networksResource, opts)) +} + +// Create takes the representation of a network and creates it. Returns the server's representation of the network, and an error, if there is any. +func (c *FakeNetworks) Create(ctx context.Context, network *v1alpha1.Network, opts v1.CreateOptions) (result *v1alpha1.Network, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(networksResource, network), &v1alpha1.Network{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Network), err +} + +// Update takes the representation of a network and updates it. Returns the server's representation of the network, and an error, if there is any. +func (c *FakeNetworks) Update(ctx context.Context, network *v1alpha1.Network, opts v1.UpdateOptions) (result *v1alpha1.Network, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(networksResource, network), &v1alpha1.Network{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Network), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeNetworks) UpdateStatus(ctx context.Context, network *v1alpha1.Network, opts v1.UpdateOptions) (*v1alpha1.Network, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(networksResource, "status", network), &v1alpha1.Network{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Network), err +} + +// Delete takes name of the network and deletes it. Returns an error if one occurs. +func (c *FakeNetworks) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(networksResource, name), &v1alpha1.Network{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeNetworks) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(networksResource, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.NetworkList{}) + return err +} + +// Patch applies the patch and returns the patched network. +func (c *FakeNetworks) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Network, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(networksResource, name, pt, data, subresources...), &v1alpha1.Network{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Network), err +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_network_client.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_network_client.go new file mode 100644 index 0000000000..93e706e95a --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_network_client.go @@ -0,0 +1,60 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" + v1alpha1 "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1" +) + +type FakeNetworkingV1alpha1 struct { + *testing.Fake +} + +func (c *FakeNetworkingV1alpha1) GKENetworkParamSets() v1alpha1.GKENetworkParamSetInterface { + return &FakeGKENetworkParamSets{c} +} + +func (c *FakeNetworkingV1alpha1) GKENetworkParamSetLists() v1alpha1.GKENetworkParamSetListInterface { + return &FakeGKENetworkParamSetLists{c} +} + +func (c *FakeNetworkingV1alpha1) Networks() v1alpha1.NetworkInterface { + return &FakeNetworks{c} +} + +func (c *FakeNetworkingV1alpha1) NetworkInterfaces(namespace string) v1alpha1.NetworkInterfaceInterface { + return &FakeNetworkInterfaces{c, namespace} +} + +func (c *FakeNetworkingV1alpha1) NetworkInterfaceLists(namespace string) v1alpha1.NetworkInterfaceListInterface { + return &FakeNetworkInterfaceLists{c, namespace} +} + +func (c *FakeNetworkingV1alpha1) NetworkLists() v1alpha1.NetworkListInterface { + return &FakeNetworkLists{c} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeNetworkingV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networkinterface.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networkinterface.go new file mode 100644 index 0000000000..cdd481b256 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networkinterface.go @@ -0,0 +1,142 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1" +) + +// FakeNetworkInterfaces implements NetworkInterfaceInterface +type FakeNetworkInterfaces struct { + Fake *FakeNetworkingV1alpha1 + ns string +} + +var networkinterfacesResource = schema.GroupVersionResource{Group: "networking.gke.io", Version: "v1alpha1", Resource: "networkinterfaces"} + +var networkinterfacesKind = schema.GroupVersionKind{Group: "networking.gke.io", Version: "v1alpha1", Kind: "NetworkInterface"} + +// Get takes name of the networkInterface, and returns the corresponding networkInterface object, and an error if there is any. +func (c *FakeNetworkInterfaces) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.NetworkInterface, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(networkinterfacesResource, c.ns, name), &v1alpha1.NetworkInterface{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.NetworkInterface), err +} + +// List takes label and field selectors, and returns the list of NetworkInterfaces that match those selectors. +func (c *FakeNetworkInterfaces) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.NetworkInterfaceList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(networkinterfacesResource, networkinterfacesKind, c.ns, opts), &v1alpha1.NetworkInterfaceList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.NetworkInterfaceList{ListMeta: obj.(*v1alpha1.NetworkInterfaceList).ListMeta} + for _, item := range obj.(*v1alpha1.NetworkInterfaceList).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 networkInterfaces. +func (c *FakeNetworkInterfaces) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(networkinterfacesResource, c.ns, opts)) + +} + +// Create takes the representation of a networkInterface and creates it. Returns the server's representation of the networkInterface, and an error, if there is any. +func (c *FakeNetworkInterfaces) Create(ctx context.Context, networkInterface *v1alpha1.NetworkInterface, opts v1.CreateOptions) (result *v1alpha1.NetworkInterface, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(networkinterfacesResource, c.ns, networkInterface), &v1alpha1.NetworkInterface{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.NetworkInterface), err +} + +// Update takes the representation of a networkInterface and updates it. Returns the server's representation of the networkInterface, and an error, if there is any. +func (c *FakeNetworkInterfaces) Update(ctx context.Context, networkInterface *v1alpha1.NetworkInterface, opts v1.UpdateOptions) (result *v1alpha1.NetworkInterface, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(networkinterfacesResource, c.ns, networkInterface), &v1alpha1.NetworkInterface{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.NetworkInterface), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeNetworkInterfaces) UpdateStatus(ctx context.Context, networkInterface *v1alpha1.NetworkInterface, opts v1.UpdateOptions) (*v1alpha1.NetworkInterface, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(networkinterfacesResource, "status", c.ns, networkInterface), &v1alpha1.NetworkInterface{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.NetworkInterface), err +} + +// Delete takes name of the networkInterface and deletes it. Returns an error if one occurs. +func (c *FakeNetworkInterfaces) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(networkinterfacesResource, c.ns, name), &v1alpha1.NetworkInterface{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeNetworkInterfaces) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(networkinterfacesResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.NetworkInterfaceList{}) + return err +} + +// Patch applies the patch and returns the patched networkInterface. +func (c *FakeNetworkInterfaces) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.NetworkInterface, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(networkinterfacesResource, c.ns, name, pt, data, subresources...), &v1alpha1.NetworkInterface{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.NetworkInterface), err +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networkinterfacelist.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networkinterfacelist.go new file mode 100644 index 0000000000..65be024427 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networkinterfacelist.go @@ -0,0 +1,49 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + testing "k8s.io/client-go/testing" + v1alpha1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1" +) + +// FakeNetworkInterfaceLists implements NetworkInterfaceListInterface +type FakeNetworkInterfaceLists struct { + Fake *FakeNetworkingV1alpha1 + ns string +} + +var networkinterfacelistsResource = schema.GroupVersionResource{Group: "networking.gke.io", Version: "v1alpha1", Resource: "networkinterfacelists"} + +var networkinterfacelistsKind = schema.GroupVersionKind{Group: "networking.gke.io", Version: "v1alpha1", Kind: "NetworkInterfaceList"} + +// Get takes name of the networkInterfaceList, and returns the corresponding networkInterfaceList object, and an error if there is any. +func (c *FakeNetworkInterfaceLists) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.NetworkInterfaceList, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(networkinterfacelistsResource, c.ns, name), &v1alpha1.NetworkInterfaceList{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.NetworkInterfaceList), err +} diff --git a/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networklist.go b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networklist.go new file mode 100644 index 0000000000..a3d52b5573 --- /dev/null +++ b/vendor/k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/typed/network/v1alpha1/fake/fake_networklist.go @@ -0,0 +1,47 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + testing "k8s.io/client-go/testing" + v1alpha1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1" +) + +// FakeNetworkLists implements NetworkListInterface +type FakeNetworkLists struct { + Fake *FakeNetworkingV1alpha1 +} + +var networklistsResource = schema.GroupVersionResource{Group: "networking.gke.io", Version: "v1alpha1", Resource: "networklists"} + +var networklistsKind = schema.GroupVersionKind{Group: "networking.gke.io", Version: "v1alpha1", Kind: "NetworkList"} + +// Get takes name of the networkList, and returns the corresponding networkList object, and an error if there is any. +func (c *FakeNetworkLists) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.NetworkList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(networklistsResource, name), &v1alpha1.NetworkList{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.NetworkList), err +} From dbc0dcbf528845620a8937c14f8091db0724c6f1 Mon Sep 17 00:00:00 2001 From: mmamczur Date: Thu, 11 May 2023 16:35:52 +0200 Subject: [PATCH 2/2] Multi Network support in the NEG controller --- cmd/glbc/main.go | 2 + pkg/neg/controller.go | 60 ++- pkg/neg/controller_test.go | 3 + pkg/neg/manager.go | 1 + pkg/neg/syncers/endpoints_calculator.go | 26 +- pkg/neg/syncers/endpoints_calculator_test.go | 113 ++++- pkg/neg/syncers/subsets.go | 11 +- pkg/neg/syncers/subsets_test.go | 97 +++- pkg/neg/syncers/transaction.go | 6 +- pkg/neg/syncers/transaction_test.go | 4 +- pkg/neg/syncers/utils_test.go | 11 +- pkg/neg/types/cloudprovideradapter.go | 8 + pkg/neg/types/fakes.go | 8 + pkg/neg/types/interfaces.go | 2 + pkg/neg/types/testing.go | 54 ++- pkg/neg/types/types_test.go | 9 +- pkg/network/network.go | 124 ++++- pkg/network/network_test.go | 468 +++++++++++++++++++ 18 files changed, 916 insertions(+), 91 deletions(-) create mode 100644 pkg/network/network_test.go diff --git a/cmd/glbc/main.go b/cmd/glbc/main.go index 2b7aced0e1..2a5e33cc25 100644 --- a/cmd/glbc/main.go +++ b/cmd/glbc/main.go @@ -357,6 +357,8 @@ func runControllers(ctx *ingctx.ControllerContext) { ctx.NodeInformer, ctx.EndpointSliceInformer, ctx.SvcNegInformer, + ctx.NetworkInformer, + ctx.GKENetworkParamsInformer, ctx.HasSynced, ctx.ControllerMetrics, ctx.L4Namer, diff --git a/pkg/neg/controller.go b/pkg/neg/controller.go index 6e1eb288c4..c240dbf648 100644 --- a/pkg/neg/controller.go +++ b/pkg/neg/controller.go @@ -73,6 +73,8 @@ type Controller struct { hasSynced func() bool ingressLister cache.Indexer serviceLister cache.Indexer + networkLister cache.Indexer + gkeNetworkParamSetLister cache.Indexer client kubernetes.Interface defaultBackendService utils.ServicePort enableASM bool @@ -116,6 +118,8 @@ func NewController( nodeInformer cache.SharedIndexInformer, endpointSliceInformer cache.SharedIndexInformer, svcNegInformer cache.SharedIndexInformer, + networkInformer cache.SharedIndexInformer, + gkeNetworkParamSetInformer cache.SharedIndexInformer, hasSynced func() bool, controllerMetrics *usageMetrics.ControllerMetrics, l4Namer namer2.L4ResourcesNamer, @@ -192,29 +196,39 @@ func NewController( } manager.reflector = reflector + var networkIndexer cache.Indexer + if networkInformer != nil { + networkIndexer = networkInformer.GetIndexer() + } + var gkeNetworkParamSetIndexer cache.Indexer + if gkeNetworkParamSetInformer != nil { + gkeNetworkParamSetIndexer = gkeNetworkParamSetInformer.GetIndexer() + } negController := &Controller{ - client: kubeClient, - manager: manager, - resyncPeriod: resyncPeriod, - gcPeriod: gcPeriod, - recorder: recorder, - zoneGetter: zoneGetter, - cloud: cloud, - namer: namer, - l4Namer: l4Namer, - defaultBackendService: defaultBackendService, - hasSynced: hasSynced, - ingressLister: ingressInformer.GetIndexer(), - serviceLister: serviceInformer.GetIndexer(), - serviceQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "neg_service_queue"), - endpointQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "neg_endpoint_queue"), - nodeQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "neg_node_queue"), - syncTracker: utils.NewTimeTracker(), - reflector: reflector, - usageCollector: controllerMetrics, - syncerMetrics: syncerMetrics, - runL4: runL4Controller, - logger: logger, + client: kubeClient, + manager: manager, + resyncPeriod: resyncPeriod, + gcPeriod: gcPeriod, + recorder: recorder, + zoneGetter: zoneGetter, + cloud: cloud, + namer: namer, + l4Namer: l4Namer, + defaultBackendService: defaultBackendService, + hasSynced: hasSynced, + ingressLister: ingressInformer.GetIndexer(), + serviceLister: serviceInformer.GetIndexer(), + networkLister: networkIndexer, + gkeNetworkParamSetLister: gkeNetworkParamSetIndexer, + serviceQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "neg_service_queue"), + endpointQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "neg_endpoint_queue"), + nodeQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "neg_node_queue"), + syncTracker: utils.NewTimeTracker(), + reflector: reflector, + usageCollector: controllerMetrics, + syncerMetrics: syncerMetrics, + runL4: runL4Controller, + logger: logger, } if runIngress { ingressInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ @@ -452,7 +466,7 @@ func (c *Controller) processService(key string) error { } negUsage := usageMetrics.NegServiceState{} svcPortInfoMap := make(negtypes.PortInfoMap) - networkInfo, err := network.ServiceNetwork(service, c.cloud) + networkInfo, err := network.ServiceNetwork(service, c.networkLister, c.gkeNetworkParamSetLister, c.cloud, c.logger) if err != nil { return err } diff --git a/pkg/neg/controller_test.go b/pkg/neg/controller_test.go index 007a6c3b6c..9b9c59fa5e 100644 --- a/pkg/neg/controller_test.go +++ b/pkg/neg/controller_test.go @@ -111,6 +111,7 @@ var ( } defaultNetwork = &network.NetworkInfo{ + IsDefault: true, K8sNetwork: "default", } ) @@ -129,6 +130,8 @@ func newTestControllerWithParamsAndContext(kubeClient kubernetes.Interface, test testContext.NodeInformer, testContext.EndpointSliceInformer, testContext.SvcNegInformer, + testContext.NetworkInformer, + testContext.GKENetworkParamSetInformer, func() bool { return true }, metrics.FakeControllerMetrics(), testContext.L4Namer, diff --git a/pkg/neg/manager.go b/pkg/neg/manager.go index a75d21e759..f6e85c330e 100644 --- a/pkg/neg/manager.go +++ b/pkg/neg/manager.go @@ -237,6 +237,7 @@ func (manager *syncerManager) EnsureSyncers(namespace, name string, newPorts neg manager.logger.WithValues("service", klog.KRef(syncerKey.Namespace, syncerKey.Name), "negName", syncerKey.NegName), manager.enableDualStackNEG, manager.syncerMetrics, + &portInfo.NetworkInfo, ) syncer = negsyncer.NewTransactionSyncer( syncerKey, diff --git a/pkg/neg/syncers/endpoints_calculator.go b/pkg/neg/syncers/endpoints_calculator.go index 913525d328..7ebfde68fe 100644 --- a/pkg/neg/syncers/endpoints_calculator.go +++ b/pkg/neg/syncers/endpoints_calculator.go @@ -27,6 +27,7 @@ import ( "k8s.io/ingress-gce/pkg/neg/metrics" "k8s.io/ingress-gce/pkg/neg/types" negtypes "k8s.io/ingress-gce/pkg/neg/types" + "k8s.io/ingress-gce/pkg/network" "k8s.io/ingress-gce/pkg/utils" "k8s.io/klog/v2" ) @@ -45,15 +46,17 @@ type LocalL4ILBEndpointsCalculator struct { subsetSizeLimit int svcId string logger klog.Logger + networkInfo *network.NetworkInfo } -func NewLocalL4ILBEndpointsCalculator(nodeLister listers.NodeLister, zoneGetter types.ZoneGetter, svcId string, logger klog.Logger) *LocalL4ILBEndpointsCalculator { +func NewLocalL4ILBEndpointsCalculator(nodeLister listers.NodeLister, zoneGetter types.ZoneGetter, svcId string, logger klog.Logger, networkInfo *network.NetworkInfo) *LocalL4ILBEndpointsCalculator { return &LocalL4ILBEndpointsCalculator{ nodeLister: nodeLister, zoneGetter: zoneGetter, subsetSizeLimit: maxSubsetSizeLocal, svcId: svcId, logger: logger.WithName("LocalL4ILBEndpointsCalculator"), + networkInfo: networkInfo, } } @@ -93,6 +96,10 @@ func (l *LocalL4ILBEndpointsCalculator) CalculateEndpoints(eds []types.Endpoints l.logger.Info("Dropping Node from subset since it is not a valid LB candidate", "nodeName", node.Name) continue } + if !l.networkInfo.IsNodeConnected(node) { + l.logger.Info("Node not connected to service network", "nodeName", node.Name, "network", l.networkInfo.K8sNetwork) + continue + } zone, err := l.zoneGetter.GetZoneForNode(node.Name) if err != nil { l.logger.Error(err, "Unable to find zone for node, skipping", "nodeName", node.Name) @@ -107,7 +114,7 @@ func (l *LocalL4ILBEndpointsCalculator) CalculateEndpoints(eds []types.Endpoints } // Compute the networkEndpoints, with total endpoints count <= l.subsetSizeLimit klog.V(2).Infof("Got zoneNodeMap as input for service", "zoneNodeMap", nodeMapToString(zoneNodeMap), "serviceID", l.svcId) - subsetMap, err := getSubsetPerZone(zoneNodeMap, l.subsetSizeLimit, l.svcId, currentMap, l.logger) + subsetMap, err := getSubsetPerZone(zoneNodeMap, l.subsetSizeLimit, l.svcId, currentMap, l.logger, l.networkInfo) return subsetMap, nil, 0, err } @@ -135,18 +142,21 @@ type ClusterL4ILBEndpointsCalculator struct { // subsetSizeLimit is the max value of the subset size in this mode. subsetSizeLimit int // svcId is the unique identifier for the service, that is used as a salt when hashing nodenames. - svcId string + svcId string + networkInfo *network.NetworkInfo logger klog.Logger } -func NewClusterL4ILBEndpointsCalculator(nodeLister listers.NodeLister, zoneGetter types.ZoneGetter, svcId string, logger klog.Logger) *ClusterL4ILBEndpointsCalculator { +func NewClusterL4ILBEndpointsCalculator(nodeLister listers.NodeLister, zoneGetter types.ZoneGetter, svcId string, logger klog.Logger, networkInfo *network.NetworkInfo) *ClusterL4ILBEndpointsCalculator { return &ClusterL4ILBEndpointsCalculator{ nodeLister: nodeLister, zoneGetter: zoneGetter, subsetSizeLimit: maxSubsetSizeDefault, svcId: svcId, - logger: logger.WithName("ClusterL4ILBEndpointsCalculator")} + logger: logger.WithName("ClusterL4ILBEndpointsCalculator"), + networkInfo: networkInfo, + } } // Mode indicates the mode that the EndpointsCalculator is operating in. @@ -161,6 +171,10 @@ func (l *ClusterL4ILBEndpointsCalculator) CalculateEndpoints(_ []types.Endpoints zoneNodeMap := make(map[string][]*v1.Node) for _, node := range nodes { + if !l.networkInfo.IsNodeConnected(node) { + l.logger.Info("Node not connected to service network", "nodeName", node.Name, "network", l.networkInfo.K8sNetwork) + continue + } zone, err := l.zoneGetter.GetZoneForNode(node.Name) if err != nil { l.logger.Error(err, "Unable to find zone for node skipping", "nodeName", node.Name) @@ -170,7 +184,7 @@ func (l *ClusterL4ILBEndpointsCalculator) CalculateEndpoints(_ []types.Endpoints } klog.V(2).Infof("Got zoneNodeMap as input for service", "zoneNodeMap", nodeMapToString(zoneNodeMap), "serviceID", l.svcId) // Compute the networkEndpoints, with total endpoints <= l.subsetSizeLimit. - subsetMap, err := getSubsetPerZone(zoneNodeMap, l.subsetSizeLimit, l.svcId, currentMap, l.logger) + subsetMap, err := getSubsetPerZone(zoneNodeMap, l.subsetSizeLimit, l.svcId, currentMap, l.logger, l.networkInfo) return subsetMap, nil, 0, err } diff --git a/pkg/neg/syncers/endpoints_calculator_test.go b/pkg/neg/syncers/endpoints_calculator_test.go index e103bf0992..808fafa4a1 100644 --- a/pkg/neg/syncers/endpoints_calculator_test.go +++ b/pkg/neg/syncers/endpoints_calculator_test.go @@ -22,14 +22,17 @@ import ( "reflect" "testing" + "github.com/google/go-cmp/cmp" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" + networkv1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1" "k8s.io/cloud-provider-gcp/providers/gce" "k8s.io/ingress-gce/pkg/neg/metrics" negtypes "k8s.io/ingress-gce/pkg/neg/types" + "k8s.io/ingress-gce/pkg/network" "k8s.io/ingress-gce/pkg/utils" "k8s.io/klog/v2" ) @@ -42,32 +45,36 @@ func TestLocalGetEndpointSet(t *testing.T) { _, transactionSyncer := newL4ILBTestTransactionSyncer(negtypes.NewAdapter(gce.NewFakeGCECloud(gce.DefaultTestClusterValues())), mode) zoneGetter := negtypes.NewFakeZoneGetter() nodeLister := listers.NewNodeLister(transactionSyncer.nodeLister) + defaultNetwork := network.NetworkInfo{IsDefault: true, K8sNetwork: "default"} testCases := []struct { desc string endpointsData []negtypes.EndpointsData - endpointSets map[string]negtypes.NetworkEndpointSet + wantEndpointSets map[string]negtypes.NetworkEndpointSet networkEndpointType negtypes.NetworkEndpointType nodeLabelsMap map[string]map[string]string + nodeAnnotationsMap map[string]map[string]string nodeReadyStatusMap map[string]v1.ConditionStatus nodeNames []string + network network.NetworkInfo }{ { desc: "default endpoints", endpointsData: negtypes.EndpointsDataFromEndpointSlices(getDefaultEndpointSlices()), // only 4 out of 6 nodes are picked since there are > 4 endpoints, but they are found only on 4 nodes. - endpointSets: map[string]negtypes.NetworkEndpointSet{ + wantEndpointSets: map[string]negtypes.NetworkEndpointSet{ negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.1", Node: testInstance1}, negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.3", Node: testInstance3}, negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}), }, networkEndpointType: negtypes.VmIpEndpointType, nodeNames: []string{testInstance1, testInstance2, testInstance3, testInstance4, testInstance5, testInstance6}, + network: defaultNetwork, }, { desc: "default endpoints, all nodes unready", endpointsData: negtypes.EndpointsDataFromEndpointSlices(getDefaultEndpointSlices()), // only 4 out of 6 nodes are picked since there are > 4 endpoints, but they are found only on 4 nodes. - endpointSets: map[string]negtypes.NetworkEndpointSet{ + wantEndpointSets: map[string]negtypes.NetworkEndpointSet{ negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.1", Node: testInstance1}, negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.3", Node: testInstance3}, negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}), }, @@ -76,6 +83,7 @@ func TestLocalGetEndpointSet(t *testing.T) { nodeReadyStatusMap: map[string]v1.ConditionStatus{ testInstance1: v1.ConditionFalse, testInstance2: v1.ConditionFalse, testInstance3: v1.ConditionFalse, testInstance4: v1.ConditionFalse, testInstance5: v1.ConditionFalse, testInstance6: v1.ConditionFalse, }, + network: defaultNetwork, }, { desc: "default endpoints, some nodes marked as non-candidates", @@ -88,36 +96,73 @@ func TestLocalGetEndpointSet(t *testing.T) { }, nodeNames: []string{testInstance1, testInstance2, testInstance3, testInstance4, testInstance5, testInstance6}, // only 2 out of 6 nodes are picked since there are > 4 endpoints, but they are found only on 4 nodes. 2 out of those 4 are non-candidates. - endpointSets: map[string]negtypes.NetworkEndpointSet{ + wantEndpointSets: map[string]negtypes.NetworkEndpointSet{ negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}), }, networkEndpointType: negtypes.VmIpEndpointType, + network: defaultNetwork, }, { desc: "no endpoints", endpointsData: []negtypes.EndpointsData{}, // No nodes are picked as there are no service endpoints. - endpointSets: nil, + wantEndpointSets: nil, networkEndpointType: negtypes.VmIpEndpointType, nodeNames: []string{testInstance1, testInstance2, testInstance3, testInstance4, testInstance5, testInstance6}, + network: defaultNetwork, + }, + { + desc: "multinetwork, endpoints only for nodes connected to a matching non-default network", + endpointsData: negtypes.EndpointsDataFromEndpointSlices(getDefaultEndpointSlices()), + network: network.NetworkInfo{IsDefault: false, K8sNetwork: "other"}, + nodeAnnotationsMap: map[string]map[string]string{ + testInstance1: {networkv1.NorthInterfacesAnnotationKey: nodeInterfacesAnnotation(t, "other", "20.2.3.1")}, + testInstance2: {networkv1.NorthInterfacesAnnotationKey: nodeInterfacesAnnotation(t, "other", "20.2.3.2")}, + testInstance3: {networkv1.NorthInterfacesAnnotationKey: nodeInterfacesAnnotation(t, "other", "20.2.3.3")}, + }, + //networkEndpointType: negtypes.VmIpEndpointType, + nodeNames: []string{testInstance1, testInstance2, testInstance3, testInstance4, testInstance5, testInstance6}, + // only 3 out of 6 nodes are picked because only 3 have multi-nic annotation with a matching network name + wantEndpointSets: map[string]negtypes.NetworkEndpointSet{ + negtypes.TestZone1: negtypes.NewNetworkEndpointSet( + negtypes.NetworkEndpoint{IP: "20.2.3.1", Node: testInstance1}, + negtypes.NetworkEndpoint{IP: "20.2.3.2", Node: testInstance2}), + negtypes.TestZone2: negtypes.NewNetworkEndpointSet( + negtypes.NetworkEndpoint{IP: "20.2.3.3", Node: testInstance3}), + }, }, } svcKey := fmt.Sprintf("%s/%s", testServiceName, testServiceNamespace) - ec := NewLocalL4ILBEndpointsCalculator(nodeLister, zoneGetter, svcKey, klog.TODO()) for _, tc := range testCases { - createNodes(t, tc.nodeNames, tc.nodeLabelsMap, tc.nodeReadyStatusMap, transactionSyncer.nodeLister) + createNodes(t, tc.nodeNames, tc.nodeLabelsMap, tc.nodeAnnotationsMap, tc.nodeReadyStatusMap, transactionSyncer.nodeLister) + ec := NewLocalL4ILBEndpointsCalculator(nodeLister, zoneGetter, svcKey, klog.TODO(), &tc.network) retSet, _, _, err := ec.CalculateEndpoints(tc.endpointsData, nil) if err != nil { t.Errorf("For case %q, expect nil error, but got %v.", tc.desc, err) } - if !reflect.DeepEqual(retSet, tc.endpointSets) { - t.Errorf("For case %q, expecting endpoint set %v, but got %v.", tc.desc, tc.endpointSets, retSet) + if diff := cmp.Diff(retSet, tc.wantEndpointSets); diff != "" { + t.Errorf("For case %q, expecting endpoint set %v, but got %v.\nDiff: (-want +got):\n%s", tc.desc, tc.wantEndpointSets, retSet, diff) } deleteNodes(t, tc.nodeNames, transactionSyncer.nodeLister) } } +func nodeInterfacesAnnotation(t *testing.T, network, ip string) string { + t.Helper() + annotation, err := networkv1.MarshalNorthInterfacesAnnotation(networkv1.NorthInterfacesAnnotation{ + { + Network: network, + IpAddress: ip, + }, + }) + if err != nil { + t.Errorf("could not create node annotations") + return "" + } + return annotation +} + // TestClusterGetEndpointSet verifies the GetEndpointSet method implemented by the ClusterL4ILBEndpointsCalculator. func TestClusterGetEndpointSet(t *testing.T) { t.Parallel() @@ -126,33 +171,36 @@ func TestClusterGetEndpointSet(t *testing.T) { _, transactionSyncer := newL4ILBTestTransactionSyncer(negtypes.NewAdapter(gce.NewFakeGCECloud(gce.DefaultTestClusterValues())), mode) zoneGetter := negtypes.NewFakeZoneGetter() nodeLister := listers.NewNodeLister(transactionSyncer.nodeLister) + defaultNetwork := network.NetworkInfo{IsDefault: true, K8sNetwork: "default"} testCases := []struct { desc string endpointsData []negtypes.EndpointsData - endpointSets map[string]negtypes.NetworkEndpointSet + wantEndpointSets map[string]negtypes.NetworkEndpointSet networkEndpointType negtypes.NetworkEndpointType nodeLabelsMap map[string]map[string]string nodeAnnotationsMap map[string]map[string]string nodeReadyStatusMap map[string]v1.ConditionStatus nodeNames []string + network network.NetworkInfo }{ { desc: "default endpoints", endpointsData: negtypes.EndpointsDataFromEndpointSlices(getDefaultEndpointSlices()), // all nodes are picked since, in this mode, endpoints running do not need to run on the selected node. - endpointSets: map[string]negtypes.NetworkEndpointSet{ + wantEndpointSets: map[string]negtypes.NetworkEndpointSet{ negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.1", Node: testInstance1}, negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.3", Node: testInstance3}, negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}, negtypes.NetworkEndpoint{IP: "1.2.3.5", Node: testInstance5}, negtypes.NetworkEndpoint{IP: "1.2.3.6", Node: testInstance6}), }, networkEndpointType: negtypes.VmIpEndpointType, nodeNames: []string{testInstance1, testInstance2, testInstance3, testInstance4, testInstance5, testInstance6}, + network: defaultNetwork, }, { desc: "default endpoints, all nodes unready", endpointsData: negtypes.EndpointsDataFromEndpointSlices(getDefaultEndpointSlices()), // all nodes are picked since, in this mode, endpoints running do not need to run on the selected node. - endpointSets: map[string]negtypes.NetworkEndpointSet{ + wantEndpointSets: map[string]negtypes.NetworkEndpointSet{ negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.1", Node: testInstance1}, negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.3", Node: testInstance3}, negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}, negtypes.NetworkEndpoint{IP: "1.2.3.5", Node: testInstance5}, negtypes.NetworkEndpoint{IP: "1.2.3.6", Node: testInstance6}), @@ -162,12 +210,13 @@ func TestClusterGetEndpointSet(t *testing.T) { nodeReadyStatusMap: map[string]v1.ConditionStatus{ testInstance1: v1.ConditionFalse, testInstance2: v1.ConditionFalse, testInstance3: v1.ConditionFalse, testInstance4: v1.ConditionFalse, testInstance5: v1.ConditionFalse, testInstance6: v1.ConditionFalse, }, + network: defaultNetwork, }, { desc: "default endpoints, some nodes marked as non-candidates", endpointsData: negtypes.EndpointsDataFromEndpointSlices(getDefaultEndpointSlices()), // all valid candidate nodes are picked since, in this mode, endpoints running do not need to run on the selected node. - endpointSets: map[string]negtypes.NetworkEndpointSet{ + wantEndpointSets: map[string]negtypes.NetworkEndpointSet{ negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}, negtypes.NetworkEndpoint{IP: "1.2.3.5", Node: testInstance5}, negtypes.NetworkEndpoint{IP: "1.2.3.6", Node: testInstance6}), @@ -180,31 +229,50 @@ func TestClusterGetEndpointSet(t *testing.T) { // label for testInstance4 will not remove it from endpoints list, since operation value is "random". testInstance4: {utils.GKECurrentOperationLabel: "random"}, }, + network: defaultNetwork, }, { desc: "no endpoints", // all nodes are picked since, in this mode, endpoints running do not need to run on the selected node. // Even when there are no service endpoints, nodes are selected at random. endpointsData: []negtypes.EndpointsData{}, - endpointSets: map[string]negtypes.NetworkEndpointSet{ + wantEndpointSets: map[string]negtypes.NetworkEndpointSet{ negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.1", Node: testInstance1}, negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.3", Node: testInstance3}, negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}, negtypes.NetworkEndpoint{IP: "1.2.3.5", Node: testInstance5}, negtypes.NetworkEndpoint{IP: "1.2.3.6", Node: testInstance6}), }, networkEndpointType: negtypes.VmIpEndpointType, nodeNames: []string{testInstance1, testInstance2, testInstance3, testInstance4, testInstance5, testInstance6}, + network: defaultNetwork, + }, + { + desc: "multinetwork endpoints, only for nodes connected to the specified network", + endpointsData: negtypes.EndpointsDataFromEndpointSlices(getDefaultEndpointSlices()), + wantEndpointSets: map[string]negtypes.NetworkEndpointSet{ + negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "20.2.3.1", Node: testInstance1}, negtypes.NetworkEndpoint{IP: "20.2.3.2", Node: testInstance2}), + negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "20.2.3.3", Node: testInstance3}, negtypes.NetworkEndpoint{IP: "20.2.3.6", Node: testInstance6}), + }, + network: network.NetworkInfo{IsDefault: false, K8sNetwork: "other"}, + nodeAnnotationsMap: map[string]map[string]string{ + testInstance1: {networkv1.NorthInterfacesAnnotationKey: nodeInterfacesAnnotation(t, "other", "20.2.3.1")}, + testInstance2: {networkv1.NorthInterfacesAnnotationKey: nodeInterfacesAnnotation(t, "other", "20.2.3.2")}, + testInstance3: {networkv1.NorthInterfacesAnnotationKey: nodeInterfacesAnnotation(t, "other", "20.2.3.3")}, + testInstance6: {networkv1.NorthInterfacesAnnotationKey: nodeInterfacesAnnotation(t, "other", "20.2.3.6")}, + }, + networkEndpointType: negtypes.VmIpEndpointType, + nodeNames: []string{testInstance1, testInstance2, testInstance3, testInstance4, testInstance5, testInstance6}, }, } svcKey := fmt.Sprintf("%s/%s", testServiceName, testServiceNamespace) - ec := NewClusterL4ILBEndpointsCalculator(nodeLister, zoneGetter, svcKey, klog.TODO()) for _, tc := range testCases { - createNodes(t, tc.nodeNames, tc.nodeLabelsMap, tc.nodeReadyStatusMap, transactionSyncer.nodeLister) + ec := NewClusterL4ILBEndpointsCalculator(nodeLister, zoneGetter, svcKey, klog.TODO(), &tc.network) + createNodes(t, tc.nodeNames, tc.nodeLabelsMap, tc.nodeAnnotationsMap, tc.nodeReadyStatusMap, transactionSyncer.nodeLister) retSet, _, _, err := ec.CalculateEndpoints(tc.endpointsData, nil) if err != nil { t.Errorf("For case %q, expect nil error, but got %v.", tc.desc, err) } - if !reflect.DeepEqual(retSet, tc.endpointSets) { - t.Errorf("For case %q, expecting endpoint set %v, but got %v.", tc.desc, tc.endpointSets, retSet) + if !reflect.DeepEqual(retSet, tc.wantEndpointSets) { + t.Errorf("For case %q, expecting endpoint set %v, but got %v.", tc.desc, tc.wantEndpointSets, retSet) } deleteNodes(t, tc.nodeNames, transactionSyncer.nodeLister) } @@ -247,8 +315,8 @@ func TestValidateEndpoints(t *testing.T) { nodeLister := testContext.NodeInformer.GetIndexer() serviceLister := testContext.ServiceInformer.GetIndexer() L7EndpointsCalculator := NewL7EndpointsCalculator(zoneGetter, podLister, nodeLister, serviceLister, svcPort, klog.TODO(), testContext.EnableDualStackNEG, metrics.FakeSyncerMetrics()) - L4LocalEndpointCalculator := NewLocalL4ILBEndpointsCalculator(listers.NewNodeLister(nodeLister), zoneGetter, svcKey, klog.TODO()) - L4ClusterEndpointCalculator := NewClusterL4ILBEndpointsCalculator(listers.NewNodeLister(nodeLister), zoneGetter, svcKey, klog.TODO()) + L4LocalEndpointCalculator := NewLocalL4ILBEndpointsCalculator(listers.NewNodeLister(nodeLister), zoneGetter, svcKey, klog.TODO(), &network.NetworkInfo{}) + L4ClusterEndpointCalculator := NewClusterL4ILBEndpointsCalculator(listers.NewNodeLister(nodeLister), zoneGetter, svcKey, klog.TODO(), &network.NetworkInfo{}) testEndpointPodMap := map[negtypes.NetworkEndpoint]types.NamespacedName{ { @@ -829,7 +897,7 @@ func TestValidateEndpoints(t *testing.T) { } } -func createNodes(t *testing.T, nodeNames []string, nodeLabels map[string]map[string]string, nodeReadyStatus map[string]v1.ConditionStatus, nodeIndexer cache.Indexer) { +func createNodes(t *testing.T, nodeNames []string, nodeLabels map[string]map[string]string, nodeAnnotations map[string]map[string]string, nodeReadyStatus map[string]v1.ConditionStatus, nodeIndexer cache.Indexer) { t.Helper() for i, nodeName := range nodeNames { var labels, annotations map[string]string @@ -837,6 +905,9 @@ func createNodes(t *testing.T, nodeNames []string, nodeLabels map[string]map[str if nodeLabels != nil { labels = nodeLabels[nodeName] } + if nodeAnnotations != nil { + annotations = nodeAnnotations[nodeName] + } if nodeReadyStatus != nil { status, ok := nodeReadyStatus[nodeName] if ok { diff --git a/pkg/neg/syncers/subsets.go b/pkg/neg/syncers/subsets.go index 7da9dd117c..a0bb6a4669 100644 --- a/pkg/neg/syncers/subsets.go +++ b/pkg/neg/syncers/subsets.go @@ -24,6 +24,7 @@ import ( v1 "k8s.io/api/core/v1" negtypes "k8s.io/ingress-gce/pkg/neg/types" + "k8s.io/ingress-gce/pkg/network" "k8s.io/ingress-gce/pkg/utils" "k8s.io/klog/v2" ) @@ -158,7 +159,7 @@ func sortZones(nodesPerZone map[string][]*v1.Node) []ZoneInfo { // Since the number of nodes will keep increasing in successive zones due to the sorting, even if fewer nodes were // present in some zones, more nodes will be picked from other nodes, taking the total subset size to the given limit // whenever possible. -func getSubsetPerZone(nodesPerZone map[string][]*v1.Node, totalLimit int, svcID string, currentMap map[string]negtypes.NetworkEndpointSet, logger klog.Logger) (map[string]negtypes.NetworkEndpointSet, error) { +func getSubsetPerZone(nodesPerZone map[string][]*v1.Node, totalLimit int, svcID string, currentMap map[string]negtypes.NetworkEndpointSet, logger klog.Logger, networkInfo *network.NetworkInfo) (map[string]negtypes.NetworkEndpointSet, error) { result := make(map[string]negtypes.NetworkEndpointSet) var currentList []negtypes.NetworkEndpoint @@ -182,7 +183,13 @@ func getSubsetPerZone(nodesPerZone map[string][]*v1.Node, totalLimit int, svcID } subset := pickSubsetsMinRemovals(nodesPerZone[zone.Name], svcID, subsetSize, currentList) for _, node := range subset { - result[zone.Name].Insert(negtypes.NetworkEndpoint{Node: node.Name, IP: utils.GetNodePrimaryIP(node)}) + var ip string + if !networkInfo.IsDefault { + ip = network.GetNodeIPForNetwork(node, networkInfo.K8sNetwork) + } else { + ip = utils.GetNodePrimaryIP(node) + } + result[zone.Name].Insert(negtypes.NetworkEndpoint{Node: node.Name, IP: ip}) } totalLimit -= len(subset) zonesRemaining-- diff --git a/pkg/neg/syncers/subsets_test.go b/pkg/neg/syncers/subsets_test.go index 466d3cd8b1..7a8822123e 100644 --- a/pkg/neg/syncers/subsets_test.go +++ b/pkg/neg/syncers/subsets_test.go @@ -21,7 +21,9 @@ import ( "strings" "testing" + networkv1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1" "k8s.io/ingress-gce/pkg/neg/types" + "k8s.io/ingress-gce/pkg/network" "k8s.io/klog/v2" v1 "k8s.io/api/core/v1" @@ -189,7 +191,7 @@ func TestUnevenNodesInZones(t *testing.T) { }, } for _, tc := range testCases { - subsetMap, err := getSubsetPerZone(tc.nodesMap, tc.subsetLimit, tc.svcKey, nil, klog.TODO()) + subsetMap, err := getSubsetPerZone(tc.nodesMap, tc.subsetLimit, tc.svcKey, nil, klog.TODO(), &network.NetworkInfo{}) if err != nil { t.Errorf("Failed to get subset for test '%s', err %v", tc.description, err) } @@ -211,6 +213,70 @@ func TestUnevenNodesInZones(t *testing.T) { } } +func TestGetSubsetPerZoneMultinetwork(t *testing.T) { + t.Parallel() + testCases := []struct { + description string + nodesMap map[string][]*v1.Node + svcKey string + expectedCount int + // expectEmpty indicates that some zones can have empty subsets + expectEmpty bool + networkInfo network.NetworkInfo + expectedNodesMap map[string]map[string]string + }{ + { + description: "Default network, gets primary interface", + nodesMap: map[string][]*v1.Node{ + "zone1": {makeNodeWithNetwork(t, "n1_1", map[string]string{"net1": "172.168.1.1"}), makeNodeWithNetwork(t, "n1_2", map[string]string{"net1": "172.168.1.2", "net2": "192.168.1.2"})}, + "zone2": {makeNodeWithNetwork(t, "n2_1", map[string]string{"net1": "172.168.2.1"}), makeNodeWithNetwork(t, "n2_2", map[string]string{"net1": "172.168.2.2"})}, + "zone3": {makeNodeWithNetwork(t, "n3_1", map[string]string{"net1": "172.168.3.1", "net2": "192.168.3.1"})}, + }, + svcKey: "svc123", + // empty IPs since test can't get the primary IP + expectedNodesMap: map[string]map[string]string{ + "zone1": {"n1_1": "", "n1_2": ""}, + "zone2": {"n2_1": "", "n2_2": ""}, + "zone3": {"n3_1": ""}, + }, + }, + { + description: "non-default network IPs", + nodesMap: map[string][]*v1.Node{ + "zone1": {makeNodeWithNetwork(t, "n1_1", map[string]string{"net1": "172.168.1.1"}), makeNodeWithNetwork(t, "n1_2", map[string]string{"net2": "192.168.1.2", "net1": "172.168.1.2"})}, + "zone2": {makeNodeWithNetwork(t, "n2_1", map[string]string{"net1": "172.168.2.1"}), makeNodeWithNetwork(t, "n2_2", map[string]string{"net1": "172.168.2.2"})}, + "zone3": {makeNodeWithNetwork(t, "n3_1", map[string]string{"net1": "172.168.3.1", "net2": "192.168.3.1"})}, + }, + svcKey: "svc123", + networkInfo: network.NetworkInfo{ + IsDefault: false, + K8sNetwork: "net1", + }, + expectedNodesMap: map[string]map[string]string{ + "zone1": {"n1_1": "172.168.1.1", "n1_2": "172.168.1.2"}, + "zone2": {"n2_1": "172.168.2.1", "n2_2": "172.168.2.2"}, + "zone3": {"n3_1": "172.168.3.1"}, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + subsetMap, err := getSubsetPerZone(tc.nodesMap, maxSubsetSizeLocal, tc.svcKey, nil, klog.TODO(), &tc.networkInfo) + if err != nil { + t.Errorf("Failed to get subset for test '%s', err %v", tc.description, err) + } + for zone, wantNodesAndIPs := range tc.expectedNodesMap { + + for node, ip := range wantNodesAndIPs { + if (!subsetMap[zone].Has(types.NetworkEndpoint{Node: node, IP: ip})) { + t.Errorf("node %s in zone %s was supposed to have IP %s but got zone endpoints %+v", node, zone, ip, subsetMap[zone]) + } + } + } + }) + } +} + func makeNodes(startIndex, count int) []*v1.Node { nodes := []*v1.Node{} for i := startIndex; i < startIndex+count; i++ { @@ -219,6 +285,35 @@ func makeNodes(startIndex, count int) []*v1.Node { return nodes } +// makeNodeWithNetwork creates a node with multi-networking annotations +// networksAndIPs param should contain a map of network names to the IPs of the interface +// of that network. +func makeNodeWithNetwork(t *testing.T, name string, networksAndIPs map[string]string) *v1.Node { + t.Helper() + node := &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Annotations: map[string]string{}, + }, + } + var northInterfaces networkv1.NorthInterfacesAnnotation + for netName, ip := range networksAndIPs { + northInterfaces = append(northInterfaces, networkv1.NorthInterface{ + Network: netName, + IpAddress: ip, + }) + + } + if len(northInterfaces) > 0 { + annotation, err := networkv1.MarshalNorthInterfacesAnnotation(northInterfaces) + if err != nil { + t.Errorf("could not create node annotations") + } + node.ObjectMeta.Annotations[networkv1.NorthInterfacesAnnotationKey] = annotation + } + return node +} + func TestNoRemovals(t *testing.T) { t.Parallel() // pick a random startIndex which is used to construct nodeName. diff --git a/pkg/neg/syncers/transaction.go b/pkg/neg/syncers/transaction.go index 56582b56ef..f81e509677 100644 --- a/pkg/neg/syncers/transaction.go +++ b/pkg/neg/syncers/transaction.go @@ -183,15 +183,15 @@ func NewTransactionSyncer( return syncer } -func GetEndpointsCalculator(podLister, nodeLister, serviceLister cache.Indexer, zoneGetter negtypes.ZoneGetter, syncerKey negtypes.NegSyncerKey, mode negtypes.EndpointsCalculatorMode, logger klog.Logger, enableDualStackNEG bool, syncMetricsCollector *metrics.SyncerMetrics) negtypes.NetworkEndpointsCalculator { +func GetEndpointsCalculator(podLister, nodeLister, serviceLister cache.Indexer, zoneGetter negtypes.ZoneGetter, syncerKey negtypes.NegSyncerKey, mode negtypes.EndpointsCalculatorMode, logger klog.Logger, enableDualStackNEG bool, syncMetricsCollector *metrics.SyncerMetrics, networkInfo *network.NetworkInfo) negtypes.NetworkEndpointsCalculator { serviceKey := strings.Join([]string{syncerKey.Name, syncerKey.Namespace}, "/") if syncerKey.NegType == negtypes.VmIpEndpointType { nodeLister := listers.NewNodeLister(nodeLister) switch mode { case negtypes.L4LocalMode: - return NewLocalL4ILBEndpointsCalculator(nodeLister, zoneGetter, serviceKey, logger) + return NewLocalL4ILBEndpointsCalculator(nodeLister, zoneGetter, serviceKey, logger, networkInfo) default: - return NewClusterL4ILBEndpointsCalculator(nodeLister, zoneGetter, serviceKey, logger) + return NewClusterL4ILBEndpointsCalculator(nodeLister, zoneGetter, serviceKey, logger, networkInfo) } } return NewL7EndpointsCalculator( diff --git a/pkg/neg/syncers/transaction_test.go b/pkg/neg/syncers/transaction_test.go index 8290826c19..ac3cf68c96 100644 --- a/pkg/neg/syncers/transaction_test.go +++ b/pkg/neg/syncers/transaction_test.go @@ -2092,7 +2092,7 @@ func TestCollectLabelStats(t *testing.T) { func newL4ILBTestTransactionSyncer(fakeGCE negtypes.NetworkEndpointGroupCloud, mode negtypes.EndpointsCalculatorMode) (negtypes.NegSyncer, *transactionSyncer) { negsyncer, ts := newTestTransactionSyncer(fakeGCE, negtypes.VmIpEndpointType, false) - ts.endpointsCalculator = GetEndpointsCalculator(ts.podLister, ts.nodeLister, ts.serviceLister, ts.zoneGetter, ts.NegSyncerKey, mode, klog.TODO(), false, nil) + ts.endpointsCalculator = GetEndpointsCalculator(ts.podLister, ts.nodeLister, ts.serviceLister, ts.zoneGetter, ts.NegSyncerKey, mode, klog.TODO(), false, nil, &network.NetworkInfo{IsDefault: true}) return negsyncer, ts } @@ -2134,7 +2134,7 @@ func newTestTransactionSyncer(fakeGCE negtypes.NetworkEndpointGroupCloud, negTyp testContext.SvcNegInformer.GetIndexer(), reflector, GetEndpointsCalculator(testContext.PodInformer.GetIndexer(), testContext.NodeInformer.GetIndexer(), testContext.ServiceInformer.GetIndexer(), - fakeZoneGetter, svcPort, mode, klog.TODO(), testContext.EnableDualStackNEG, metrics.FakeSyncerMetrics()), + fakeZoneGetter, svcPort, mode, klog.TODO(), testContext.EnableDualStackNEG, metrics.FakeSyncerMetrics(), &network.NetworkInfo{IsDefault: true}), string(kubeSystemUID), testContext.SvcNegClient, metrics.FakeSyncerMetrics(), diff --git a/pkg/neg/syncers/utils_test.go b/pkg/neg/syncers/utils_test.go index f2f7402ddf..d1963b6710 100644 --- a/pkg/neg/syncers/utils_test.go +++ b/pkg/neg/syncers/utils_test.go @@ -314,6 +314,7 @@ func TestEnsureNetworkEndpointGroup(t *testing.T) { testSubnetwork = cloud.ResourcePath("subnetwork", &meta.Key{Zone: testZone, Name: "test-subnetwork"}) testKubesystemUID = "kube-system-uid" testPort = "80" + defaultNetwork = network.NetworkInfo{IsDefault: true, K8sNetwork: "default"} ) testCases := []struct { @@ -333,6 +334,7 @@ func TestEnsureNetworkEndpointGroup(t *testing.T) { networkEndpointType: negtypes.VmIpPortEndpointType, expectedSubnetwork: testSubnetwork, apiVersion: meta.VersionGA, + networkInfo: defaultNetwork, }, { description: "Create NEG of type NON_GCP_PRIVATE_IP_PORT", @@ -341,6 +343,7 @@ func TestEnsureNetworkEndpointGroup(t *testing.T) { networkEndpointType: negtypes.NonGCPPrivateEndpointType, expectedSubnetwork: "", apiVersion: meta.VersionGA, + networkInfo: defaultNetwork, }, { description: "Create NEG of type GCE_VM_IP", @@ -349,6 +352,7 @@ func TestEnsureNetworkEndpointGroup(t *testing.T) { networkEndpointType: negtypes.VmIpEndpointType, expectedSubnetwork: testSubnetwork, apiVersion: meta.VersionAlpha, + networkInfo: defaultNetwork, }, { description: "Create NEG of type GCE_VM_IP_PORT with Neg CRD", @@ -357,6 +361,7 @@ func TestEnsureNetworkEndpointGroup(t *testing.T) { networkEndpointType: negtypes.VmIpPortEndpointType, expectedSubnetwork: testSubnetwork, apiVersion: meta.VersionGA, + networkInfo: defaultNetwork, }, { description: "Create NEG of type NON_GCP_PRIVATE_IP_PORT with Neg CRD", @@ -365,6 +370,7 @@ func TestEnsureNetworkEndpointGroup(t *testing.T) { networkEndpointType: negtypes.NonGCPPrivateEndpointType, expectedSubnetwork: "", apiVersion: meta.VersionGA, + networkInfo: defaultNetwork, }, { description: "Create NEG of type GCE_VM_IP with Neg CRD", @@ -373,6 +379,7 @@ func TestEnsureNetworkEndpointGroup(t *testing.T) { networkEndpointType: negtypes.VmIpEndpointType, expectedSubnetwork: testSubnetwork, apiVersion: meta.VersionAlpha, + networkInfo: defaultNetwork, }, { description: "Create NEG of type GCE_VM_IP_PORT in alternate network", @@ -383,7 +390,7 @@ func TestEnsureNetworkEndpointGroup(t *testing.T) { expectedSubnetwork: cloud.ResourcePath("subnetwork", &meta.Key{Zone: testZone, Name: "other-subnet"}), apiVersion: meta.VersionGA, networkInfo: network.NetworkInfo{ - IsNonDefault: true, + IsDefault: false, K8sNetwork: "other-network", NetworkURL: cloud.ResourcePath("network", &meta.Key{Name: "other-network"}), SubnetworkURL: cloud.ResourcePath("subnetwork", &meta.Key{Zone: testZone, Name: "other-subnet"}), @@ -393,7 +400,7 @@ func TestEnsureNetworkEndpointGroup(t *testing.T) { for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { fakeCloud := negtypes.NewFakeNetworkEndpointGroupCloud(testSubnetwork, testNetwork) - if !tc.networkInfo.IsNonDefault { + if tc.networkInfo.IsDefault { tc.networkInfo.NetworkURL = fakeCloud.NetworkURL() tc.networkInfo.SubnetworkURL = fakeCloud.SubnetworkURL() } diff --git a/pkg/neg/types/cloudprovideradapter.go b/pkg/neg/types/cloudprovideradapter.go index b9134a3eef..6e42ae4112 100644 --- a/pkg/neg/types/cloudprovideradapter.go +++ b/pkg/neg/types/cloudprovideradapter.go @@ -112,3 +112,11 @@ func (a *cloudProviderAdapter) NetworkURL() string { func (a *cloudProviderAdapter) SubnetworkURL() string { return a.subnetworkURL } + +func (a *cloudProviderAdapter) NetworkProjectID() string { + return a.c.NetworkProjectID() +} + +func (a *cloudProviderAdapter) Region() string { + return a.c.Region() +} diff --git a/pkg/neg/types/fakes.go b/pkg/neg/types/fakes.go index 260a84f5f5..dd6a01ba4a 100644 --- a/pkg/neg/types/fakes.go +++ b/pkg/neg/types/fakes.go @@ -264,3 +264,11 @@ func (f *FakeNetworkEndpointGroupCloud) NetworkURL() string { func (f *FakeNetworkEndpointGroupCloud) SubnetworkURL() string { return f.Subnetwork } + +func (f *FakeNetworkEndpointGroupCloud) NetworkProjectID() string { + return "test-network-project-id" +} + +func (f *FakeNetworkEndpointGroupCloud) Region() string { + return "test-region" +} diff --git a/pkg/neg/types/interfaces.go b/pkg/neg/types/interfaces.go index 1cb2f5bd0c..485f305b01 100644 --- a/pkg/neg/types/interfaces.go +++ b/pkg/neg/types/interfaces.go @@ -40,6 +40,8 @@ type NetworkEndpointGroupCloud interface { ListNetworkEndpoints(name, zone string, showHealthStatus bool, version meta.Version) ([]*composite.NetworkEndpointWithHealthStatus, error) NetworkURL() string SubnetworkURL() string + NetworkProjectID() string + Region() string } // NetworkEndpointGroupNamer is an interface for generating network endpoint group name. diff --git a/pkg/neg/types/testing.go b/pkg/neg/types/testing.go index 4eeec949bc..e57fc4bc19 100644 --- a/pkg/neg/types/testing.go +++ b/pkg/neg/types/testing.go @@ -27,6 +27,9 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/cache" + netfake "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake" + informernetwork "k8s.io/cloud-provider-gcp/crd/client/network/informers/externalversions/network/v1" + informergkenetworkparamset "k8s.io/cloud-provider-gcp/crd/client/network/informers/externalversions/network/v1alpha1" "k8s.io/cloud-provider-gcp/providers/gce" svcnegclient "k8s.io/ingress-gce/pkg/svcneg/client/clientset/versioned" negfake "k8s.io/ingress-gce/pkg/svcneg/client/clientset/versioned/fake" @@ -52,13 +55,15 @@ type TestContext struct { NegNamer NetworkEndpointGroupNamer L4Namer namer.L4ResourcesNamer - IngressInformer cache.SharedIndexInformer - PodInformer cache.SharedIndexInformer - ServiceInformer cache.SharedIndexInformer - NodeInformer cache.SharedIndexInformer - EndpointInformer cache.SharedIndexInformer - EndpointSliceInformer cache.SharedIndexInformer - SvcNegInformer cache.SharedIndexInformer + IngressInformer cache.SharedIndexInformer + PodInformer cache.SharedIndexInformer + ServiceInformer cache.SharedIndexInformer + NodeInformer cache.SharedIndexInformer + EndpointInformer cache.SharedIndexInformer + EndpointSliceInformer cache.SharedIndexInformer + SvcNegInformer cache.SharedIndexInformer + NetworkInformer cache.SharedIndexInformer + GKENetworkParamSetInformer cache.SharedIndexInformer KubeSystemUID types.UID ResyncPeriod time.Duration @@ -73,6 +78,7 @@ func NewTestContext() *TestContext { func NewTestContextWithKubeClient(kubeClient kubernetes.Interface) *TestContext { negClient := negfake.NewSimpleClientset() + networkClient := netfake.NewSimpleClientset() fakeGCE := gce.NewFakeGCECloud(gce.DefaultTestClusterValues()) MockNetworkEndpointAPIs(fakeGCE) @@ -80,21 +86,23 @@ func NewTestContextWithKubeClient(kubeClient kubernetes.Interface) *TestContext l4namer := namer.NewL4Namer(kubeSystemUID, clusterNamer) return &TestContext{ - KubeClient: kubeClient, - SvcNegClient: negClient, - Cloud: fakeGCE, - NegNamer: clusterNamer, - L4Namer: l4namer, - IngressInformer: informernetworking.NewIngressInformer(kubeClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), - PodInformer: informerv1.NewPodInformer(kubeClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), - ServiceInformer: informerv1.NewServiceInformer(kubeClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), - EndpointInformer: informerv1.NewEndpointsInformer(kubeClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), - EndpointSliceInformer: discoveryinformer.NewEndpointSliceInformer(kubeClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), - NodeInformer: informerv1.NewNodeInformer(kubeClient, resyncPeriod, utils.NewNamespaceIndexer()), - SvcNegInformer: informersvcneg.NewServiceNetworkEndpointGroupInformer(negClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), - KubeSystemUID: kubeSystemUID, - ResyncPeriod: resyncPeriod, - NumGCWorkers: numGCWorkers, - EnableDualStackNEG: false, + KubeClient: kubeClient, + SvcNegClient: negClient, + Cloud: fakeGCE, + NegNamer: clusterNamer, + L4Namer: l4namer, + IngressInformer: informernetworking.NewIngressInformer(kubeClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), + PodInformer: informerv1.NewPodInformer(kubeClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), + ServiceInformer: informerv1.NewServiceInformer(kubeClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), + EndpointInformer: informerv1.NewEndpointsInformer(kubeClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), + EndpointSliceInformer: discoveryinformer.NewEndpointSliceInformer(kubeClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), + NodeInformer: informerv1.NewNodeInformer(kubeClient, resyncPeriod, utils.NewNamespaceIndexer()), + SvcNegInformer: informersvcneg.NewServiceNetworkEndpointGroupInformer(negClient, namespace, resyncPeriod, utils.NewNamespaceIndexer()), + NetworkInformer: informernetwork.NewNetworkInformer(networkClient, resyncPeriod, utils.NewNamespaceIndexer()), + GKENetworkParamSetInformer: informergkenetworkparamset.NewGKENetworkParamSetInformer(networkClient, resyncPeriod, utils.NewNamespaceIndexer()), + KubeSystemUID: kubeSystemUID, + ResyncPeriod: resyncPeriod, + NumGCWorkers: numGCWorkers, + EnableDualStackNEG: false, } } diff --git a/pkg/neg/types/types_test.go b/pkg/neg/types/types_test.go index 171ee016cf..b8002778d8 100644 --- a/pkg/neg/types/types_test.go +++ b/pkg/neg/types/types_test.go @@ -44,6 +44,7 @@ func TestPortInfoMapMerge(t *testing.T) { namespace := "namespace" name := "name" defaultNetwork := &network.NetworkInfo{ + IsDefault: true, NetworkURL: "defaultNetwork", SubnetworkURL: "defaultSubnetwork", } @@ -216,6 +217,7 @@ func TestPortInfoMapDifference(t *testing.T) { namespace := "namespace" name := "name" defaultNetwork := &network.NetworkInfo{ + IsDefault: true, NetworkURL: "defaultNetwork", SubnetworkURL: "defaultSubnetwork", } @@ -299,9 +301,9 @@ func TestPortInfoMapDifference(t *testing.T) { }, { "difference of two non-empty maps with different network", - NewPortInfoMap(namespace, name, NewSvcPortTupleSet(SvcPortTuple{Port: 80, TargetPort: "namedport"}, SvcPortTuple{Port: 443, TargetPort: "3000"}), namer, false, nil, &network.NetworkInfo{IsNonDefault: true, NetworkURL: "nonDefault", SubnetworkURL: "nonDefault"}), + NewPortInfoMap(namespace, name, NewSvcPortTupleSet(SvcPortTuple{Port: 80, TargetPort: "namedport"}, SvcPortTuple{Port: 443, TargetPort: "3000"}), namer, false, nil, &network.NetworkInfo{IsDefault: false, NetworkURL: "nonDefault", SubnetworkURL: "nonDefault"}), NewPortInfoMap(namespace, name, NewSvcPortTupleSet(SvcPortTuple{Port: 80, TargetPort: "namedport"}, SvcPortTuple{Port: 443, TargetPort: "3000"}), namer, false, nil, defaultNetwork), - NewPortInfoMap(namespace, name, NewSvcPortTupleSet(SvcPortTuple{Port: 80, TargetPort: "namedport"}, SvcPortTuple{Port: 443, TargetPort: "3000"}), namer, false, nil, &network.NetworkInfo{IsNonDefault: true, NetworkURL: "nonDefault", SubnetworkURL: "nonDefault"}), + NewPortInfoMap(namespace, name, NewSvcPortTupleSet(SvcPortTuple{Port: 80, TargetPort: "namedport"}, SvcPortTuple{Port: 443, TargetPort: "3000"}), namer, false, nil, &network.NetworkInfo{IsDefault: false, NetworkURL: "nonDefault", SubnetworkURL: "nonDefault"}), }, } @@ -358,6 +360,7 @@ func TestNegsWithReadinessGate(t *testing.T) { namespace := "namespace" name := "name" defaultNetwork := &network.NetworkInfo{ + IsDefault: true, NetworkURL: "defaultNetwork", SubnetworkURL: "defaultSubnetwork", } @@ -416,6 +419,7 @@ func TestCustomNamedNegs(t *testing.T) { svcPortTuple1 = SvcPortTuple{Port: port1, TargetPort: targetPort1} svcPortTuple2 = SvcPortTuple{Port: port2, TargetPort: targetPort2} defaultNetwork = &network.NetworkInfo{ + IsDefault: true, NetworkURL: "defaultNetwork", SubnetworkURL: "defaultSubnetwork", } @@ -707,6 +711,7 @@ func TestEndpointsDataFromEndpointSlicesNodeNameFromTopology(t *testing.T) { func TestEndpointsCalculatorMode(t *testing.T) { testContext := NewTestContext() defaultNetwork := &network.NetworkInfo{ + IsDefault: true, NetworkURL: "defaultNetwork", SubnetworkURL: "defaultSubnetwork", } diff --git a/pkg/network/network.go b/pkg/network/network.go index e7ecf87699..7f30c12e8e 100644 --- a/pkg/network/network.go +++ b/pkg/network/network.go @@ -17,30 +17,134 @@ limitations under the License. package network import ( + "fmt" + "strings" + + "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud" + "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta" apiv1 "k8s.io/api/core/v1" + "k8s.io/client-go/tools/cache" + networkv1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1" + gkenetworkparamsetv1alpha1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1" + "k8s.io/klog/v2" +) + +const ( + networkingGKEGroup = gkenetworkparamsetv1alpha1.GroupName + gkeNetworkParamSetKind = "gkenetworkparamset" + networkSelector = networkv1.NetworkAnnotationKey ) // ServiceNetwork determines the network data to be used for the LB resources. // This function currently returns only the default network but will provide // secondary networks information for multi-networked services in the future. -func ServiceNetwork(_ *apiv1.Service, cloudProvider cloudNetworkProvider) (*NetworkInfo, error) { +func ServiceNetwork(service *apiv1.Service, networkLister, gkeNetworkParamSetLister cache.Indexer, cloudProvider cloudNetworkProvider, logger klog.Logger) (*NetworkInfo, error) { + if networkLister == nil || gkeNetworkParamSetLister == nil { + return defaultNetwork(cloudProvider), nil + } + logger.Info("Network lookup for service", "service", service.Name, "namespace", service.Namespace) + networkName, ok := service.Spec.Selector[networkSelector] + if !ok || networkName == "" || networkName == networkv1.DefaultPodNetworkName { + return defaultNetwork(cloudProvider), nil + } + obj, exists, err := networkLister.GetByKey(networkName) + if err != nil { + return nil, err + } + if !exists { + return nil, fmt.Errorf("network %s does not exist", networkName) + } + network := obj.(*networkv1.Network) + if network == nil { + return nil, fmt.Errorf("cannot convert to Network (%T)", obj) + } + logger.Info("Found network for service", "network", network.Name, "service", service.Name, "namespace", service.Namespace) + parametersRef := network.Spec.ParametersRef + if !refersGKENetworkParamSet(parametersRef) { + return nil, fmt.Errorf("network.Spec.ParametersRef does not refer a GKENetworkParamSet resource") + } + if parametersRef.Namespace != nil { + return nil, fmt.Errorf("network.Spec.ParametersRef.namespace must not be set for GKENetworkParamSet reference as it is a cluster scope resource") + } + gkeParamsObj, exists, err := gkeNetworkParamSetLister.GetByKey(parametersRef.Name) + if err != nil { + return nil, err + } + if !exists { + return nil, fmt.Errorf("GKENetworkParamSet %s was not found", parametersRef.Name) + } + gkeNetworkParamSet := gkeParamsObj.(*gkenetworkparamsetv1alpha1.GKENetworkParamSet) + if network == nil { + return nil, fmt.Errorf("cannot convert to GKENetworkParamSet (%T)", gkeParamsObj) + } + netURL := networkURL(cloudProvider, gkeNetworkParamSet.Spec.VPC) + subnetURL := subnetworkURL(cloudProvider, gkeNetworkParamSet.Spec.VPCSubnet) + + logger.Info("Found GKE network parameters for service", "NetworkURL", netURL, "SubnetworkURL", subnetURL, "service", service.Name, "namespace", service.Namespace) + return &NetworkInfo{ + IsDefault: false, + K8sNetwork: networkName, + NetworkURL: netURL, + SubnetworkURL: subnetURL, + }, nil +} + +func defaultNetwork(cloudProvider cloudNetworkProvider) *NetworkInfo { return &NetworkInfo{ - IsNonDefault: false, - K8sNetwork: "default", + IsDefault: true, + K8sNetwork: networkv1.DefaultPodNetworkName, NetworkURL: cloudProvider.NetworkURL(), SubnetworkURL: cloudProvider.SubnetworkURL(), - }, nil + } +} + +func refersGKENetworkParamSet(parametersRef *networkv1.NetworkParametersReference) bool { + return parametersRef != nil && + parametersRef.Group == networkingGKEGroup && + strings.ToLower(parametersRef.Kind) == gkeNetworkParamSetKind && + parametersRef.Name != "" +} + +func networkURL(cloudProvider cloudNetworkProvider, vpc string) string { + key := meta.GlobalKey(vpc) + return cloud.SelfLink(meta.VersionGA, cloudProvider.NetworkProjectID(), "networks", key) +} + +func subnetworkURL(cloudProvider cloudNetworkProvider, subnetwork string) string { + key := meta.RegionalKey(subnetwork, cloudProvider.Region()) + return cloud.SelfLink(meta.VersionGA, cloudProvider.NetworkProjectID(), "subnetworks", key) +} + +// GetNodeIPForNetwork retrieves the IP of the interface of the node connected to the network. +// The addresses come from the 'networking.gke.io/north-interfaces' annotation. +func GetNodeIPForNetwork(node *apiv1.Node, network string) string { + northInterfacesAnnotation, ok := node.Annotations[networkv1.NorthInterfacesAnnotationKey] + if !ok || northInterfacesAnnotation == "" { + return "" + } + northInterfaces, err := networkv1.ParseNorthInterfacesAnnotation(northInterfacesAnnotation) + if err != nil { + return "" + } + for _, northInterface := range northInterfaces { + if northInterface.Network == network { + return northInterface.IpAddress + } + } + return "" } type cloudNetworkProvider interface { NetworkURL() string SubnetworkURL() string + NetworkProjectID() string + Region() string } // NetworkInfo contains the information about the network the LB resources should be created in. type NetworkInfo struct { - // IsNonDefault indicates if the network is not the default one. - IsNonDefault bool + // IsDefault indicates if the network is the default one. + IsDefault bool // K8sNetwork is the network name of the Network resource in the cluster. // This name should be used when referring to k8s API network. K8sNetwork string @@ -49,3 +153,11 @@ type NetworkInfo struct { // SubnetworkURL is the GCE subnetwork URL (to be used in GCE LB resources). SubnetworkURL string } + +// IsNodeConnected checks if the node is connected to the given network. +// All nodes are connected to the default network. +// For non default networks the result is based on the data from +// the 'networking.gke.io/north-interfaces' node annotation. +func (ni *NetworkInfo) IsNodeConnected(node *apiv1.Node) bool { + return ni.IsDefault || GetNodeIPForNetwork(node, ni.K8sNetwork) != "" +} diff --git a/pkg/network/network_test.go b/pkg/network/network_test.go new file mode 100644 index 0000000000..ac488abb1f --- /dev/null +++ b/pkg/network/network_test.go @@ -0,0 +1,468 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 network + +import ( + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "k8s.io/klog/v2" + + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + networkv1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1" + gkenetworkparamsetv1alpha1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1alpha1" + netfake "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake" + informernetwork "k8s.io/cloud-provider-gcp/crd/client/network/informers/externalversions/network/v1" + informergkenetworkparamset "k8s.io/cloud-provider-gcp/crd/client/network/informers/externalversions/network/v1alpha1" + "k8s.io/ingress-gce/pkg/utils" +) + +func TestServiceNetwork(t *testing.T) { + + namespace := "test" + + serviceWithSecondaryNet := &apiv1.Service{ + ObjectMeta: metav1.ObjectMeta{Name: "testService"}, + Spec: apiv1.ServiceSpec{ + Selector: map[string]string{ + networkSelector: "secondary-network", + }, + }, + } + cloud := fakeCloud{} + + cases := []struct { + desc string + network *networkv1.Network + gkeNetworkParamSet *gkenetworkparamsetv1alpha1.GKENetworkParamSet + service *apiv1.Service + want *NetworkInfo + wantErr string + }{ + { + desc: "valid setup", + network: testNetwork("secondary-network", "secondary-network-params"), + gkeNetworkParamSet: testGKENetworkParamSet("secondary-network-params", "secondary-vpc", "secondary-subnet"), + service: serviceWithSecondaryNet, + want: &NetworkInfo{ + IsDefault: false, + K8sNetwork: "secondary-network", + NetworkURL: "https://www.googleapis.com/compute/v1/projects/test-project/global/networks/secondary-vpc", + SubnetworkURL: "https://www.googleapis.com/compute/v1/projects/test-project/regions/test-region/subnetworks/secondary-subnet", + }, + }, + { + desc: "service without network selector", + service: &apiv1.Service{ + ObjectMeta: metav1.ObjectMeta{Name: "testService"}, + Spec: apiv1.ServiceSpec{ + Selector: map[string]string{ + "app": "someapp", + }, + }, + }, + want: defaultNetwork(cloud), + }, + { + desc: "service with empty network selector", + service: &apiv1.Service{ + ObjectMeta: metav1.ObjectMeta{Name: "testService"}, + Spec: apiv1.ServiceSpec{ + Selector: map[string]string{ + networkSelector: "", + }, + }, + }, + want: defaultNetwork(cloud), + }, + { + desc: "service with network selector for the default network", + service: &apiv1.Service{ + ObjectMeta: metav1.ObjectMeta{Name: "testService"}, + Spec: apiv1.ServiceSpec{ + Selector: map[string]string{ + networkSelector: networkv1.DefaultPodNetworkName, + }, + }, + }, + want: defaultNetwork(cloud), + }, + { + desc: "network not defined", + service: serviceWithSecondaryNet, + wantErr: "network secondary-network does not exist", + }, + { + desc: "network paramsRef for non GKENetworkParamSet", + network: &networkv1.Network{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secondary-network", + }, + Spec: networkv1.NetworkSpec{ + Type: "L3", + ParametersRef: &networkv1.NetworkParametersReference{ + Group: networkingGKEGroup, + Kind: "UnsupportedNetworkParams", + Name: "secondary-network-params", + }, + }, + }, + gkeNetworkParamSet: testGKENetworkParamSet("secondary-network-params", "secondary-vpc", "secondary-subnet"), + service: serviceWithSecondaryNet, + wantErr: "network.Spec.ParametersRef does not refer a GKENetworkParamSet resource", + }, + { + desc: "network paramsRef for GKENetworkParamSet with namespace", + network: &networkv1.Network{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secondary-network", + }, + Spec: networkv1.NetworkSpec{ + Type: "L3", + ParametersRef: &networkv1.NetworkParametersReference{ + Group: networkingGKEGroup, + Kind: "GKENetworkParamSet", + Name: "secondary-network-params", + Namespace: &namespace, + }, + }, + }, + gkeNetworkParamSet: testGKENetworkParamSet("secondary-network-params", "secondary-vpc", "secondary-subnet"), + service: serviceWithSecondaryNet, + wantErr: "network.Spec.ParametersRef.namespace must not be set for GKENetworkParamSet reference as it is a cluster scope resource", + }, + { + desc: "missing GKENetworkParamSet", + network: testNetwork("secondary-network", "secondary-network-params"), + gkeNetworkParamSet: nil, + service: serviceWithSecondaryNet, + wantErr: "GKENetworkParamSet secondary-network-params was not found", + }, + } + + for _, tc := range cases { + + t.Run(tc.desc, func(t *testing.T) { + networkClient := netfake.NewSimpleClientset() + + networkInformer := informernetwork.NewNetworkInformer(networkClient, time.Minute*10, utils.NewNamespaceIndexer()) + gkeNetworkParamSetInformer := informergkenetworkparamset.NewGKENetworkParamSetInformer(networkClient, time.Minute*10, utils.NewNamespaceIndexer()) + + networkIndexer := networkInformer.GetIndexer() + gkeNetworkParamSetIndexer := gkeNetworkParamSetInformer.GetIndexer() + + if tc.network != nil { + networkIndexer.Add(tc.network) + } + if tc.gkeNetworkParamSet != nil { + gkeNetworkParamSetIndexer.Add(tc.gkeNetworkParamSet) + } + + network, err := ServiceNetwork(tc.service, networkIndexer, gkeNetworkParamSetIndexer, fakeCloud{}, klog.Background()) + if err != nil { + if tc.wantErr == "" { + t.Fatalf("determining network info returned an error, err=%v", err) + } + if !strings.Contains(err.Error(), tc.wantErr) { + t.Fatalf("expected error containing message %q but got error %v", tc.wantErr, err) + } + } + if err == nil && tc.wantErr != "" { + t.Fatalf("expected error containing message %q but got no error", tc.wantErr) + } + diff := cmp.Diff(tc.want, network) + if diff != "" { + t.Errorf("Expected NetworkInfo ranges: %v, got ranges %v, diff: %s", tc.want, network, diff) + } + }) + } +} + +type fakeCloud struct { +} + +func (f fakeCloud) NetworkProjectID() string { + return "test-project" +} + +func (f fakeCloud) Region() string { + return "test-region" +} + +func (f fakeCloud) NetworkURL() string { + return "https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default" +} + +func (f fakeCloud) SubnetworkURL() string { + return "https://www.googleapis.com/compute/v1/projects/test-region/regions/test-region/subnetworks/secondary-subnet" +} +func testNetwork(name, gkeNetworkParamSetName string) *networkv1.Network { + return &networkv1.Network{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: networkv1.NetworkSpec{ + Type: "L3", + ParametersRef: &networkv1.NetworkParametersReference{ + Group: networkingGKEGroup, + Kind: "GKENetworkParamSet", + Name: gkeNetworkParamSetName, + }, + }, + } +} + +func TestRefersGKENetworkParamSet(t *testing.T) { + cases := []struct { + desc string + ref *networkv1.NetworkParametersReference + want bool + }{ + { + desc: "valid", + ref: &networkv1.NetworkParametersReference{ + Group: networkingGKEGroup, + Kind: "GKENetworkParamSet", + Name: "test-params", + }, + want: true, + }, + { + desc: "valid case insensitive kind", + ref: &networkv1.NetworkParametersReference{ + Group: networkingGKEGroup, + Kind: "gKeNeTwOrkParamSet", + Name: "test-params", + }, + want: true, + }, + { + desc: "nil ref", + ref: nil, + want: false, + }, + { + desc: "invalid group", + ref: &networkv1.NetworkParametersReference{ + Group: "somethingelse.k8s.io", + Kind: "GKENetworkParamSet", + Name: "test-params", + }, + want: false, + }, + { + desc: "invalid kind", + ref: &networkv1.NetworkParametersReference{ + Group: networkingGKEGroup, + Kind: "OtherParamSet", + Name: "test-params", + }, + want: false, + }, + { + desc: "empty name", + ref: &networkv1.NetworkParametersReference{ + Group: networkingGKEGroup, + Kind: "GKENetworkParamSet", + Name: "", + }, + want: false, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + got := refersGKENetworkParamSet(tc.ref) + if tc.want != got { + t.Errorf("refersGKENetworkParamSet(%+v) wanted %v but got %v", tc.ref, tc.want, got) + } + }) + } +} + +func TestNodeIPForNetwork(t *testing.T) { + cases := []struct { + desc string + node *apiv1.Node + network string + want string + }{ + { + desc: "no annotation", + network: "test-network", + node: &apiv1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "test-node", Annotations: map[string]string{}}, + }, + want: "", + }, + { + desc: "annotation that has the network", + network: "test-network", + node: &apiv1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "test-node", Annotations: map[string]string{ + networkv1.NorthInterfacesAnnotationKey: northInterfacesAnnotation(t, networkv1.NorthInterfacesAnnotation{ + { + Network: "another-network", + IpAddress: "10.0.0.1", + }, + { + Network: "test-network", + IpAddress: "192.168.0.1", + }, + }), + }}, + }, + want: "192.168.0.1", + }, + { + desc: "annotation that does not have the network", + network: "test-network", + node: &apiv1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "test-node", Annotations: map[string]string{ + networkv1.NorthInterfacesAnnotationKey: northInterfacesAnnotation(t, networkv1.NorthInterfacesAnnotation{ + { + Network: "another-network", + IpAddress: "10.0.0.1", + }, + { + Network: "other-network", + IpAddress: "192.168.0.1", + }, + }), + }}, + }, + want: "", + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + got := GetNodeIPForNetwork(tc.node, tc.network) + if tc.want != got { + t.Errorf("GetNodeIPForNetwork(%+v, %q) wanted %v but got %v", tc.node, tc.network, tc.want, got) + } + }) + } +} + +func TestIsConnectedToNetwork(t *testing.T) { + cases := []struct { + desc string + node *apiv1.Node + networkInfo NetworkInfo + want bool + }{ + { + desc: "no annotation", + networkInfo: NetworkInfo{ + IsDefault: false, + K8sNetwork: "test-network", + }, + node: &apiv1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "test-node", Annotations: map[string]string{}}, + }, + want: false, + }, + { + desc: "always connected to the default network", + networkInfo: NetworkInfo{ + IsDefault: true, + K8sNetwork: "default", + }, + node: &apiv1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "test-node", Annotations: map[string]string{}}, + }, + want: true, + }, + { + desc: "annotation that has the network", + networkInfo: NetworkInfo{ + IsDefault: false, + K8sNetwork: "test-network", + }, + node: &apiv1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "test-node", Annotations: map[string]string{ + networkv1.NorthInterfacesAnnotationKey: northInterfacesAnnotation(t, networkv1.NorthInterfacesAnnotation{ + { + Network: "default", + IpAddress: "10.0.0.1", + }, + { + Network: "test-network", + IpAddress: "192.168.0.1", + }, + }), + }}, + }, + want: true, + }, + { + desc: "annotation that does not have the network", + networkInfo: NetworkInfo{ + IsDefault: false, + K8sNetwork: "test-network", + }, + node: &apiv1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "test-node", Annotations: map[string]string{ + networkv1.NorthInterfacesAnnotationKey: northInterfacesAnnotation(t, networkv1.NorthInterfacesAnnotation{ + { + Network: "default", + IpAddress: "10.0.0.1", + }, + { + Network: "other-network", + IpAddress: "192.168.0.1", + }, + }), + }}, + }, + want: false, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + got := tc.networkInfo.IsNodeConnected(tc.node) + if tc.want != got { + t.Errorf("IsConnectedToNetwork(%+v, %q) wanted %v but got %v", tc.node, tc.networkInfo.K8sNetwork, tc.want, got) + } + }) + } +} + +func northInterfacesAnnotation(t *testing.T, annotation networkv1.NorthInterfacesAnnotation) string { + annotationString, err := networkv1.MarshalNorthInterfacesAnnotation(annotation) + if err != nil { + t.Errorf("failed to marshal north interfaces annotation") + } + return annotationString +} + +func testGKENetworkParamSet(name, vpc, subnet string) *gkenetworkparamsetv1alpha1.GKENetworkParamSet { + return &gkenetworkparamsetv1alpha1.GKENetworkParamSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: gkenetworkparamsetv1alpha1.GKENetworkParamSetSpec{ + VPC: vpc, + VPCSubnet: subnet, + }, + } +}