mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-30 05:43:58 +00:00
Merge pull request #46580 from Huawei-PaaS/kube-proxy-ipvs-pr
Automatic merge from submit-queue (batch tested with PRs 51377, 46580, 50998, 51466, 49749) Implement IPVS-based in-cluster service load balancing **What this PR does / why we need it**: Implement IPVS-based in-cluster service load balancing. It can provide some performance enhancement and some other benefits to kube-proxy while comparing iptables and userspace mode. Besides, it also support more sophisticated load balancing algorithms than iptables (least conns, weighted, hash and so on). **Which issue this PR fixes** #17470 #44063 **Special notes for your reviewer**: * Since the PR is a bit large, I splitted it and move the commits related to ipvs util pkg to PR #48994. Hopefully can make it easier to review. @thockin @quinton-hoole @kevin-wangzefeng @deepak-vij @haibinxie @dhilipkumars @fisherxu **Release note**: ```release-note Implement IPVS-based in-cluster service load balancing ```
This commit is contained in:
@@ -52,6 +52,19 @@ type KubeProxyIPTablesConfiguration struct {
|
||||
MinSyncPeriod metav1.Duration
|
||||
}
|
||||
|
||||
// KubeProxyIPVSConfiguration contains ipvs-related configuration
|
||||
// details for the Kubernetes proxy server.
|
||||
type KubeProxyIPVSConfiguration struct {
|
||||
// syncPeriod is the period that ipvs rules are refreshed (e.g. '5s', '1m',
|
||||
// '2h22m'). Must be greater than 0.
|
||||
SyncPeriod metav1.Duration
|
||||
// minSyncPeriod is the minimum period that ipvs rules are refreshed (e.g. '5s', '1m',
|
||||
// '2h22m').
|
||||
MinSyncPeriod metav1.Duration
|
||||
// ipvs scheduler
|
||||
Scheduler string
|
||||
}
|
||||
|
||||
// KubeProxyConntrackConfiguration contains conntrack settings for
|
||||
// the Kubernetes proxy server.
|
||||
type KubeProxyConntrackConfiguration struct {
|
||||
@@ -112,6 +125,8 @@ type KubeProxyConfiguration struct {
|
||||
ClientConnection ClientConnectionConfiguration
|
||||
// iptables contains iptables-related configuration options.
|
||||
IPTables KubeProxyIPTablesConfiguration
|
||||
// ipvs contains ipvs-related configuration options.
|
||||
IPVS KubeProxyIPVSConfiguration
|
||||
// oomScoreAdj is the oom-score-adj value for kube-proxy process. Values must be within
|
||||
// the range [-1000, 1000]
|
||||
OOMScoreAdj *int32
|
||||
|
||||
@@ -52,6 +52,19 @@ type KubeProxyIPTablesConfiguration struct {
|
||||
MinSyncPeriod metav1.Duration `json:"minSyncPeriod"`
|
||||
}
|
||||
|
||||
// KubeProxyIPVSConfiguration contains ipvs-related configuration
|
||||
// details for the Kubernetes proxy server.
|
||||
type KubeProxyIPVSConfiguration struct {
|
||||
// syncPeriod is the period that ipvs rules are refreshed (e.g. '5s', '1m',
|
||||
// '2h22m'). Must be greater than 0.
|
||||
SyncPeriod metav1.Duration `json:"syncPeriod"`
|
||||
// minSyncPeriod is the minimum period that ipvs rules are refreshed (e.g. '5s', '1m',
|
||||
// '2h22m').
|
||||
MinSyncPeriod metav1.Duration `json:"minSyncPeriod"`
|
||||
// ipvs scheduler
|
||||
Scheduler string `json:"scheduler"`
|
||||
}
|
||||
|
||||
// KubeProxyConntrackConfiguration contains conntrack settings for
|
||||
// the Kubernetes proxy server.
|
||||
type KubeProxyConntrackConfiguration struct {
|
||||
@@ -112,6 +125,8 @@ type KubeProxyConfiguration struct {
|
||||
ClientConnection ClientConnectionConfiguration `json:"clientConnection"`
|
||||
// iptables contains iptables-related configuration options.
|
||||
IPTables KubeProxyIPTablesConfiguration `json:"iptables"`
|
||||
// ipvs contains ipvs-related configuration options.
|
||||
IPVS KubeProxyIPVSConfiguration `json:"ipvs"`
|
||||
// oomScoreAdj is the oom-score-adj value for kube-proxy process. Values must be within
|
||||
// the range [-1000, 1000]
|
||||
OOMScoreAdj *int32 `json:"oomScoreAdj"`
|
||||
|
||||
@@ -44,6 +44,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
||||
Convert_componentconfig_KubeProxyConntrackConfiguration_To_v1alpha1_KubeProxyConntrackConfiguration,
|
||||
Convert_v1alpha1_KubeProxyIPTablesConfiguration_To_componentconfig_KubeProxyIPTablesConfiguration,
|
||||
Convert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration,
|
||||
Convert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration,
|
||||
Convert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration,
|
||||
Convert_v1alpha1_KubeSchedulerConfiguration_To_componentconfig_KubeSchedulerConfiguration,
|
||||
Convert_componentconfig_KubeSchedulerConfiguration_To_v1alpha1_KubeSchedulerConfiguration,
|
||||
Convert_v1alpha1_LeaderElectionConfiguration_To_componentconfig_LeaderElectionConfiguration,
|
||||
@@ -93,6 +95,9 @@ func autoConvert_v1alpha1_KubeProxyConfiguration_To_componentconfig_KubeProxyCon
|
||||
if err := Convert_v1alpha1_KubeProxyIPTablesConfiguration_To_componentconfig_KubeProxyIPTablesConfiguration(&in.IPTables, &out.IPTables, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Convert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration(&in.IPVS, &out.IPVS, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.OOMScoreAdj = (*int32)(unsafe.Pointer(in.OOMScoreAdj))
|
||||
out.Mode = componentconfig.ProxyMode(in.Mode)
|
||||
out.PortRange = in.PortRange
|
||||
@@ -124,6 +129,9 @@ func autoConvert_componentconfig_KubeProxyConfiguration_To_v1alpha1_KubeProxyCon
|
||||
if err := Convert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration(&in.IPTables, &out.IPTables, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Convert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration(&in.IPVS, &out.IPVS, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.OOMScoreAdj = (*int32)(unsafe.Pointer(in.OOMScoreAdj))
|
||||
out.Mode = ProxyMode(in.Mode)
|
||||
out.PortRange = in.PortRange
|
||||
@@ -195,6 +203,30 @@ func Convert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProx
|
||||
return autoConvert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration(in *KubeProxyIPVSConfiguration, out *componentconfig.KubeProxyIPVSConfiguration, s conversion.Scope) error {
|
||||
out.SyncPeriod = in.SyncPeriod
|
||||
out.MinSyncPeriod = in.MinSyncPeriod
|
||||
out.Scheduler = in.Scheduler
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration(in *KubeProxyIPVSConfiguration, out *componentconfig.KubeProxyIPVSConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration(in *componentconfig.KubeProxyIPVSConfiguration, out *KubeProxyIPVSConfiguration, s conversion.Scope) error {
|
||||
out.SyncPeriod = in.SyncPeriod
|
||||
out.MinSyncPeriod = in.MinSyncPeriod
|
||||
out.Scheduler = in.Scheduler
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration is an autogenerated conversion function.
|
||||
func Convert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration(in *componentconfig.KubeProxyIPVSConfiguration, out *KubeProxyIPVSConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_KubeSchedulerConfiguration_To_componentconfig_KubeSchedulerConfiguration(in *KubeSchedulerConfiguration, out *componentconfig.KubeSchedulerConfiguration, s conversion.Scope) error {
|
||||
out.Port = int32(in.Port)
|
||||
out.Address = in.Address
|
||||
|
||||
@@ -52,6 +52,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
in.(*KubeProxyIPTablesConfiguration).DeepCopyInto(out.(*KubeProxyIPTablesConfiguration))
|
||||
return nil
|
||||
}, InType: reflect.TypeOf(&KubeProxyIPTablesConfiguration{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
in.(*KubeProxyIPVSConfiguration).DeepCopyInto(out.(*KubeProxyIPVSConfiguration))
|
||||
return nil
|
||||
}, InType: reflect.TypeOf(&KubeProxyIPVSConfiguration{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
in.(*KubeSchedulerConfiguration).DeepCopyInto(out.(*KubeSchedulerConfiguration))
|
||||
return nil
|
||||
@@ -85,6 +89,7 @@ func (in *KubeProxyConfiguration) DeepCopyInto(out *KubeProxyConfiguration) {
|
||||
out.TypeMeta = in.TypeMeta
|
||||
out.ClientConnection = in.ClientConnection
|
||||
in.IPTables.DeepCopyInto(&out.IPTables)
|
||||
out.IPVS = in.IPVS
|
||||
if in.OOMScoreAdj != nil {
|
||||
in, out := &in.OOMScoreAdj, &out.OOMScoreAdj
|
||||
if *in == nil {
|
||||
@@ -164,6 +169,24 @@ func (in *KubeProxyIPTablesConfiguration) DeepCopy() *KubeProxyIPTablesConfigura
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubeProxyIPVSConfiguration) DeepCopyInto(out *KubeProxyIPVSConfiguration) {
|
||||
*out = *in
|
||||
out.SyncPeriod = in.SyncPeriod
|
||||
out.MinSyncPeriod = in.MinSyncPeriod
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeProxyIPVSConfiguration.
|
||||
func (in *KubeProxyIPVSConfiguration) DeepCopy() *KubeProxyIPVSConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(KubeProxyIPVSConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubeSchedulerConfiguration) DeepCopyInto(out *KubeSchedulerConfiguration) {
|
||||
*out = *in
|
||||
|
||||
@@ -64,6 +64,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
in.(*KubeProxyIPTablesConfiguration).DeepCopyInto(out.(*KubeProxyIPTablesConfiguration))
|
||||
return nil
|
||||
}, InType: reflect.TypeOf(&KubeProxyIPTablesConfiguration{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
in.(*KubeProxyIPVSConfiguration).DeepCopyInto(out.(*KubeProxyIPVSConfiguration))
|
||||
return nil
|
||||
}, InType: reflect.TypeOf(&KubeProxyIPVSConfiguration{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
in.(*KubeSchedulerConfiguration).DeepCopyInto(out.(*KubeSchedulerConfiguration))
|
||||
return nil
|
||||
@@ -206,6 +210,7 @@ func (in *KubeProxyConfiguration) DeepCopyInto(out *KubeProxyConfiguration) {
|
||||
out.TypeMeta = in.TypeMeta
|
||||
out.ClientConnection = in.ClientConnection
|
||||
in.IPTables.DeepCopyInto(&out.IPTables)
|
||||
out.IPVS = in.IPVS
|
||||
if in.OOMScoreAdj != nil {
|
||||
in, out := &in.OOMScoreAdj, &out.OOMScoreAdj
|
||||
if *in == nil {
|
||||
@@ -285,6 +290,24 @@ func (in *KubeProxyIPTablesConfiguration) DeepCopy() *KubeProxyIPTablesConfigura
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubeProxyIPVSConfiguration) DeepCopyInto(out *KubeProxyIPVSConfiguration) {
|
||||
*out = *in
|
||||
out.SyncPeriod = in.SyncPeriod
|
||||
out.MinSyncPeriod = in.MinSyncPeriod
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeProxyIPVSConfiguration.
|
||||
func (in *KubeProxyIPVSConfiguration) DeepCopy() *KubeProxyIPVSConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(KubeProxyIPVSConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubeSchedulerConfiguration) DeepCopyInto(out *KubeSchedulerConfiguration) {
|
||||
*out = *in
|
||||
|
||||
@@ -127,6 +127,12 @@ const (
|
||||
// Taint nodes based on their condition status for 'NetworkUnavailable',
|
||||
// 'MemoryPressure', 'OutOfDisk' and 'DiskPressure'.
|
||||
TaintNodesByCondition utilfeature.Feature = "TaintNodesByCondition"
|
||||
|
||||
// owner: @haibinxie
|
||||
// alpha: v1.8
|
||||
//
|
||||
// Implement IPVS-based in-cluster service load balancing
|
||||
SupportIPVSProxyMode utilfeature.Feature = "SupportIPVSProxyMode"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -164,4 +170,5 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
|
||||
// inherited features from apiextensions-apiserver, relisted here to get a conflict if it is changed
|
||||
// unintentionally on either side:
|
||||
apiextensionsfeatures.CustomResourceValidation: {Default: false, PreRelease: utilfeature.Alpha},
|
||||
SupportIPVSProxyMode: {Default: false, PreRelease: utilfeature.Alpha},
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ filegroup(
|
||||
"//pkg/proxy/config:all-srcs",
|
||||
"//pkg/proxy/healthcheck:all-srcs",
|
||||
"//pkg/proxy/iptables:all-srcs",
|
||||
"//pkg/proxy/ipvs:all-srcs",
|
||||
"//pkg/proxy/userspace:all-srcs",
|
||||
"//pkg/proxy/util:all-srcs",
|
||||
"//pkg/proxy/winuserspace:all-srcs",
|
||||
|
||||
79
pkg/proxy/ipvs/BUILD
Normal file
79
pkg/proxy/ipvs/BUILD
Normal file
@@ -0,0 +1,79 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = select({
|
||||
"@io_bazel_rules_go//go/platform:linux_amd64": [
|
||||
"proxier_test.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = select({
|
||||
"@io_bazel_rules_go//go/platform:linux_amd64": [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/proxy:go_default_library",
|
||||
"//pkg/proxy/util:go_default_library",
|
||||
"//pkg/util/iptables:go_default_library",
|
||||
"//pkg/util/iptables/testing:go_default_library",
|
||||
"//pkg/util/ipvs:go_default_library",
|
||||
"//pkg/util/ipvs/testing:go_default_library",
|
||||
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec/testing:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["proxier.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/helper:go_default_library",
|
||||
"//pkg/api/service:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/proxy:go_default_library",
|
||||
"//pkg/proxy/healthcheck:go_default_library",
|
||||
"//pkg/proxy/util:go_default_library",
|
||||
"//pkg/util/iptables:go_default_library",
|
||||
"//pkg/util/ipvs:go_default_library",
|
||||
"//pkg/util/sysctl:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
1498
pkg/proxy/ipvs/proxier.go
Normal file
1498
pkg/proxy/ipvs/proxier.go
Normal file
File diff suppressed because it is too large
Load Diff
2180
pkg/proxy/ipvs/proxier_test.go
Normal file
2180
pkg/proxy/ipvs/proxier_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@ filegroup(
|
||||
"//pkg/util/io:all-srcs",
|
||||
"//pkg/util/ipconfig:all-srcs",
|
||||
"//pkg/util/iptables:all-srcs",
|
||||
"//pkg/util/ipvs:all-srcs",
|
||||
"//pkg/util/keymutex:all-srcs",
|
||||
"//pkg/util/labels:all-srcs",
|
||||
"//pkg/util/limitwriter:all-srcs",
|
||||
|
||||
71
pkg/util/ipvs/BUILD
Normal file
71
pkg/util/ipvs/BUILD
Normal file
@@ -0,0 +1,71 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"ipvs_test.go",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:linux_amd64": [
|
||||
"ipvs_linux_test.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = select({
|
||||
"@io_bazel_rules_go//go/platform:linux_amd64": [
|
||||
"//vendor/github.com/docker/libnetwork/ipvs:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec/testing:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"ipvs.go",
|
||||
"ipvs_unsupported.go",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:linux_amd64": [
|
||||
"ipvs_linux.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:linux_amd64": [
|
||||
"//vendor/github.com/docker/libnetwork/ipvs:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/util/ipvs/testing:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
92
pkg/util/ipvs/ipvs.go
Normal file
92
pkg/util/ipvs/ipvs.go
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2017 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 ipvs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Interface is an injectable interface for running ipvs commands. Implementations must be goroutine-safe.
|
||||
type Interface interface {
|
||||
// Flush clears all virtual servers in system. return occurred error immediately.
|
||||
Flush() error
|
||||
// EnsureVirtualServerAddressBind checks if virtual server's address is bound to dummy interface and, if not, binds it. If the address is already bound, return true.
|
||||
EnsureVirtualServerAddressBind(serv *VirtualServer, dev string) (exist bool, err error)
|
||||
// UnbindVirtualServerAddress checks if virtual server's address is bound to dummy interface and, if so, unbinds it.
|
||||
UnbindVirtualServerAddress(serv *VirtualServer, dev string) error
|
||||
// AddVirtualServer creates the specified virtual server.
|
||||
AddVirtualServer(*VirtualServer) error
|
||||
// UpdateVirtualServer updates an already existing virtual server. If the virtual server does not exist, return error.
|
||||
UpdateVirtualServer(*VirtualServer) error
|
||||
// DeleteVirtualServer deletes the specified virtual server. If the virtual server does not exist, return error.
|
||||
DeleteVirtualServer(*VirtualServer) error
|
||||
// Given a partial virtual server, GetVirtualServer will return the specified virtual server information in the system.
|
||||
GetVirtualServer(*VirtualServer) (*VirtualServer, error)
|
||||
// GetVirtualServers lists all virtual servers in the system.
|
||||
GetVirtualServers() ([]*VirtualServer, error)
|
||||
// AddRealServer creates the specified real server for the specified virtual server.
|
||||
AddRealServer(*VirtualServer, *RealServer) error
|
||||
// GetRealServers returns all real servers for the specified virtual server.
|
||||
GetRealServers(*VirtualServer) ([]*RealServer, error)
|
||||
// DeleteRealServer deletes the specified real server from the specified virtual server.
|
||||
DeleteRealServer(*VirtualServer, *RealServer) error
|
||||
}
|
||||
|
||||
// VirtualServer is an user-oriented definition of an IPVS virtual server in its entirety.
|
||||
type VirtualServer struct {
|
||||
Address net.IP
|
||||
Protocol string
|
||||
Port uint16
|
||||
Scheduler string
|
||||
Flags ServiceFlags
|
||||
Timeout uint32
|
||||
}
|
||||
|
||||
// ServiceFlags is used to specify session affinity, ip hash etc.
|
||||
type ServiceFlags uint32
|
||||
|
||||
const (
|
||||
// FlagPersistent specify IPVS service session affinity
|
||||
FlagPersistent = 0x1
|
||||
)
|
||||
|
||||
// Equal check the equality of virtual server.
|
||||
// We don't use struct == since it doesn't work because of slice.
|
||||
func (svc *VirtualServer) Equal(other *VirtualServer) bool {
|
||||
return svc.Address.Equal(other.Address) &&
|
||||
svc.Protocol == other.Protocol &&
|
||||
svc.Port == other.Port &&
|
||||
svc.Scheduler == other.Scheduler &&
|
||||
svc.Flags == other.Flags &&
|
||||
svc.Timeout == other.Timeout
|
||||
}
|
||||
|
||||
func (svc *VirtualServer) String() string {
|
||||
return net.JoinHostPort(svc.Address.String(), strconv.Itoa(int(svc.Port))) + "/" + svc.Protocol
|
||||
}
|
||||
|
||||
// RealServer is an user-oriented definition of an IPVS real server in its entirety.
|
||||
type RealServer struct {
|
||||
Address net.IP
|
||||
Port uint16
|
||||
Weight int
|
||||
}
|
||||
|
||||
func (dest *RealServer) String() string {
|
||||
return net.JoinHostPort(dest.Address.String(), strconv.Itoa(int(dest.Port)))
|
||||
}
|
||||
302
pkg/util/ipvs/ipvs_linux.go
Normal file
302
pkg/util/ipvs/ipvs_linux.go
Normal file
@@ -0,0 +1,302 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2017 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 ipvs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/libnetwork/ipvs"
|
||||
"github.com/golang/glog"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
const cmdIP = "ip"
|
||||
|
||||
// runner implements Interface.
|
||||
type runner struct {
|
||||
exec utilexec.Interface
|
||||
ipvsHandle *ipvs.Handle
|
||||
}
|
||||
|
||||
// New returns a new Interface which will call ipvs APIs.
|
||||
func New(exec utilexec.Interface) Interface {
|
||||
ihandle, err := ipvs.New("")
|
||||
if err != nil {
|
||||
glog.Errorf("IPVS interface can't be initialized, error: %v", err)
|
||||
return nil
|
||||
}
|
||||
return &runner{
|
||||
exec: exec,
|
||||
ipvsHandle: ihandle,
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureVirtualServerAddressBind is part of Interface.
|
||||
func (runner *runner) EnsureVirtualServerAddressBind(vs *VirtualServer, dummyDev string) (exist bool, err error) {
|
||||
addr := vs.Address.String() + "/32"
|
||||
args := []string{"addr", "add", addr, "dev", dummyDev}
|
||||
out, err := runner.exec.Command(cmdIP, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
// "exit status 2" will be returned if the address is already bound to dummy device
|
||||
if ee, ok := err.(utilexec.ExitError); ok {
|
||||
if ee.Exited() && ee.ExitStatus() == 2 {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, fmt.Errorf("error bind address: %s to dummy interface: %s, err: %v: %s", vs.Address.String(), dummyDev, err, out)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// UnbindVirtualServerAddress is part of Interface.
|
||||
func (runner *runner) UnbindVirtualServerAddress(vs *VirtualServer, dummyDev string) error {
|
||||
addr := vs.Address.String() + "/32"
|
||||
args := []string{"addr", "del", addr, "dev", dummyDev}
|
||||
out, err := runner.exec.Command(cmdIP, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error unbind address: %s from dummy interface: %s, err: %v: %s", vs.Address.String(), dummyDev, err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddVirtualServer is part of Interface.
|
||||
func (runner *runner) AddVirtualServer(vs *VirtualServer) error {
|
||||
eSvc, err := toBackendService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runner.ipvsHandle.NewService(eSvc)
|
||||
}
|
||||
|
||||
// UpdateVirtualServer is part of Interface.
|
||||
func (runner *runner) UpdateVirtualServer(vs *VirtualServer) error {
|
||||
bSvc, err := toBackendService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runner.ipvsHandle.UpdateService(bSvc)
|
||||
}
|
||||
|
||||
// DeleteVirtualServer is part of Interface.
|
||||
func (runner *runner) DeleteVirtualServer(vs *VirtualServer) error {
|
||||
bSvc, err := toBackendService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runner.ipvsHandle.DelService(bSvc)
|
||||
}
|
||||
|
||||
// GetVirtualServer is part of Interface.
|
||||
func (runner *runner) GetVirtualServer(vs *VirtualServer) (*VirtualServer, error) {
|
||||
bSvc, err := toBackendService(vs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipvsService, err := runner.ipvsHandle.GetService(bSvc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
virtualServer, err := toVirtualServer(ipvsService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return virtualServer, nil
|
||||
}
|
||||
|
||||
// GetVirtualServers is part of Interface.
|
||||
func (runner *runner) GetVirtualServers() ([]*VirtualServer, error) {
|
||||
ipvsServices, err := runner.ipvsHandle.GetServices()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vss := make([]*VirtualServer, 0)
|
||||
for _, ipvsService := range ipvsServices {
|
||||
vs, err := toVirtualServer(ipvsService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vss = append(vss, vs)
|
||||
}
|
||||
return vss, nil
|
||||
}
|
||||
|
||||
// Flush is part of Interface. Currently we delete IPVS services one by one
|
||||
func (runner *runner) Flush() error {
|
||||
vss, err := runner.GetVirtualServers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, vs := range vss {
|
||||
err := runner.DeleteVirtualServer(vs)
|
||||
// TODO: aggregate errors?
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRealServer is part of Interface.
|
||||
func (runner *runner) AddRealServer(vs *VirtualServer, rs *RealServer) error {
|
||||
bSvc, err := toBackendService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bDst, err := toBackendDestination(rs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runner.ipvsHandle.NewDestination(bSvc, bDst)
|
||||
}
|
||||
|
||||
// DeleteRealServer is part of Interface.
|
||||
func (runner *runner) DeleteRealServer(vs *VirtualServer, rs *RealServer) error {
|
||||
bSvc, err := toBackendService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bDst, err := toBackendDestination(rs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runner.ipvsHandle.DelDestination(bSvc, bDst)
|
||||
}
|
||||
|
||||
// GetRealServers is part of Interface.
|
||||
func (runner *runner) GetRealServers(vs *VirtualServer) ([]*RealServer, error) {
|
||||
bSvc, err := toBackendService(vs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bDestinations, err := runner.ipvsHandle.GetDestinations(bSvc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
realServers := make([]*RealServer, 0)
|
||||
for _, dest := range bDestinations {
|
||||
dst, err := toRealServer(dest)
|
||||
// TODO: aggregate errors?
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
realServers = append(realServers, dst)
|
||||
}
|
||||
return realServers, nil
|
||||
}
|
||||
|
||||
// toVirtualServer converts an IPVS service representation to the equivalent virtual server structure.
|
||||
func toVirtualServer(svc *ipvs.Service) (*VirtualServer, error) {
|
||||
if svc == nil {
|
||||
return nil, errors.New("ipvs svc should not be empty")
|
||||
}
|
||||
vs := &VirtualServer{
|
||||
Address: svc.Address,
|
||||
Port: svc.Port,
|
||||
Scheduler: svc.SchedName,
|
||||
Protocol: protocolNumbeToString(ProtoType(svc.Protocol)),
|
||||
Flags: ServiceFlags(svc.Flags),
|
||||
Timeout: svc.Timeout,
|
||||
}
|
||||
|
||||
if vs.Address == nil {
|
||||
if svc.AddressFamily == syscall.AF_INET {
|
||||
vs.Address = net.IPv4zero
|
||||
} else {
|
||||
vs.Address = net.IPv6zero
|
||||
}
|
||||
}
|
||||
return vs, nil
|
||||
}
|
||||
|
||||
// toRealServer converts an IPVS destination representation to the equivalent real server structure.
|
||||
func toRealServer(dst *ipvs.Destination) (*RealServer, error) {
|
||||
if dst == nil {
|
||||
return nil, errors.New("ipvs destination should not be empty")
|
||||
}
|
||||
return &RealServer{
|
||||
Address: dst.Address,
|
||||
Port: dst.Port,
|
||||
Weight: dst.Weight,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// toBackendService converts an IPVS real server representation to the equivalent "backend" service structure.
|
||||
func toBackendService(vs *VirtualServer) (*ipvs.Service, error) {
|
||||
if vs == nil {
|
||||
return nil, errors.New("virtual server should not be empty")
|
||||
}
|
||||
bakSvc := &ipvs.Service{
|
||||
Address: vs.Address,
|
||||
Protocol: stringToProtocolNumber(vs.Protocol),
|
||||
Port: vs.Port,
|
||||
SchedName: vs.Scheduler,
|
||||
Flags: uint32(vs.Flags),
|
||||
Timeout: vs.Timeout,
|
||||
}
|
||||
|
||||
if ip4 := vs.Address.To4(); ip4 != nil {
|
||||
bakSvc.AddressFamily = syscall.AF_INET
|
||||
bakSvc.Netmask = 0xffffffff
|
||||
} else {
|
||||
bakSvc.AddressFamily = syscall.AF_INET6
|
||||
bakSvc.Netmask = 128
|
||||
}
|
||||
return bakSvc, nil
|
||||
}
|
||||
|
||||
// toBackendDestination converts an IPVS real server representation to the equivalent "backend" destination structure.
|
||||
func toBackendDestination(rs *RealServer) (*ipvs.Destination, error) {
|
||||
if rs == nil {
|
||||
return nil, errors.New("real server should not be empty")
|
||||
}
|
||||
return &ipvs.Destination{
|
||||
Address: rs.Address,
|
||||
Port: rs.Port,
|
||||
Weight: rs.Weight,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// stringToProtocolNumber returns the protocol value for the given name
|
||||
func stringToProtocolNumber(protocol string) uint16 {
|
||||
switch strings.ToLower(protocol) {
|
||||
case "tcp":
|
||||
return uint16(syscall.IPPROTO_TCP)
|
||||
case "udp":
|
||||
return uint16(syscall.IPPROTO_UDP)
|
||||
}
|
||||
return uint16(0)
|
||||
}
|
||||
|
||||
// protocolNumbeToString returns the name for the given protocol value.
|
||||
func protocolNumbeToString(proto ProtoType) string {
|
||||
switch proto {
|
||||
case syscall.IPPROTO_TCP:
|
||||
return "TCP"
|
||||
case syscall.IPPROTO_UDP:
|
||||
return "UDP"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ProtoType is IPVS service protocol type
|
||||
type ProtoType uint16
|
||||
448
pkg/util/ipvs/ipvs_linux_test.go
Normal file
448
pkg/util/ipvs/ipvs_linux_test.go
Normal file
@@ -0,0 +1,448 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2017 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 ipvs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/utils/exec"
|
||||
fakeexec "k8s.io/utils/exec/testing"
|
||||
|
||||
"github.com/docker/libnetwork/ipvs"
|
||||
)
|
||||
|
||||
const dummyDevice = "kube-ipvs0"
|
||||
|
||||
func TestEnsureVirtualServerAddressBind(t *testing.T) {
|
||||
vs := &VirtualServer{
|
||||
Address: net.ParseIP("10.20.30.40"),
|
||||
Port: uint16(1234),
|
||||
Protocol: string("TCP"),
|
||||
}
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success.
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Exists.
|
||||
func() ([]byte, error) { return nil, &fakeexec.FakeExitError{Status: 2} },
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
// Success.
|
||||
exists, err := runner.EnsureVirtualServerAddressBind(vs, dummyDevice)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if exists {
|
||||
t.Errorf("expected exists = false")
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ip", "addr", "add", "10.20.30.40/32", "dev", "kube-ipvs0") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
// Exists.
|
||||
exists, err = runner.EnsureVirtualServerAddressBind(vs, dummyDevice)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if !exists {
|
||||
t.Errorf("expected exists = true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnbindVirtualServerAddress(t *testing.T) {
|
||||
svc := &VirtualServer{
|
||||
Address: net.ParseIP("10.20.30.41"),
|
||||
Port: uint16(80),
|
||||
Protocol: string("TCP"),
|
||||
}
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success.
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Failure.
|
||||
func() ([]byte, error) { return nil, &fakeexec.FakeExitError{Status: 2} },
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
// Success.
|
||||
err := runner.UnbindVirtualServerAddress(svc, dummyDevice)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ip", "addr", "del", "10.20.30.41/32", "dev", "kube-ipvs0") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
// Failure.
|
||||
err = runner.UnbindVirtualServerAddress(svc, dummyDevice)
|
||||
if err == nil {
|
||||
t.Errorf("expected failure")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toFrontendService(t *testing.T) {
|
||||
Tests := []struct {
|
||||
ipvsService ipvs.Service
|
||||
virtualServer VirtualServer
|
||||
}{
|
||||
{
|
||||
ipvs.Service{
|
||||
Protocol: syscall.IPPROTO_TCP,
|
||||
Port: 80,
|
||||
FWMark: 0,
|
||||
SchedName: "",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
Netmask: 0xffffffff,
|
||||
AddressFamily: syscall.AF_INET,
|
||||
Address: nil,
|
||||
PEName: "",
|
||||
},
|
||||
VirtualServer{
|
||||
Address: net.ParseIP("0.0.0.0"),
|
||||
Protocol: "TCP",
|
||||
Port: 80,
|
||||
Scheduler: "",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
ipvs.Service{
|
||||
Protocol: syscall.IPPROTO_UDP,
|
||||
Port: 33434,
|
||||
FWMark: 0,
|
||||
SchedName: "wlc",
|
||||
Flags: 1234,
|
||||
Timeout: 100,
|
||||
Netmask: 128,
|
||||
AddressFamily: syscall.AF_INET6,
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
PEName: "",
|
||||
},
|
||||
VirtualServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Protocol: "UDP",
|
||||
Port: 33434,
|
||||
Scheduler: "wlc",
|
||||
Flags: 1234,
|
||||
Timeout: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
ipvs.Service{
|
||||
Protocol: 0,
|
||||
Port: 0,
|
||||
FWMark: 0,
|
||||
SchedName: "lc",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
Netmask: 0xffffffff,
|
||||
AddressFamily: syscall.AF_INET,
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
PEName: "",
|
||||
},
|
||||
VirtualServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Protocol: "",
|
||||
Port: 0,
|
||||
Scheduler: "lc",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
ipvs.Service{
|
||||
Protocol: 0,
|
||||
Port: 0,
|
||||
FWMark: 0,
|
||||
SchedName: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
Netmask: 128,
|
||||
AddressFamily: syscall.AF_INET6,
|
||||
Address: nil,
|
||||
PEName: "",
|
||||
},
|
||||
VirtualServer{
|
||||
Address: net.ParseIP("::0"),
|
||||
Protocol: "",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i := range Tests {
|
||||
got, err := toVirtualServer(&Tests[i].ipvsService)
|
||||
if err != nil {
|
||||
t.Errorf("case: %d, unexpected error: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(*got, Tests[i].virtualServer) {
|
||||
t.Errorf("case: %d, got %#v, want %#v", i, *got, Tests[i].virtualServer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toBackendService(t *testing.T) {
|
||||
Tests := []struct {
|
||||
ipvsService ipvs.Service
|
||||
virtualServer VirtualServer
|
||||
}{
|
||||
{
|
||||
ipvs.Service{
|
||||
Protocol: syscall.IPPROTO_TCP,
|
||||
Port: 80,
|
||||
FWMark: 0,
|
||||
SchedName: "",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
Netmask: 0xffffffff,
|
||||
AddressFamily: syscall.AF_INET,
|
||||
Address: net.ParseIP("0.0.0.0"),
|
||||
PEName: "",
|
||||
},
|
||||
VirtualServer{
|
||||
Address: net.ParseIP("0.0.0.0"),
|
||||
Protocol: "TCP",
|
||||
Port: 80,
|
||||
Scheduler: "",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
ipvs.Service{
|
||||
Protocol: syscall.IPPROTO_UDP,
|
||||
Port: 33434,
|
||||
FWMark: 0,
|
||||
SchedName: "wlc",
|
||||
Flags: 1234,
|
||||
Timeout: 100,
|
||||
Netmask: 128,
|
||||
AddressFamily: syscall.AF_INET6,
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
PEName: "",
|
||||
},
|
||||
VirtualServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Protocol: "UDP",
|
||||
Port: 33434,
|
||||
Scheduler: "wlc",
|
||||
Flags: 1234,
|
||||
Timeout: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
ipvs.Service{
|
||||
Protocol: 0,
|
||||
Port: 0,
|
||||
FWMark: 0,
|
||||
SchedName: "lc",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
Netmask: 0xffffffff,
|
||||
AddressFamily: syscall.AF_INET,
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
PEName: "",
|
||||
},
|
||||
VirtualServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Protocol: "",
|
||||
Port: 0,
|
||||
Scheduler: "lc",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
ipvs.Service{
|
||||
Protocol: 0,
|
||||
Port: 0,
|
||||
FWMark: 0,
|
||||
SchedName: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
Netmask: 128,
|
||||
AddressFamily: syscall.AF_INET6,
|
||||
Address: net.ParseIP("::0"),
|
||||
PEName: "",
|
||||
},
|
||||
VirtualServer{
|
||||
Address: net.ParseIP("::0"),
|
||||
Protocol: "",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i := range Tests {
|
||||
got, err := toBackendService(&Tests[i].virtualServer)
|
||||
if err != nil {
|
||||
t.Errorf("case: %d, unexpected error: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(*got, Tests[i].ipvsService) {
|
||||
t.Errorf("case: %d - got %#v, want %#v", i, *got, Tests[i].ipvsService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toFrontendDestination(t *testing.T) {
|
||||
Tests := []struct {
|
||||
ipvsDestination ipvs.Destination
|
||||
realServer RealServer
|
||||
}{
|
||||
{
|
||||
ipvs.Destination{
|
||||
Port: 54321,
|
||||
ConnectionFlags: 0,
|
||||
Weight: 1,
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
},
|
||||
RealServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Port: 54321,
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
ipvs.Destination{
|
||||
Port: 53,
|
||||
ConnectionFlags: 0,
|
||||
Weight: 1,
|
||||
Address: net.ParseIP("2002::cafe"),
|
||||
},
|
||||
RealServer{
|
||||
Address: net.ParseIP("2002::cafe"),
|
||||
Port: 53,
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := range Tests {
|
||||
got, err := toRealServer(&Tests[i].ipvsDestination)
|
||||
if err != nil {
|
||||
t.Errorf("case %d unexpected error: %d", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(*got, Tests[i].realServer) {
|
||||
t.Errorf("case %d Failed to translate Destination - got %#v, want %#v", i, *got, Tests[i].realServer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toBackendDestination(t *testing.T) {
|
||||
Tests := []struct {
|
||||
realServer RealServer
|
||||
ipvsDestination ipvs.Destination
|
||||
}{
|
||||
{
|
||||
RealServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Port: 54321,
|
||||
Weight: 1,
|
||||
},
|
||||
ipvs.Destination{
|
||||
Port: 54321,
|
||||
ConnectionFlags: 0,
|
||||
Weight: 1,
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
},
|
||||
},
|
||||
{
|
||||
RealServer{
|
||||
Address: net.ParseIP("2002::cafe"),
|
||||
Port: 53,
|
||||
Weight: 1,
|
||||
},
|
||||
ipvs.Destination{
|
||||
Port: 53,
|
||||
ConnectionFlags: 0,
|
||||
Weight: 1,
|
||||
Address: net.ParseIP("2002::cafe"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := range Tests {
|
||||
got, err := toBackendDestination(&Tests[i].realServer)
|
||||
if err != nil {
|
||||
t.Errorf("case %d unexpected error: %d", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(*got, Tests[i].ipvsDestination) {
|
||||
t.Errorf("case %d Failed to translate Destination - got %#v, want %#v", i, *got, Tests[i].ipvsDestination)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_stringToProtocolNumber(t *testing.T) {
|
||||
tests := []string{
|
||||
"TCP", "UDP", "ICMP",
|
||||
}
|
||||
expecteds := []uint16{
|
||||
uint16(syscall.IPPROTO_TCP), uint16(syscall.IPPROTO_UDP), uint16(0),
|
||||
}
|
||||
for i := range tests {
|
||||
got := stringToProtocolNumber(tests[i])
|
||||
if got != expecteds[i] {
|
||||
t.Errorf("stringToProtocolNumber() failed - got %#v, want %#v",
|
||||
got, expecteds[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_protocolNumberToString(t *testing.T) {
|
||||
tests := []ProtoType{
|
||||
syscall.IPPROTO_TCP, syscall.IPPROTO_UDP, ProtoType(0),
|
||||
}
|
||||
expecteds := []string{
|
||||
"TCP", "UDP", "",
|
||||
}
|
||||
for i := range tests {
|
||||
got := protocolNumbeToString(tests[i])
|
||||
if got != expecteds[i] {
|
||||
t.Errorf("protocolNumbeToString() failed - got %#v, want %#v",
|
||||
got, expecteds[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
265
pkg/util/ipvs/ipvs_test.go
Normal file
265
pkg/util/ipvs/ipvs_test.go
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
Copyright 2017 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 ipvs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVirtualServerEqual(t *testing.T) {
|
||||
Tests := []struct {
|
||||
svcA *VirtualServer
|
||||
svcB *VirtualServer
|
||||
equal bool
|
||||
reason string
|
||||
}{
|
||||
{
|
||||
svcA: &VirtualServer{
|
||||
Address: net.ParseIP("10.20.30.40"),
|
||||
Protocol: "",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
svcB: &VirtualServer{
|
||||
Address: net.ParseIP("10.20.30.41"),
|
||||
Protocol: "",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
equal: false,
|
||||
reason: "IPv4 address not equal",
|
||||
},
|
||||
{
|
||||
svcA: &VirtualServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Protocol: "",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
svcB: &VirtualServer{
|
||||
Address: net.ParseIP("2017::beef"),
|
||||
Protocol: "",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
equal: false,
|
||||
reason: "IPv6 address not equal",
|
||||
},
|
||||
{
|
||||
svcA: &VirtualServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Protocol: "TCP",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
svcB: &VirtualServer{
|
||||
Address: net.ParseIP("2012::beeef"),
|
||||
Protocol: "UDP",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
equal: false,
|
||||
reason: "Protocol not equal",
|
||||
},
|
||||
{
|
||||
svcA: &VirtualServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Protocol: "TCP",
|
||||
Port: 80,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
svcB: &VirtualServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Protocol: "TCP",
|
||||
Port: 8080,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
equal: false,
|
||||
reason: "Port not equal",
|
||||
},
|
||||
{
|
||||
svcA: &VirtualServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Protocol: "TCP",
|
||||
Port: 80,
|
||||
Scheduler: "rr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
svcB: &VirtualServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Protocol: "TCP",
|
||||
Port: 80,
|
||||
Scheduler: "wlc",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
equal: false,
|
||||
reason: "Scheduler not equal",
|
||||
},
|
||||
{
|
||||
svcA: &VirtualServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Protocol: "TCP",
|
||||
Port: 80,
|
||||
Scheduler: "rr",
|
||||
Flags: 2,
|
||||
Timeout: 0,
|
||||
},
|
||||
svcB: &VirtualServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Protocol: "TCP",
|
||||
Port: 80,
|
||||
Scheduler: "rr",
|
||||
Flags: 3,
|
||||
Timeout: 0,
|
||||
},
|
||||
equal: false,
|
||||
reason: "Flags not equal",
|
||||
},
|
||||
{
|
||||
svcA: &VirtualServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Protocol: "",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
svcB: &VirtualServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Protocol: "",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 10800,
|
||||
},
|
||||
equal: false,
|
||||
reason: "Timeout not equal",
|
||||
},
|
||||
{
|
||||
svcA: &VirtualServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Protocol: "TCP",
|
||||
Port: 80,
|
||||
Scheduler: "rr",
|
||||
Flags: 0x1,
|
||||
Timeout: 10800,
|
||||
},
|
||||
svcB: &VirtualServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Protocol: "TCP",
|
||||
Port: 80,
|
||||
Scheduler: "rr",
|
||||
Flags: 0x1,
|
||||
Timeout: 10800,
|
||||
},
|
||||
equal: true,
|
||||
reason: "All fields equal",
|
||||
},
|
||||
}
|
||||
|
||||
for i := range Tests {
|
||||
equal := Tests[i].svcA.Equal(Tests[i].svcB)
|
||||
if equal != Tests[i].equal {
|
||||
t.Errorf("case: %d got %v, expected %v, reason: %s", i, equal, Tests[i].equal, Tests[i].reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrontendServiceString(t *testing.T) {
|
||||
Tests := []struct {
|
||||
svc *VirtualServer
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
svc: &VirtualServer{
|
||||
Address: net.ParseIP("10.20.30.40"),
|
||||
Protocol: "TCP",
|
||||
Port: 80,
|
||||
},
|
||||
expected: "10.20.30.40:80/TCP",
|
||||
},
|
||||
{
|
||||
svc: &VirtualServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Protocol: "UDP",
|
||||
Port: 8080,
|
||||
},
|
||||
expected: "[2012::beef]:8080/UDP",
|
||||
},
|
||||
{
|
||||
svc: &VirtualServer{
|
||||
Address: net.ParseIP("10.20.30.41"),
|
||||
Protocol: "ESP",
|
||||
Port: 1234,
|
||||
},
|
||||
expected: "10.20.30.41:1234/ESP",
|
||||
},
|
||||
}
|
||||
|
||||
for i := range Tests {
|
||||
if Tests[i].expected != Tests[i].svc.String() {
|
||||
t.Errorf("case: %d got %v, expected %v", i, Tests[i].svc.String(), Tests[i].expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrontendDestinationString(t *testing.T) {
|
||||
Tests := []struct {
|
||||
svc *RealServer
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
svc: &RealServer{
|
||||
Address: net.ParseIP("10.20.30.40"),
|
||||
Port: 80,
|
||||
},
|
||||
expected: "10.20.30.40:80",
|
||||
},
|
||||
{
|
||||
svc: &RealServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Port: 8080,
|
||||
},
|
||||
expected: "[2012::beef]:8080",
|
||||
},
|
||||
}
|
||||
|
||||
for i := range Tests {
|
||||
if Tests[i].expected != Tests[i].svc.String() {
|
||||
t.Errorf("case: %d got %v, expected %v", i, Tests[i].svc.String(), Tests[i].expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
79
pkg/util/ipvs/ipvs_unsupported.go
Normal file
79
pkg/util/ipvs/ipvs_unsupported.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright 2017 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 ipvs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
// New returns a dummy Interface for unsupported platform.
|
||||
func New(utilexec.Interface) Interface {
|
||||
return &runner{}
|
||||
}
|
||||
|
||||
type runner struct {
|
||||
}
|
||||
|
||||
func (runner *runner) Flush() error {
|
||||
return fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
func (runner *runner) EnsureVirtualServerAddressBind(*VirtualServer, string) (bool, error) {
|
||||
return false, fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
func (runner *runner) UnbindVirtualServerAddress(*VirtualServer, string) error {
|
||||
return fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
func (runner *runner) AddVirtualServer(*VirtualServer) error {
|
||||
return fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
func (runner *runner) UpdateVirtualServer(*VirtualServer) error {
|
||||
return fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
func (runner *runner) DeleteVirtualServer(*VirtualServer) error {
|
||||
return fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
func (runner *runner) GetVirtualServer(*VirtualServer) (*VirtualServer, error) {
|
||||
return nil, fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
func (runner *runner) GetVirtualServers() ([]*VirtualServer, error) {
|
||||
return nil, fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
func (runner *runner) AddRealServer(*VirtualServer, *RealServer) error {
|
||||
return fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
func (runner *runner) GetRealServers(*VirtualServer) ([]*RealServer, error) {
|
||||
return nil, fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
func (runner *runner) DeleteRealServer(*VirtualServer, *RealServer) error {
|
||||
return fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
var _ = Interface(&runner{})
|
||||
28
pkg/util/ipvs/testing/BUILD
Normal file
28
pkg/util/ipvs/testing/BUILD
Normal file
@@ -0,0 +1,28 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["fake.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/util/ipvs:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
168
pkg/util/ipvs/testing/fake.go
Normal file
168
pkg/util/ipvs/testing/fake.go
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright 2017 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 testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
utilipvs "k8s.io/kubernetes/pkg/util/ipvs"
|
||||
)
|
||||
|
||||
//FakeIPVS no-op implementation of ipvs Interface
|
||||
type FakeIPVS struct {
|
||||
Scheduler string
|
||||
Services map[serviceKey]*utilipvs.VirtualServer
|
||||
Destinations map[serviceKey][]*utilipvs.RealServer
|
||||
}
|
||||
|
||||
type serviceKey struct {
|
||||
IP string
|
||||
Port uint16
|
||||
Protocol string
|
||||
}
|
||||
|
||||
func (s *serviceKey) String() string {
|
||||
return fmt.Sprintf("%s:%d/%s", s.IP, s.Port, s.Protocol)
|
||||
}
|
||||
|
||||
//NewFake creates a fake ipvs strucuter
|
||||
func NewFake() *FakeIPVS {
|
||||
return &FakeIPVS{
|
||||
Services: make(map[serviceKey]*utilipvs.VirtualServer),
|
||||
Destinations: make(map[serviceKey][]*utilipvs.RealServer),
|
||||
}
|
||||
}
|
||||
|
||||
func toServiceKey(serv *utilipvs.VirtualServer) serviceKey {
|
||||
return serviceKey{
|
||||
IP: serv.Address.To4().String(),
|
||||
Port: serv.Port,
|
||||
Protocol: serv.Protocol,
|
||||
}
|
||||
}
|
||||
|
||||
//EnsureVirtualServerAddressBind is a fake implementation
|
||||
func (*FakeIPVS) EnsureVirtualServerAddressBind(serv *utilipvs.VirtualServer, dev string) (exist bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
//UnbindVirtualServerAddress is a fake implementation
|
||||
func (*FakeIPVS) UnbindVirtualServerAddress(serv *utilipvs.VirtualServer, dev string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//AddVirtualServer is a fake implementation
|
||||
func (f *FakeIPVS) AddVirtualServer(serv *utilipvs.VirtualServer) error {
|
||||
if serv == nil {
|
||||
return fmt.Errorf("Failed to add service: service can't be nil")
|
||||
}
|
||||
key := toServiceKey(serv)
|
||||
f.Services[key] = serv
|
||||
// make sure no destination present when creating new service
|
||||
f.Destinations = make(map[serviceKey][]*utilipvs.RealServer)
|
||||
return nil
|
||||
}
|
||||
|
||||
//UpdateVirtualServer is a fake implementation
|
||||
func (f *FakeIPVS) UpdateVirtualServer(serv *utilipvs.VirtualServer) error {
|
||||
if serv == nil {
|
||||
return fmt.Errorf("Failed to update service, service can't be nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//DeleteVirtualServer is a fake implementation
|
||||
func (f *FakeIPVS) DeleteVirtualServer(serv *utilipvs.VirtualServer) error {
|
||||
if serv == nil {
|
||||
return fmt.Errorf("Failed to delete service: service can't be nil")
|
||||
}
|
||||
key := toServiceKey(serv)
|
||||
delete(f.Services, key)
|
||||
// clear specific destinations as well
|
||||
f.Destinations[key] = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
//GetVirtualServer is a fake implementation
|
||||
func (f *FakeIPVS) GetVirtualServer(serv *utilipvs.VirtualServer) (*utilipvs.VirtualServer, error) {
|
||||
if serv == nil {
|
||||
return nil, fmt.Errorf("Failed to get service: service can't be nil")
|
||||
}
|
||||
key := toServiceKey(serv)
|
||||
svc, found := f.Services[key]
|
||||
if found {
|
||||
return svc, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Not found serv: %v", key.String())
|
||||
}
|
||||
|
||||
//GetVirtualServers is a fake implementation
|
||||
func (f *FakeIPVS) GetVirtualServers() ([]*utilipvs.VirtualServer, error) {
|
||||
res := make([]*utilipvs.VirtualServer, 0)
|
||||
for _, svc := range f.Services {
|
||||
res = append(res, svc)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
//Flush is a fake implementation
|
||||
func (f *FakeIPVS) Flush() error {
|
||||
// directly drop old data
|
||||
f.Services = nil
|
||||
f.Destinations = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
//AddRealServer is a fake implementation
|
||||
func (f *FakeIPVS) AddRealServer(serv *utilipvs.VirtualServer, dest *utilipvs.RealServer) error {
|
||||
if serv == nil || dest == nil {
|
||||
return fmt.Errorf("Failed to add destination for service, neither service nor destination shouldn't be nil")
|
||||
}
|
||||
key := toServiceKey(serv)
|
||||
if _, ok := f.Services[key]; !ok {
|
||||
return fmt.Errorf("Failed to add destination for service %v, service not found", key.String())
|
||||
}
|
||||
dests := f.Destinations[key]
|
||||
if dests == nil {
|
||||
dests = make([]*utilipvs.RealServer, 0)
|
||||
f.Destinations[key] = dests
|
||||
}
|
||||
f.Destinations[key] = append(f.Destinations[key], dest)
|
||||
return nil
|
||||
}
|
||||
|
||||
//GetRealServers is a fake implementation
|
||||
func (f *FakeIPVS) GetRealServers(serv *utilipvs.VirtualServer) ([]*utilipvs.RealServer, error) {
|
||||
if serv == nil {
|
||||
return nil, fmt.Errorf("Failed to get destination for nil service")
|
||||
}
|
||||
key := toServiceKey(serv)
|
||||
if _, ok := f.Services[key]; !ok {
|
||||
return nil, fmt.Errorf("Failed to get destinations for service %v, service not found", key.String())
|
||||
}
|
||||
return f.Destinations[key], nil
|
||||
}
|
||||
|
||||
//DeleteRealServer is a fake implementation
|
||||
func (*FakeIPVS) DeleteRealServer(serv *utilipvs.VirtualServer, dest *utilipvs.RealServer) error {
|
||||
if serv == nil || dest == nil {
|
||||
return fmt.Errorf("Failed to delete destination, neither service nor destination can't be nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ = utilipvs.Interface(&FakeIPVS{})
|
||||
Reference in New Issue
Block a user