Skip to content

Commit 10879d3

Browse files
committed
Add lenient decoding path for v1alpha1 kube-proxy
Removed unneeded comments Matched style from other PR's Only print error when lenient decoding is successful Update Bazel for BUILD Comment out existing strict decoder tests Added tests for leniant path Added comments to explain test additions Cleanup TODO's and tests Add explicit newline for appended config
1 parent d0d4572 commit 10879d3

File tree

3 files changed

+100
-19
lines changed

3 files changed

+100
-19
lines changed

cmd/kube-proxy/app/BUILD

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ go_library(
2626
"//pkg/proxy/apis:go_default_library",
2727
"//pkg/proxy/apis/config:go_default_library",
2828
"//pkg/proxy/apis/config/scheme:go_default_library",
29+
"//pkg/proxy/apis/config/v1alpha1:go_default_library",
2930
"//pkg/proxy/apis/config/validation:go_default_library",
3031
"//pkg/proxy/config:go_default_library",
3132
"//pkg/proxy/healthcheck:go_default_library",
@@ -46,6 +47,7 @@ go_library(
4647
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
4748
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
4849
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
50+
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
4951
"//staging/src/k8s.io/apimachinery/pkg/selection:go_default_library",
5052
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
5153
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
@@ -67,6 +69,7 @@ go_library(
6769
"//staging/src/k8s.io/component-base/version/verflag:go_default_library",
6870
"//staging/src/k8s.io/kube-proxy/config/v1alpha1:go_default_library",
6971
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
72+
"//vendor/github.com/pkg/errors:go_default_library",
7073
"//vendor/github.com/spf13/cobra:go_default_library",
7174
"//vendor/github.com/spf13/pflag:go_default_library",
7275
"//vendor/k8s.io/klog:go_default_library",
@@ -167,7 +170,6 @@ go_test(
167170
"//pkg/proxy/apis/config:go_default_library",
168171
"//pkg/util/configz:go_default_library",
169172
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
170-
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
171173
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
172174
"//staging/src/k8s.io/component-base/config:go_default_library",
173175
"//vendor/github.com/stretchr/testify/assert:go_default_library",

cmd/kube-proxy/app/server.go

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ import (
3232
"github.com/spf13/cobra"
3333
"github.com/spf13/pflag"
3434

35+
gerrors "github.com/pkg/errors"
3536
v1 "k8s.io/api/core/v1"
3637
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3738
"k8s.io/apimachinery/pkg/labels"
3839
"k8s.io/apimachinery/pkg/runtime"
40+
"k8s.io/apimachinery/pkg/runtime/serializer"
3941
"k8s.io/apimachinery/pkg/selection"
4042
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
4143
"k8s.io/apimachinery/pkg/util/wait"
@@ -65,6 +67,7 @@ import (
6567
"k8s.io/kubernetes/pkg/proxy/apis"
6668
kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config"
6769
proxyconfigscheme "k8s.io/kubernetes/pkg/proxy/apis/config/scheme"
70+
kubeproxyconfigv1alpha1 "k8s.io/kubernetes/pkg/proxy/apis/config/v1alpha1"
6871
"k8s.io/kubernetes/pkg/proxy/apis/config/validation"
6972
"k8s.io/kubernetes/pkg/proxy/config"
7073
"k8s.io/kubernetes/pkg/proxy/healthcheck"
@@ -369,6 +372,20 @@ func addressFromDeprecatedFlags(addr string, port int32) string {
369372
return proxyutil.AppendPortIfNeeded(addr, port)
370373
}
371374

375+
// newLenientSchemeAndCodecs returns a scheme that has only v1alpha1 registered into
376+
// it and a CodecFactory with strict decoding disabled.
377+
func newLenientSchemeAndCodecs() (*runtime.Scheme, *serializer.CodecFactory, error) {
378+
lenientScheme := runtime.NewScheme()
379+
if err := kubeproxyconfig.AddToScheme(lenientScheme); err != nil {
380+
return nil, nil, fmt.Errorf("failed to add kube-proxy config API to lenient scheme: %v", err)
381+
}
382+
if err := kubeproxyconfigv1alpha1.AddToScheme(lenientScheme); err != nil {
383+
return nil, nil, fmt.Errorf("failed to add kube-proxy config v1alpha1 API to lenient scheme: %v", err)
384+
}
385+
lenientCodecs := serializer.NewCodecFactory(lenientScheme, serializer.DisableStrict)
386+
return lenientScheme, &lenientCodecs, nil
387+
}
388+
372389
// loadConfigFromFile loads the contents of file and decodes it as a
373390
// KubeProxyConfiguration object.
374391
func (o *Options) loadConfigFromFile(file string) (*kubeproxyconfig.KubeProxyConfiguration, error) {
@@ -380,12 +397,34 @@ func (o *Options) loadConfigFromFile(file string) (*kubeproxyconfig.KubeProxyCon
380397
return o.loadConfig(data)
381398
}
382399

383-
// loadConfig decodes data as a KubeProxyConfiguration object.
400+
// loadConfig decodes a serialized KubeProxyConfiguration to the internal type.
384401
func (o *Options) loadConfig(data []byte) (*kubeproxyconfig.KubeProxyConfiguration, error) {
402+
385403
configObj, gvk, err := proxyconfigscheme.Codecs.UniversalDecoder().Decode(data, nil, nil)
386404
if err != nil {
387-
return nil, err
405+
// Try strict decoding first. If that fails decode with a lenient
406+
// decoder, which has only v1alpha1 registered, and log a warning.
407+
// The lenient path is to be dropped when support for v1alpha1 is dropped.
408+
if !runtime.IsStrictDecodingError(err) {
409+
return nil, gerrors.Wrap(err, "failed to decode")
410+
}
411+
412+
_, lenientCodecs, lenientErr := newLenientSchemeAndCodecs()
413+
if lenientErr != nil {
414+
return nil, lenientErr
415+
}
416+
417+
configObj, gvk, lenientErr = lenientCodecs.UniversalDecoder().Decode(data, nil, nil)
418+
if lenientErr != nil {
419+
// Lenient decoding failed with the current version, return the
420+
// original strict error.
421+
return nil, fmt.Errorf("failed lenient decoding: %v", err)
422+
}
423+
424+
// Continue with the v1alpha1 object that was decoded leniently, but emit a warning.
425+
klog.Warningf("using lenient decoding as strict decoding failed: %v", err)
388426
}
427+
389428
proxyConfig, ok := configObj.(*kubeproxyconfig.KubeProxyConfiguration)
390429
if !ok {
391430
return nil, fmt.Errorf("got unexpected config type: %v", gvk)

cmd/kube-proxy/app/server_test.go

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import (
3333
utilpointer "k8s.io/utils/pointer"
3434

3535
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36-
kuberuntime "k8s.io/apimachinery/pkg/runtime"
3736
"k8s.io/apimachinery/pkg/util/diff"
3837
componentbaseconfig "k8s.io/component-base/config"
3938
kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config"
@@ -161,6 +160,7 @@ nodePortAddresses:
161160
clusterCIDR string
162161
healthzBindAddress string
163162
metricsBindAddress string
163+
extraConfig string
164164
}{
165165
{
166166
name: "iptables mode, IPv4 all-zeros bind address",
@@ -219,6 +219,30 @@ nodePortAddresses:
219219
healthzBindAddress: "[fd00:1::5]:12345",
220220
metricsBindAddress: "[fd00:2::5]:23456",
221221
},
222+
{
223+
// Test for unknown field within config.
224+
// For v1alpha1 a lenient path is implemented and will throw a
225+
// strict decoding warning instead of failing to load
226+
name: "unknown field",
227+
mode: "iptables",
228+
bindAddress: "9.8.7.6",
229+
clusterCIDR: "1.2.3.0/24",
230+
healthzBindAddress: "1.2.3.4:12345",
231+
metricsBindAddress: "2.3.4.5:23456",
232+
extraConfig: "foo: bar",
233+
},
234+
{
235+
// Test for duplicate field within config.
236+
// For v1alpha1 a lenient path is implemented and will throw a
237+
// strict decoding warning instead of failing to load
238+
name: "duplicate field",
239+
mode: "iptables",
240+
bindAddress: "9.8.7.6",
241+
clusterCIDR: "1.2.3.0/24",
242+
healthzBindAddress: "1.2.3.4:12345",
243+
metricsBindAddress: "2.3.4.5:23456",
244+
extraConfig: "bindAddress: 9.8.7.6",
245+
},
222246
}
223247

224248
for _, tc := range testCases {
@@ -268,11 +292,17 @@ nodePortAddresses:
268292

269293
options := NewOptions()
270294

271-
yaml := fmt.Sprintf(
295+
baseYAML := fmt.Sprintf(
272296
yamlTemplate, tc.bindAddress, tc.clusterCIDR,
273297
tc.healthzBindAddress, tc.metricsBindAddress, tc.mode)
298+
299+
// Append additional configuration to the base yaml template
300+
yaml := fmt.Sprintf("%s\n%s", baseYAML, tc.extraConfig)
301+
274302
config, err := options.loadConfig([]byte(yaml))
303+
275304
assert.NoError(t, err, "unexpected error for %s: %v", tc.name, err)
305+
276306
if !reflect.DeepEqual(expected, config) {
277307
t.Fatalf("unexpected config for %s, diff = %s", tc.name, diff.ObjectDiff(config, expected))
278308
}
@@ -281,10 +311,15 @@ nodePortAddresses:
281311

282312
// TestLoadConfigFailures tests failure modes for loadConfig()
283313
func TestLoadConfigFailures(t *testing.T) {
284-
yamlTemplate := `bindAddress: 0.0.0.0
285-
clusterCIDR: "1.2.3.0/24"
286-
configSyncPeriod: 15s
287-
kind: KubeProxyConfiguration`
314+
// TODO(phenixblue): Uncomment below template when v1alpha2+ of kube-proxy config is
315+
// released with strict decoding. These associated tests will fail with
316+
// the lenient codec and only one config API version.
317+
/*
318+
yamlTemplate := `bindAddress: 0.0.0.0
319+
clusterCIDR: "1.2.3.0/24"
320+
configSyncPeriod: 15s
321+
kind: KubeProxyConfiguration`
322+
*/
288323

289324
testCases := []struct {
290325
name string
@@ -307,16 +342,21 @@ kind: KubeProxyConfiguration`
307342
config: "bindAddress: ::",
308343
expErr: "mapping values are not allowed in this context",
309344
},
310-
{
311-
name: "Duplicate fields",
312-
config: fmt.Sprintf("%s\nbindAddess: 1.2.3.4", yamlTemplate),
313-
checkFn: kuberuntime.IsStrictDecodingError,
314-
},
315-
{
316-
name: "Unknown field",
317-
config: fmt.Sprintf("%s\nfoo: bar", yamlTemplate),
318-
checkFn: kuberuntime.IsStrictDecodingError,
319-
},
345+
// TODO(phenixblue): Uncomment below tests when v1alpha2+ of kube-proxy config is
346+
// released with strict decoding. These tests will fail with the
347+
// lenient codec and only one config API version.
348+
/*
349+
{
350+
name: "Duplicate fields",
351+
config: fmt.Sprintf("%s\nbindAddress: 1.2.3.4", yamlTemplate),
352+
checkFn: kuberuntime.IsStrictDecodingError,
353+
},
354+
{
355+
name: "Unknown field",
356+
config: fmt.Sprintf("%s\nfoo: bar", yamlTemplate),
357+
checkFn: kuberuntime.IsStrictDecodingError,
358+
},
359+
*/
320360
}
321361

322362
version := "apiVersion: kubeproxy.config.k8s.io/v1alpha1"

0 commit comments

Comments
 (0)