mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	kubeadm: Group centric component configs
kubeadm's current implementation of component config support is "kind" centric. This has its downsides. Namely: - Kind names and numbers can change between config versions. Newer kinds can be ignored. Therefore, detection of a version change is considerably harder. - A component config can have only one kind that is managed by kubeadm. Thus a more appropriate way to identify component configs is required. Probably the best solution identified so far is a config group. A group name is unlikely to change between versions, while the kind names and structure can. Tracking component configs by group name allows us to: - Spot more easily config version changes and manage alternate versions. - Support more than one kind in a config group/version. - Abstract component configs by hiding their exact structure. Hence, this change rips off the old kind based support for component configs and replaces it with a group name based one. This also has the following extra benefits: - More tests were added. - kubeadm now errors out if an unsupported version of a known component group is used. Signed-off-by: Rostislav M. Georgiev <rostislavg@vmware.com>
This commit is contained in:
		@@ -25,8 +25,6 @@ go_library(
 | 
			
		||||
        "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/cluster-bootstrap/token/util:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/cluster-bootstrap/util/secrets:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/kube-proxy/config/v1alpha1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/kubelet/config/v1beta1:go_default_library",
 | 
			
		||||
        "//vendor/github.com/pkg/errors:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
 | 
			
		||||
	return []interface{}{
 | 
			
		||||
		fuzzInitConfiguration,
 | 
			
		||||
		fuzzClusterConfiguration,
 | 
			
		||||
		fuzzComponentConfigs,
 | 
			
		||||
		fuzzComponentConfigMap,
 | 
			
		||||
		fuzzDNS,
 | 
			
		||||
		fuzzNodeRegistration,
 | 
			
		||||
		fuzzLocalEtcd,
 | 
			
		||||
@@ -116,7 +116,7 @@ func fuzzDNS(obj *kubeadm.DNS, c fuzz.Continue) {
 | 
			
		||||
	obj.Type = kubeadm.CoreDNS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fuzzComponentConfigs(obj *kubeadm.ComponentConfigs, c fuzz.Continue) {
 | 
			
		||||
func fuzzComponentConfigMap(obj *kubeadm.ComponentConfigMap, c fuzz.Continue) {
 | 
			
		||||
	// This is intentionally empty because component config does not exists in the public api
 | 
			
		||||
	// (empty mean all ComponentConfigs fields nil, and this is necessary for getting roundtrip passing)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,8 @@ package kubeadm
 | 
			
		||||
import (
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	kubeproxyconfigv1alpha1 "k8s.io/kube-proxy/config/v1alpha1"
 | 
			
		||||
	kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 | 
			
		||||
@@ -62,9 +62,9 @@ type InitConfiguration struct {
 | 
			
		||||
type ClusterConfiguration struct {
 | 
			
		||||
	metav1.TypeMeta
 | 
			
		||||
 | 
			
		||||
	// ComponentConfigs holds internal ComponentConfig struct types known to kubeadm, should long-term only exist in the internal kubeadm API
 | 
			
		||||
	// ComponentConfigs holds component configs known to kubeadm, should long-term only exist in the internal kubeadm API
 | 
			
		||||
	// +k8s:conversion-gen=false
 | 
			
		||||
	ComponentConfigs ComponentConfigs
 | 
			
		||||
	ComponentConfigs ComponentConfigMap
 | 
			
		||||
 | 
			
		||||
	// Etcd holds configuration for etcd.
 | 
			
		||||
	Etcd Etcd
 | 
			
		||||
@@ -181,14 +181,6 @@ type ImageMeta struct {
 | 
			
		||||
	//TODO: evaluate if we need also a ImageName based on user feedbacks
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ComponentConfigs holds known internal ComponentConfig types for other components
 | 
			
		||||
type ComponentConfigs struct {
 | 
			
		||||
	// Kubelet holds the ComponentConfiguration for the kubelet
 | 
			
		||||
	Kubelet *kubeletconfigv1beta1.KubeletConfiguration
 | 
			
		||||
	// KubeProxy holds the ComponentConfiguration for the kube-proxy
 | 
			
		||||
	KubeProxy *kubeproxyconfigv1alpha1.KubeProxyConfiguration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 | 
			
		||||
 | 
			
		||||
// ClusterStatus contains the cluster status. The ClusterStatus will be stored in the kubeadm-config
 | 
			
		||||
@@ -424,3 +416,25 @@ type HostPathMount struct {
 | 
			
		||||
	// PathType is the type of the HostPath.
 | 
			
		||||
	PathType v1.HostPathType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DocumentMap is a convenient way to describe a map between a YAML document and its GVK type
 | 
			
		||||
// +k8s:deepcopy-gen=false
 | 
			
		||||
type DocumentMap map[schema.GroupVersionKind][]byte
 | 
			
		||||
 | 
			
		||||
// ComponentConfig holds a known component config
 | 
			
		||||
type ComponentConfig interface {
 | 
			
		||||
	// DeepCopy should create a new deep copy of the component config in place
 | 
			
		||||
	DeepCopy() ComponentConfig
 | 
			
		||||
 | 
			
		||||
	// Marshal is marshalling the config into a YAML document returned as a byte slice
 | 
			
		||||
	Marshal() ([]byte, error)
 | 
			
		||||
 | 
			
		||||
	// Unmarshal loads the config from a document map. No config in the document map is no error.
 | 
			
		||||
	Unmarshal(docmap DocumentMap) error
 | 
			
		||||
 | 
			
		||||
	// Default patches the component config with kubeadm preferred defaults
 | 
			
		||||
	Default(cfg *ClusterConfiguration, localAPIEndpoint *APIEndpoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ComponentConfigMap is a map between a group name (as in GVK group) and a ComponentConfig
 | 
			
		||||
type ComponentConfigMap map[string]ComponentConfig
 | 
			
		||||
 
 | 
			
		||||
@@ -32,12 +32,9 @@ go_test(
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//cmd/kubeadm/app/apis/kubeadm:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/kube-proxy/config/v1alpha1:go_default_library",
 | 
			
		||||
        "//vendor/github.com/spf13/pflag:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/utils/pointer:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ func ValidateClusterConfiguration(c *kubeadm.ClusterConfiguration) field.ErrorLi
 | 
			
		||||
	allErrs = append(allErrs, ValidateFeatureGates(c.FeatureGates, field.NewPath("featureGates"))...)
 | 
			
		||||
	allErrs = append(allErrs, ValidateHostPort(c.ControlPlaneEndpoint, field.NewPath("controlPlaneEndpoint"))...)
 | 
			
		||||
	allErrs = append(allErrs, ValidateEtcd(&c.Etcd, field.NewPath("etcd"))...)
 | 
			
		||||
	allErrs = append(allErrs, componentconfigs.Known.Validate(c)...)
 | 
			
		||||
	allErrs = append(allErrs, componentconfigs.Validate(c)...)
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,16 +20,12 @@ import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/pflag"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/sets"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/validation/field"
 | 
			
		||||
	kubeproxyconfigv1alpha1 "k8s.io/kube-proxy/config/v1alpha1"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	utilpointer "k8s.io/utils/pointer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestValidateToken(t *testing.T) {
 | 
			
		||||
@@ -448,31 +444,6 @@ func TestValidateInitConfiguration(t *testing.T) {
 | 
			
		||||
							DataDir: "/some/path",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					ComponentConfigs: kubeadm.ComponentConfigs{
 | 
			
		||||
						KubeProxy: &kubeproxyconfigv1alpha1.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: kubeproxyconfigv1alpha1.KubeProxyIPTablesConfiguration{
 | 
			
		||||
								MasqueradeAll: true,
 | 
			
		||||
								SyncPeriod:    metav1.Duration{Duration: 5 * time.Second},
 | 
			
		||||
								MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
 | 
			
		||||
							},
 | 
			
		||||
							IPVS: kubeproxyconfigv1alpha1.KubeProxyIPVSConfiguration{
 | 
			
		||||
								SyncPeriod:    metav1.Duration{Duration: 10 * time.Second},
 | 
			
		||||
								MinSyncPeriod: metav1.Duration{Duration: 5 * time.Second},
 | 
			
		||||
							},
 | 
			
		||||
							Conntrack: kubeproxyconfigv1alpha1.KubeProxyConntrackConfiguration{
 | 
			
		||||
								MaxPerCore:            utilpointer.Int32Ptr(1),
 | 
			
		||||
								Min:                   utilpointer.Int32Ptr(1),
 | 
			
		||||
								TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
 | 
			
		||||
								TCPCloseWaitTimeout:   &metav1.Duration{Duration: 5 * time.Second},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Networking: kubeadm.Networking{
 | 
			
		||||
						ServiceSubnet: "10.96.0.1/12",
 | 
			
		||||
						DNSDomain:     "cluster.local",
 | 
			
		||||
@@ -494,31 +465,6 @@ func TestValidateInitConfiguration(t *testing.T) {
 | 
			
		||||
							DataDir: "/some/path",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					ComponentConfigs: kubeadm.ComponentConfigs{
 | 
			
		||||
						KubeProxy: &kubeproxyconfigv1alpha1.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: kubeproxyconfigv1alpha1.KubeProxyIPTablesConfiguration{
 | 
			
		||||
								MasqueradeAll: true,
 | 
			
		||||
								SyncPeriod:    metav1.Duration{Duration: 5 * time.Second},
 | 
			
		||||
								MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
 | 
			
		||||
							},
 | 
			
		||||
							IPVS: kubeproxyconfigv1alpha1.KubeProxyIPVSConfiguration{
 | 
			
		||||
								SyncPeriod:    metav1.Duration{Duration: 10 * time.Second},
 | 
			
		||||
								MinSyncPeriod: metav1.Duration{Duration: 5 * time.Second},
 | 
			
		||||
							},
 | 
			
		||||
							Conntrack: kubeproxyconfigv1alpha1.KubeProxyConntrackConfiguration{
 | 
			
		||||
								MaxPerCore:            utilpointer.Int32Ptr(1),
 | 
			
		||||
								Min:                   utilpointer.Int32Ptr(1),
 | 
			
		||||
								TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
 | 
			
		||||
								TCPCloseWaitTimeout:   &metav1.Duration{Duration: 5 * time.Second},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Networking: kubeadm.Networking{
 | 
			
		||||
						ServiceSubnet: "2001:db8::1/98",
 | 
			
		||||
						DNSDomain:     "cluster.local",
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@ import (
 | 
			
		||||
	corev1 "k8s.io/api/core/v1"
 | 
			
		||||
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	runtime "k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	v1alpha1 "k8s.io/kube-proxy/config/v1alpha1"
 | 
			
		||||
	v1beta1 "k8s.io/kubelet/config/v1beta1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
@@ -152,7 +150,13 @@ func (in *BootstrapTokenString) DeepCopy() *BootstrapTokenString {
 | 
			
		||||
func (in *ClusterConfiguration) DeepCopyInto(out *ClusterConfiguration) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	out.TypeMeta = in.TypeMeta
 | 
			
		||||
	in.ComponentConfigs.DeepCopyInto(&out.ComponentConfigs)
 | 
			
		||||
	if in.ComponentConfigs != nil {
 | 
			
		||||
		in, out := &in.ComponentConfigs, &out.ComponentConfigs
 | 
			
		||||
		*out = make(ComponentConfigMap, len(*in))
 | 
			
		||||
		for key, val := range *in {
 | 
			
		||||
			(*out)[key] = val.DeepCopy()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	in.Etcd.DeepCopyInto(&out.Etcd)
 | 
			
		||||
	out.Networking = in.Networking
 | 
			
		||||
	in.APIServer.DeepCopyInto(&out.APIServer)
 | 
			
		||||
@@ -220,29 +224,25 @@ func (in *ClusterStatus) DeepCopyObject() runtime.Object {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *ComponentConfigs) DeepCopyInto(out *ComponentConfigs) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	if in.Kubelet != nil {
 | 
			
		||||
		in, out := &in.Kubelet, &out.Kubelet
 | 
			
		||||
		*out = new(v1beta1.KubeletConfiguration)
 | 
			
		||||
		(*in).DeepCopyInto(*out)
 | 
			
		||||
func (in ComponentConfigMap) DeepCopyInto(out *ComponentConfigMap) {
 | 
			
		||||
	{
 | 
			
		||||
		in := &in
 | 
			
		||||
		*out = make(ComponentConfigMap, len(*in))
 | 
			
		||||
		for key, val := range *in {
 | 
			
		||||
			(*out)[key] = val.DeepCopy()
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if in.KubeProxy != nil {
 | 
			
		||||
		in, out := &in.KubeProxy, &out.KubeProxy
 | 
			
		||||
		*out = new(v1alpha1.KubeProxyConfiguration)
 | 
			
		||||
		(*in).DeepCopyInto(*out)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentConfigs.
 | 
			
		||||
func (in *ComponentConfigs) DeepCopy() *ComponentConfigs {
 | 
			
		||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentConfigMap.
 | 
			
		||||
func (in ComponentConfigMap) DeepCopy() ComponentConfigMap {
 | 
			
		||||
	if in == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	out := new(ComponentConfigs)
 | 
			
		||||
	out := new(ComponentConfigMap)
 | 
			
		||||
	in.DeepCopyInto(out)
 | 
			
		||||
	return out
 | 
			
		||||
	return *out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,6 @@ go_test(
 | 
			
		||||
        "//cmd/kubeadm/app/apis/output/scheme:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/apis/output/v1alpha1:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/cmd/options:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/componentconfigs:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/constants:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/features:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/util:go_default_library",
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ func NewCmdConfigPrintJoinDefaults(out io.Writer) *cobra.Command {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCmdConfigPrintActionDefaults(out io.Writer, action string, configBytesProc func() ([]byte, error)) *cobra.Command {
 | 
			
		||||
	componentConfigs := []string{}
 | 
			
		||||
	kinds := []string{}
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   fmt.Sprintf("%s-defaults", action),
 | 
			
		||||
		Short: fmt.Sprintf("Print default %s configuration, that can be used for 'kubeadm %s'", action, action),
 | 
			
		||||
@@ -127,11 +127,15 @@ func newCmdConfigPrintActionDefaults(out io.Writer, action string, configBytesPr
 | 
			
		||||
			not perform the real computation for creating a token.
 | 
			
		||||
		`), action, action, placeholderToken),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			return runConfigPrintActionDefaults(out, componentConfigs, configBytesProc)
 | 
			
		||||
			groups, err := mapLegacyKindsToGroups(kinds)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return runConfigPrintActionDefaults(out, groups, configBytesProc)
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmd.Flags().StringSliceVar(&componentConfigs, "component-configs", componentConfigs,
 | 
			
		||||
		fmt.Sprintf("A comma-separated list for component config API objects to print the default values for. Available values: %v. If this flag is not set, no component configs will be printed.", getSupportedComponentConfigAPIObjects()))
 | 
			
		||||
	cmd.Flags().StringSliceVar(&kinds, "component-configs", kinds,
 | 
			
		||||
		fmt.Sprintf("A comma-separated list for component config API objects to print the default values for. Available values: %v. If this flag is not set, no component configs will be printed.", getSupportedComponentConfigKinds()))
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -154,35 +158,49 @@ func runConfigPrintActionDefaults(out io.Writer, componentConfigs []string, conf
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefaultComponentConfigBytes(apiObject string) ([]byte, error) {
 | 
			
		||||
	registration, ok := componentconfigs.Known[componentconfigs.RegistrationKind(apiObject)]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return []byte{}, errors.Errorf("--component-configs needs to contain some of %v", getSupportedComponentConfigAPIObjects())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func getDefaultComponentConfigBytes(group string) ([]byte, error) {
 | 
			
		||||
	defaultedInitConfig, err := getDefaultedInitConfig()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return []byte{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	realObj, ok := registration.GetFromInternalConfig(&defaultedInitConfig.ClusterConfiguration)
 | 
			
		||||
	componentCfg, ok := defaultedInitConfig.ComponentConfigs[group]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return []byte{}, errors.New("GetFromInternalConfig failed")
 | 
			
		||||
		return []byte{}, errors.Errorf("cannot get defaulted config for component group %q", group)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return registration.Marshal(realObj)
 | 
			
		||||
	return componentCfg.Marshal()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getSupportedComponentConfigAPIObjects returns all currently supported component config API object names
 | 
			
		||||
func getSupportedComponentConfigAPIObjects() []string {
 | 
			
		||||
// legacyKindToGroupMap maps between the old API object types and the new way of specifying component configs (by group)
 | 
			
		||||
var legacyKindToGroupMap = map[string]string{
 | 
			
		||||
	"KubeletConfiguration":   componentconfigs.KubeletGroup,
 | 
			
		||||
	"KubeProxyConfiguration": componentconfigs.KubeProxyGroup,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getSupportedComponentConfigKinds returns all currently supported component config API object names
 | 
			
		||||
func getSupportedComponentConfigKinds() []string {
 | 
			
		||||
	objects := []string{}
 | 
			
		||||
	for componentType := range componentconfigs.Known {
 | 
			
		||||
	for componentType := range legacyKindToGroupMap {
 | 
			
		||||
		objects = append(objects, string(componentType))
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(objects)
 | 
			
		||||
	return objects
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mapLegacyKindsToGroups(kinds []string) ([]string, error) {
 | 
			
		||||
	groups := []string{}
 | 
			
		||||
	for _, kind := range kinds {
 | 
			
		||||
		group, ok := legacyKindToGroupMap[kind]
 | 
			
		||||
		if ok {
 | 
			
		||||
			groups = append(groups, group)
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil, errors.Errorf("--component-configs needs to contain some of %v", getSupportedComponentConfigKinds())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return groups, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefaultedInitConfig() (*kubeadmapi.InitConfiguration, error) {
 | 
			
		||||
	initCfg := &kubeadmapiv1beta2.InitConfiguration{
 | 
			
		||||
		LocalAPIEndpoint: kubeadmapiv1beta2.APIEndpoint{AdvertiseAddress: "1.2.3.4"},
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,6 @@ import (
 | 
			
		||||
	"github.com/lithammer/dedent"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
 | 
			
		||||
	configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
 | 
			
		||||
@@ -299,7 +298,7 @@ func TestNewCmdConfigPrintActionDefaults(t *testing.T) {
 | 
			
		||||
			expectedKinds: []string{
 | 
			
		||||
				constants.ClusterConfigurationKind,
 | 
			
		||||
				constants.InitConfigurationKind,
 | 
			
		||||
				string(componentconfigs.KubeProxyConfigurationKind),
 | 
			
		||||
				"KubeProxyConfiguration",
 | 
			
		||||
			},
 | 
			
		||||
			componentConfigs: "KubeProxyConfiguration",
 | 
			
		||||
			cmdProc:          NewCmdConfigPrintInitDefaults,
 | 
			
		||||
@@ -309,8 +308,8 @@ func TestNewCmdConfigPrintActionDefaults(t *testing.T) {
 | 
			
		||||
			expectedKinds: []string{
 | 
			
		||||
				constants.ClusterConfigurationKind,
 | 
			
		||||
				constants.InitConfigurationKind,
 | 
			
		||||
				string(componentconfigs.KubeProxyConfigurationKind),
 | 
			
		||||
				string(componentconfigs.KubeletConfigurationKind),
 | 
			
		||||
				"KubeProxyConfiguration",
 | 
			
		||||
				"KubeletConfiguration",
 | 
			
		||||
			},
 | 
			
		||||
			componentConfigs: "KubeProxyConfiguration,KubeletConfiguration",
 | 
			
		||||
			cmdProc:          NewCmdConfigPrintInitDefaults,
 | 
			
		||||
@@ -326,7 +325,7 @@ func TestNewCmdConfigPrintActionDefaults(t *testing.T) {
 | 
			
		||||
			name: "JoinConfiguration: KubeProxyConfiguration",
 | 
			
		||||
			expectedKinds: []string{
 | 
			
		||||
				constants.JoinConfigurationKind,
 | 
			
		||||
				string(componentconfigs.KubeProxyConfigurationKind),
 | 
			
		||||
				"KubeProxyConfiguration",
 | 
			
		||||
			},
 | 
			
		||||
			componentConfigs: "KubeProxyConfiguration",
 | 
			
		||||
			cmdProc:          NewCmdConfigPrintJoinDefaults,
 | 
			
		||||
@@ -335,8 +334,8 @@ func TestNewCmdConfigPrintActionDefaults(t *testing.T) {
 | 
			
		||||
			name: "JoinConfiguration: KubeProxyConfiguration and KubeletConfiguration",
 | 
			
		||||
			expectedKinds: []string{
 | 
			
		||||
				constants.JoinConfigurationKind,
 | 
			
		||||
				string(componentconfigs.KubeProxyConfigurationKind),
 | 
			
		||||
				string(componentconfigs.KubeletConfigurationKind),
 | 
			
		||||
				"KubeProxyConfiguration",
 | 
			
		||||
				"KubeletConfiguration",
 | 
			
		||||
			},
 | 
			
		||||
			componentConfigs: "KubeProxyConfiguration,KubeletConfiguration",
 | 
			
		||||
			cmdProc:          NewCmdConfigPrintJoinDefaults,
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ go_library(
 | 
			
		||||
        "//cmd/kubeadm/app/cmd/options:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/cmd/phases/workflow:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/cmd/util:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/componentconfigs:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/constants:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/phases/addons/dns:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/phases/addons/proxy:go_default_library",
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import (
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
 | 
			
		||||
	cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
 | 
			
		||||
	kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -71,8 +72,13 @@ func runKubeletStart(c workflow.RunData) error {
 | 
			
		||||
		return errors.Wrap(err, "error writing a dynamic environment file for the kubelet")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kubeletCfg, ok := data.Cfg().ComponentConfigs[componentconfigs.KubeletGroup]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return errors.New("no kubelet component config found in the active component config set")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Write the kubelet configuration file to disk.
 | 
			
		||||
	if err := kubeletphase.WriteConfigToDisk(data.Cfg().ComponentConfigs.Kubelet, data.KubeletDir()); err != nil {
 | 
			
		||||
	if err := kubeletphase.WriteConfigToDisk(kubeletCfg, data.KubeletDir()); err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error writing kubelet configuration to disk")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -120,7 +120,7 @@ func runUploadKubeletConfig(c workflow.RunData) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	klog.V(1).Infoln("[upload-config] Uploading the kubelet component config to a ConfigMap")
 | 
			
		||||
	if err = kubeletphase.CreateConfigMap(cfg.ClusterConfiguration.ComponentConfigs.Kubelet, cfg.KubernetesVersion, client); err != nil {
 | 
			
		||||
	if err = kubeletphase.CreateConfigMap(&cfg.ClusterConfiguration, client); err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error creating kubelet configuration ConfigMap")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 | 
			
		||||
go_library(
 | 
			
		||||
    name = "go_default_library",
 | 
			
		||||
    srcs = [
 | 
			
		||||
        "config.go",
 | 
			
		||||
        "defaults.go",
 | 
			
		||||
        "registrations.go",
 | 
			
		||||
        "configset.go",
 | 
			
		||||
        "kubelet.go",
 | 
			
		||||
        "kubeproxy.go",
 | 
			
		||||
        "scheme.go",
 | 
			
		||||
        "validation.go",
 | 
			
		||||
        "utils.go",
 | 
			
		||||
    ],
 | 
			
		||||
    importpath = "k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs",
 | 
			
		||||
    visibility = ["//visibility:public"],
 | 
			
		||||
@@ -37,16 +37,28 @@ go_library(
 | 
			
		||||
 | 
			
		||||
go_test(
 | 
			
		||||
    name = "go_default_test",
 | 
			
		||||
    srcs = ["config_test.go"],
 | 
			
		||||
    srcs = [
 | 
			
		||||
        "configset_test.go",
 | 
			
		||||
        "kubelet_test.go",
 | 
			
		||||
        "kubeproxy_test.go",
 | 
			
		||||
    ],
 | 
			
		||||
    embed = [":go_default_library"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//cmd/kubeadm/app/apis/kubeadm:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/constants:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/util/apiclient:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/features:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/util:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/component-base/config/v1alpha1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/kube-proxy/config/v1alpha1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/kubelet/config/v1beta1:go_default_library",
 | 
			
		||||
        "//vendor/github.com/lithammer/dedent:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/utils/pointer:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,92 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2018 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 componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"k8s.io/klog"
 | 
			
		||||
 | 
			
		||||
	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/version"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	kubeproxyconfigv1alpha1 "k8s.io/kube-proxy/config/v1alpha1"
 | 
			
		||||
	kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
 | 
			
		||||
	kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetFromKubeletConfigMap returns the pointer to the ComponentConfig API object read from the kubelet-config-version
 | 
			
		||||
// ConfigMap map stored in the cluster
 | 
			
		||||
func GetFromKubeletConfigMap(client clientset.Interface, version *version.Version) (runtime.Object, error) {
 | 
			
		||||
 | 
			
		||||
	// Read the ConfigMap from the cluster based on what version the kubelet is
 | 
			
		||||
	configMapName := kubeadmconstants.GetKubeletConfigMapName(version)
 | 
			
		||||
	kubeletCfg, err := apiclient.GetConfigMapWithRetry(client, metav1.NamespaceSystem, configMapName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kubeletConfigData, ok := kubeletCfg.Data[kubeadmconstants.KubeletBaseConfigurationConfigMapKey]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.Errorf("unexpected error when reading %s ConfigMap: %s key value pair missing",
 | 
			
		||||
			configMapName, kubeadmconstants.KubeletBaseConfigurationConfigMapKey)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Decodes the kubeletConfigData into the internal component config
 | 
			
		||||
	obj := &kubeletconfigv1beta1.KubeletConfiguration{}
 | 
			
		||||
	err = unmarshalObject(obj, []byte(kubeletConfigData))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return obj, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFromKubeProxyConfigMap returns the pointer to the ComponentConfig API object read from the kube-proxy
 | 
			
		||||
// ConfigMap map stored in the cluster
 | 
			
		||||
func GetFromKubeProxyConfigMap(client clientset.Interface, version *version.Version) (runtime.Object, error) {
 | 
			
		||||
 | 
			
		||||
	// Read the ConfigMap from the cluster
 | 
			
		||||
	kubeproxyCfg, err := apiclient.GetConfigMapWithRetry(client, metav1.NamespaceSystem, kubeadmconstants.KubeProxyConfigMap)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// The Kube-Proxy config map may be non-existent, because the user has decided to manage it by themselves
 | 
			
		||||
		// or to use other proxy solution. It may also be forbidden - if the kube-proxy phase was skipped we have neither
 | 
			
		||||
		// the config map, nor the RBAC rules allowing join access to it.
 | 
			
		||||
		if apierrors.IsNotFound(err) || apierrors.IsForbidden(err) {
 | 
			
		||||
			klog.Warningf("Warning: No kube-proxy config is loaded. Continuing without it: %v", err)
 | 
			
		||||
			return nil, nil
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kubeproxyConfigData, ok := kubeproxyCfg.Data[kubeadmconstants.KubeProxyConfigMapKey]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.Errorf("unexpected error when reading %s ConfigMap: %s key value pair missing",
 | 
			
		||||
			kubeadmconstants.KubeProxyConfigMap, kubeadmconstants.KubeProxyConfigMapKey)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Decodes the Config map dat into the internal component config
 | 
			
		||||
	obj := &kubeproxyconfigv1alpha1.KubeProxyConfiguration{}
 | 
			
		||||
	err = unmarshalObject(obj, []byte(kubeproxyConfigData))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return obj, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,147 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2018 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 componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/version"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	clientsetfake "k8s.io/client-go/kubernetes/fake"
 | 
			
		||||
	kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cfgFiles = map[string][]byte{
 | 
			
		||||
	"Kube-proxy_componentconfig": []byte(`
 | 
			
		||||
apiVersion: kubeproxy.config.k8s.io/v1alpha1
 | 
			
		||||
kind: KubeProxyConfiguration
 | 
			
		||||
`),
 | 
			
		||||
	"Kubelet_componentconfig": []byte(`
 | 
			
		||||
apiVersion: kubelet.config.k8s.io/v1beta1
 | 
			
		||||
kind: KubeletConfiguration
 | 
			
		||||
`),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetFromConfigMap(t *testing.T) {
 | 
			
		||||
	k8sVersion := version.MustParseGeneric(kubeadmconstants.CurrentKubernetesVersion.String())
 | 
			
		||||
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		name          string
 | 
			
		||||
		component     RegistrationKind
 | 
			
		||||
		configMap     *fakeConfigMap
 | 
			
		||||
		expectedError bool
 | 
			
		||||
		expectedNil   bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:      "valid kube-proxy",
 | 
			
		||||
			component: KubeProxyConfigurationKind,
 | 
			
		||||
			configMap: &fakeConfigMap{
 | 
			
		||||
				name: kubeadmconstants.KubeProxyConfigMap,
 | 
			
		||||
				data: map[string]string{
 | 
			
		||||
					kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        "valid kube-proxy - missing ConfigMap",
 | 
			
		||||
			component:   KubeProxyConfigurationKind,
 | 
			
		||||
			configMap:   nil,
 | 
			
		||||
			expectedNil: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:      "invalid kube-proxy - missing key",
 | 
			
		||||
			component: KubeProxyConfigurationKind,
 | 
			
		||||
			configMap: &fakeConfigMap{
 | 
			
		||||
				name: kubeadmconstants.KubeProxyConfigMap,
 | 
			
		||||
				data: map[string]string{},
 | 
			
		||||
			},
 | 
			
		||||
			expectedError: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:      "valid kubelet",
 | 
			
		||||
			component: KubeletConfigurationKind,
 | 
			
		||||
			configMap: &fakeConfigMap{
 | 
			
		||||
				name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion),
 | 
			
		||||
				data: map[string]string{
 | 
			
		||||
					kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:          "invalid kubelet - missing ConfigMap",
 | 
			
		||||
			component:     KubeletConfigurationKind,
 | 
			
		||||
			configMap:     nil,
 | 
			
		||||
			expectedError: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:      "invalid kubelet - missing key",
 | 
			
		||||
			component: KubeletConfigurationKind,
 | 
			
		||||
			configMap: &fakeConfigMap{
 | 
			
		||||
				name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion),
 | 
			
		||||
				data: map[string]string{},
 | 
			
		||||
			},
 | 
			
		||||
			expectedError: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, rt := range tests {
 | 
			
		||||
		t.Run(rt.name, func(t2 *testing.T) {
 | 
			
		||||
			client := clientsetfake.NewSimpleClientset()
 | 
			
		||||
 | 
			
		||||
			if rt.configMap != nil {
 | 
			
		||||
				err := rt.configMap.create(client)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					t.Errorf("unexpected create ConfigMap %s", rt.configMap.name)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			registration := Known[rt.component]
 | 
			
		||||
 | 
			
		||||
			obj, err := registration.GetFromConfigMap(client, k8sVersion)
 | 
			
		||||
			if rt.expectedError != (err != nil) {
 | 
			
		||||
				t.Errorf("unexpected return err from GetFromConfigMap: %v", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if rt.expectedError {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if rt.expectedNil != (obj == nil) {
 | 
			
		||||
				t.Error("unexpected return value")
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type fakeConfigMap struct {
 | 
			
		||||
	name string
 | 
			
		||||
	data map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *fakeConfigMap) create(client clientset.Interface) error {
 | 
			
		||||
	return apiclient.CreateOrUpdateConfigMap(client, &v1.ConfigMap{
 | 
			
		||||
		ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
			Name:      c.name,
 | 
			
		||||
			Namespace: metav1.NamespaceSystem,
 | 
			
		||||
		},
 | 
			
		||||
		Data: c.data,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										204
									
								
								cmd/kubeadm/app/componentconfigs/configset.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								cmd/kubeadm/app/componentconfigs/configset.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,204 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 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 componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sort"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
 | 
			
		||||
	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/validation/field"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	"k8s.io/klog"
 | 
			
		||||
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// handler is a package internal type that handles component config factory and common functionality.
 | 
			
		||||
// Every component config group should have exactly one static instance of handler.
 | 
			
		||||
type handler struct {
 | 
			
		||||
	// GroupVersion holds this handler's group name and preferred version
 | 
			
		||||
	GroupVersion schema.GroupVersion
 | 
			
		||||
 | 
			
		||||
	// AddToScheme points to a func that should add the GV types to a schema
 | 
			
		||||
	AddToScheme func(*runtime.Scheme) error
 | 
			
		||||
 | 
			
		||||
	// CreateEmpty returns an empty kubeadmapi.ComponentConfig (not even defaulted)
 | 
			
		||||
	CreateEmpty func() kubeadmapi.ComponentConfig
 | 
			
		||||
 | 
			
		||||
	// fromCluster should load the component config from a config map on the cluster.
 | 
			
		||||
	// Don't use this directly! Use FromCluster instead!
 | 
			
		||||
	fromCluster func(*handler, clientset.Interface, *kubeadmapi.ClusterConfiguration) (kubeadmapi.ComponentConfig, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FromDocumentMap looks in the document map for documents with this handler's group.
 | 
			
		||||
// If such are found a new component config is instantiated and the documents are loaded into it.
 | 
			
		||||
// No error is returned if no documents are found.
 | 
			
		||||
func (h *handler) FromDocumentMap(docmap kubeadmapi.DocumentMap) (kubeadmapi.ComponentConfig, error) {
 | 
			
		||||
	for gvk := range docmap {
 | 
			
		||||
		if gvk.Group == h.GroupVersion.Group {
 | 
			
		||||
			cfg := h.CreateEmpty()
 | 
			
		||||
			if err := cfg.Unmarshal(docmap); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return cfg, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fromConfigMap is an utility function, which will load the value of a key of a config map and use h.FromDocumentMap() to perform the parsing
 | 
			
		||||
// This is an utility func. Used by the component config support implementations. Don't use it outside of that context.
 | 
			
		||||
func (h *handler) fromConfigMap(client clientset.Interface, cmName, cmKey string, mustExist bool) (kubeadmapi.ComponentConfig, error) {
 | 
			
		||||
	configMap, err := apiclient.GetConfigMapWithRetry(client, metav1.NamespaceSystem, cmName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if !mustExist && (apierrors.IsNotFound(err) || apierrors.IsForbidden(err)) {
 | 
			
		||||
			klog.Warningf("Warning: No %s config is loaded. Continuing without it: %v", h.GroupVersion, err)
 | 
			
		||||
			return nil, nil
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	configData, ok := configMap.Data[cmKey]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.Errorf("unexpected error when reading %s ConfigMap: %s key value pair missing", cmName, cmKey)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(configData))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return h.FromDocumentMap(gvkmap)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FromCluster loads a component from a config map in the cluster
 | 
			
		||||
func (h *handler) FromCluster(clientset clientset.Interface, clusterCfg *kubeadmapi.ClusterConfiguration) (kubeadmapi.ComponentConfig, error) {
 | 
			
		||||
	return h.fromCluster(h, clientset, clusterCfg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Marshal is an utility function, used by the component config support implementations to marshal a runtime.Object to YAML with the
 | 
			
		||||
// correct group and version
 | 
			
		||||
func (h *handler) Marshal(object runtime.Object) ([]byte, error) {
 | 
			
		||||
	return kubeadmutil.MarshalToYamlForCodecs(object, h.GroupVersion, Codecs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unmarshal attempts to unmarshal a runtime.Object from a document map. If no object is found, no error is returned.
 | 
			
		||||
// If a matching group is found, but no matching version an error is returned indicating that users should do manual conversion.
 | 
			
		||||
func (h *handler) Unmarshal(from kubeadmapi.DocumentMap, into runtime.Object) error {
 | 
			
		||||
	for gvk, yaml := range from {
 | 
			
		||||
		// If this is a different group, we ignore it
 | 
			
		||||
		if gvk.Group != h.GroupVersion.Group {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If this is the correct group, but different version, we return an error
 | 
			
		||||
		if gvk.Version != h.GroupVersion.Version {
 | 
			
		||||
			// TODO: Replace this with a special error type and make UX better around it
 | 
			
		||||
			return errors.Errorf("unexpected apiVersion %q, you may have to do manual conversion to %q and execute kubeadm again", gvk.GroupVersion(), h.GroupVersion)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// As long as we support only component configs with a single kind, this is allowed
 | 
			
		||||
		return runtime.DecodeInto(Codecs.UniversalDecoder(), yaml, into)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// known holds the known component config handlers. Add new component configs here.
 | 
			
		||||
var known = []*handler{
 | 
			
		||||
	&kubeProxyHandler,
 | 
			
		||||
	&kubeletHandler,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ensureInitializedComponentConfigs is an utility func to initialize the ComponentConfigMap in ClusterConfiguration prior to possible writes to it
 | 
			
		||||
func ensureInitializedComponentConfigs(clusterCfg *kubeadmapi.ClusterConfiguration) {
 | 
			
		||||
	if clusterCfg.ComponentConfigs == nil {
 | 
			
		||||
		clusterCfg.ComponentConfigs = kubeadmapi.ComponentConfigMap{}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Default sets up defaulted component configs in the supplied ClusterConfiguration
 | 
			
		||||
func Default(clusterCfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint *kubeadmapi.APIEndpoint) {
 | 
			
		||||
	ensureInitializedComponentConfigs(clusterCfg)
 | 
			
		||||
 | 
			
		||||
	for _, handler := range known {
 | 
			
		||||
		// If the component config exists, simply default it. Otherwise, create it before defaulting.
 | 
			
		||||
		group := handler.GroupVersion.Group
 | 
			
		||||
		if componentCfg, ok := clusterCfg.ComponentConfigs[group]; ok {
 | 
			
		||||
			componentCfg.Default(clusterCfg, localAPIEndpoint)
 | 
			
		||||
		} else {
 | 
			
		||||
			componentCfg := handler.CreateEmpty()
 | 
			
		||||
			componentCfg.Default(clusterCfg, localAPIEndpoint)
 | 
			
		||||
			clusterCfg.ComponentConfigs[group] = componentCfg
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FetchFromCluster attempts to fetch all known component configs from their config maps and store them in the supplied ClusterConfiguration
 | 
			
		||||
func FetchFromCluster(clusterCfg *kubeadmapi.ClusterConfiguration, client clientset.Interface) error {
 | 
			
		||||
	ensureInitializedComponentConfigs(clusterCfg)
 | 
			
		||||
 | 
			
		||||
	for _, handler := range known {
 | 
			
		||||
		componentCfg, err := handler.FromCluster(client, clusterCfg)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if componentCfg != nil {
 | 
			
		||||
			clusterCfg.ComponentConfigs[handler.GroupVersion.Group] = componentCfg
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FetchFromDocumentMap attempts to load all known component configs from a document map into the supplied ClusterConfiguration
 | 
			
		||||
func FetchFromDocumentMap(clusterCfg *kubeadmapi.ClusterConfiguration, docmap kubeadmapi.DocumentMap) error {
 | 
			
		||||
	ensureInitializedComponentConfigs(clusterCfg)
 | 
			
		||||
 | 
			
		||||
	for _, handler := range known {
 | 
			
		||||
		componentCfg, err := handler.FromDocumentMap(docmap)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if componentCfg != nil {
 | 
			
		||||
			clusterCfg.ComponentConfigs[handler.GroupVersion.Group] = componentCfg
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate is a placeholder for performing a validation on an already loaded component configs in a ClusterConfiguration
 | 
			
		||||
// Currently it prints a warning that no validation was performed
 | 
			
		||||
func Validate(clusterCfg *kubeadmapi.ClusterConfiguration) field.ErrorList {
 | 
			
		||||
	groups := []string{}
 | 
			
		||||
	for group := range clusterCfg.ComponentConfigs {
 | 
			
		||||
		groups = append(groups, group)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(groups) // The sort is needed to make the output predictable
 | 
			
		||||
	klog.Warningf("WARNING: kubeadm cannot validate component configs for API groups %v", groups)
 | 
			
		||||
	return field.ErrorList{}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										111
									
								
								cmd/kubeadm/app/componentconfigs/configset_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								cmd/kubeadm/app/componentconfigs/configset_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 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 componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/lithammer/dedent"
 | 
			
		||||
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/version"
 | 
			
		||||
	clientsetfake "k8s.io/client-go/kubernetes/fake"
 | 
			
		||||
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestDefault(t *testing.T) {
 | 
			
		||||
	clusterCfg := &kubeadmapi.ClusterConfiguration{}
 | 
			
		||||
	localAPIEndpoint := &kubeadmapi.APIEndpoint{}
 | 
			
		||||
 | 
			
		||||
	Default(clusterCfg, localAPIEndpoint)
 | 
			
		||||
 | 
			
		||||
	if len(clusterCfg.ComponentConfigs) != len(known) {
 | 
			
		||||
		t.Errorf("missmatch between supported and defaulted type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(known))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFromCluster(t *testing.T) {
 | 
			
		||||
	clusterCfg := &kubeadmapi.ClusterConfiguration{
 | 
			
		||||
		KubernetesVersion: constants.CurrentKubernetesVersion.String(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k8sVersion := version.MustParseGeneric(clusterCfg.KubernetesVersion)
 | 
			
		||||
 | 
			
		||||
	objects := []runtime.Object{
 | 
			
		||||
		&v1.ConfigMap{
 | 
			
		||||
			ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
				Name:      constants.KubeProxyConfigMap,
 | 
			
		||||
				Namespace: metav1.NamespaceSystem,
 | 
			
		||||
			},
 | 
			
		||||
			Data: map[string]string{
 | 
			
		||||
				constants.KubeProxyConfigMapKey: dedent.Dedent(`
 | 
			
		||||
					apiVersion: kubeproxy.config.k8s.io/v1alpha1
 | 
			
		||||
					kind: KubeProxyConfiguration
 | 
			
		||||
				`),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		&v1.ConfigMap{
 | 
			
		||||
			ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
				Name:      constants.GetKubeletConfigMapName(k8sVersion),
 | 
			
		||||
				Namespace: metav1.NamespaceSystem,
 | 
			
		||||
			},
 | 
			
		||||
			Data: map[string]string{
 | 
			
		||||
				constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
 | 
			
		||||
					apiVersion: kubelet.config.k8s.io/v1beta1
 | 
			
		||||
					kind: KubeletConfiguration
 | 
			
		||||
				`),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	client := clientsetfake.NewSimpleClientset(objects...)
 | 
			
		||||
 | 
			
		||||
	if err := FetchFromCluster(clusterCfg, client); err != nil {
 | 
			
		||||
		t.Fatalf("FetchFromCluster failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(clusterCfg.ComponentConfigs) != len(objects) {
 | 
			
		||||
		t.Fatalf("missmatch between supplied and loaded type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(objects))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFetchFromDocumentMap(t *testing.T) {
 | 
			
		||||
	test := dedent.Dedent(`
 | 
			
		||||
	apiVersion: kubeproxy.config.k8s.io/v1alpha1
 | 
			
		||||
	kind: KubeProxyConfiguration
 | 
			
		||||
	---
 | 
			
		||||
	apiVersion: kubelet.config.k8s.io/v1beta1
 | 
			
		||||
	kind: KubeletConfiguration
 | 
			
		||||
	`)
 | 
			
		||||
	gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clusterCfg := &kubeadmapi.ClusterConfiguration{}
 | 
			
		||||
	if err = FetchFromDocumentMap(clusterCfg, gvkmap); err != nil {
 | 
			
		||||
		t.Fatalf("FetchFromDocumentMap failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(clusterCfg.ComponentConfigs) != len(gvkmap) {
 | 
			
		||||
		t.Fatalf("missmatch between supplied and loaded type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(gvkmap))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,188 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2018 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 componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/klog"
 | 
			
		||||
 | 
			
		||||
	kubeproxyconfigv1alpha1 "k8s.io/kube-proxy/config/v1alpha1"
 | 
			
		||||
	kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/features"
 | 
			
		||||
	utilpointer "k8s.io/utils/pointer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// kubeproxyKubeConfigFileName defines the file name for the kube-proxy's kubeconfig file
 | 
			
		||||
	kubeproxyKubeConfigFileName = "/var/lib/kube-proxy/kubeconfig.conf"
 | 
			
		||||
 | 
			
		||||
	// kubeletReadOnlyPort specifies the default insecure http server port
 | 
			
		||||
	// 0 will disable insecure http server.
 | 
			
		||||
	kubeletReadOnlyPort int32 = 0
 | 
			
		||||
 | 
			
		||||
	// kubeletRotateCertificates specifies the default value to enable certificate rotation
 | 
			
		||||
	kubeletRotateCertificates = true
 | 
			
		||||
 | 
			
		||||
	// kubeletAuthenticationAnonymousEnabled specifies the default value to disable anonymous access
 | 
			
		||||
	kubeletAuthenticationAnonymousEnabled = false
 | 
			
		||||
 | 
			
		||||
	// kubeletAuthorizationMode specifies the default authorization mode
 | 
			
		||||
	kubeletAuthorizationMode = kubeletconfigv1beta1.KubeletAuthorizationModeWebhook
 | 
			
		||||
 | 
			
		||||
	// kubeletAuthenticationWebhookEnabled set the default value to enable authentication webhook
 | 
			
		||||
	kubeletAuthenticationWebhookEnabled = true
 | 
			
		||||
 | 
			
		||||
	// kubeletHealthzBindAddress specifies the default healthz bind address
 | 
			
		||||
	kubeletHealthzBindAddress = "127.0.0.1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DefaultKubeProxyConfiguration assigns default values for the kube-proxy ComponentConfig
 | 
			
		||||
func DefaultKubeProxyConfiguration(internalcfg *kubeadmapi.ClusterConfiguration) {
 | 
			
		||||
	kind := "KubeProxyConfiguration"
 | 
			
		||||
 | 
			
		||||
	if internalcfg.ComponentConfigs.KubeProxy == nil {
 | 
			
		||||
		internalcfg.ComponentConfigs.KubeProxy = &kubeproxyconfigv1alpha1.KubeProxyConfiguration{
 | 
			
		||||
			FeatureGates: map[string]bool{},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// The below code is necessary because while KubeProxy may be defined, the user may not
 | 
			
		||||
	// have defined any feature-gates, thus FeatureGates will be nil and the later insertion
 | 
			
		||||
	// of any feature-gates (e.g. IPv6DualStack) will cause a panic.
 | 
			
		||||
	if internalcfg.ComponentConfigs.KubeProxy.FeatureGates == nil {
 | 
			
		||||
		internalcfg.ComponentConfigs.KubeProxy.FeatureGates = map[string]bool{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	externalproxycfg := internalcfg.ComponentConfigs.KubeProxy
 | 
			
		||||
 | 
			
		||||
	if externalproxycfg.ClusterCIDR == "" && internalcfg.Networking.PodSubnet != "" {
 | 
			
		||||
		externalproxycfg.ClusterCIDR = internalcfg.Networking.PodSubnet
 | 
			
		||||
	} else if internalcfg.Networking.PodSubnet != "" && externalproxycfg.ClusterCIDR != internalcfg.Networking.PodSubnet {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "clusterCIDR", internalcfg.Networking.PodSubnet, externalproxycfg.ClusterCIDR)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if externalproxycfg.ClientConnection.Kubeconfig == "" {
 | 
			
		||||
		externalproxycfg.ClientConnection.Kubeconfig = kubeproxyKubeConfigFileName
 | 
			
		||||
	} else if externalproxycfg.ClientConnection.Kubeconfig != kubeproxyKubeConfigFileName {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "clientConnection.kubeconfig", kubeproxyKubeConfigFileName, externalproxycfg.ClientConnection.Kubeconfig)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: The following code should be remvoved after dual-stack is GA.
 | 
			
		||||
	// Note: The user still retains the ability to explicitly set feature-gates and that value will overwrite this base value.
 | 
			
		||||
	if enabled, present := internalcfg.FeatureGates[features.IPv6DualStack]; present {
 | 
			
		||||
		externalproxycfg.FeatureGates[features.IPv6DualStack] = enabled
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultKubeletConfiguration assigns default values for the kubelet ComponentConfig
 | 
			
		||||
func DefaultKubeletConfiguration(internalcfg *kubeadmapi.ClusterConfiguration) {
 | 
			
		||||
	kind := "KubeletConfiguration"
 | 
			
		||||
 | 
			
		||||
	if internalcfg.ComponentConfigs.Kubelet == nil {
 | 
			
		||||
		internalcfg.ComponentConfigs.Kubelet = &kubeletconfigv1beta1.KubeletConfiguration{
 | 
			
		||||
			FeatureGates: map[string]bool{},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	externalkubeletcfg := internalcfg.ComponentConfigs.Kubelet
 | 
			
		||||
 | 
			
		||||
	if externalkubeletcfg.StaticPodPath == "" {
 | 
			
		||||
		externalkubeletcfg.StaticPodPath = kubeadmapiv1beta2.DefaultManifestsDir
 | 
			
		||||
	} else if externalkubeletcfg.StaticPodPath != kubeadmapiv1beta2.DefaultManifestsDir {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "staticPodPath", kubeadmapiv1beta2.DefaultManifestsDir, externalkubeletcfg.StaticPodPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clusterDNS := ""
 | 
			
		||||
	dnsIP, err := constants.GetDNSIP(internalcfg.Networking.ServiceSubnet, features.Enabled(internalcfg.FeatureGates, features.IPv6DualStack))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		clusterDNS = kubeadmapiv1beta2.DefaultClusterDNSIP
 | 
			
		||||
	} else {
 | 
			
		||||
		clusterDNS = dnsIP.String()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if externalkubeletcfg.ClusterDNS == nil {
 | 
			
		||||
		externalkubeletcfg.ClusterDNS = []string{clusterDNS}
 | 
			
		||||
	} else if len(externalkubeletcfg.ClusterDNS) != 1 || externalkubeletcfg.ClusterDNS[0] != clusterDNS {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "clusterDNS", []string{clusterDNS}, externalkubeletcfg.ClusterDNS)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if externalkubeletcfg.ClusterDomain == "" {
 | 
			
		||||
		externalkubeletcfg.ClusterDomain = internalcfg.Networking.DNSDomain
 | 
			
		||||
	} else if internalcfg.Networking.DNSDomain != "" && externalkubeletcfg.ClusterDomain != internalcfg.Networking.DNSDomain {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "clusterDomain", internalcfg.Networking.DNSDomain, externalkubeletcfg.ClusterDomain)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Require all clients to the kubelet API to have client certs signed by the cluster CA
 | 
			
		||||
	clientCAFile := filepath.Join(internalcfg.CertificatesDir, constants.CACertName)
 | 
			
		||||
	if externalkubeletcfg.Authentication.X509.ClientCAFile == "" {
 | 
			
		||||
		externalkubeletcfg.Authentication.X509.ClientCAFile = clientCAFile
 | 
			
		||||
	} else if externalkubeletcfg.Authentication.X509.ClientCAFile != clientCAFile {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "authentication.x509.clientCAFile", clientCAFile, externalkubeletcfg.Authentication.X509.ClientCAFile)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if externalkubeletcfg.Authentication.Anonymous.Enabled == nil {
 | 
			
		||||
		externalkubeletcfg.Authentication.Anonymous.Enabled = utilpointer.BoolPtr(kubeletAuthenticationAnonymousEnabled)
 | 
			
		||||
	} else if *externalkubeletcfg.Authentication.Anonymous.Enabled != kubeletAuthenticationAnonymousEnabled {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "authentication.anonymous.enabled", kubeletAuthenticationAnonymousEnabled, *externalkubeletcfg.Authentication.Anonymous.Enabled)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// On every client request to the kubelet API, execute a webhook (SubjectAccessReview request) to the API server
 | 
			
		||||
	// and ask it whether the client is authorized to access the kubelet API
 | 
			
		||||
	if externalkubeletcfg.Authorization.Mode == "" {
 | 
			
		||||
		externalkubeletcfg.Authorization.Mode = kubeletAuthorizationMode
 | 
			
		||||
	} else if externalkubeletcfg.Authorization.Mode != kubeletAuthorizationMode {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "authorization.mode", kubeletAuthorizationMode, externalkubeletcfg.Authorization.Mode)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Let clients using other authentication methods like ServiceAccount tokens also access the kubelet API
 | 
			
		||||
	if externalkubeletcfg.Authentication.Webhook.Enabled == nil {
 | 
			
		||||
		externalkubeletcfg.Authentication.Webhook.Enabled = utilpointer.BoolPtr(kubeletAuthenticationWebhookEnabled)
 | 
			
		||||
	} else if *externalkubeletcfg.Authentication.Webhook.Enabled != kubeletAuthenticationWebhookEnabled {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "authentication.webhook.enabled", kubeletAuthenticationWebhookEnabled, *externalkubeletcfg.Authentication.Webhook.Enabled)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Serve a /healthz webserver on localhost:10248 that kubeadm can talk to
 | 
			
		||||
	if externalkubeletcfg.HealthzBindAddress == "" {
 | 
			
		||||
		externalkubeletcfg.HealthzBindAddress = kubeletHealthzBindAddress
 | 
			
		||||
	} else if externalkubeletcfg.HealthzBindAddress != kubeletHealthzBindAddress {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "healthzBindAddress", kubeletHealthzBindAddress, externalkubeletcfg.HealthzBindAddress)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if externalkubeletcfg.HealthzPort == nil {
 | 
			
		||||
		externalkubeletcfg.HealthzPort = utilpointer.Int32Ptr(constants.KubeletHealthzPort)
 | 
			
		||||
	} else if *externalkubeletcfg.HealthzPort != constants.KubeletHealthzPort {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "healthzPort", constants.KubeletHealthzPort, *externalkubeletcfg.HealthzPort)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if externalkubeletcfg.ReadOnlyPort != kubeletReadOnlyPort {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "readOnlyPort", kubeletReadOnlyPort, externalkubeletcfg.ReadOnlyPort)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We cannot show a warning for RotateCertificates==false and we must hardcode it to true.
 | 
			
		||||
	// There is no way to determine if the user has set this or not, given the field is a non-pointer.
 | 
			
		||||
	externalkubeletcfg.RotateCertificates = kubeletRotateCertificates
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// warnDefaultComponentConfigValue prints a warning if the user modified a field in a certain
 | 
			
		||||
// CompomentConfig from the default recommended value in kubeadm.
 | 
			
		||||
func warnDefaultComponentConfigValue(componentConfigKind, paramName string, defaultValue, userValue interface{}) {
 | 
			
		||||
	klog.Warningf("The recommended value for %q in %q is: %v; the provided value is: %v",
 | 
			
		||||
		paramName, componentConfigKind, defaultValue, userValue)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										176
									
								
								cmd/kubeadm/app/componentconfigs/kubelet.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								cmd/kubeadm/app/componentconfigs/kubelet.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 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 componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/version"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	kubeletconfig "k8s.io/kubelet/config/v1beta1"
 | 
			
		||||
	utilpointer "k8s.io/utils/pointer"
 | 
			
		||||
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/features"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// KubeletGroup is a pointer to the used API group name for the kubelet config
 | 
			
		||||
	KubeletGroup = kubeletconfig.GroupName
 | 
			
		||||
 | 
			
		||||
	// kubeletReadOnlyPort specifies the default insecure http server port
 | 
			
		||||
	// 0 will disable insecure http server.
 | 
			
		||||
	kubeletReadOnlyPort int32 = 0
 | 
			
		||||
 | 
			
		||||
	// kubeletRotateCertificates specifies the default value to enable certificate rotation
 | 
			
		||||
	kubeletRotateCertificates = true
 | 
			
		||||
 | 
			
		||||
	// kubeletAuthenticationAnonymousEnabled specifies the default value to disable anonymous access
 | 
			
		||||
	kubeletAuthenticationAnonymousEnabled = false
 | 
			
		||||
 | 
			
		||||
	// kubeletAuthenticationWebhookEnabled set the default value to enable authentication webhook
 | 
			
		||||
	kubeletAuthenticationWebhookEnabled = true
 | 
			
		||||
 | 
			
		||||
	// kubeletHealthzBindAddress specifies the default healthz bind address
 | 
			
		||||
	kubeletHealthzBindAddress = "127.0.0.1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// kubeletHandler is the handler instance for the kubelet component config
 | 
			
		||||
var kubeletHandler = handler{
 | 
			
		||||
	GroupVersion: kubeletconfig.SchemeGroupVersion,
 | 
			
		||||
	AddToScheme:  kubeletconfig.AddToScheme,
 | 
			
		||||
	CreateEmpty: func() kubeadmapi.ComponentConfig {
 | 
			
		||||
		return &kubeletConfig{}
 | 
			
		||||
	},
 | 
			
		||||
	fromCluster: kubeletConfigFromCluster,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func kubeletConfigFromCluster(h *handler, clientset clientset.Interface, clusterCfg *kubeadmapi.ClusterConfiguration) (kubeadmapi.ComponentConfig, error) {
 | 
			
		||||
	// Read the ConfigMap from the cluster based on what version the kubelet is
 | 
			
		||||
	k8sVersion, err := version.ParseGeneric(clusterCfg.KubernetesVersion)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	configMapName := constants.GetKubeletConfigMapName(k8sVersion)
 | 
			
		||||
	return h.fromConfigMap(clientset, configMapName, constants.KubeletBaseConfigurationConfigMapKey, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// kubeletConfig implements the kubeadmapi.ComponentConfig interface for kubelet
 | 
			
		||||
type kubeletConfig struct {
 | 
			
		||||
	config kubeletconfig.KubeletConfiguration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kc *kubeletConfig) DeepCopy() kubeadmapi.ComponentConfig {
 | 
			
		||||
	result := &kubeletConfig{}
 | 
			
		||||
	kc.config.DeepCopyInto(&result.config)
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kc *kubeletConfig) Marshal() ([]byte, error) {
 | 
			
		||||
	return kubeletHandler.Marshal(&kc.config)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kc *kubeletConfig) Unmarshal(docmap kubeadmapi.DocumentMap) error {
 | 
			
		||||
	return kubeletHandler.Unmarshal(docmap, &kc.config)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kc *kubeletConfig) Default(cfg *kubeadmapi.ClusterConfiguration, _ *kubeadmapi.APIEndpoint) {
 | 
			
		||||
	const kind = "KubeletConfiguration"
 | 
			
		||||
 | 
			
		||||
	if kc.config.FeatureGates == nil {
 | 
			
		||||
		kc.config.FeatureGates = map[string]bool{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if kc.config.StaticPodPath == "" {
 | 
			
		||||
		kc.config.StaticPodPath = kubeadmapiv1beta2.DefaultManifestsDir
 | 
			
		||||
	} else if kc.config.StaticPodPath != kubeadmapiv1beta2.DefaultManifestsDir {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "staticPodPath", kubeadmapiv1beta2.DefaultManifestsDir, kc.config.StaticPodPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clusterDNS := ""
 | 
			
		||||
	dnsIP, err := constants.GetDNSIP(cfg.Networking.ServiceSubnet, features.Enabled(cfg.FeatureGates, features.IPv6DualStack))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		clusterDNS = kubeadmapiv1beta2.DefaultClusterDNSIP
 | 
			
		||||
	} else {
 | 
			
		||||
		clusterDNS = dnsIP.String()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if kc.config.ClusterDNS == nil {
 | 
			
		||||
		kc.config.ClusterDNS = []string{clusterDNS}
 | 
			
		||||
	} else if len(kc.config.ClusterDNS) != 1 || kc.config.ClusterDNS[0] != clusterDNS {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "clusterDNS", []string{clusterDNS}, kc.config.ClusterDNS)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if kc.config.ClusterDomain == "" {
 | 
			
		||||
		kc.config.ClusterDomain = cfg.Networking.DNSDomain
 | 
			
		||||
	} else if cfg.Networking.DNSDomain != "" && kc.config.ClusterDomain != cfg.Networking.DNSDomain {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "clusterDomain", cfg.Networking.DNSDomain, kc.config.ClusterDomain)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Require all clients to the kubelet API to have client certs signed by the cluster CA
 | 
			
		||||
	clientCAFile := filepath.Join(cfg.CertificatesDir, constants.CACertName)
 | 
			
		||||
	if kc.config.Authentication.X509.ClientCAFile == "" {
 | 
			
		||||
		kc.config.Authentication.X509.ClientCAFile = clientCAFile
 | 
			
		||||
	} else if kc.config.Authentication.X509.ClientCAFile != clientCAFile {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "authentication.x509.clientCAFile", clientCAFile, kc.config.Authentication.X509.ClientCAFile)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if kc.config.Authentication.Anonymous.Enabled == nil {
 | 
			
		||||
		kc.config.Authentication.Anonymous.Enabled = utilpointer.BoolPtr(kubeletAuthenticationAnonymousEnabled)
 | 
			
		||||
	} else if *kc.config.Authentication.Anonymous.Enabled != kubeletAuthenticationAnonymousEnabled {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "authentication.anonymous.enabled", kubeletAuthenticationAnonymousEnabled, *kc.config.Authentication.Anonymous.Enabled)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// On every client request to the kubelet API, execute a webhook (SubjectAccessReview request) to the API server
 | 
			
		||||
	// and ask it whether the client is authorized to access the kubelet API
 | 
			
		||||
	if kc.config.Authorization.Mode == "" {
 | 
			
		||||
		kc.config.Authorization.Mode = kubeletconfig.KubeletAuthorizationModeWebhook
 | 
			
		||||
	} else if kc.config.Authorization.Mode != kubeletconfig.KubeletAuthorizationModeWebhook {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "authorization.mode", kubeletconfig.KubeletAuthorizationModeWebhook, kc.config.Authorization.Mode)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Let clients using other authentication methods like ServiceAccount tokens also access the kubelet API
 | 
			
		||||
	if kc.config.Authentication.Webhook.Enabled == nil {
 | 
			
		||||
		kc.config.Authentication.Webhook.Enabled = utilpointer.BoolPtr(kubeletAuthenticationWebhookEnabled)
 | 
			
		||||
	} else if *kc.config.Authentication.Webhook.Enabled != kubeletAuthenticationWebhookEnabled {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "authentication.webhook.enabled", kubeletAuthenticationWebhookEnabled, *kc.config.Authentication.Webhook.Enabled)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Serve a /healthz webserver on localhost:10248 that kubeadm can talk to
 | 
			
		||||
	if kc.config.HealthzBindAddress == "" {
 | 
			
		||||
		kc.config.HealthzBindAddress = kubeletHealthzBindAddress
 | 
			
		||||
	} else if kc.config.HealthzBindAddress != kubeletHealthzBindAddress {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "healthzBindAddress", kubeletHealthzBindAddress, kc.config.HealthzBindAddress)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if kc.config.HealthzPort == nil {
 | 
			
		||||
		kc.config.HealthzPort = utilpointer.Int32Ptr(constants.KubeletHealthzPort)
 | 
			
		||||
	} else if *kc.config.HealthzPort != constants.KubeletHealthzPort {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "healthzPort", constants.KubeletHealthzPort, *kc.config.HealthzPort)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if kc.config.ReadOnlyPort != kubeletReadOnlyPort {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "readOnlyPort", kubeletReadOnlyPort, kc.config.ReadOnlyPort)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We cannot show a warning for RotateCertificates==false and we must hardcode it to true.
 | 
			
		||||
	// There is no way to determine if the user has set this or not, given the field is a non-pointer.
 | 
			
		||||
	kc.config.RotateCertificates = kubeletRotateCertificates
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										488
									
								
								cmd/kubeadm/app/componentconfigs/kubelet_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										488
									
								
								cmd/kubeadm/app/componentconfigs/kubelet_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,488 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 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 componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/lithammer/dedent"
 | 
			
		||||
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/version"
 | 
			
		||||
	clientsetfake "k8s.io/client-go/kubernetes/fake"
 | 
			
		||||
	kubeletconfig "k8s.io/kubelet/config/v1beta1"
 | 
			
		||||
	utilpointer "k8s.io/utils/pointer"
 | 
			
		||||
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/features"
 | 
			
		||||
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// kubeletMarshalCases holds common marshal test cases for both the marshal and unmarshal tests
 | 
			
		||||
var kubeletMarshalCases = []struct {
 | 
			
		||||
	name string
 | 
			
		||||
	obj  *kubeletConfig
 | 
			
		||||
	yaml string
 | 
			
		||||
}{
 | 
			
		||||
	{
 | 
			
		||||
		name: "Empty config",
 | 
			
		||||
		obj: &kubeletConfig{
 | 
			
		||||
			config: kubeletconfig.KubeletConfiguration{},
 | 
			
		||||
		},
 | 
			
		||||
		yaml: dedent.Dedent(`
 | 
			
		||||
			apiVersion: kubelet.config.k8s.io/v1beta1
 | 
			
		||||
			authentication:
 | 
			
		||||
			  anonymous: {}
 | 
			
		||||
			  webhook:
 | 
			
		||||
			    cacheTTL: 0s
 | 
			
		||||
			  x509: {}
 | 
			
		||||
			authorization:
 | 
			
		||||
			  webhook:
 | 
			
		||||
			    cacheAuthorizedTTL: 0s
 | 
			
		||||
			    cacheUnauthorizedTTL: 0s
 | 
			
		||||
			cpuManagerReconcilePeriod: 0s
 | 
			
		||||
			evictionPressureTransitionPeriod: 0s
 | 
			
		||||
			fileCheckFrequency: 0s
 | 
			
		||||
			httpCheckFrequency: 0s
 | 
			
		||||
			imageMinimumGCAge: 0s
 | 
			
		||||
			kind: KubeletConfiguration
 | 
			
		||||
			nodeStatusReportFrequency: 0s
 | 
			
		||||
			nodeStatusUpdateFrequency: 0s
 | 
			
		||||
			runtimeRequestTimeout: 0s
 | 
			
		||||
			streamingConnectionIdleTimeout: 0s
 | 
			
		||||
			syncFrequency: 0s
 | 
			
		||||
			volumeStatsAggPeriod: 0s
 | 
			
		||||
		`),
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		name: "Non empty config",
 | 
			
		||||
		obj: &kubeletConfig{
 | 
			
		||||
			config: kubeletconfig.KubeletConfiguration{
 | 
			
		||||
				Address:            "1.2.3.4",
 | 
			
		||||
				Port:               12345,
 | 
			
		||||
				RotateCertificates: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		yaml: dedent.Dedent(`
 | 
			
		||||
			address: 1.2.3.4
 | 
			
		||||
			apiVersion: kubelet.config.k8s.io/v1beta1
 | 
			
		||||
			authentication:
 | 
			
		||||
			  anonymous: {}
 | 
			
		||||
			  webhook:
 | 
			
		||||
			    cacheTTL: 0s
 | 
			
		||||
			  x509: {}
 | 
			
		||||
			authorization:
 | 
			
		||||
			  webhook:
 | 
			
		||||
			    cacheAuthorizedTTL: 0s
 | 
			
		||||
			    cacheUnauthorizedTTL: 0s
 | 
			
		||||
			cpuManagerReconcilePeriod: 0s
 | 
			
		||||
			evictionPressureTransitionPeriod: 0s
 | 
			
		||||
			fileCheckFrequency: 0s
 | 
			
		||||
			httpCheckFrequency: 0s
 | 
			
		||||
			imageMinimumGCAge: 0s
 | 
			
		||||
			kind: KubeletConfiguration
 | 
			
		||||
			nodeStatusReportFrequency: 0s
 | 
			
		||||
			nodeStatusUpdateFrequency: 0s
 | 
			
		||||
			port: 12345
 | 
			
		||||
			rotateCertificates: true
 | 
			
		||||
			runtimeRequestTimeout: 0s
 | 
			
		||||
			streamingConnectionIdleTimeout: 0s
 | 
			
		||||
			syncFrequency: 0s
 | 
			
		||||
			volumeStatsAggPeriod: 0s
 | 
			
		||||
		`),
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKubeletMarshal(t *testing.T) {
 | 
			
		||||
	for _, test := range kubeletMarshalCases {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			b, err := test.obj.Marshal()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("Marshal failed: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			got := strings.TrimSpace(string(b))
 | 
			
		||||
			expected := strings.TrimSpace(test.yaml)
 | 
			
		||||
			if expected != string(got) {
 | 
			
		||||
				t.Fatalf("Missmatch between expected and got:\nExpected:\n%s\n---\nGot:\n%s", expected, string(got))
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKubeletUnmarshal(t *testing.T) {
 | 
			
		||||
	for _, test := range kubeletMarshalCases {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test.yaml))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			got := &kubeletConfig{}
 | 
			
		||||
			if err = got.Unmarshal(gvkmap); err != nil {
 | 
			
		||||
				t.Fatalf("unexpected failure of Unmarshal: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			expected := test.obj.DeepCopy().(*kubeletConfig)
 | 
			
		||||
			expected.config.APIVersion = kubeletHandler.GroupVersion.String()
 | 
			
		||||
			expected.config.Kind = "KubeletConfiguration"
 | 
			
		||||
 | 
			
		||||
			if !reflect.DeepEqual(got, expected) {
 | 
			
		||||
				t.Fatalf("Missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", expected, got)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKubeletDefault(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name       string
 | 
			
		||||
		clusterCfg kubeadmapi.ClusterConfiguration
 | 
			
		||||
		expected   kubeletConfig
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:       "No specific defaulting works",
 | 
			
		||||
			clusterCfg: kubeadmapi.ClusterConfiguration{},
 | 
			
		||||
			expected: kubeletConfig{
 | 
			
		||||
				config: kubeletconfig.KubeletConfiguration{
 | 
			
		||||
					FeatureGates:  map[string]bool{},
 | 
			
		||||
					StaticPodPath: kubeadmapiv1beta2.DefaultManifestsDir,
 | 
			
		||||
					ClusterDNS:    []string{kubeadmapiv1beta2.DefaultClusterDNSIP},
 | 
			
		||||
					Authentication: kubeletconfig.KubeletAuthentication{
 | 
			
		||||
						X509: kubeletconfig.KubeletX509Authentication{
 | 
			
		||||
							ClientCAFile: constants.CACertName,
 | 
			
		||||
						},
 | 
			
		||||
						Anonymous: kubeletconfig.KubeletAnonymousAuthentication{
 | 
			
		||||
							Enabled: utilpointer.BoolPtr(kubeletAuthenticationAnonymousEnabled),
 | 
			
		||||
						},
 | 
			
		||||
						Webhook: kubeletconfig.KubeletWebhookAuthentication{
 | 
			
		||||
							Enabled: utilpointer.BoolPtr(kubeletAuthenticationWebhookEnabled),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Authorization: kubeletconfig.KubeletAuthorization{
 | 
			
		||||
						Mode: kubeletconfig.KubeletAuthorizationModeWebhook,
 | 
			
		||||
					},
 | 
			
		||||
					HealthzBindAddress: kubeletHealthzBindAddress,
 | 
			
		||||
					HealthzPort:        utilpointer.Int32Ptr(constants.KubeletHealthzPort),
 | 
			
		||||
					RotateCertificates: kubeletRotateCertificates,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Service subnet, no dual stack defaulting works",
 | 
			
		||||
			clusterCfg: kubeadmapi.ClusterConfiguration{
 | 
			
		||||
				Networking: kubeadmapi.Networking{
 | 
			
		||||
					ServiceSubnet: "192.168.0.0/16",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: kubeletConfig{
 | 
			
		||||
				config: kubeletconfig.KubeletConfiguration{
 | 
			
		||||
					FeatureGates:  map[string]bool{},
 | 
			
		||||
					StaticPodPath: kubeadmapiv1beta2.DefaultManifestsDir,
 | 
			
		||||
					ClusterDNS:    []string{"192.168.0.10"},
 | 
			
		||||
					Authentication: kubeletconfig.KubeletAuthentication{
 | 
			
		||||
						X509: kubeletconfig.KubeletX509Authentication{
 | 
			
		||||
							ClientCAFile: constants.CACertName,
 | 
			
		||||
						},
 | 
			
		||||
						Anonymous: kubeletconfig.KubeletAnonymousAuthentication{
 | 
			
		||||
							Enabled: utilpointer.BoolPtr(kubeletAuthenticationAnonymousEnabled),
 | 
			
		||||
						},
 | 
			
		||||
						Webhook: kubeletconfig.KubeletWebhookAuthentication{
 | 
			
		||||
							Enabled: utilpointer.BoolPtr(kubeletAuthenticationWebhookEnabled),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Authorization: kubeletconfig.KubeletAuthorization{
 | 
			
		||||
						Mode: kubeletconfig.KubeletAuthorizationModeWebhook,
 | 
			
		||||
					},
 | 
			
		||||
					HealthzBindAddress: kubeletHealthzBindAddress,
 | 
			
		||||
					HealthzPort:        utilpointer.Int32Ptr(constants.KubeletHealthzPort),
 | 
			
		||||
					RotateCertificates: kubeletRotateCertificates,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Service subnet, dual stack defaulting works",
 | 
			
		||||
			clusterCfg: kubeadmapi.ClusterConfiguration{
 | 
			
		||||
				FeatureGates: map[string]bool{
 | 
			
		||||
					features.IPv6DualStack: true,
 | 
			
		||||
				},
 | 
			
		||||
				Networking: kubeadmapi.Networking{
 | 
			
		||||
					ServiceSubnet: "192.168.0.0/16",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: kubeletConfig{
 | 
			
		||||
				config: kubeletconfig.KubeletConfiguration{
 | 
			
		||||
					FeatureGates:  map[string]bool{},
 | 
			
		||||
					StaticPodPath: kubeadmapiv1beta2.DefaultManifestsDir,
 | 
			
		||||
					ClusterDNS:    []string{"192.168.0.10"},
 | 
			
		||||
					Authentication: kubeletconfig.KubeletAuthentication{
 | 
			
		||||
						X509: kubeletconfig.KubeletX509Authentication{
 | 
			
		||||
							ClientCAFile: constants.CACertName,
 | 
			
		||||
						},
 | 
			
		||||
						Anonymous: kubeletconfig.KubeletAnonymousAuthentication{
 | 
			
		||||
							Enabled: utilpointer.BoolPtr(kubeletAuthenticationAnonymousEnabled),
 | 
			
		||||
						},
 | 
			
		||||
						Webhook: kubeletconfig.KubeletWebhookAuthentication{
 | 
			
		||||
							Enabled: utilpointer.BoolPtr(kubeletAuthenticationWebhookEnabled),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Authorization: kubeletconfig.KubeletAuthorization{
 | 
			
		||||
						Mode: kubeletconfig.KubeletAuthorizationModeWebhook,
 | 
			
		||||
					},
 | 
			
		||||
					HealthzBindAddress: kubeletHealthzBindAddress,
 | 
			
		||||
					HealthzPort:        utilpointer.Int32Ptr(constants.KubeletHealthzPort),
 | 
			
		||||
					RotateCertificates: kubeletRotateCertificates,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "DNS domain defaulting works",
 | 
			
		||||
			clusterCfg: kubeadmapi.ClusterConfiguration{
 | 
			
		||||
				Networking: kubeadmapi.Networking{
 | 
			
		||||
					DNSDomain: "example.com",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: kubeletConfig{
 | 
			
		||||
				config: kubeletconfig.KubeletConfiguration{
 | 
			
		||||
					FeatureGates:  map[string]bool{},
 | 
			
		||||
					StaticPodPath: kubeadmapiv1beta2.DefaultManifestsDir,
 | 
			
		||||
					ClusterDNS:    []string{kubeadmapiv1beta2.DefaultClusterDNSIP},
 | 
			
		||||
					ClusterDomain: "example.com",
 | 
			
		||||
					Authentication: kubeletconfig.KubeletAuthentication{
 | 
			
		||||
						X509: kubeletconfig.KubeletX509Authentication{
 | 
			
		||||
							ClientCAFile: constants.CACertName,
 | 
			
		||||
						},
 | 
			
		||||
						Anonymous: kubeletconfig.KubeletAnonymousAuthentication{
 | 
			
		||||
							Enabled: utilpointer.BoolPtr(kubeletAuthenticationAnonymousEnabled),
 | 
			
		||||
						},
 | 
			
		||||
						Webhook: kubeletconfig.KubeletWebhookAuthentication{
 | 
			
		||||
							Enabled: utilpointer.BoolPtr(kubeletAuthenticationWebhookEnabled),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Authorization: kubeletconfig.KubeletAuthorization{
 | 
			
		||||
						Mode: kubeletconfig.KubeletAuthorizationModeWebhook,
 | 
			
		||||
					},
 | 
			
		||||
					HealthzBindAddress: kubeletHealthzBindAddress,
 | 
			
		||||
					HealthzPort:        utilpointer.Int32Ptr(constants.KubeletHealthzPort),
 | 
			
		||||
					RotateCertificates: kubeletRotateCertificates,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "CertificatesDir defaulting works",
 | 
			
		||||
			clusterCfg: kubeadmapi.ClusterConfiguration{
 | 
			
		||||
				CertificatesDir: "/path/to/certs",
 | 
			
		||||
			},
 | 
			
		||||
			expected: kubeletConfig{
 | 
			
		||||
				config: kubeletconfig.KubeletConfiguration{
 | 
			
		||||
					FeatureGates:  map[string]bool{},
 | 
			
		||||
					StaticPodPath: kubeadmapiv1beta2.DefaultManifestsDir,
 | 
			
		||||
					ClusterDNS:    []string{kubeadmapiv1beta2.DefaultClusterDNSIP},
 | 
			
		||||
					Authentication: kubeletconfig.KubeletAuthentication{
 | 
			
		||||
						X509: kubeletconfig.KubeletX509Authentication{
 | 
			
		||||
							ClientCAFile: filepath.Join("/path/to/certs", constants.CACertName),
 | 
			
		||||
						},
 | 
			
		||||
						Anonymous: kubeletconfig.KubeletAnonymousAuthentication{
 | 
			
		||||
							Enabled: utilpointer.BoolPtr(kubeletAuthenticationAnonymousEnabled),
 | 
			
		||||
						},
 | 
			
		||||
						Webhook: kubeletconfig.KubeletWebhookAuthentication{
 | 
			
		||||
							Enabled: utilpointer.BoolPtr(kubeletAuthenticationWebhookEnabled),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Authorization: kubeletconfig.KubeletAuthorization{
 | 
			
		||||
						Mode: kubeletconfig.KubeletAuthorizationModeWebhook,
 | 
			
		||||
					},
 | 
			
		||||
					HealthzBindAddress: kubeletHealthzBindAddress,
 | 
			
		||||
					HealthzPort:        utilpointer.Int32Ptr(constants.KubeletHealthzPort),
 | 
			
		||||
					RotateCertificates: kubeletRotateCertificates,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			got := &kubeletConfig{}
 | 
			
		||||
			got.Default(&test.clusterCfg, &kubeadmapi.APIEndpoint{})
 | 
			
		||||
			if !reflect.DeepEqual(got, &test.expected) {
 | 
			
		||||
				t.Fatalf("Missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.expected, got)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// runKubeletFromTest holds common test case data and evaluation code for kubeletHandler.From* functions
 | 
			
		||||
func runKubeletFromTest(t *testing.T, perform func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error)) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name      string
 | 
			
		||||
		in        string
 | 
			
		||||
		out       *kubeletConfig
 | 
			
		||||
		expectErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "Empty document map should return nothing successfully",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Non-empty non-kubelet document map returns nothing successfully",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: api.example.com/v1
 | 
			
		||||
				kind: Configuration
 | 
			
		||||
			`),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Old kubelet version returns an error",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: kubelet.config.k8s.io/v1alpha1
 | 
			
		||||
				kind: KubeletConfiguration
 | 
			
		||||
			`),
 | 
			
		||||
			expectErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "New kubelet version returns an error",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: kubelet.config.k8s.io/v1
 | 
			
		||||
				kind: KubeletConfiguration
 | 
			
		||||
			`),
 | 
			
		||||
			expectErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Wrong kubelet kind returns an error",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: kubelet.config.k8s.io/v1beta1
 | 
			
		||||
				kind: Configuration
 | 
			
		||||
			`),
 | 
			
		||||
			expectErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Valid kubelet only config gets loaded",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: kubelet.config.k8s.io/v1beta1
 | 
			
		||||
				kind: KubeletConfiguration
 | 
			
		||||
				address: 1.2.3.4
 | 
			
		||||
				port: 12345
 | 
			
		||||
				rotateCertificates: true
 | 
			
		||||
			`),
 | 
			
		||||
			out: &kubeletConfig{
 | 
			
		||||
				config: kubeletconfig.KubeletConfiguration{
 | 
			
		||||
					TypeMeta: metav1.TypeMeta{
 | 
			
		||||
						APIVersion: kubeletHandler.GroupVersion.String(),
 | 
			
		||||
						Kind:       "KubeletConfiguration",
 | 
			
		||||
					},
 | 
			
		||||
					Address:            "1.2.3.4",
 | 
			
		||||
					Port:               12345,
 | 
			
		||||
					RotateCertificates: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Valid kubelet config gets loaded when coupled with an extra document",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: api.example.com/v1
 | 
			
		||||
				kind: Configuration
 | 
			
		||||
				---
 | 
			
		||||
				apiVersion: kubelet.config.k8s.io/v1beta1
 | 
			
		||||
				kind: KubeletConfiguration
 | 
			
		||||
				address: 1.2.3.4
 | 
			
		||||
				port: 12345
 | 
			
		||||
				rotateCertificates: true
 | 
			
		||||
			`),
 | 
			
		||||
			out: &kubeletConfig{
 | 
			
		||||
				config: kubeletconfig.KubeletConfiguration{
 | 
			
		||||
					TypeMeta: metav1.TypeMeta{
 | 
			
		||||
						APIVersion: kubeletHandler.GroupVersion.String(),
 | 
			
		||||
						Kind:       "KubeletConfiguration",
 | 
			
		||||
					},
 | 
			
		||||
					Address:            "1.2.3.4",
 | 
			
		||||
					Port:               12345,
 | 
			
		||||
					RotateCertificates: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			componentCfg, err := perform(t, test.in)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if !test.expectErr {
 | 
			
		||||
					t.Errorf("unexpected failure: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if test.expectErr {
 | 
			
		||||
					t.Error("unexpected success")
 | 
			
		||||
				} else {
 | 
			
		||||
					if componentCfg == nil {
 | 
			
		||||
						if test.out != nil {
 | 
			
		||||
							t.Error("unexpected nil result")
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						if got, ok := componentCfg.(*kubeletConfig); !ok {
 | 
			
		||||
							t.Error("different result type")
 | 
			
		||||
						} else {
 | 
			
		||||
							if test.out == nil {
 | 
			
		||||
								t.Errorf("unexpected result: %v", got)
 | 
			
		||||
							} else if !reflect.DeepEqual(test.out, got) {
 | 
			
		||||
								t.Errorf("missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.out, got)
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKubeletFromDocumentMap(t *testing.T) {
 | 
			
		||||
	runKubeletFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
 | 
			
		||||
		gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(in))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return kubeletHandler.FromDocumentMap(gvkmap)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKubeletFromCluster(t *testing.T) {
 | 
			
		||||
	runKubeletFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
 | 
			
		||||
		clusterCfg := &kubeadmapi.ClusterConfiguration{
 | 
			
		||||
			KubernetesVersion: constants.CurrentKubernetesVersion.String(),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		k8sVersion := version.MustParseGeneric(clusterCfg.KubernetesVersion)
 | 
			
		||||
 | 
			
		||||
		client := clientsetfake.NewSimpleClientset(
 | 
			
		||||
			&v1.ConfigMap{
 | 
			
		||||
				ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
					Name:      constants.GetKubeletConfigMapName(k8sVersion),
 | 
			
		||||
					Namespace: metav1.NamespaceSystem,
 | 
			
		||||
				},
 | 
			
		||||
				Data: map[string]string{
 | 
			
		||||
					constants.KubeletBaseConfigurationConfigMapKey: in,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		return kubeletHandler.FromCluster(client, clusterCfg)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								cmd/kubeadm/app/componentconfigs/kubeproxy.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								cmd/kubeadm/app/componentconfigs/kubeproxy.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 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 componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net"
 | 
			
		||||
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	kubeproxyconfig "k8s.io/kube-proxy/config/v1alpha1"
 | 
			
		||||
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/features"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// KubeProxyGroup is a pointer to the used API group name for the kube-proxy config
 | 
			
		||||
	KubeProxyGroup = kubeproxyconfig.GroupName
 | 
			
		||||
 | 
			
		||||
	// kubeproxyKubeConfigFileName is used during defaulting. It's here so it can be accessed from the tests.
 | 
			
		||||
	kubeproxyKubeConfigFileName = "/var/lib/kube-proxy/kubeconfig.conf"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// kubeProxyHandler is the handler instance for the kube-proxy component config
 | 
			
		||||
var kubeProxyHandler = handler{
 | 
			
		||||
	GroupVersion: kubeproxyconfig.SchemeGroupVersion,
 | 
			
		||||
	AddToScheme:  kubeproxyconfig.AddToScheme,
 | 
			
		||||
	CreateEmpty: func() kubeadmapi.ComponentConfig {
 | 
			
		||||
		return &kubeProxyConfig{}
 | 
			
		||||
	},
 | 
			
		||||
	fromCluster: kubeProxyConfigFromCluster,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func kubeProxyConfigFromCluster(h *handler, clientset clientset.Interface, _ *kubeadmapi.ClusterConfiguration) (kubeadmapi.ComponentConfig, error) {
 | 
			
		||||
	return h.fromConfigMap(clientset, kubeadmconstants.KubeProxyConfigMap, kubeadmconstants.KubeProxyConfigMapKey, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// kubeProxyConfig implements the kubeadmapi.ComponentConfig interface for kube-proxy
 | 
			
		||||
type kubeProxyConfig struct {
 | 
			
		||||
	config kubeproxyconfig.KubeProxyConfiguration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kp *kubeProxyConfig) DeepCopy() kubeadmapi.ComponentConfig {
 | 
			
		||||
	result := &kubeProxyConfig{}
 | 
			
		||||
	kp.config.DeepCopyInto(&result.config)
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kp *kubeProxyConfig) Marshal() ([]byte, error) {
 | 
			
		||||
	return kubeProxyHandler.Marshal(&kp.config)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kp *kubeProxyConfig) Unmarshal(docmap kubeadmapi.DocumentMap) error {
 | 
			
		||||
	return kubeProxyHandler.Unmarshal(docmap, &kp.config)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func kubeProxyDefaultBindAddress(localAdvertiseAddress string) string {
 | 
			
		||||
	ip := net.ParseIP(localAdvertiseAddress)
 | 
			
		||||
	if ip.To4() != nil {
 | 
			
		||||
		return kubeadmapiv1beta2.DefaultProxyBindAddressv4
 | 
			
		||||
	}
 | 
			
		||||
	return kubeadmapiv1beta2.DefaultProxyBindAddressv6
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kp *kubeProxyConfig) Default(cfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint *kubeadmapi.APIEndpoint) {
 | 
			
		||||
	const kind = "KubeProxyConfiguration"
 | 
			
		||||
 | 
			
		||||
	// The below code is necessary because while KubeProxy may be defined, the user may not
 | 
			
		||||
	// have defined any feature-gates, thus FeatureGates will be nil and the later insertion
 | 
			
		||||
	// of any feature-gates (e.g. IPv6DualStack) will cause a panic.
 | 
			
		||||
	if kp.config.FeatureGates == nil {
 | 
			
		||||
		kp.config.FeatureGates = map[string]bool{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defaultBindAddress := kubeProxyDefaultBindAddress(localAPIEndpoint.AdvertiseAddress)
 | 
			
		||||
	if kp.config.BindAddress == "" {
 | 
			
		||||
		kp.config.BindAddress = defaultBindAddress
 | 
			
		||||
	} else if kp.config.BindAddress != defaultBindAddress {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "bindAddress", kp.config.BindAddress, defaultBindAddress)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if kp.config.ClusterCIDR == "" && cfg.Networking.PodSubnet != "" {
 | 
			
		||||
		kp.config.ClusterCIDR = cfg.Networking.PodSubnet
 | 
			
		||||
	} else if cfg.Networking.PodSubnet != "" && kp.config.ClusterCIDR != cfg.Networking.PodSubnet {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "clusterCIDR", cfg.Networking.PodSubnet, kp.config.ClusterCIDR)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if kp.config.ClientConnection.Kubeconfig == "" {
 | 
			
		||||
		kp.config.ClientConnection.Kubeconfig = kubeproxyKubeConfigFileName
 | 
			
		||||
	} else if kp.config.ClientConnection.Kubeconfig != kubeproxyKubeConfigFileName {
 | 
			
		||||
		warnDefaultComponentConfigValue(kind, "clientConnection.kubeconfig", kubeproxyKubeConfigFileName, kp.config.ClientConnection.Kubeconfig)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: The following code should be removed after dual-stack is GA.
 | 
			
		||||
	// Note: The user still retains the ability to explicitly set feature-gates and that value will overwrite this base value.
 | 
			
		||||
	if enabled, present := cfg.FeatureGates[features.IPv6DualStack]; present {
 | 
			
		||||
		kp.config.FeatureGates[features.IPv6DualStack] = enabled
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										440
									
								
								cmd/kubeadm/app/componentconfigs/kubeproxy_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										440
									
								
								cmd/kubeadm/app/componentconfigs/kubeproxy_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,440 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 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 componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/lithammer/dedent"
 | 
			
		||||
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	clientsetfake "k8s.io/client-go/kubernetes/fake"
 | 
			
		||||
	componentbaseconfig "k8s.io/component-base/config/v1alpha1"
 | 
			
		||||
	kubeproxyconfig "k8s.io/kube-proxy/config/v1alpha1"
 | 
			
		||||
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/features"
 | 
			
		||||
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// kubeProxyMarshalCases holds common marshal test cases for both the marshal and unmarshal tests
 | 
			
		||||
var kubeProxyMarshalCases = []struct {
 | 
			
		||||
	name string
 | 
			
		||||
	obj  *kubeProxyConfig
 | 
			
		||||
	yaml string
 | 
			
		||||
}{
 | 
			
		||||
	{
 | 
			
		||||
		name: "Empty config",
 | 
			
		||||
		obj: &kubeProxyConfig{
 | 
			
		||||
			config: kubeproxyconfig.KubeProxyConfiguration{},
 | 
			
		||||
		},
 | 
			
		||||
		yaml: dedent.Dedent(`
 | 
			
		||||
			apiVersion: kubeproxy.config.k8s.io/v1alpha1
 | 
			
		||||
			bindAddress: ""
 | 
			
		||||
			clientConnection:
 | 
			
		||||
			  acceptContentTypes: ""
 | 
			
		||||
			  burst: 0
 | 
			
		||||
			  contentType: ""
 | 
			
		||||
			  kubeconfig: ""
 | 
			
		||||
			  qps: 0
 | 
			
		||||
			clusterCIDR: ""
 | 
			
		||||
			configSyncPeriod: 0s
 | 
			
		||||
			conntrack:
 | 
			
		||||
			  maxPerCore: null
 | 
			
		||||
			  min: null
 | 
			
		||||
			  tcpCloseWaitTimeout: null
 | 
			
		||||
			  tcpEstablishedTimeout: null
 | 
			
		||||
			enableProfiling: false
 | 
			
		||||
			healthzBindAddress: ""
 | 
			
		||||
			hostnameOverride: ""
 | 
			
		||||
			iptables:
 | 
			
		||||
			  masqueradeAll: false
 | 
			
		||||
			  masqueradeBit: null
 | 
			
		||||
			  minSyncPeriod: 0s
 | 
			
		||||
			  syncPeriod: 0s
 | 
			
		||||
			ipvs:
 | 
			
		||||
			  excludeCIDRs: null
 | 
			
		||||
			  minSyncPeriod: 0s
 | 
			
		||||
			  scheduler: ""
 | 
			
		||||
			  strictARP: false
 | 
			
		||||
			  syncPeriod: 0s
 | 
			
		||||
			kind: KubeProxyConfiguration
 | 
			
		||||
			metricsBindAddress: ""
 | 
			
		||||
			mode: ""
 | 
			
		||||
			nodePortAddresses: null
 | 
			
		||||
			oomScoreAdj: null
 | 
			
		||||
			portRange: ""
 | 
			
		||||
			udpIdleTimeout: 0s
 | 
			
		||||
			winkernel:
 | 
			
		||||
			  enableDSR: false
 | 
			
		||||
			  networkName: ""
 | 
			
		||||
			  sourceVip: ""
 | 
			
		||||
		`),
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		name: "Non empty config",
 | 
			
		||||
		obj: &kubeProxyConfig{
 | 
			
		||||
			config: kubeproxyconfig.KubeProxyConfiguration{
 | 
			
		||||
				BindAddress:     "1.2.3.4",
 | 
			
		||||
				EnableProfiling: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		yaml: dedent.Dedent(`
 | 
			
		||||
			apiVersion: kubeproxy.config.k8s.io/v1alpha1
 | 
			
		||||
			bindAddress: 1.2.3.4
 | 
			
		||||
			clientConnection:
 | 
			
		||||
			  acceptContentTypes: ""
 | 
			
		||||
			  burst: 0
 | 
			
		||||
			  contentType: ""
 | 
			
		||||
			  kubeconfig: ""
 | 
			
		||||
			  qps: 0
 | 
			
		||||
			clusterCIDR: ""
 | 
			
		||||
			configSyncPeriod: 0s
 | 
			
		||||
			conntrack:
 | 
			
		||||
			  maxPerCore: null
 | 
			
		||||
			  min: null
 | 
			
		||||
			  tcpCloseWaitTimeout: null
 | 
			
		||||
			  tcpEstablishedTimeout: null
 | 
			
		||||
			enableProfiling: true
 | 
			
		||||
			healthzBindAddress: ""
 | 
			
		||||
			hostnameOverride: ""
 | 
			
		||||
			iptables:
 | 
			
		||||
			  masqueradeAll: false
 | 
			
		||||
			  masqueradeBit: null
 | 
			
		||||
			  minSyncPeriod: 0s
 | 
			
		||||
			  syncPeriod: 0s
 | 
			
		||||
			ipvs:
 | 
			
		||||
			  excludeCIDRs: null
 | 
			
		||||
			  minSyncPeriod: 0s
 | 
			
		||||
			  scheduler: ""
 | 
			
		||||
			  strictARP: false
 | 
			
		||||
			  syncPeriod: 0s
 | 
			
		||||
			kind: KubeProxyConfiguration
 | 
			
		||||
			metricsBindAddress: ""
 | 
			
		||||
			mode: ""
 | 
			
		||||
			nodePortAddresses: null
 | 
			
		||||
			oomScoreAdj: null
 | 
			
		||||
			portRange: ""
 | 
			
		||||
			udpIdleTimeout: 0s
 | 
			
		||||
			winkernel:
 | 
			
		||||
			  enableDSR: false
 | 
			
		||||
			  networkName: ""
 | 
			
		||||
			  sourceVip: ""
 | 
			
		||||
		`),
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKubeProxyMarshal(t *testing.T) {
 | 
			
		||||
	for _, test := range kubeProxyMarshalCases {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			b, err := test.obj.Marshal()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("Marshal failed: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			got := strings.TrimSpace(string(b))
 | 
			
		||||
			expected := strings.TrimSpace(test.yaml)
 | 
			
		||||
			if expected != string(got) {
 | 
			
		||||
				t.Fatalf("Missmatch between expected and got:\nExpected:\n%s\n---\nGot:\n%s", expected, string(got))
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKubeProxyUnmarshal(t *testing.T) {
 | 
			
		||||
	for _, test := range kubeProxyMarshalCases {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test.yaml))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			got := &kubeProxyConfig{}
 | 
			
		||||
			if err = got.Unmarshal(gvkmap); err != nil {
 | 
			
		||||
				t.Fatalf("unexpected failure of Unmarshal: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			expected := test.obj.DeepCopy().(*kubeProxyConfig)
 | 
			
		||||
			expected.config.APIVersion = kubeProxyHandler.GroupVersion.String()
 | 
			
		||||
			expected.config.Kind = "KubeProxyConfiguration"
 | 
			
		||||
 | 
			
		||||
			if !reflect.DeepEqual(got, expected) {
 | 
			
		||||
				t.Fatalf("Missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", expected, got)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKubeProxyDefault(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name       string
 | 
			
		||||
		clusterCfg kubeadmapi.ClusterConfiguration
 | 
			
		||||
		endpoint   kubeadmapi.APIEndpoint
 | 
			
		||||
		expected   kubeProxyConfig
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:       "No specific defaulting works",
 | 
			
		||||
			clusterCfg: kubeadmapi.ClusterConfiguration{},
 | 
			
		||||
			endpoint:   kubeadmapi.APIEndpoint{},
 | 
			
		||||
			expected: kubeProxyConfig{
 | 
			
		||||
				config: kubeproxyconfig.KubeProxyConfiguration{
 | 
			
		||||
					FeatureGates: map[string]bool{},
 | 
			
		||||
					BindAddress:  kubeadmapiv1beta2.DefaultProxyBindAddressv6,
 | 
			
		||||
					ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
 | 
			
		||||
						Kubeconfig: kubeproxyKubeConfigFileName,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "IPv4 bind address",
 | 
			
		||||
			clusterCfg: kubeadmapi.ClusterConfiguration{},
 | 
			
		||||
			endpoint: kubeadmapi.APIEndpoint{
 | 
			
		||||
				AdvertiseAddress: "1.2.3.4",
 | 
			
		||||
			},
 | 
			
		||||
			expected: kubeProxyConfig{
 | 
			
		||||
				config: kubeproxyconfig.KubeProxyConfiguration{
 | 
			
		||||
					FeatureGates: map[string]bool{},
 | 
			
		||||
					BindAddress:  kubeadmapiv1beta2.DefaultProxyBindAddressv4,
 | 
			
		||||
					ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
 | 
			
		||||
						Kubeconfig: kubeproxyKubeConfigFileName,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "ClusterCIDR is fetched from PodSubnet",
 | 
			
		||||
			clusterCfg: kubeadmapi.ClusterConfiguration{
 | 
			
		||||
				Networking: kubeadmapi.Networking{
 | 
			
		||||
					PodSubnet: "192.168.0.0/16",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			endpoint: kubeadmapi.APIEndpoint{},
 | 
			
		||||
			expected: kubeProxyConfig{
 | 
			
		||||
				config: kubeproxyconfig.KubeProxyConfiguration{
 | 
			
		||||
					FeatureGates: map[string]bool{},
 | 
			
		||||
					BindAddress:  kubeadmapiv1beta2.DefaultProxyBindAddressv6,
 | 
			
		||||
					ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
 | 
			
		||||
						Kubeconfig: kubeproxyKubeConfigFileName,
 | 
			
		||||
					},
 | 
			
		||||
					ClusterCIDR: "192.168.0.0/16",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "IPv6DualStack feature gate set to true",
 | 
			
		||||
			clusterCfg: kubeadmapi.ClusterConfiguration{
 | 
			
		||||
				FeatureGates: map[string]bool{
 | 
			
		||||
					features.IPv6DualStack: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			endpoint: kubeadmapi.APIEndpoint{},
 | 
			
		||||
			expected: kubeProxyConfig{
 | 
			
		||||
				config: kubeproxyconfig.KubeProxyConfiguration{
 | 
			
		||||
					FeatureGates: map[string]bool{
 | 
			
		||||
						features.IPv6DualStack: true,
 | 
			
		||||
					},
 | 
			
		||||
					BindAddress: kubeadmapiv1beta2.DefaultProxyBindAddressv6,
 | 
			
		||||
					ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
 | 
			
		||||
						Kubeconfig: kubeproxyKubeConfigFileName,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "IPv6DualStack feature gate set to false",
 | 
			
		||||
			clusterCfg: kubeadmapi.ClusterConfiguration{
 | 
			
		||||
				FeatureGates: map[string]bool{
 | 
			
		||||
					features.IPv6DualStack: false,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			endpoint: kubeadmapi.APIEndpoint{},
 | 
			
		||||
			expected: kubeProxyConfig{
 | 
			
		||||
				config: kubeproxyconfig.KubeProxyConfiguration{
 | 
			
		||||
					FeatureGates: map[string]bool{
 | 
			
		||||
						features.IPv6DualStack: false,
 | 
			
		||||
					},
 | 
			
		||||
					BindAddress: kubeadmapiv1beta2.DefaultProxyBindAddressv6,
 | 
			
		||||
					ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
 | 
			
		||||
						Kubeconfig: kubeproxyKubeConfigFileName,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			got := &kubeProxyConfig{}
 | 
			
		||||
			got.Default(&test.clusterCfg, &test.endpoint)
 | 
			
		||||
			if !reflect.DeepEqual(got, &test.expected) {
 | 
			
		||||
				t.Fatalf("Missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.expected, got)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// runKubeProxyFromTest holds common test case data and evaluation code for kubeProxyHandler.From* functions
 | 
			
		||||
func runKubeProxyFromTest(t *testing.T, perform func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error)) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name      string
 | 
			
		||||
		in        string
 | 
			
		||||
		out       *kubeProxyConfig
 | 
			
		||||
		expectErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "Empty document map should return nothing successfully",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Non-empty non-kube-proxy document map returns nothing successfully",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: api.example.com/v1
 | 
			
		||||
				kind: Configuration
 | 
			
		||||
			`),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Old kube-proxy version returns an error",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: kubeproxy.config.k8s.io/v1alpha0
 | 
			
		||||
				kind: KubeProxyConfiguration
 | 
			
		||||
			`),
 | 
			
		||||
			expectErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "New kube-proxy version returns an error",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: kubeproxy.config.k8s.io/v1beta1
 | 
			
		||||
				kind: KubeProxyConfiguration
 | 
			
		||||
			`),
 | 
			
		||||
			expectErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Wrong kube-proxy kind returns an error",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: kubeproxy.config.k8s.io/v1alpha1
 | 
			
		||||
				kind: Configuration
 | 
			
		||||
			`),
 | 
			
		||||
			expectErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Valid kube-proxy only config gets loaded",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: kubeproxy.config.k8s.io/v1alpha1
 | 
			
		||||
				kind: KubeProxyConfiguration
 | 
			
		||||
				bindAddress: 1.2.3.4
 | 
			
		||||
				enableProfiling: true
 | 
			
		||||
			`),
 | 
			
		||||
			out: &kubeProxyConfig{
 | 
			
		||||
				config: kubeproxyconfig.KubeProxyConfiguration{
 | 
			
		||||
					TypeMeta: metav1.TypeMeta{
 | 
			
		||||
						APIVersion: kubeProxyHandler.GroupVersion.String(),
 | 
			
		||||
						Kind:       "KubeProxyConfiguration",
 | 
			
		||||
					},
 | 
			
		||||
					BindAddress:     "1.2.3.4",
 | 
			
		||||
					EnableProfiling: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Valid kube-proxy config gets loaded when coupled with an extra document",
 | 
			
		||||
			in: dedent.Dedent(`
 | 
			
		||||
				apiVersion: api.example.com/v1
 | 
			
		||||
				kind: Configuration
 | 
			
		||||
				---
 | 
			
		||||
				apiVersion: kubeproxy.config.k8s.io/v1alpha1
 | 
			
		||||
				kind: KubeProxyConfiguration
 | 
			
		||||
				bindAddress: 1.2.3.4
 | 
			
		||||
				enableProfiling: true
 | 
			
		||||
			`),
 | 
			
		||||
			out: &kubeProxyConfig{
 | 
			
		||||
				config: kubeproxyconfig.KubeProxyConfiguration{
 | 
			
		||||
					TypeMeta: metav1.TypeMeta{
 | 
			
		||||
						APIVersion: kubeProxyHandler.GroupVersion.String(),
 | 
			
		||||
						Kind:       "KubeProxyConfiguration",
 | 
			
		||||
					},
 | 
			
		||||
					BindAddress:     "1.2.3.4",
 | 
			
		||||
					EnableProfiling: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			componentCfg, err := perform(t, test.in)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if !test.expectErr {
 | 
			
		||||
					t.Errorf("unexpected failure: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if test.expectErr {
 | 
			
		||||
					t.Error("unexpected success")
 | 
			
		||||
				} else {
 | 
			
		||||
					if componentCfg == nil {
 | 
			
		||||
						if test.out != nil {
 | 
			
		||||
							t.Error("unexpected nil result")
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						if got, ok := componentCfg.(*kubeProxyConfig); !ok {
 | 
			
		||||
							t.Error("different result type")
 | 
			
		||||
						} else {
 | 
			
		||||
							if test.out == nil {
 | 
			
		||||
								t.Errorf("unexpected result: %v", got)
 | 
			
		||||
							} else if !reflect.DeepEqual(test.out, got) {
 | 
			
		||||
								t.Errorf("missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.out, got)
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKubeProxyFromDocumentMap(t *testing.T) {
 | 
			
		||||
	runKubeProxyFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
 | 
			
		||||
		gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(in))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return kubeProxyHandler.FromDocumentMap(gvkmap)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKubeProxyFromCluster(t *testing.T) {
 | 
			
		||||
	runKubeProxyFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
 | 
			
		||||
		client := clientsetfake.NewSimpleClientset(
 | 
			
		||||
			&v1.ConfigMap{
 | 
			
		||||
				ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
					Name:      constants.KubeProxyConfigMap,
 | 
			
		||||
					Namespace: metav1.NamespaceSystem,
 | 
			
		||||
				},
 | 
			
		||||
				Data: map[string]string{
 | 
			
		||||
					constants.KubeProxyConfigMapKey: in,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		return kubeProxyHandler.FromCluster(client, &kubeadmapi.ClusterConfiguration{})
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@@ -1,158 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2018 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 componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/validation/field"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/version"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	kubeproxyconfigv1alpha1 "k8s.io/kube-proxy/config/v1alpha1"
 | 
			
		||||
	kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AddToSchemeFunc is a function that adds known types and API GroupVersions to a scheme
 | 
			
		||||
type AddToSchemeFunc func(*runtime.Scheme) error
 | 
			
		||||
 | 
			
		||||
// Registration is an object for registering a Kubernetes ComponentConfig type to be recognized and handled by kubeadm
 | 
			
		||||
type Registration struct {
 | 
			
		||||
	// MarshalGroupVersion is the preferred external API version to use when marshalling the ComponentConfig
 | 
			
		||||
	MarshalGroupVersion schema.GroupVersion
 | 
			
		||||
	// AddToSchemeFuncs are a set of functions that register APIs to the scheme
 | 
			
		||||
	AddToSchemeFuncs []AddToSchemeFunc
 | 
			
		||||
	// DefaulterFunc is a function that based on the internal kubeadm configuration defaults the ComponentConfig struct
 | 
			
		||||
	DefaulterFunc func(*kubeadmapi.ClusterConfiguration)
 | 
			
		||||
	// ValidateFunc is a function that should validate the ComponentConfig type embedded in the internal kubeadm config struct
 | 
			
		||||
	ValidateFunc func(*kubeadmapi.ClusterConfiguration, *field.Path) field.ErrorList
 | 
			
		||||
	// EmptyValue holds a pointer to an empty struct of the internal ComponentConfig type
 | 
			
		||||
	EmptyValue runtime.Object
 | 
			
		||||
	// GetFromInternalConfig returns the pointer to the ComponentConfig API object from the internal kubeadm config struct
 | 
			
		||||
	GetFromInternalConfig func(*kubeadmapi.ClusterConfiguration) (runtime.Object, bool)
 | 
			
		||||
	// SetToInternalConfig sets the pointer to a ComponentConfig API object embedded in the internal kubeadm config struct
 | 
			
		||||
	SetToInternalConfig func(runtime.Object, *kubeadmapi.ClusterConfiguration) bool
 | 
			
		||||
	// GetFromConfigMap returns the pointer to the ComponentConfig API object read from the config map stored in the cluster
 | 
			
		||||
	GetFromConfigMap func(clientset.Interface, *version.Version) (runtime.Object, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Marshal marshals obj to bytes for the current Registration
 | 
			
		||||
func (r Registration) Marshal(obj runtime.Object) ([]byte, error) {
 | 
			
		||||
	return kubeadmutil.MarshalToYamlForCodecs(obj, r.MarshalGroupVersion, Codecs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unmarshal unmarshals the bytes to a runtime.Object using the Codecs registered in this Scheme
 | 
			
		||||
func (r Registration) Unmarshal(fileContent []byte) (runtime.Object, error) {
 | 
			
		||||
	// Do a deepcopy of the empty value so we don't mutate it, which could lead to strange errors
 | 
			
		||||
	obj := r.EmptyValue.DeepCopyObject()
 | 
			
		||||
 | 
			
		||||
	// Decode the file content into obj which is a pointer to an empty struct of the internal ComponentConfig
 | 
			
		||||
	if err := unmarshalObject(obj, fileContent); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return obj, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unmarshalObject(obj runtime.Object, fileContent []byte) error {
 | 
			
		||||
	// Decode the file content  using the componentconfig Codecs that knows about all APIs
 | 
			
		||||
	return runtime.DecodeInto(Codecs.UniversalDecoder(), fileContent, obj)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// KubeletConfigurationKind is the kind for the kubelet ComponentConfig
 | 
			
		||||
	KubeletConfigurationKind RegistrationKind = "KubeletConfiguration"
 | 
			
		||||
	// KubeProxyConfigurationKind is the kind for the kubelet ComponentConfig
 | 
			
		||||
	KubeProxyConfigurationKind RegistrationKind = "KubeProxyConfiguration"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RegistrationKind is a string type to ensure not any string can be a key in the Registrations map
 | 
			
		||||
type RegistrationKind string
 | 
			
		||||
 | 
			
		||||
// Registrations holds a set of ComponentConfig Registration objects, where the map key is the kind
 | 
			
		||||
type Registrations map[RegistrationKind]Registration
 | 
			
		||||
 | 
			
		||||
// Known contains the known ComponentConfig registrations to kubeadm
 | 
			
		||||
var Known Registrations = map[RegistrationKind]Registration{
 | 
			
		||||
	KubeProxyConfigurationKind: {
 | 
			
		||||
		// TODO: When a beta version of the kube-proxy ComponentConfig API is available, start using it
 | 
			
		||||
		MarshalGroupVersion: kubeproxyconfigv1alpha1.SchemeGroupVersion,
 | 
			
		||||
		// AddToSchemeFuncs must use v1alpha1scheme defined in k8s.io/kubernetes, because the schema defined in k8s.io/kube-proxy doesn't have defaulting functions
 | 
			
		||||
		AddToSchemeFuncs: []AddToSchemeFunc{kubeproxyconfigv1alpha1.AddToScheme},
 | 
			
		||||
		DefaulterFunc:    DefaultKubeProxyConfiguration,
 | 
			
		||||
		ValidateFunc:     NoValidator("kube-proxy"),
 | 
			
		||||
		EmptyValue:       &kubeproxyconfigv1alpha1.KubeProxyConfiguration{},
 | 
			
		||||
		GetFromInternalConfig: func(cfg *kubeadmapi.ClusterConfiguration) (runtime.Object, bool) {
 | 
			
		||||
			return cfg.ComponentConfigs.KubeProxy, cfg.ComponentConfigs.KubeProxy != nil
 | 
			
		||||
		},
 | 
			
		||||
		SetToInternalConfig: func(obj runtime.Object, cfg *kubeadmapi.ClusterConfiguration) bool {
 | 
			
		||||
			kubeproxyConfig, ok := obj.(*kubeproxyconfigv1alpha1.KubeProxyConfiguration)
 | 
			
		||||
			if ok {
 | 
			
		||||
				cfg.ComponentConfigs.KubeProxy = kubeproxyConfig
 | 
			
		||||
			}
 | 
			
		||||
			return ok
 | 
			
		||||
		},
 | 
			
		||||
		GetFromConfigMap: GetFromKubeProxyConfigMap,
 | 
			
		||||
	},
 | 
			
		||||
	KubeletConfigurationKind: {
 | 
			
		||||
		MarshalGroupVersion: kubeletconfigv1beta1.SchemeGroupVersion,
 | 
			
		||||
		// PAddToSchemeFuncs must use v1alpha1scheme defined in k8s.io/kubernetes, because the schema defined in k8s.io/kubelet doesn't have defaulting functions
 | 
			
		||||
		AddToSchemeFuncs: []AddToSchemeFunc{kubeletconfigv1beta1.AddToScheme},
 | 
			
		||||
		DefaulterFunc:    DefaultKubeletConfiguration,
 | 
			
		||||
		ValidateFunc:     NoValidator("kubelet"),
 | 
			
		||||
		EmptyValue:       &kubeletconfigv1beta1.KubeletConfiguration{},
 | 
			
		||||
		GetFromInternalConfig: func(cfg *kubeadmapi.ClusterConfiguration) (runtime.Object, bool) {
 | 
			
		||||
			return cfg.ComponentConfigs.Kubelet, cfg.ComponentConfigs.Kubelet != nil
 | 
			
		||||
		},
 | 
			
		||||
		SetToInternalConfig: func(obj runtime.Object, cfg *kubeadmapi.ClusterConfiguration) bool {
 | 
			
		||||
			kubeletConfig, ok := obj.(*kubeletconfigv1beta1.KubeletConfiguration)
 | 
			
		||||
			if ok {
 | 
			
		||||
				cfg.ComponentConfigs.Kubelet = kubeletConfig
 | 
			
		||||
			}
 | 
			
		||||
			return ok
 | 
			
		||||
		},
 | 
			
		||||
		GetFromConfigMap: GetFromKubeletConfigMap,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddToScheme adds all the known ComponentConfig API types referenced in the Registrations object to the scheme
 | 
			
		||||
func (rs *Registrations) AddToScheme(scheme *runtime.Scheme) error {
 | 
			
		||||
	for _, registration := range *rs {
 | 
			
		||||
		for _, addToSchemeFunc := range registration.AddToSchemeFuncs {
 | 
			
		||||
			if err := addToSchemeFunc(scheme); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Default applies to the ComponentConfig defaults to the internal kubeadm API type
 | 
			
		||||
func (rs *Registrations) Default(internalcfg *kubeadmapi.ClusterConfiguration) {
 | 
			
		||||
	for _, registration := range *rs {
 | 
			
		||||
		registration.DefaulterFunc(internalcfg)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate validates the ComponentConfig parts of the internal kubeadm API type
 | 
			
		||||
func (rs *Registrations) Validate(internalcfg *kubeadmapi.ClusterConfiguration) field.ErrorList {
 | 
			
		||||
	allErrs := field.ErrorList{}
 | 
			
		||||
	for kind, registration := range *rs {
 | 
			
		||||
		allErrs = append(allErrs, registration.ValidateFunc(internalcfg, field.NewPath(string(kind)))...)
 | 
			
		||||
	}
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
@@ -37,5 +37,7 @@ func init() {
 | 
			
		||||
 | 
			
		||||
// AddToScheme builds the kubeadm ComponentConfig scheme using all known ComponentConfig versions.
 | 
			
		||||
func AddToScheme(scheme *runtime.Scheme) {
 | 
			
		||||
	utilruntime.Must(Known.AddToScheme(scheme))
 | 
			
		||||
	for _, handler := range known {
 | 
			
		||||
		utilruntime.Must(handler.AddToScheme(scheme))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,15 +17,12 @@ limitations under the License.
 | 
			
		||||
package componentconfigs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/validation/field"
 | 
			
		||||
	"k8s.io/klog"
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NoValidator returns a dummy validator function when no validation method is available for the component
 | 
			
		||||
func NoValidator(component string) func(*kubeadmapi.ClusterConfiguration, *field.Path) field.ErrorList {
 | 
			
		||||
	return func(_ *kubeadmapi.ClusterConfiguration, _ *field.Path) field.ErrorList {
 | 
			
		||||
		klog.Warningf("Cannot validate %s config - no validator is available", component)
 | 
			
		||||
		return field.ErrorList{}
 | 
			
		||||
	}
 | 
			
		||||
// warnDefaultComponentConfigValue prints a warning if the user modified a field in a certain
 | 
			
		||||
// CompomentConfig from the default recommended value in kubeadm.
 | 
			
		||||
func warnDefaultComponentConfigValue(componentConfigKind, paramName string, defaultValue, userValue interface{}) {
 | 
			
		||||
	klog.Warningf("The recommended value for %q in %q is: %v; the provided value is: %v",
 | 
			
		||||
		paramName, componentConfigKind, defaultValue, userValue)
 | 
			
		||||
}
 | 
			
		||||
@@ -17,14 +17,11 @@ go_test(
 | 
			
		||||
        "//cmd/kubeadm/app/util/config:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/apps/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/client-go/testing:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/kube-proxy/config/v1alpha1:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/utils/pointer:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,12 @@ func EnsureProxyAddon(cfg *kubeadmapi.ClusterConfiguration, localEndpoint *kubea
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	proxyBytes, err := componentconfigs.Known[componentconfigs.KubeProxyConfigurationKind].Marshal(cfg.ComponentConfigs.KubeProxy)
 | 
			
		||||
	kubeProxyCfg, ok := cfg.ComponentConfigs[componentconfigs.KubeProxyGroup]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return errors.New("no kube-proxy component config found in the active component config set")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	proxyBytes, err := kubeProxyCfg.Marshal()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error when marshaling")
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,22 +19,18 @@ package proxy
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	apps "k8s.io/api/apps/v1"
 | 
			
		||||
	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	clientsetfake "k8s.io/client-go/kubernetes/fake"
 | 
			
		||||
	clientsetscheme "k8s.io/client-go/kubernetes/scheme"
 | 
			
		||||
	core "k8s.io/client-go/testing"
 | 
			
		||||
	kubeproxyconfigv1alpha1 "k8s.io/kube-proxy/config/v1alpha1"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
 | 
			
		||||
	configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
 | 
			
		||||
	"k8s.io/utils/pointer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestCreateServiceAccount(t *testing.T) {
 | 
			
		||||
@@ -206,22 +202,6 @@ func TestEnsureProxyAddon(t *testing.T) {
 | 
			
		||||
				t.Errorf("test failed to convert external to internal version")
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			intControlPlane.ComponentConfigs.KubeProxy = &kubeproxyconfigv1alpha1.KubeProxyConfiguration{
 | 
			
		||||
				BindAddress:        "",
 | 
			
		||||
				HealthzBindAddress: "0.0.0.0:10256",
 | 
			
		||||
				MetricsBindAddress: "127.0.0.1:10249",
 | 
			
		||||
				Conntrack: kubeproxyconfigv1alpha1.KubeProxyConntrackConfiguration{
 | 
			
		||||
					MaxPerCore:            pointer.Int32Ptr(1),
 | 
			
		||||
					Min:                   pointer.Int32Ptr(1),
 | 
			
		||||
					TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
 | 
			
		||||
					TCPCloseWaitTimeout:   &metav1.Duration{Duration: 5 * time.Second},
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
			// Run dynamic defaulting again as we changed the internal cfg
 | 
			
		||||
			if err := configutil.SetInitDynamicDefaults(intControlPlane); err != nil {
 | 
			
		||||
				t.Errorf("test failed to set dynamic defaults: %v", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			err = EnsureProxyAddon(&intControlPlane.ClusterConfiguration, &intControlPlane.LocalAPIEndpoint, client)
 | 
			
		||||
 | 
			
		||||
			// Compare actual to expected errors
 | 
			
		||||
@@ -240,18 +220,6 @@ func TestEnsureProxyAddon(t *testing.T) {
 | 
			
		||||
					expErr,
 | 
			
		||||
					actErr)
 | 
			
		||||
			}
 | 
			
		||||
			if intControlPlane.ComponentConfigs.KubeProxy.BindAddress != tc.expBindAddr {
 | 
			
		||||
				t.Errorf("%s test failed, expected: %s, got: %s",
 | 
			
		||||
					tc.name,
 | 
			
		||||
					tc.expBindAddr,
 | 
			
		||||
					intControlPlane.ComponentConfigs.KubeProxy.BindAddress)
 | 
			
		||||
			}
 | 
			
		||||
			if intControlPlane.ComponentConfigs.KubeProxy.ClusterCIDR != tc.expClusterCIDR {
 | 
			
		||||
				t.Errorf("%s test failed, expected: %s, got: %s",
 | 
			
		||||
					tc.name,
 | 
			
		||||
					tc.expClusterCIDR,
 | 
			
		||||
					intControlPlane.ComponentConfigs.KubeProxy.ClusterCIDR)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@ go_library(
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/kubelet/config/v1beta1:go_default_library",
 | 
			
		||||
        "//vendor/github.com/pkg/errors:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/klog:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/utils/exec:go_default_library",
 | 
			
		||||
@@ -42,14 +41,15 @@ go_test(
 | 
			
		||||
    embed = [":go_default_library"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//cmd/kubeadm/app/apis/kubeadm:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/constants:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/util/config:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/client-go/testing:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/kubelet/config/v1beta1:go_default_library",
 | 
			
		||||
        "//vendor/github.com/pkg/errors:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/utils/exec:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ import (
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/version"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
 | 
			
		||||
	kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
 | 
			
		||||
@@ -38,9 +38,9 @@ import (
 | 
			
		||||
 | 
			
		||||
// WriteConfigToDisk writes the kubelet config object down to a file
 | 
			
		||||
// Used at "kubeadm init" and "kubeadm upgrade" time
 | 
			
		||||
func WriteConfigToDisk(kubeletConfig *kubeletconfigv1beta1.KubeletConfiguration, kubeletDir string) error {
 | 
			
		||||
func WriteConfigToDisk(kubeletCfg kubeadmapi.ComponentConfig, kubeletDir string) error {
 | 
			
		||||
 | 
			
		||||
	kubeletBytes, err := getConfigBytes(kubeletConfig)
 | 
			
		||||
	kubeletBytes, err := kubeletCfg.Marshal()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -49,9 +49,9 @@ func WriteConfigToDisk(kubeletConfig *kubeletconfigv1beta1.KubeletConfiguration,
 | 
			
		||||
 | 
			
		||||
// CreateConfigMap creates a ConfigMap with the generic kubelet configuration.
 | 
			
		||||
// Used at "kubeadm init" and "kubeadm upgrade" time
 | 
			
		||||
func CreateConfigMap(cfg *kubeletconfigv1beta1.KubeletConfiguration, k8sVersionStr string, client clientset.Interface) error {
 | 
			
		||||
func CreateConfigMap(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface) error {
 | 
			
		||||
 | 
			
		||||
	k8sVersion, err := version.ParseSemantic(k8sVersionStr)
 | 
			
		||||
	k8sVersion, err := version.ParseSemantic(cfg.KubernetesVersion)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -59,7 +59,12 @@ func CreateConfigMap(cfg *kubeletconfigv1beta1.KubeletConfiguration, k8sVersionS
 | 
			
		||||
	configMapName := kubeadmconstants.GetKubeletConfigMapName(k8sVersion)
 | 
			
		||||
	fmt.Printf("[kubelet] Creating a ConfigMap %q in namespace %s with the configuration for the kubelets in the cluster\n", configMapName, metav1.NamespaceSystem)
 | 
			
		||||
 | 
			
		||||
	kubeletBytes, err := getConfigBytes(cfg)
 | 
			
		||||
	kubeletCfg, ok := cfg.ComponentConfigs[componentconfigs.KubeletGroup]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return errors.New("no kubelet component config found in the active component config set")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kubeletBytes, err := kubeletCfg.Marshal()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -152,11 +157,6 @@ func configMapRBACName(k8sVersion *version.Version) string {
 | 
			
		||||
	return fmt.Sprintf("%s%d.%d", kubeadmconstants.KubeletBaseConfigMapRolePrefix, k8sVersion.Major(), k8sVersion.Minor())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getConfigBytes marshals a KubeletConfiguration object to bytes
 | 
			
		||||
func getConfigBytes(kubeletConfig *kubeletconfigv1beta1.KubeletConfiguration) ([]byte, error) {
 | 
			
		||||
	return componentconfigs.Known[componentconfigs.KubeletConfigurationKind].Marshal(kubeletConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeConfigBytesToDisk writes a byte slice down to disk at the specific location of the kubelet config file
 | 
			
		||||
func writeConfigBytesToDisk(b []byte, kubeletDir string) error {
 | 
			
		||||
	configFile := filepath.Join(kubeletDir, kubeadmconstants.KubeletConfigurationFileName)
 | 
			
		||||
 
 | 
			
		||||
@@ -19,22 +19,20 @@ package kubelet
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/version"
 | 
			
		||||
	"k8s.io/client-go/kubernetes/fake"
 | 
			
		||||
	core "k8s.io/client-go/testing"
 | 
			
		||||
	kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestCreateConfigMap(t *testing.T) {
 | 
			
		||||
	nodeName := "fake-node"
 | 
			
		||||
	client := fake.NewSimpleClientset()
 | 
			
		||||
	k8sVersionStr := constants.CurrentKubernetesVersion.String()
 | 
			
		||||
	cfg := &kubeletconfigv1beta1.KubeletConfiguration{}
 | 
			
		||||
 | 
			
		||||
	client.PrependReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
 | 
			
		||||
		return true, &v1.Node{
 | 
			
		||||
			ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
@@ -53,7 +51,15 @@ func TestCreateConfigMap(t *testing.T) {
 | 
			
		||||
		return true, nil, nil
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err := CreateConfigMap(cfg, k8sVersionStr, client); err != nil {
 | 
			
		||||
	clusterCfg := &kubeadmapiv1beta2.ClusterConfiguration{
 | 
			
		||||
		KubernetesVersion: constants.CurrentKubernetesVersion.String(),
 | 
			
		||||
	}
 | 
			
		||||
	internalcfg, err := configutil.DefaultedInitConfiguration(&kubeadmapiv1beta2.InitConfiguration{}, clusterCfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected failure by DefaultedInitConfiguration: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := CreateConfigMap(&internalcfg.ClusterConfiguration, client); err != nil {
 | 
			
		||||
		t.Errorf("CreateConfigMap: unexpected error %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.InitCon
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create the new, version-branched kubelet ComponentConfig ConfigMap
 | 
			
		||||
	if err := kubeletphase.CreateConfigMap(cfg.ClusterConfiguration.ComponentConfigs.Kubelet, cfg.KubernetesVersion, client); err != nil {
 | 
			
		||||
	if err := kubeletphase.CreateConfigMap(&cfg.ClusterConfiguration, client); err != nil {
 | 
			
		||||
		errs = append(errs, errors.Wrap(err, "error creating kubelet configuration ConfigMap"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -70,7 +70,7 @@ func UploadConfiguration(cfg *kubeadmapi.InitConfiguration, client clientset.Int
 | 
			
		||||
	// The components store their config in their own ConfigMaps, then reset the .ComponentConfig struct;
 | 
			
		||||
	// We don't want to mutate the cfg itself, so create a copy of it using .DeepCopy of it first
 | 
			
		||||
	clusterConfigurationToUpload := cfg.ClusterConfiguration.DeepCopy()
 | 
			
		||||
	clusterConfigurationToUpload.ComponentConfigs = kubeadmapi.ComponentConfigs{}
 | 
			
		||||
	clusterConfigurationToUpload.ComponentConfigs = kubeadmapi.ComponentConfigMap{}
 | 
			
		||||
 | 
			
		||||
	// Marshal the ClusterConfiguration into YAML
 | 
			
		||||
	clusterConfigurationYaml, err := configutil.MarshalKubeadmConfigObject(clusterConfigurationToUpload)
 | 
			
		||||
 
 | 
			
		||||
@@ -87,13 +87,12 @@ func TestUploadConfiguration(t *testing.T) {
 | 
			
		||||
			}
 | 
			
		||||
			cfg, err := configutil.DefaultedInitConfiguration(initialcfg, clustercfg)
 | 
			
		||||
 | 
			
		||||
			// cleans up component config to make cfg and decodedcfg comparable (now component config are not stored anymore in kubeadm-config config map)
 | 
			
		||||
			cfg.ComponentConfigs = kubeadmapi.ComponentConfigs{}
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t2.Fatalf("UploadConfiguration() error = %v", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			cfg.ComponentConfigs = kubeadmapi.ComponentConfigMap{}
 | 
			
		||||
 | 
			
		||||
			status := &kubeadmapi.ClusterStatus{
 | 
			
		||||
				APIEndpoints: map[string]kubeadmapi.APIEndpoint{
 | 
			
		||||
					"node-foo": cfg.LocalAPIEndpoint,
 | 
			
		||||
@@ -135,8 +134,15 @@ func TestUploadConfiguration(t *testing.T) {
 | 
			
		||||
					t2.Fatalf("unable to decode config from bytes: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if len(decodedCfg.ComponentConfigs) != 0 {
 | 
			
		||||
					t2.Errorf("unexpected component configs in decodedCfg: %d", len(decodedCfg.ComponentConfigs))
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Force initialize with an empty map so that reflect.DeepEqual works
 | 
			
		||||
				decodedCfg.ComponentConfigs = kubeadmapi.ComponentConfigMap{}
 | 
			
		||||
 | 
			
		||||
				if !reflect.DeepEqual(decodedCfg, &cfg.ClusterConfiguration) {
 | 
			
		||||
					t2.Errorf("the initial and decoded ClusterConfiguration didn't match")
 | 
			
		||||
					t2.Errorf("the initial and decoded ClusterConfiguration didn't match:\n%t\n===\n%t", decodedCfg.ComponentConfigs == nil, cfg.ComponentConfigs == nil)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				statusData := controlPlaneCfg.Data[kubeadmconstants.ClusterStatusConfigMapKey]
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,7 @@ go_test(
 | 
			
		||||
        "//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/componentconfigs:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/constants:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/util:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/util/apiclient:go_default_library",
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,6 @@ import (
 | 
			
		||||
	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/version"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	"k8s.io/client-go/tools/clientcmd"
 | 
			
		||||
	certutil "k8s.io/client-go/util/cert"
 | 
			
		||||
@@ -79,7 +78,7 @@ func getInitConfigurationFromCluster(kubeconfigDir string, client clientset.Inte
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// gets the component configs from the corresponding config maps
 | 
			
		||||
	if err := getComponentConfigs(client, &initcfg.ClusterConfiguration); err != nil {
 | 
			
		||||
	if err := componentconfigs.FetchFromCluster(&initcfg.ClusterConfiguration, client); err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to get component configs")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -193,28 +192,6 @@ func getAPIEndpoint(data map[string]string, nodeName string, apiEndpoint *kubead
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getComponentConfigs gets the component configs from the corresponding config maps
 | 
			
		||||
func getComponentConfigs(client clientset.Interface, clusterConfiguration *kubeadmapi.ClusterConfiguration) error {
 | 
			
		||||
	// some config maps is versioned, so we need the KubernetesVersion for getting the right config map
 | 
			
		||||
	k8sVersion := version.MustParseGeneric(clusterConfiguration.KubernetesVersion)
 | 
			
		||||
	for kind, registration := range componentconfigs.Known {
 | 
			
		||||
		obj, err := registration.GetFromConfigMap(client, k8sVersion)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Some components may not be installed or managed by kubeadm, hence GetFromConfigMap won't return an error or an object
 | 
			
		||||
		if obj == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ok := registration.SetToInternalConfig(obj, clusterConfiguration); !ok {
 | 
			
		||||
			return errors.Errorf("couldn't save componentconfig value for kind %q", string(kind))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetClusterStatus returns the kubeadm cluster status read from the kubeadm-config ConfigMap
 | 
			
		||||
func GetClusterStatus(client clientset.Interface) (*kubeadmapi.ClusterStatus, error) {
 | 
			
		||||
	configMap, err := apiclient.GetConfigMapWithRetry(client, metav1.NamespaceSystem, constants.KubeadmConfigConfigMap)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,12 +23,13 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/version"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	clientsetfake "k8s.io/client-go/kubernetes/fake"
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
 | 
			
		||||
	kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
 | 
			
		||||
)
 | 
			
		||||
@@ -404,88 +405,6 @@ func TestGetAPIEndpoint(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetComponentConfigs(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		name          string
 | 
			
		||||
		configMaps    []fakeConfigMap
 | 
			
		||||
		expectedError bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "valid",
 | 
			
		||||
			configMaps: []fakeConfigMap{
 | 
			
		||||
				{
 | 
			
		||||
					name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
 | 
			
		||||
					data: map[string]string{
 | 
			
		||||
						kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
 | 
			
		||||
					data: map[string]string{
 | 
			
		||||
						kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid - No kubelet component config ConfigMap",
 | 
			
		||||
			configMaps: []fakeConfigMap{
 | 
			
		||||
				{
 | 
			
		||||
					name: kubeadmconstants.KubeProxyConfigMap,
 | 
			
		||||
					data: map[string]string{
 | 
			
		||||
						kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedError: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid - No kube-proxy component config ConfigMap",
 | 
			
		||||
			configMaps: []fakeConfigMap{
 | 
			
		||||
				{
 | 
			
		||||
					name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion),
 | 
			
		||||
					data: map[string]string{
 | 
			
		||||
						kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, rt := range tests {
 | 
			
		||||
		t.Run(rt.name, func(t *testing.T) {
 | 
			
		||||
			client := clientsetfake.NewSimpleClientset()
 | 
			
		||||
 | 
			
		||||
			for _, c := range rt.configMaps {
 | 
			
		||||
				err := c.create(client)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					t.Errorf("couldn't create ConfigMap %s", c.name)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			cfg := &kubeadmapi.InitConfiguration{
 | 
			
		||||
				ClusterConfiguration: kubeadmapi.ClusterConfiguration{
 | 
			
		||||
					KubernetesVersion: k8sVersionString,
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
			err := getComponentConfigs(client, &cfg.ClusterConfiguration)
 | 
			
		||||
			if rt.expectedError != (err != nil) {
 | 
			
		||||
				t.Errorf("unexpected return err from getInitConfigurationFromCluster: %v", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if rt.expectedError {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Test expected values in InitConfiguration
 | 
			
		||||
			if cfg.ComponentConfigs.Kubelet == nil {
 | 
			
		||||
				t.Errorf("invalid cfg.ComponentConfigs.Kubelet")
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetInitConfigurationFromCluster(t *testing.T) {
 | 
			
		||||
	tmpdir, err := ioutil.TempDir("", "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -685,11 +604,11 @@ func TestGetInitConfigurationFromCluster(t *testing.T) {
 | 
			
		||||
			if !rt.newControlPlane && (cfg.LocalAPIEndpoint.AdvertiseAddress != "1.2.3.4" || cfg.LocalAPIEndpoint.BindPort != 1234) {
 | 
			
		||||
				t.Errorf("invalid cfg.LocalAPIEndpoint")
 | 
			
		||||
			}
 | 
			
		||||
			if cfg.ComponentConfigs.Kubelet == nil {
 | 
			
		||||
				t.Errorf("invalid cfg.ComponentConfigs.Kubelet")
 | 
			
		||||
			if _, ok := cfg.ComponentConfigs[componentconfigs.KubeletGroup]; !ok {
 | 
			
		||||
				t.Errorf("no cfg.ComponentConfigs[%q]", componentconfigs.KubeletGroup)
 | 
			
		||||
			}
 | 
			
		||||
			if cfg.ComponentConfigs.KubeProxy == nil {
 | 
			
		||||
				t.Errorf("invalid cfg.ComponentConfigs.KubeProxy")
 | 
			
		||||
			if _, ok := cfg.ComponentConfigs[componentconfigs.KubeProxyGroup]; !ok {
 | 
			
		||||
				t.Errorf("no cfg.ComponentConfigs[%q]", componentconfigs.KubeProxyGroup)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ func SetInitDynamicDefaults(cfg *kubeadmapi.InitConfiguration) error {
 | 
			
		||||
	if err := SetAPIEndpointDynamicDefaults(&cfg.LocalAPIEndpoint); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return SetClusterDynamicDefaults(&cfg.ClusterConfiguration, cfg.LocalAPIEndpoint.AdvertiseAddress, cfg.LocalAPIEndpoint.BindPort)
 | 
			
		||||
	return SetClusterDynamicDefaults(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetBootstrapTokensDynamicDefaults checks and sets configuration values for the BootstrapTokens object
 | 
			
		||||
@@ -141,16 +141,9 @@ func SetAPIEndpointDynamicDefaults(cfg *kubeadmapi.APIEndpoint) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetClusterDynamicDefaults checks and sets values for the ClusterConfiguration object
 | 
			
		||||
func SetClusterDynamicDefaults(cfg *kubeadmapi.ClusterConfiguration, advertiseAddress string, bindPort int32) error {
 | 
			
		||||
func SetClusterDynamicDefaults(cfg *kubeadmapi.ClusterConfiguration, LocalAPIEndpoint *kubeadmapi.APIEndpoint) error {
 | 
			
		||||
	// Default all the embedded ComponentConfig structs
 | 
			
		||||
	componentconfigs.Known.Default(cfg)
 | 
			
		||||
 | 
			
		||||
	ip := net.ParseIP(advertiseAddress)
 | 
			
		||||
	if ip.To4() != nil {
 | 
			
		||||
		cfg.ComponentConfigs.KubeProxy.BindAddress = kubeadmapiv1beta2.DefaultProxyBindAddressv4
 | 
			
		||||
	} else {
 | 
			
		||||
		cfg.ComponentConfigs.KubeProxy.BindAddress = kubeadmapiv1beta2.DefaultProxyBindAddressv6
 | 
			
		||||
	}
 | 
			
		||||
	componentconfigs.Default(cfg, LocalAPIEndpoint)
 | 
			
		||||
 | 
			
		||||
	// Resolve possible version labels and validate version string
 | 
			
		||||
	if err := NormalizeKubernetesVersion(cfg); err != nil {
 | 
			
		||||
@@ -166,7 +159,7 @@ func SetClusterDynamicDefaults(cfg *kubeadmapi.ClusterConfiguration, advertiseAd
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if port == "" {
 | 
			
		||||
			cfg.ControlPlaneEndpoint = net.JoinHostPort(host, strconv.FormatInt(int64(bindPort), 10))
 | 
			
		||||
			cfg.ControlPlaneEndpoint = net.JoinHostPort(host, strconv.FormatInt(int64(LocalAPIEndpoint.BindPort), 10))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -243,10 +236,9 @@ func BytesToInitConfiguration(b []byte) (*kubeadmapi.InitConfiguration, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// documentMapToInitConfiguration converts a map of GVKs and YAML documents to defaulted and validated configuration object.
 | 
			
		||||
func documentMapToInitConfiguration(gvkmap map[schema.GroupVersionKind][]byte, allowDeprecated bool) (*kubeadmapi.InitConfiguration, error) {
 | 
			
		||||
func documentMapToInitConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated bool) (*kubeadmapi.InitConfiguration, error) {
 | 
			
		||||
	var initcfg *kubeadmapi.InitConfiguration
 | 
			
		||||
	var clustercfg *kubeadmapi.ClusterConfiguration
 | 
			
		||||
	decodedComponentConfigObjects := map[componentconfigs.RegistrationKind]runtime.Object{}
 | 
			
		||||
 | 
			
		||||
	for gvk, fileContent := range gvkmap {
 | 
			
		||||
		// first, check if this GVK is supported and possibly not deprecated
 | 
			
		||||
@@ -257,18 +249,6 @@ func documentMapToInitConfiguration(gvkmap map[schema.GroupVersionKind][]byte, a
 | 
			
		||||
		// verify the validity of the YAML
 | 
			
		||||
		strict.VerifyUnmarshalStrict(fileContent, gvk)
 | 
			
		||||
 | 
			
		||||
		// Try to get the registration for the ComponentConfig based on the kind
 | 
			
		||||
		regKind := componentconfigs.RegistrationKind(gvk.Kind)
 | 
			
		||||
		if registration, found := componentconfigs.Known[regKind]; found {
 | 
			
		||||
			// Unmarshal the bytes from the YAML document into a runtime.Object containing the ComponentConfiguration struct
 | 
			
		||||
			obj, err := registration.Unmarshal(fileContent)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			decodedComponentConfigObjects[regKind] = obj
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if kubeadmutil.GroupVersionKindsHasInitConfiguration(gvk) {
 | 
			
		||||
			// Set initcfg to an empty struct value the deserializer will populate
 | 
			
		||||
			initcfg = &kubeadmapi.InitConfiguration{}
 | 
			
		||||
@@ -313,16 +293,9 @@ func documentMapToInitConfiguration(gvkmap map[schema.GroupVersionKind][]byte, a
 | 
			
		||||
		initcfg.ClusterConfiguration = *clustercfg
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Save the loaded ComponentConfig objects in the initcfg object
 | 
			
		||||
	for kind, obj := range decodedComponentConfigObjects {
 | 
			
		||||
		if registration, found := componentconfigs.Known[kind]; found {
 | 
			
		||||
			if ok := registration.SetToInternalConfig(obj, &initcfg.ClusterConfiguration); !ok {
 | 
			
		||||
				return nil, errors.Errorf("couldn't save componentconfig value for kind %q", string(kind))
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// This should never happen in practice
 | 
			
		||||
			fmt.Printf("[config] WARNING: Decoded a kind that couldn't be saved to the internal configuration: %q\n", string(kind))
 | 
			
		||||
		}
 | 
			
		||||
	// Load any component configs
 | 
			
		||||
	if err := componentconfigs.FetchFromDocumentMap(&initcfg.ClusterConfiguration, gvkmap); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Applies dynamic defaults to settings not provided with flags
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	"k8s.io/klog"
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
 | 
			
		||||
@@ -89,7 +88,7 @@ func LoadJoinConfigurationFromFile(cfgPath string) (*kubeadmapi.JoinConfiguratio
 | 
			
		||||
 | 
			
		||||
// documentMapToJoinConfiguration takes a map between GVKs and YAML documents (as returned by SplitYAMLDocuments),
 | 
			
		||||
// finds a JoinConfiguration, decodes it, dynamically defaults it and then validates it prior to return.
 | 
			
		||||
func documentMapToJoinConfiguration(gvkmap map[schema.GroupVersionKind][]byte, allowDeprecated bool) (*kubeadmapi.JoinConfiguration, error) {
 | 
			
		||||
func documentMapToJoinConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated bool) (*kubeadmapi.JoinConfiguration, error) {
 | 
			
		||||
	joinBytes := []byte{}
 | 
			
		||||
	for gvk, bytes := range gvkmap {
 | 
			
		||||
		// not interested in anything other than JoinConfiguration
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ go_test(
 | 
			
		||||
    embed = [":go_default_library"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/componentconfigs:go_default_library",
 | 
			
		||||
        "//cmd/kubeadm/app/constants:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/kube-proxy/config/v1alpha1:go_default_library",
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@ import (
 | 
			
		||||
	kubeproxyconfigv1alpha1 "k8s.io/kube-proxy/config/v1alpha1"
 | 
			
		||||
	kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -55,13 +54,13 @@ func TestVerifyUnmarshalStrict(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fileName:      "invalid_duplicate_field_kubeletcfg.yaml",
 | 
			
		||||
			kind:          string(componentconfigs.KubeletConfigurationKind),
 | 
			
		||||
			kind:          "KubeletConfiguration",
 | 
			
		||||
			groupVersion:  kubeletconfigv1beta1.SchemeGroupVersion,
 | 
			
		||||
			expectedError: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fileName:      "invalid_duplicate_field_kubeproxycfg.yaml",
 | 
			
		||||
			kind:          string(componentconfigs.KubeProxyConfigurationKind),
 | 
			
		||||
			kind:          "KubeProxyConfiguration",
 | 
			
		||||
			groupVersion:  kubeproxyconfigv1alpha1.SchemeGroupVersion,
 | 
			
		||||
			expectedError: true,
 | 
			
		||||
		},
 | 
			
		||||
@@ -85,13 +84,13 @@ func TestVerifyUnmarshalStrict(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fileName:      "invalid_unknown_field_kubeletcfg.yaml",
 | 
			
		||||
			kind:          string(componentconfigs.KubeletConfigurationKind),
 | 
			
		||||
			kind:          "KubeletConfiguration",
 | 
			
		||||
			groupVersion:  kubeletconfigv1beta1.SchemeGroupVersion,
 | 
			
		||||
			expectedError: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fileName:      "invalid_unknown_field_kubeproxycfg.yaml",
 | 
			
		||||
			kind:          string(componentconfigs.KubeProxyConfigurationKind),
 | 
			
		||||
			kind:          "KubeProxyConfiguration",
 | 
			
		||||
			groupVersion:  kubeproxyconfigv1alpha1.SchemeGroupVersion,
 | 
			
		||||
			expectedError: true,
 | 
			
		||||
		},
 | 
			
		||||
@@ -129,13 +128,13 @@ func TestVerifyUnmarshalStrict(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fileName:      "valid_kubeletcfg.yaml",
 | 
			
		||||
			kind:          string(componentconfigs.KubeletConfigurationKind),
 | 
			
		||||
			kind:          "KubeletConfiguration",
 | 
			
		||||
			groupVersion:  kubeletconfigv1beta1.SchemeGroupVersion,
 | 
			
		||||
			expectedError: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fileName:      "valid_kubeproxycfg.yaml",
 | 
			
		||||
			kind:          string(componentconfigs.KubeProxyConfigurationKind),
 | 
			
		||||
			kind:          "KubeProxyConfiguration",
 | 
			
		||||
			groupVersion:  kubeproxyconfigv1alpha1.SchemeGroupVersion,
 | 
			
		||||
			expectedError: false,
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ import (
 | 
			
		||||
	errorsutil "k8s.io/apimachinery/pkg/util/errors"
 | 
			
		||||
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
 | 
			
		||||
	clientsetscheme "k8s.io/client-go/kubernetes/scheme"
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -73,8 +74,8 @@ func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs se
 | 
			
		||||
 | 
			
		||||
// SplitYAMLDocuments reads the YAML bytes per-document, unmarshals the TypeMeta information from each document
 | 
			
		||||
// and returns a map between the GroupVersionKind of the document and the document bytes
 | 
			
		||||
func SplitYAMLDocuments(yamlBytes []byte) (map[schema.GroupVersionKind][]byte, error) {
 | 
			
		||||
	gvkmap := map[schema.GroupVersionKind][]byte{}
 | 
			
		||||
func SplitYAMLDocuments(yamlBytes []byte) (kubeadmapi.DocumentMap, error) {
 | 
			
		||||
	gvkmap := kubeadmapi.DocumentMap{}
 | 
			
		||||
	knownKinds := map[string]bool{}
 | 
			
		||||
	errs := []error{}
 | 
			
		||||
	buf := bytes.NewBuffer(yamlBytes)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import (
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/serializer"
 | 
			
		||||
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | 
			
		||||
	kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
 | 
			
		||||
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | 
			
		||||
)
 | 
			
		||||
@@ -159,20 +160,20 @@ func TestSplitYAMLDocuments(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		name         string
 | 
			
		||||
		fileContents []byte
 | 
			
		||||
		gvkmap       map[schema.GroupVersionKind][]byte
 | 
			
		||||
		gvkmap       kubeadmapi.DocumentMap
 | 
			
		||||
		expectedErr  bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:         "FooOnly",
 | 
			
		||||
			fileContents: files["foo"],
 | 
			
		||||
			gvkmap: map[schema.GroupVersionKind][]byte{
 | 
			
		||||
			gvkmap: kubeadmapi.DocumentMap{
 | 
			
		||||
				{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"}: files["foo"],
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:         "FooBar",
 | 
			
		||||
			fileContents: bytes.Join([][]byte{files["foo"], files["bar"]}, []byte(constants.YAMLDocumentSeparator)),
 | 
			
		||||
			gvkmap: map[schema.GroupVersionKind][]byte{
 | 
			
		||||
			gvkmap: kubeadmapi.DocumentMap{
 | 
			
		||||
				{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"}: files["foo"],
 | 
			
		||||
				{Group: "bar.k8s.io", Version: "v2", Kind: "Bar"}: files["bar"],
 | 
			
		||||
			},
 | 
			
		||||
 
 | 
			
		||||
@@ -180,6 +180,16 @@ func TestCmdInitConfig(t *testing.T) {
 | 
			
		||||
			args:     "--kubernetes-version=1.11.0 --config=testdata/init/v1beta2.yaml",
 | 
			
		||||
			expected: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "can load current component config",
 | 
			
		||||
			args:     "--config=testdata/init/current-component-config.yaml",
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "can't load old component config",
 | 
			
		||||
			args:     "--config=testdata/init/old-component-config.yaml",
 | 
			
		||||
			expected: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, rt := range initTest {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								cmd/kubeadm/test/cmd/testdata/init/current-component-config.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cmd/kubeadm/test/cmd/testdata/init/current-component-config.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
apiVersion: kubeadm.k8s.io/v1beta2
 | 
			
		||||
kind: ClusterConfiguration
 | 
			
		||||
---
 | 
			
		||||
apiVersion: kubelet.config.k8s.io/v1beta1
 | 
			
		||||
kind: KubeletConfiguration
 | 
			
		||||
							
								
								
									
										5
									
								
								cmd/kubeadm/test/cmd/testdata/init/old-component-config.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cmd/kubeadm/test/cmd/testdata/init/old-component-config.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
apiVersion: kubeadm.k8s.io/v1beta2
 | 
			
		||||
kind: ClusterConfiguration
 | 
			
		||||
---
 | 
			
		||||
apiVersion: kubelet.config.k8s.io/v1alpha1
 | 
			
		||||
kind: KubeletConfiguration
 | 
			
		||||
		Reference in New Issue
	
	Block a user