Skip to content

Commit 81e3adc

Browse files
committed
kubeadm: Fix omitempty in v1beta2
There are a couple of problems with regards to the `omitempty` in v1beta1: - It is not applied to certain fields. This makes emitting YAML configuration files in v1beta1 config format verbose by both kubeadm and third party Go lang tools. Certain fields, that were never given an explicit value would show up in the marshalled YAML document. This can cause confusion and even misconfiguration. - It can be used in inappropriate places. In this case it's used for fields, that need to be always serialized. The only one such field at the moment is `NodeRegistrationOptions.Taints`. If the `Taints` field is nil, then it's defaulted to a slice containing a single control plane node taint. If it's an empty slice, no taints are applied, thus, the cluster behaves differently. With that in mind, a Go program, that uses v1beta1 with `omitempty` on the `Taints` field has no way to specify an explicit empty slice of taints, as this would get lost after marshalling to YAML. To fix these issues the following is done in this change: - A whole bunch of additional omitemptys are placed at many fields in v1beta2. - `omitempty` is removed from `NodeRegistrationOptions.Taints` - A test, that verifies the ability to specify empty slice value for `Taints` is included. Signed-off-by: Rostislav M. Georgiev <[email protected]>
1 parent 1626aa5 commit 81e3adc

File tree

5 files changed

+101
-29
lines changed

5 files changed

+101
-29
lines changed

