diff --git a/pkg/controller/multi-ip/pod/pod_test.go b/pkg/controller/multi-ip/pod/pod_test.go new file mode 100644 index 00000000..ba0b512e --- /dev/null +++ b/pkg/controller/multi-ip/pod/pod_test.go @@ -0,0 +1,124 @@ +package pod + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Pod Controller", func() { + ctx := context.Background() + + BeforeEach(func() { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "default"}, + Spec: corev1.PodSpec{ + NodeName: "node-az-1", + Containers: []corev1.Container{ + { + Name: "foo", + Image: "busybox", + }, + }, + }, + } + err := k8sClient.Create(ctx, pod) + Expect(err).NotTo(HaveOccurred()) + }) + AfterEach(func() { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "default"}, + } + err := k8sClient.Delete(ctx, pod) + Expect(err).NotTo(HaveOccurred()) + }) + + Context("Reconcile", func() { + It("Reconcile should succeed", func() { + n := &ReconcilePod{ + client: k8sClient, + } + _, err := n.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: "default", + Name: "pod", + }, + }) + + Expect(err).To(BeNil()) + }) + }) +}) + +var _ = Describe("NeedProcess Function", func() { + var pod *corev1.Pod + + BeforeEach(func() { + pod = &corev1.Pod{ + Spec: corev1.PodSpec{ + NodeName: "node1", + }, + } + }) + + Context("When object is not a Pod", func() { + It("returns false", func() { + obj := &corev1.Service{} + result := needProcess(obj) + Expect(result).To(BeFalse()) + }) + }) + + Context("When Pod has no NodeName", func() { + It("returns false", func() { + pod.Spec.NodeName = "" + result := needProcess(pod) + Expect(result).To(BeFalse()) + }) + }) + + Context("When Pod uses HostNetwork", func() { + It("returns false", func() { + pod.Spec.HostNetwork = true + result := needProcess(pod) + Expect(result).To(BeFalse()) + }) + }) + + Context("When Pod is ignored by Terway", func() { + It("returns false", func() { + pod.ObjectMeta.Labels = map[string]string{"k8s.aliyun.com/ignore-by-terway": "true"} + result := needProcess(pod) + Expect(result).To(BeFalse()) + }) + }) + + Context("When Pod uses ENI", func() { + It("returns false", func() { + pod.ObjectMeta.Annotations = map[string]string{"k8s.aliyun.com/pod-eni": "true"} + result := needProcess(pod) + Expect(result).To(BeFalse()) + }) + }) + + Context("When Pod sandbox not exited and has PodIP", func() { + It("returns false", func() { + pod.Status.PodIP = "192.168.1.1" + result := needProcess(pod) + Expect(result).To(BeFalse()) + }) + }) + + Context("When Pod is ready to process", func() { + It("returns true", func() { + result := needProcess(pod) + Expect(result).To(BeTrue()) + }) + }) +}) diff --git a/pkg/controller/multi-ip/pod/suite_test.go b/pkg/controller/multi-ip/pod/suite_test.go new file mode 100644 index 00000000..81710c14 --- /dev/null +++ b/pkg/controller/multi-ip/pod/suite_test.go @@ -0,0 +1,88 @@ +/* +Copyright 2024. + +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 pod + +import ( + "fmt" + "path/filepath" + "runtime" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + networkv1beta1 "github.com/AliyunContainerService/terway/pkg/apis/network.alibabacloud.com/v1beta1" + //+kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "apis", "crds")}, + ErrorIfCRDPathMissing: true, + + // The BinaryAssetsDirectory is only required if you want to run the tests directly + // without call the makefile target test. If not informed it will look for the + // default path defined in controller-runtime which is /usr/local/kubebuilder/. + // Note that you must have the required binaries setup under the bin directory to perform + // the tests directly. When we run make test it will be setup and used automatically. + BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", + fmt.Sprintf("1.29.0-%s-%s", runtime.GOOS, runtime.GOARCH)), + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = networkv1beta1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/pkg/controller/webhook/validate.go b/pkg/controller/webhook/validate.go index 40001d4c..e152ee16 100644 --- a/pkg/controller/webhook/validate.go +++ b/pkg/controller/webhook/validate.go @@ -47,12 +47,10 @@ func ValidateHook() *webhook.Admission { return admission.Denied("security group can not more than 5") } - if podNetworking.Spec.AllocationType.ReleaseStrategy == v1beta1.IPAllocTypeFixed { - if podNetworking.Spec.AllocationType.ReleaseStrategy == v1beta1.ReleaseStrategyTTL { - _, err = time.ParseDuration(podNetworking.Spec.AllocationType.ReleaseAfter) - if err != nil { - return webhook.Denied(fmt.Sprintf("invalid releaseAfter %s", podNetworking.Spec.AllocationType.ReleaseAfter)) - } + if podNetworking.Spec.AllocationType.ReleaseStrategy == v1beta1.ReleaseStrategyTTL { + _, err = time.ParseDuration(podNetworking.Spec.AllocationType.ReleaseAfter) + if err != nil { + return webhook.Denied(fmt.Sprintf("invalid releaseAfter %s", podNetworking.Spec.AllocationType.ReleaseAfter)) } } return webhook.Allowed("checked") diff --git a/pkg/controller/webhook/validate_test.go b/pkg/controller/webhook/validate_test.go new file mode 100644 index 00000000..f6a4c0f1 --- /dev/null +++ b/pkg/controller/webhook/validate_test.go @@ -0,0 +1,198 @@ +package webhook + +import ( + "context" + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/api/admission/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/AliyunContainerService/terway/pkg/apis/network.alibabacloud.com/v1beta1" +) + +func TestValidateHookAllowsWhenKindIsNotPodNetworking(t *testing.T) { + req := webhook.AdmissionRequest{ + AdmissionRequest: v1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: "", + Version: "", + Kind: "Foo", + }, + }, + } + resp := ValidateHook().Handle(context.Background(), req) + assert.True(t, resp.Allowed) + assert.Equal(t, "not care", resp.Result.Message) +} + +func TestValidateHookDeniesWhenPodSelectorAndNamespaceSelectorAreNil(t *testing.T) { + podNetworking := &v1beta1.PodNetworking{ + Spec: v1beta1.PodNetworkingSpec{ + Selector: v1beta1.Selector{}, + }, + } + raw, _ := json.Marshal(podNetworking) + req := webhook.AdmissionRequest{ + AdmissionRequest: v1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: "", + Version: "", + Kind: "PodNetworking", + }, + Object: runtime.RawExtension{ + Raw: raw, + }, + }, + } + resp := ValidateHook().Handle(context.Background(), req) + assert.False(t, resp.Allowed) + assert.Equal(t, "neither the PodSelector nor the NamespaceSelector is set", resp.Result.Message) +} + +func TestValidateHookDeniesWhenVSwitchOptionsIsEmpty(t *testing.T) { + podNetworking := &v1beta1.PodNetworking{ + Spec: v1beta1.PodNetworkingSpec{ + Selector: v1beta1.Selector{ + PodSelector: &metav1.LabelSelector{}, + }, + }, + } + raw, _ := json.Marshal(podNetworking) + req := webhook.AdmissionRequest{ + AdmissionRequest: v1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: "", + Version: "", + Kind: "PodNetworking", + }, + Object: runtime.RawExtension{ + Raw: raw, + }, + }, + } + resp := ValidateHook().Handle(context.Background(), req) + assert.False(t, resp.Allowed) + assert.Equal(t, "vSwitchOptions is not set", resp.Result.Message) +} + +func TestValidateHookDeniesWhenSecurityGroupIDsIsEmpty(t *testing.T) { + podNetworking := &v1beta1.PodNetworking{ + Spec: v1beta1.PodNetworkingSpec{ + Selector: v1beta1.Selector{ + PodSelector: &metav1.LabelSelector{}, + }, + VSwitchOptions: []string{"vsw-123"}, + }, + } + raw, _ := json.Marshal(podNetworking) + req := webhook.AdmissionRequest{ + AdmissionRequest: v1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: "", + Version: "", + Kind: "PodNetworking", + }, + Object: runtime.RawExtension{ + Raw: raw, + }, + }, + } + resp := ValidateHook().Handle(context.Background(), req) + assert.False(t, resp.Allowed) + assert.Equal(t, "security group is not set", resp.Result.Message) +} + +func TestValidateHookDeniesWhenSecurityGroupIDsExceedsLimit(t *testing.T) { + podNetworking := &v1beta1.PodNetworking{ + Spec: v1beta1.PodNetworkingSpec{ + Selector: v1beta1.Selector{ + PodSelector: &metav1.LabelSelector{}, + }, + VSwitchOptions: []string{"vsw-123"}, + SecurityGroupIDs: []string{"sg-1", "sg-2", "sg-3", "sg-4", "sg-5", "sg-6"}, + }, + } + raw, _ := json.Marshal(podNetworking) + req := webhook.AdmissionRequest{ + AdmissionRequest: v1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: "", + Version: "", + Kind: "PodNetworking", + }, + Object: runtime.RawExtension{ + Raw: raw, + }, + }, + } + resp := ValidateHook().Handle(context.Background(), req) + assert.False(t, resp.Allowed) + assert.Equal(t, "security group can not more than 5", resp.Result.Message) +} + +func TestValidateHookDeniesWhenReleaseAfterIsInvalid(t *testing.T) { + podNetworking := &v1beta1.PodNetworking{ + Spec: v1beta1.PodNetworkingSpec{ + Selector: v1beta1.Selector{ + PodSelector: &metav1.LabelSelector{}, + }, + VSwitchOptions: []string{"vsw-123"}, + SecurityGroupIDs: []string{"sg-1"}, + AllocationType: v1beta1.AllocationType{ + ReleaseStrategy: v1beta1.ReleaseStrategyTTL, + ReleaseAfter: "invalid-duration", + }, + }, + } + raw, _ := json.Marshal(podNetworking) + req := webhook.AdmissionRequest{ + AdmissionRequest: v1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: "", + Version: "", + Kind: "PodNetworking", + }, + Object: runtime.RawExtension{ + Raw: raw, + }, + }, + } + resp := ValidateHook().Handle(context.Background(), req) + assert.False(t, resp.Allowed) +} + +func TestValidateHookAllowsWhenAllConditionsAreMet(t *testing.T) { + podNetworking := &v1beta1.PodNetworking{ + Spec: v1beta1.PodNetworkingSpec{ + Selector: v1beta1.Selector{ + PodSelector: &metav1.LabelSelector{}, + }, + VSwitchOptions: []string{"vsw-123"}, + SecurityGroupIDs: []string{"sg-1"}, + AllocationType: v1beta1.AllocationType{ + ReleaseStrategy: v1beta1.ReleaseStrategyTTL, + ReleaseAfter: "1h", + }, + }, + } + raw, _ := json.Marshal(podNetworking) + req := webhook.AdmissionRequest{ + AdmissionRequest: v1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: "", + Version: "", + Kind: "PodNetworking", + }, + Object: runtime.RawExtension{ + Raw: raw, + }, + }, + } + resp := ValidateHook().Handle(context.Background(), req) + assert.True(t, resp.Allowed) + assert.Equal(t, "checked", resp.Result.Message) +} diff --git a/pkg/eni/crdv2.go b/pkg/eni/crdv2.go index b53a9c44..bf208027 100644 --- a/pkg/eni/crdv2.go +++ b/pkg/eni/crdv2.go @@ -45,7 +45,7 @@ func NewCRDV2(nodeName string) *CRDV2 { Cache: cache.Options{ HTTPClient: nil, Scheme: nil, - Mapper: nil, + Mapper: types.NewRESTMapper(), SyncPeriod: nil, Namespaces: nil, DefaultLabelSelector: nil, diff --git a/pkg/eni/crdv2_test.go b/pkg/eni/crdv2_test.go new file mode 100644 index 00000000..66dbdde3 --- /dev/null +++ b/pkg/eni/crdv2_test.go @@ -0,0 +1,168 @@ +package eni + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + networkv1beta1 "github.com/AliyunContainerService/terway/pkg/apis/network.alibabacloud.com/v1beta1" + "github.com/AliyunContainerService/terway/pkg/backoff" + "github.com/AliyunContainerService/terway/types" +) + +func TestGetTrunkENIReturnsErrorWhenNodeNotInitialized(t *testing.T) { + backoff.Backoff(backoff.WaitPodENIStatus) + backoff.OverrideBackoff(map[string]wait.Backoff{ + backoff.WaitPodENIStatus: { + Duration: 0, + Factor: 0, + Jitter: 0, + Steps: 1, + Cap: 0, + }, + }) + + crd := &CRDV2{ + nodeName: "node1", + } + ctx := context.Background() + + fakeClient := fake.NewClientBuilder().WithScheme(types.Scheme).WithObjects(&networkv1beta1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "node1"}, + Spec: networkv1beta1.NodeSpec{ENISpec: nil}, + }).Build() + crd.client = fakeClient + + eni, err := crd.getTrunkENI(ctx) + assert.Nil(t, eni) + assert.Error(t, err) + assert.Contains(t, err.Error(), "has not been initialized") +} + +func TestGetTrunkENIReturnsErrorWhenTrunkNotEnabled(t *testing.T) { + backoff.Backoff(backoff.WaitPodENIStatus) + backoff.OverrideBackoff(map[string]wait.Backoff{ + backoff.WaitPodENIStatus: { + Duration: 0, + Factor: 0, + Jitter: 0, + Steps: 1, + Cap: 0, + }, + }) + + crd := &CRDV2{ + nodeName: "node1", + } + ctx := context.Background() + + fakeClient := fake.NewClientBuilder().WithScheme(types.Scheme).WithObjects(&networkv1beta1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "node1"}, + Spec: networkv1beta1.NodeSpec{ENISpec: &networkv1beta1.ENISpec{EnableTrunk: false}}, + }).Build() + crd.client = fakeClient + + eni, err := crd.getTrunkENI(ctx) + assert.Nil(t, eni) + assert.NoError(t, err) +} + +func TestGetTrunkENIReturnsErrorWhenTrunkIDNotFound(t *testing.T) { + backoff.Backoff(backoff.WaitPodENIStatus) + backoff.OverrideBackoff(map[string]wait.Backoff{ + backoff.WaitPodENIStatus: { + Duration: 0, + Factor: 0, + Jitter: 0, + Steps: 1, + Cap: 0, + }, + }) + + crd := &CRDV2{ + nodeName: "node1", + } + ctx := context.Background() + + // Create a fake client with the initial state + fakeClient := fake.NewClientBuilder().WithScheme(types.Scheme).WithObjects( + &networkv1beta1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "node1"}, + Spec: networkv1beta1.NodeSpec{ENISpec: &networkv1beta1.ENISpec{EnableTrunk: true}}, + }, + &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "node1", Annotations: map[string]string{}}, + }, + ).Build() + crd.client = fakeClient + + eni, err := crd.getTrunkENI(ctx) + assert.Nil(t, eni) + assert.Error(t, err) + assert.Contains(t, err.Error(), "has not been initialized") +} + +func TestGetTrunkENIReturnsValidENI(t *testing.T) { + backoff.Backoff(backoff.WaitPodENIStatus) + backoff.OverrideBackoff(map[string]wait.Backoff{ + backoff.WaitPodENIStatus: { + Duration: 0, + Factor: 0, + Jitter: 0, + Steps: 1, + Cap: 0, + }, + }) + + crd := &CRDV2{ + nodeName: "node1", + } + ctx := context.Background() + + // Create a fake client with the initial state + fakeClient := fake.NewClientBuilder().WithScheme(types.Scheme).WithObjects( + &networkv1beta1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "node1"}, + Spec: networkv1beta1.NodeSpec{ + ENISpec: &networkv1beta1.ENISpec{ + EnableTrunk: true, + EnableIPv4: true, + EnableIPv6: true, + }}, + Status: networkv1beta1.NodeStatus{ + NetworkInterfaces: map[string]*networkv1beta1.NetworkInterface{ + "eni-1": { + ID: "eni-1", + MacAddress: "00:00:00:00:00:01", + SecurityGroupIDs: []string{"sg-1"}, + IPv4CIDR: "192.168.1.0/24", + IPv6CIDR: "fd00::/64", + PrimaryIPAddress: "192.168.1.10", + NetworkInterfaceType: networkv1beta1.ENITypeTrunk, + NetworkInterfaceTrafficMode: networkv1beta1.NetworkInterfaceTrafficModeStandard, + }, + }, + }, + }, + &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "node1", Annotations: map[string]string{types.TrunkOn: "eni-1"}}, + }, + ).Build() + crd.client = fakeClient + + eni, err := crd.getTrunkENI(ctx) + assert.NotNil(t, eni) + assert.NoError(t, err) + assert.Equal(t, "eni-1", eni.ID) + assert.Equal(t, "00:00:00:00:00:01", eni.MAC) + assert.Equal(t, []string{"sg-1"}, eni.SecurityGroupIDs) + assert.Equal(t, "192.168.1.10", eni.PrimaryIP.IPv4.String()) + assert.Equal(t, "192.168.1.253", eni.GatewayIP.IPv4.String()) + assert.Equal(t, "192.168.1.0/24", eni.VSwitchCIDR.IPv4.String()) + assert.Equal(t, "fd00::/64", eni.VSwitchCIDR.IPv6.String()) +} diff --git a/pkg/link/interface.go b/pkg/link/interface.go deleted file mode 100644 index cba0d22d..00000000 --- a/pkg/link/interface.go +++ /dev/null @@ -1,16 +0,0 @@ -package link - -import "net" - -// ipNetEqual returns true iff both IPNet are equal -func ipNetEqual(ipn1 *net.IPNet, ipn2 *net.IPNet) bool { - if ipn1 == ipn2 { - return true - } - if ipn1 == nil || ipn2 == nil { - return false - } - m1, _ := ipn1.Mask.Size() - m2, _ := ipn2.Mask.Size() - return m1 == m2 && ipn1.IP.Equal(ipn2.IP) -} diff --git a/pkg/link/interface_linux.go b/pkg/link/interface_linux.go index 6817ca36..5e3c7d49 100644 --- a/pkg/link/interface_linux.go +++ b/pkg/link/interface_linux.go @@ -4,12 +4,9 @@ package link import ( "fmt" - "net" - "os" "github.com/pkg/errors" "github.com/vishvananda/netlink" - "k8s.io/klog/v2" ) // GetDeviceNumber get interface device number by mac address @@ -30,53 +27,3 @@ func GetDeviceNumber(mac string) (int32, error) { } return 0, errors.Wrapf(ErrNotFound, "can't found dev by mac %s", mac) } - -// DeleteIPRulesByIP delete all ip rule related to the addr -func DeleteIPRulesByIP(addr *net.IPNet) error { - family := netlink.FAMILY_V4 - if addr.IP.To4() == nil { - family = netlink.FAMILY_V6 - } - rules, err := netlink.RuleList(family) - if err != nil { - return err - } - for _, r := range rules { - if ipNetEqual(addr, r.Src) || ipNetEqual(addr, r.Dst) { - klog.Infof("del ip rule %s", r.String()) - err := netlink.RuleDel(&r) - if err == nil { - continue - } - if os.IsNotExist(err) { - // keep the old behave - r.IifName = "" - _ = netlink.RuleDel(&r) - } - return err - } - } - return nil -} - -// DeleteRouteByIP delete all route related to the addr -func DeleteRouteByIP(addr *net.IPNet) error { - family := netlink.FAMILY_V4 - if addr.IP.To4() == nil { - family = netlink.FAMILY_V6 - } - routes, err := netlink.RouteList(nil, family) - if err != nil { - return err - } - for _, r := range routes { - if r.Dst != nil && r.Dst.IP.Equal(addr.IP) { - klog.Infof("del route %s", r.String()) - err := netlink.RouteDel(&r) - if err != nil { - return err - } - } - } - return nil -} diff --git a/pkg/link/interface_unsupport.go b/pkg/link/interface_unsupport.go index 95ad6629..42cf11b2 100644 --- a/pkg/link/interface_unsupport.go +++ b/pkg/link/interface_unsupport.go @@ -3,26 +3,7 @@ package link -import ( - "net" -) - // GetDeviceNumber get interface device number by mac address func GetDeviceNumber(mac string) (int32, error) { return 0, ErrUnsupported } - -// GetDeviceName get interface device name by mac address -func GetDeviceName(mac string) (string, error) { - return "", ErrUnsupported -} - -// DeleteIPRulesByIP delete all ip rule related to the addr -func DeleteIPRulesByIP(addr *net.IPNet) error { - return ErrUnsupported -} - -// DeleteRouteByIP delete all route related to the addr -func DeleteRouteByIP(addr *net.IPNet) error { - return ErrUnsupported -} diff --git a/pkg/link/interface_windows.go b/pkg/link/interface_windows.go index e27f2a0a..3d9e89bd 100644 --- a/pkg/link/interface_windows.go +++ b/pkg/link/interface_windows.go @@ -1,12 +1,9 @@ package link import ( - "net" - "github.com/pkg/errors" "github.com/AliyunContainerService/terway/pkg/windows/iface" - "github.com/AliyunContainerService/terway/pkg/windows/ipforward" ) // GetDeviceNumber get interface device number by mac address @@ -17,49 +14,3 @@ func GetDeviceNumber(mac string) (int32, error) { } return int32(macIface.Index), nil } - -// GetDeviceName get interface device name by mac address -func GetDeviceName(mac string) (string, error) { - macIface, err := iface.GetInterfaceByMAC(mac, true) - if err != nil { - return "", errors.Wrapf(err, "failed to get interface by MAC %s", mac) - } - return macIface.Name, nil -} - -// DeleteIPRulesByIP delete all ip rule related to the addr -func DeleteIPRulesByIP(addr *net.IPNet) error { - var routes, err = ipforward.GetNetRoutes() - if err != nil { - return errors.Wrapf(err, "failed to get all routes") - } - for _, r := range routes { - if ipNetEqual(addr, r.DestinationSubnet) || - ipNetEqual(addr, &net.IPNet{IP: r.NextHop, Mask: net.IPv4Mask(255, 255, 255, 255)}) { - err = ipforward.RemoveNetRoutesForInterface(r.LinkIndex, r.DestinationSubnet, &r.NextHop) - if err != nil { - return errors.Wrapf(err, "failed to remove ip route %s(%s) on interface %d", - r.DestinationSubnet, r.NextHop, r.LinkIndex) - } - } - } - return nil -} - -// DeleteRouteByIP delete all route related to the addr -func DeleteRouteByIP(addr *net.IPNet) error { - var routes, err = ipforward.GetNetRoutes() - if err != nil { - return errors.Wrapf(err, "failed to get all routes") - } - for _, r := range routes { - if ipNetEqual(addr, r.DestinationSubnet) { - err = ipforward.RemoveNetRoutesForInterface(r.LinkIndex, r.DestinationSubnet, &r.NextHop) - if err != nil { - return errors.Wrapf(err, "failed to remove route %s(%s) on interface %d", - r.DestinationSubnet, r.NextHop, r.LinkIndex) - } - } - } - return nil -} diff --git a/types/help_test.go b/types/help_test.go new file mode 100644 index 00000000..24d112b7 --- /dev/null +++ b/types/help_test.go @@ -0,0 +1,65 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/AliyunContainerService/terway/rpc" +) + +func TestBuildIPNetReturnsEmptyWhenIPAndSubnetAreNil(t *testing.T) { + ipnet, err := BuildIPNet(nil, nil) + assert.NoError(t, err) + assert.NotNil(t, ipnet) + assert.Nil(t, ipnet.IPv4) + assert.Nil(t, ipnet.IPv6) +} + +func TestBuildIPNetReturnsErrorWhenIPv4IsInvalid(t *testing.T) { + ip := &rpc.IPSet{IPv4: "invalid-ip"} + subnet := &rpc.IPSet{IPv4: "192.168.0.0/24"} + _, err := BuildIPNet(ip, subnet) + assert.Error(t, err) +} + +func TestBuildIPNetReturnsErrorWhenIPv6IsInvalid(t *testing.T) { + ip := &rpc.IPSet{IPv6: "invalid-ip"} + subnet := &rpc.IPSet{IPv6: "fd00::/64"} + _, err := BuildIPNet(ip, subnet) + assert.Error(t, err) +} + +func TestBuildIPNetReturnsErrorWhenSubnetIPv4IsInvalid(t *testing.T) { + ip := &rpc.IPSet{IPv4: "192.168.0.1"} + subnet := &rpc.IPSet{IPv4: "invalid-subnet"} + _, err := BuildIPNet(ip, subnet) + assert.Error(t, err) +} + +func TestBuildIPNetReturnsErrorWhenSubnetIPv6IsInvalid(t *testing.T) { + ip := &rpc.IPSet{IPv6: "fd00::1"} + subnet := &rpc.IPSet{IPv6: "invalid-subnet"} + _, err := BuildIPNet(ip, subnet) + assert.Error(t, err) +} + +func TestBuildIPNetReturnsCorrectIPNetForValidIPv4(t *testing.T) { + ip := &rpc.IPSet{IPv4: "192.168.0.1"} + subnet := &rpc.IPSet{IPv4: "192.168.0.0/24"} + ipnet, err := BuildIPNet(ip, subnet) + assert.NoError(t, err) + assert.NotNil(t, ipnet.IPv4) + assert.Equal(t, "192.168.0.1/24", ipnet.IPv4.String()) + assert.Nil(t, ipnet.IPv6) +} + +func TestBuildIPNetReturnsCorrectIPNetForValidIPv6(t *testing.T) { + ip := &rpc.IPSet{IPv6: "fd00::1"} + subnet := &rpc.IPSet{IPv6: "fd00::/64"} + ipnet, err := BuildIPNet(ip, subnet) + assert.NoError(t, err) + assert.NotNil(t, ipnet.IPv6) + assert.Equal(t, "fd00::1/64", ipnet.IPv6.String()) + assert.Nil(t, ipnet.IPv4) +} diff --git a/types/scheme.go b/types/scheme.go index 83b37637..f1ef8fda 100644 --- a/types/scheme.go +++ b/types/scheme.go @@ -81,7 +81,7 @@ var rootScopedKinds = map[schema.GroupKind]bool{ {Group: "metrics.k8s.io", Kind: "NodeMetrics"}: true, - {Group: "wardle.example.com", Kind: "Fischer"}: true, - {Group: "network.alibabacloud.com", Kind: "PodNetworking"}: true, + + {Group: "network.alibabacloud.com", Kind: "Node"}: true, }