Skip to content

Commit 7fd399e

Browse files
authored
Merge pull request kubernetes#84143 from phenixblue/master
Add lenient decoding path for v1alpha1 kube-proxy config
2 parents c53ac65 + 10879d3 commit 7fd399e

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)