From fd82adb0124cf2a9b7437c329697174d203bc729 Mon Sep 17 00:00:00 2001 From: allencloud Date: Tue, 18 Jul 2017 14:12:57 +0800 Subject: [PATCH] validate kube-proxy options Signed-off-by: allencloud Signed-off-by: Allen Sun --- cmd/kube-proxy/app/BUILD | 9 +- cmd/kube-proxy/app/server.go | 4 + cmd/kube-proxy/app/validation.go | 156 +++++++ cmd/kube-proxy/app/validation_test.go | 403 ++++++++++++++++++ pkg/apis/componentconfig/BUILD | 1 - pkg/apis/componentconfig/validation/BUILD | 24 -- .../componentconfig/validation/validation.go | 17 - 7 files changed, 571 insertions(+), 43 deletions(-) create mode 100644 cmd/kube-proxy/app/validation.go create mode 100644 cmd/kube-proxy/app/validation_test.go delete mode 100644 pkg/apis/componentconfig/validation/BUILD delete mode 100644 pkg/apis/componentconfig/validation/validation.go diff --git a/cmd/kube-proxy/app/BUILD b/cmd/kube-proxy/app/BUILD index d8b9a49c1b5..c5d12c388f5 100644 --- a/cmd/kube-proxy/app/BUILD +++ b/cmd/kube-proxy/app/BUILD @@ -12,6 +12,7 @@ go_library( "conntrack.go", "server.go", "server_others.go", + "validation.go", ] + select({ "@io_bazel_rules_go//go/platform:windows_amd64": [ "server_windows.go", @@ -20,6 +21,7 @@ go_library( }), deps = [ "//pkg/api:go_default_library", + "//pkg/api/validation:go_default_library", "//pkg/apis/componentconfig:go_default_library", "//pkg/apis/componentconfig/v1alpha1:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", @@ -58,6 +60,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library", "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", @@ -79,7 +82,10 @@ go_library( go_test( name = "go_default_test", - srcs = ["server_test.go"], + srcs = [ + "server_test.go", + "validation_test.go", + ], library = ":go_default_library", deps = [ "//pkg/api:go_default_library", @@ -92,6 +98,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", ], ) diff --git a/cmd/kube-proxy/app/server.go b/cmd/kube-proxy/app/server.go index b7b7eda54e6..a79122e9fba 100644 --- a/cmd/kube-proxy/app/server.go +++ b/cmd/kube-proxy/app/server.go @@ -203,6 +203,10 @@ func (o *Options) Validate(args []string) error { return errors.New("no arguments are supported") } + if errs := Validate(o.config); len(errs) != 0 { + return errs.ToAggregate() + } + return nil } diff --git a/cmd/kube-proxy/app/validation.go b/cmd/kube-proxy/app/validation.go new file mode 100644 index 00000000000..4b3715db211 --- /dev/null +++ b/cmd/kube-proxy/app/validation.go @@ -0,0 +1,156 @@ +/* +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 app + +import ( + "fmt" + "net" + "strconv" + "strings" + + utilnet "k8s.io/apimachinery/pkg/util/net" + "k8s.io/apimachinery/pkg/util/validation/field" + apivalidation "k8s.io/kubernetes/pkg/api/validation" + "k8s.io/kubernetes/pkg/apis/componentconfig" +) + +// Validate validates the configuration of kube-proxy +func Validate(config *componentconfig.KubeProxyConfiguration) field.ErrorList { + allErrs := field.ErrorList{} + + newPath := field.NewPath("KubeProxyConfiguration") + + allErrs = append(allErrs, validateKubeProxyIPTablesConfiguration(config.IPTables, newPath.Child("KubeProxyIPTablesConfiguration"))...) + allErrs = append(allErrs, validateKubeProxyConntrackConfiguration(config.Conntrack, newPath.Child("KubeProxyConntrackConfiguration"))...) + allErrs = append(allErrs, validateProxyMode(config.Mode, newPath.Child("Mode"))...) + allErrs = append(allErrs, validateClientConnectionConfiguration(config.ClientConnection, newPath.Child("ClientConnection"))...) + + if config.OOMScoreAdj != nil && (*config.OOMScoreAdj < -1000 || *config.OOMScoreAdj > 1000) { + allErrs = append(allErrs, field.Invalid(newPath.Child("OOMScoreAdj"), *config.OOMScoreAdj, "must be within the range [-1000, 1000]")) + } + + if config.UDPIdleTimeout.Duration <= 0 { + allErrs = append(allErrs, field.Invalid(newPath.Child("UDPIdleTimeout"), config.UDPIdleTimeout, "must be greater than 0")) + } + + if config.ConfigSyncPeriod.Duration <= 0 { + allErrs = append(allErrs, field.Invalid(newPath.Child("ConfigSyncPeriod"), config.ConfigSyncPeriod, "must be greater than 0")) + } + + if net.ParseIP(config.BindAddress) == nil { + allErrs = append(allErrs, field.Invalid(newPath.Child("BindAddress"), config.BindAddress, "not a valid textual representation of an IP address")) + } + + allErrs = append(allErrs, validateHostPort(config.HealthzBindAddress, newPath.Child("HealthzBindAddress"))...) + allErrs = append(allErrs, validateHostPort(config.MetricsBindAddress, newPath.Child("MetricsBindAddress"))...) + + if _, _, err := net.ParseCIDR(config.ClusterCIDR); err != nil { + allErrs = append(allErrs, field.Invalid(newPath.Child("ClusterCIDR"), config.ClusterCIDR, "must be a valid CIDR block (e.g. 10.100.0.0/16)")) + } + + if _, err := utilnet.ParsePortRange(config.PortRange); err != nil { + allErrs = append(allErrs, field.Invalid(newPath.Child("PortRange"), config.PortRange, "must be a valid port range (e.g. 300-2000)")) + } + + return allErrs +} + +func validateKubeProxyIPTablesConfiguration(config componentconfig.KubeProxyIPTablesConfiguration, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if config.MasqueradeBit != nil && (*config.MasqueradeBit < 0 || *config.MasqueradeBit > 31) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("MasqueradeBit"), config.MasqueradeBit, "must be within the range [0, 31]")) + } + + if config.SyncPeriod.Duration <= 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("SyncPeriod"), config.SyncPeriod, "must be greater than 0")) + } + + if config.MinSyncPeriod.Duration < 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("MinSyncPeriod"), config.MinSyncPeriod, "must be greater than or equal to 0")) + } + + return allErrs +} + +func validateKubeProxyConntrackConfiguration(config componentconfig.KubeProxyConntrackConfiguration, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if config.Max < 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("Max"), config.Max, "must be greater than or equal to 0")) + } + + if config.MaxPerCore < 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("MaxPerCore"), config.MaxPerCore, "must be greater than or equal to 0")) + } + + if config.Min < 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("Min"), config.Min, "must be greater than or equal to 0")) + } + + if config.TCPEstablishedTimeout.Duration <= 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("TCPEstablishedTimeout"), config.TCPEstablishedTimeout, "must be greater than 0")) + } + + if config.TCPCloseWaitTimeout.Duration <= 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("TCPCloseWaitTimeout"), config.TCPCloseWaitTimeout, "must be greater than 0")) + } + + return allErrs +} + +func validateProxyMode(mode componentconfig.ProxyMode, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + switch mode { + case componentconfig.ProxyModeUserspace: + case componentconfig.ProxyModeIPTables: + case "": + default: + modes := []string{string(componentconfig.ProxyModeUserspace), string(componentconfig.ProxyModeIPTables)} + errMsg := fmt.Sprintf("must be %s or blank (blank means the best-available proxy (currently iptables)", strings.Join(modes, ",")) + allErrs = append(allErrs, field.Invalid(fldPath.Child("ProxyMode"), string(mode), errMsg)) + } + return allErrs +} + +func validateClientConnectionConfiguration(config componentconfig.ClientConnectionConfiguration, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(config.Burst), fldPath.Child("Burst"))...) + return allErrs +} + +func validateHostPort(input string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + hostIP, port, err := net.SplitHostPort(input) + if err != nil { + allErrs = append(allErrs, field.Invalid(fldPath, input, "must be IP:port")) + return allErrs + } + + if ip := net.ParseIP(hostIP); ip == nil { + allErrs = append(allErrs, field.Invalid(fldPath, hostIP, "must be a valid IP")) + } + + if p, err := strconv.Atoi(port); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath, port, "must be a valid port")) + } else if p < 1 || p > 65535 { + allErrs = append(allErrs, field.Invalid(fldPath, port, "must be a valid port")) + } + + return allErrs +} diff --git a/cmd/kube-proxy/app/validation_test.go b/cmd/kube-proxy/app/validation_test.go new file mode 100644 index 00000000000..cd30adcdfa8 --- /dev/null +++ b/cmd/kube-proxy/app/validation_test.go @@ -0,0 +1,403 @@ +/* +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 app + +import ( + "strings" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/kubernetes/pkg/apis/componentconfig" +) + +func TestValidateKubeProxyConfiguration(t *testing.T) { + successCases := []componentconfig.KubeProxyConfiguration{ + { + BindAddress: "192.168.59.103", + HealthzBindAddress: "0.0.0.0:10256", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second}, + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: componentconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(2), + MaxPerCore: int32(1), + Min: int32(1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + }, + } + + for _, successCase := range successCases { + if errs := Validate(&successCase); len(errs) != 0 { + t.Errorf("expected success: %v", errs) + } + } + + errorCases := []struct { + config componentconfig.KubeProxyConfiguration + msg string + }{ + { + config: componentconfig.KubeProxyConfiguration{ + // only BindAddress is invalid + BindAddress: "10.10.12.11:2000", + HealthzBindAddress: "0.0.0.0:10256", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second}, + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: componentconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(2), + MaxPerCore: int32(1), + Min: int32(1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + }, + msg: "not a valid textual representation of an IP address", + }, + { + config: componentconfig.KubeProxyConfiguration{ + BindAddress: "10.10.12.11", + // only HealthzBindAddress is invalid + HealthzBindAddress: "0.0.0.0", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second}, + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: componentconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(2), + MaxPerCore: int32(1), + Min: int32(1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + }, + msg: "must be IP:port", + }, + { + config: componentconfig.KubeProxyConfiguration{ + BindAddress: "10.10.12.11", + HealthzBindAddress: "0.0.0.0:12345", + // only HealthzBindAddress is invalid + MetricsBindAddress: "127.0.0.1", + ClusterCIDR: "192.168.59.0/24", + UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second}, + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: componentconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(2), + MaxPerCore: int32(1), + Min: int32(1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + }, + msg: "must be IP:port", + }, + { + config: componentconfig.KubeProxyConfiguration{ + BindAddress: "10.10.12.11", + HealthzBindAddress: "0.0.0.0:12345", + MetricsBindAddress: "127.0.0.1:10249", + // only ClusterCIDR is invalid + ClusterCIDR: "192.168.59.0", + UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second}, + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: componentconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(2), + MaxPerCore: int32(1), + Min: int32(1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + }, + msg: "must be a valid CIDR block (e.g. 10.100.0.0/16)", + }, + { + config: componentconfig.KubeProxyConfiguration{ + BindAddress: "10.10.12.11", + HealthzBindAddress: "0.0.0.0:12345", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + // only UDPIdleTimeout is invalid + UDPIdleTimeout: metav1.Duration{Duration: -1 * time.Second}, + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: componentconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(2), + MaxPerCore: int32(1), + Min: int32(1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + }, + msg: "must be greater than 0", + }, + { + config: componentconfig.KubeProxyConfiguration{ + BindAddress: "10.10.12.11", + HealthzBindAddress: "0.0.0.0:12345", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second}, + // only ConfigSyncPeriod is invalid + ConfigSyncPeriod: metav1.Duration{Duration: -1 * time.Second}, + IPTables: componentconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(2), + MaxPerCore: int32(1), + Min: int32(1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + }, + msg: "must be greater than 0", + }, + } + + for _, errorCase := range errorCases { + if errs := Validate(&errorCase.config); len(errs) == 0 { + t.Errorf("expected failure for %s", errorCase.msg) + } else if !strings.Contains(errs[0].Error(), errorCase.msg) { + t.Errorf("unexpected error: %v, expected: %s", errs[0], errorCase.msg) + } + } +} + +func TestValidateKubeProxyIPTablesConfiguration(t *testing.T) { + valid := int32(5) + successCases := []componentconfig.KubeProxyIPTablesConfiguration{ + { + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + { + MasqueradeBit: &valid, + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + } + newPath := field.NewPath("KubeProxyConfiguration") + for _, successCase := range successCases { + if errs := validateKubeProxyIPTablesConfiguration(successCase, newPath.Child("KubeProxyIPTablesConfiguration")); len(errs) != 0 { + t.Errorf("expected success: %v", errs) + } + } + + invalid := int32(-10) + errorCases := []struct { + config componentconfig.KubeProxyIPTablesConfiguration + msg string + }{ + { + config: componentconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: -5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + msg: "must be greater than 0", + }, + { + config: componentconfig.KubeProxyIPTablesConfiguration{ + MasqueradeBit: &valid, + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: -1 * time.Second}, + }, + msg: "must be greater than or equal to 0", + }, + { + config: componentconfig.KubeProxyIPTablesConfiguration{ + MasqueradeBit: &invalid, + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + msg: "must be within the range [0, 31]", + }, + } + + for _, errorCase := range errorCases { + if errs := validateKubeProxyIPTablesConfiguration(errorCase.config, newPath.Child("KubeProxyIPTablesConfiguration")); len(errs) == 0 { + t.Errorf("expected failure for %s", errorCase.msg) + } else if !strings.Contains(errs[0].Error(), errorCase.msg) { + t.Errorf("unexpected error: %v, expected: %s", errs[0], errorCase.msg) + } + } +} + +func TestValidateKubeProxyConntrackConfiguration(t *testing.T) { + successCases := []componentconfig.KubeProxyConntrackConfiguration{ + { + Max: int32(2), + MaxPerCore: int32(1), + Min: int32(1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + { + Max: 0, + MaxPerCore: 0, + Min: 0, + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 60 * time.Second}, + }, + } + newPath := field.NewPath("KubeProxyConfiguration") + for _, successCase := range successCases { + if errs := validateKubeProxyConntrackConfiguration(successCase, newPath.Child("KubeProxyConntrackConfiguration")); len(errs) != 0 { + t.Errorf("expected success: %v", errs) + } + } + + errorCases := []struct { + config componentconfig.KubeProxyConntrackConfiguration + msg string + }{ + { + config: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(-1), + MaxPerCore: int32(1), + Min: int32(1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + msg: "must be greater than or equal to 0", + }, + { + config: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(2), + MaxPerCore: int32(-1), + Min: int32(1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + msg: "must be greater than or equal to 0", + }, + { + config: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(2), + MaxPerCore: int32(1), + Min: int32(-1), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + msg: "must be greater than or equal to 0", + }, + { + config: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(4), + MaxPerCore: int32(1), + Min: int32(3), + TCPEstablishedTimeout: metav1.Duration{Duration: -5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + msg: "must be greater than 0", + }, + { + config: componentconfig.KubeProxyConntrackConfiguration{ + Max: int32(4), + MaxPerCore: int32(1), + Min: int32(3), + TCPEstablishedTimeout: metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: metav1.Duration{Duration: -5 * time.Second}, + }, + msg: "must be greater than 0", + }, + } + + for _, errorCase := range errorCases { + if errs := validateKubeProxyConntrackConfiguration(errorCase.config, newPath.Child("KubeProxyConntrackConfiguration")); len(errs) == 0 { + t.Errorf("expected failure for %s", errorCase.msg) + } else if !strings.Contains(errs[0].Error(), errorCase.msg) { + t.Errorf("unexpected error: %v, expected: %s", errs[0], errorCase.msg) + } + } +} + +func TestValidateProxyMode(t *testing.T) { + newPath := field.NewPath("KubeProxyConfiguration") + + successCases := []componentconfig.ProxyMode{ + componentconfig.ProxyModeUserspace, + componentconfig.ProxyModeIPTables, + componentconfig.ProxyMode(""), + } + + for _, successCase := range successCases { + if errs := validateProxyMode(successCase, newPath.Child("ProxyMode")); len(errs) != 0 { + t.Errorf("expected success: %v", errs) + } + } + + errorCases := []struct { + mode componentconfig.ProxyMode + msg string + }{ + { + mode: componentconfig.ProxyMode("non-existing"), + msg: "or blank (blank means the best-available proxy (currently iptables)", + }, + } + + for _, errorCase := range errorCases { + if errs := validateProxyMode(errorCase.mode, newPath.Child("ProxyMode")); len(errs) == 0 { + t.Errorf("expected failure for %s", errorCase.msg) + } else if !strings.Contains(errs[0].Error(), errorCase.msg) { + t.Errorf("unexpected error: %v, expected: %s", errs[0], errorCase.msg) + } + } +} diff --git a/pkg/apis/componentconfig/BUILD b/pkg/apis/componentconfig/BUILD index 87287710ede..e0f31e75f52 100644 --- a/pkg/apis/componentconfig/BUILD +++ b/pkg/apis/componentconfig/BUILD @@ -46,7 +46,6 @@ filegroup( "//pkg/apis/componentconfig/fuzzer:all-srcs", "//pkg/apis/componentconfig/install:all-srcs", "//pkg/apis/componentconfig/v1alpha1:all-srcs", - "//pkg/apis/componentconfig/validation:all-srcs", ], tags = ["automanaged"], ) diff --git a/pkg/apis/componentconfig/validation/BUILD b/pkg/apis/componentconfig/validation/BUILD deleted file mode 100644 index 1a78f6d273f..00000000000 --- a/pkg/apis/componentconfig/validation/BUILD +++ /dev/null @@ -1,24 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = ["validation.go"], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/pkg/apis/componentconfig/validation/validation.go b/pkg/apis/componentconfig/validation/validation.go deleted file mode 100644 index 59f1df4414e..00000000000 --- a/pkg/apis/componentconfig/validation/validation.go +++ /dev/null @@ -1,17 +0,0 @@ -/* -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 validation