cmd/kubeadm/app/apis/kubeadm/v1beta2/types.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,13 @@ type ClusterConfiguration struct {
6161
metav1.TypeMeta `json:",inline"`
6262

6363
// Etcd holds configuration for etcd.
64-
Etcd Etcd `json:"etcd"`
64+
Etcd Etcd `json:"etcd,omitempty"`
6565

6666
// Networking holds configuration for the networking topology of the cluster.
67-
Networking Networking `json:"networking"`
67+
Networking Networking `json:"networking,omitempty"`
6868

6969
// KubernetesVersion is the target version of the control plane.
70-
KubernetesVersion string `json:"kubernetesVersion"`
70+
KubernetesVersion string `json:"kubernetesVersion,omitempty"`
7171

7272
// ControlPlaneEndpoint sets a stable IP address or DNS name for the control plane; it
7373
// can be a valid IP address or a RFC-1123 DNS subdomain, both with optional TCP port.
@@ -80,7 +80,7 @@ type ClusterConfiguration struct {
8080
// control plane instances.
8181
// e.g. in environments with enforced node recycling, the ControlPlaneEndpoint
8282
// could be used for assigning a stable DNS to the control plane.
83-
ControlPlaneEndpoint string `json:"controlPlaneEndpoint"`
83+
ControlPlaneEndpoint string `json:"controlPlaneEndpoint,omitempty"`
8484

8585
// APIServer contains extra settings for the API server control plane component
8686
APIServer APIServer `json:"apiServer,omitempty"`
@@ -92,16 +92,16 @@ type ClusterConfiguration struct {
9292
Scheduler ControlPlaneComponent `json:"scheduler,omitempty"`
9393

9494
// DNS defines the options for the DNS add-on installed in the cluster.
95-
DNS DNS `json:"dns"`
95+
DNS DNS `json:"dns,omitempty"`
9696

9797
// CertificatesDir specifies where to store or look for all required certificates.
98-
CertificatesDir string `json:"certificatesDir"`
98+
CertificatesDir string `json:"certificatesDir,omitempty"`
9999

100100
// ImageRepository sets the container registry to pull images from.
101101
// If empty, `k8s.gcr.io` will be used by default; in case of kubernetes version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`)
102102
// `gcr.io/kubernetes-ci-images` will be used as a default for control plane components and for kube-proxy, while `k8s.gcr.io`
103103
// will be used for all the other images.
104-
ImageRepository string `json:"imageRepository"`
104+
ImageRepository string `json:"imageRepository,omitempty"`
105105

106106
// UseHyperKubeImage controls if hyperkube should be used for Kubernetes components instead of their respective separate images
107107
UseHyperKubeImage bool `json:"useHyperKubeImage,omitempty"`
@@ -184,11 +184,11 @@ type ClusterStatus struct {
184184
// APIEndpoint struct contains elements of API server instance deployed on a node.
185185
type APIEndpoint struct {
186186
// AdvertiseAddress sets the IP address for the API server to advertise.
187-
AdvertiseAddress string `json:"advertiseAddress"`
187+
AdvertiseAddress string `json:"advertiseAddress,omitempty"`
188188

189189
// BindPort sets the secure port for the API Server to bind to.
190190
// Defaults to 6443.
191-
BindPort int32 `json:"bindPort"`
191+
BindPort int32 `json:"bindPort,omitempty"`
192192
}
193193

194194
// NodeRegistrationOptions holds fields that relate to registering a new control-plane or node to the cluster, either via "kubeadm init" or "kubeadm join"
@@ -205,7 +205,7 @@ type NodeRegistrationOptions struct {
205205
// Taints specifies the taints the Node API object should be registered with. If this field is unset, i.e. nil, in the `kubeadm init` process
206206
// it will be defaulted to []v1.Taint{'node-role.kubernetes.io/master=""'}. If you don't want to taint your control-plane node, set this field to an
207207
// empty slice, i.e. `taints: {}` in the YAML file. This field is solely used for Node registration.
208-
Taints []v1.Taint `json:"taints,omitempty"`
208+
Taints []v1.Taint `json:"taints"`
209209

210210
// KubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file
211211
// kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap
@@ -216,11 +216,11 @@ type NodeRegistrationOptions struct {
216216
// Networking contains elements describing cluster's networking configuration
217217
type Networking struct {
218218
// ServiceSubnet is the subnet used by k8s services. Defaults to "10.96.0.0/12".
219-
ServiceSubnet string `json:"serviceSubnet"`
219+
ServiceSubnet string `json:"serviceSubnet,omitempty"`
220220
// PodSubnet is the subnet used by pods.
221-
PodSubnet string `json:"podSubnet"`
221+
PodSubnet string `json:"podSubnet,omitempty"`
222222
// DNSDomain is the dns domain used by k8s services. Defaults to "cluster.local".
223-
DNSDomain string `json:"dnsDomain"`
223+
DNSDomain string `json:"dnsDomain,omitempty"`
224224
}
225225

226226
// BootstrapToken describes one bootstrap token, stored as a Secret in the cluster
@@ -302,12 +302,12 @@ type JoinConfiguration struct {
302302
metav1.TypeMeta `json:",inline"`
303303

304304
// NodeRegistration holds fields that relate to registering the new control-plane node to the cluster
305-
NodeRegistration NodeRegistrationOptions `json:"nodeRegistration"`
305+
NodeRegistration NodeRegistrationOptions `json:"nodeRegistration,omitempty"`
306306

307307
// CACertPath is the path to the SSL certificate authority used to
308308
// secure comunications between node and control-plane.
309309
// Defaults to "/etc/kubernetes/pki/ca.crt".
310-
CACertPath string `json:"caCertPath"`
310+
CACertPath string `json:"caCertPath,omitempty"`
311311

312312
// Discovery specifies the options for the kubelet to use during the TLS Bootstrap process
313313
Discovery Discovery `json:"discovery"`
@@ -336,7 +336,7 @@ type Discovery struct {
336336
// TLSBootstrapToken is a token used for TLS bootstrapping.
337337
// If .BootstrapToken is set, this field is defaulted to .BootstrapToken.Token, but can be overridden.
338338
// If .File is set, this field **must be set** in case the KubeConfigFile does not contain any other authentication information
339-
TLSBootstrapToken string `json:"tlsBootstrapToken"`
339+
TLSBootstrapToken string `json:"tlsBootstrapToken,omitempty"`
340340

341341
// Timeout modifies the discovery timeout
342342
Timeout *metav1.Duration `json:"timeout,omitempty"`
@@ -364,7 +364,7 @@ type BootstrapTokenDiscovery struct {
364364
// UnsafeSkipCAVerification allows token-based discovery
365365
// without CA verification via CACertHashes. This can weaken
366366
// the security of kubeadm since other nodes can impersonate the control-plane.
367-
UnsafeSkipCAVerification bool `json:"unsafeSkipCAVerification"`
367+
UnsafeSkipCAVerification bool `json:"unsafeSkipCAVerification,omitempty"`
368368
}
369369

370370
// FileDiscovery is used to specify a file or URL to a kubeconfig file from which to load cluster information

cmd/kubeadm/app/cmd/upgrade/common_test.go

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -180,21 +180,15 @@ func TestPrintConfiguration(t *testing.T) {
180180
expectedBytes: []byte(`[upgrade/config] Configuration used:
181181
apiServer: {}
182182
apiVersion: kubeadm.k8s.io/v1beta2
183-
certificatesDir: ""
184-
controlPlaneEndpoint: ""
185183
controllerManager: {}
186184
dns:
187185
type: CoreDNS
188186
etcd:
189187
local:
190188
dataDir: /some/path
191-
imageRepository: ""
192189
kind: ClusterConfiguration
193190
kubernetesVersion: v1.7.1
194-
networking:
195-
dnsDomain: ""
196-
podSubnet: ""
197-
serviceSubnet: ""
191+
networking: {}
198192
scheduler: {}
199193
`),
200194
},
@@ -217,8 +211,6 @@ func TestPrintConfiguration(t *testing.T) {
217211
expectedBytes: []byte(`[upgrade/config] Configuration used:
218212
apiServer: {}
219213
apiVersion: kubeadm.k8s.io/v1beta2
220-
certificatesDir: ""
221-
controlPlaneEndpoint: ""
222214
controllerManager: {}
223215
dns:
224216
type: CoreDNS
@@ -229,12 +221,9 @@ func TestPrintConfiguration(t *testing.T) {
229221
endpoints:
230222
- https://one-etcd-instance:2379
231223
keyFile: ""
232-
imageRepository: ""
233224
kind: ClusterConfiguration
234225
kubernetesVersion: v1.7.1
235226
networking:
236-
dnsDomain: ""
237-
podSubnet: ""
238227
serviceSubnet: 10.96.0.1/12
239228
scheduler: {}
240229
`),

cmd/kubeadm/app/util/config/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ go_test(
6868
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
6969
"//vendor/github.com/lithammer/dedent:go_default_library",
7070
"//vendor/github.com/pmezard/go-difflib/difflib:go_default_library",
71+
"//vendor/sigs.k8s.io/yaml:go_default_library",
7172
],
7273
)
7374

cmd/kubeadm/app/util/config/initconfiguration_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ import (
2626

2727
"github.com/pmezard/go-difflib/difflib"
2828

29+
"k8s.io/api/core/v1"
30+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
2932
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
33+
"sigs.k8s.io/yaml"
3034
)
3135

3236
func diff(expected, actual []byte) string {
@@ -283,3 +287,80 @@ apiVersion: foo.k8s.io/v1
283287
})
284288
}
285289
}
290+
291+
func TestDefaultTaintsMarshaling(t *testing.T) {
292+
tests := []struct {
293+
desc string
294+
cfg kubeadmapiv1beta2.InitConfiguration
295+
expectedTaintCnt int
296+
}{
297+
{
298+
desc: "Uninitialized nodeRegistration field produces a single taint (the master one)",
299+
cfg: kubeadmapiv1beta2.InitConfiguration{
300+
TypeMeta: metav1.TypeMeta{
301+
APIVersion: "kubeadm.k8s.io/v1beta2",
302+
Kind: constants.InitConfigurationKind,
303+
},
304+
},
305+
expectedTaintCnt: 1,
306+
},
307+
{
308+
desc: "Uninitialized taints field produces a single taint (the master one)",
309+
cfg: kubeadmapiv1beta2.InitConfiguration{
310+
TypeMeta: metav1.TypeMeta{
311+
APIVersion: "kubeadm.k8s.io/v1beta2",
312+
Kind: constants.InitConfigurationKind,
313+
},
314+
NodeRegistration: kubeadmapiv1beta2.NodeRegistrationOptions{},
315+
},
316+
expectedTaintCnt: 1,
317+
},
318+
{
319+
desc: "Forsing taints to an empty slice produces no taints",
320+
cfg: kubeadmapiv1beta2.InitConfiguration{
321+
TypeMeta: metav1.TypeMeta{
322+
APIVersion: "kubeadm.k8s.io/v1beta2",
323+
Kind: constants.InitConfigurationKind,
324+
},
325+
NodeRegistration: kubeadmapiv1beta2.NodeRegistrationOptions{
326+
Taints: []v1.Taint{},
327+
},
328+
},
329+
expectedTaintCnt: 0,
330+
},
331+
{
332+
desc: "Custom taints are used",
333+
cfg: kubeadmapiv1beta2.InitConfiguration{
334+
TypeMeta: metav1.TypeMeta{
335+
APIVersion: "kubeadm.k8s.io/v1beta2",
336+
Kind: constants.InitConfigurationKind,
337+
},
338+
NodeRegistration: kubeadmapiv1beta2.NodeRegistrationOptions{
339+
Taints: []v1.Taint{
340+
{Key: "taint1"},
341+
{Key: "taint2"},
342+
},
343+
},
344+
},
345+
expectedTaintCnt: 2,
346+
},
347+
}
348+
349+
for _, tc := range tests {
350+
t.Run(tc.desc, func(t *testing.T) {
351+
b, err := yaml.Marshal(tc.cfg)
352+
if err != nil {
353+
t.Fatalf("unexpected error while marshalling to YAML: %v", err)
354+
}
355+
356+
cfg, err := BytesToInitConfiguration(b)
357+
if err != nil {
358+
t.Fatalf("unexpected error of BytesToInitConfiguration: %v\nconfig: %s", err, string(b))
359+
}
360+
361+
if tc.expectedTaintCnt != len(cfg.NodeRegistration.Taints) {
362+
t.Fatalf("unexpected taints count\nexpected: %d\ngot: %d\ntaints: %v", tc.expectedTaintCnt, len(cfg.NodeRegistration.Taints), cfg.NodeRegistration.Taints)
363+
}
364+
})
365+
}
366+
}

cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ kind: JoinConfiguration
1111
nodeRegistration:
1212
criSocket: /var/run/dockershim.sock
1313
name: thegopher
14+
taints: null

0 commit comments

Comments
 (0)