kubeadm: refactor JoinConfigFileAndDefaultsToInternalConfig

Currently JoinConfigFileAndDefaultsToInternalConfig is doing a couple of
different things depending on its parameters. It:

- loads a versioned JoinConfiguration from an YAML file.
- returns defaulted JoinConfiguration allowing for some overrides.

In order to make code more manageable, the following steps are taken:

- Introduce LoadJoinConfigurationFromFile, which loads a versioned
  JoinConfiguration from an YAML file, defaults it (both dynamically and
  statically), converts it to internal JoinConfiguration and validates it.

- Introduce DefaultedJoinConfiguration, which returns defaulted (both
  dynamically and statically) and verified internal JoinConfiguration.
  The possibility of overwriting defaults via versioned JoinConfiguration is
  retained.

- Re-implement JoinConfigFileAndDefaultsToInternalConfig to use
  LoadJoinConfigurationFromFile and DefaultedJoinConfiguration.

- Replace some calls to JoinConfigFileAndDefaultsToInternalConfig with calls to
  either LoadJoinConfigurationFromFile or DefaultedJoinConfiguration where
  appropriate.

- Rename JoinConfigFileAndDefaultsToInternalConfig to the more appropriate name
  LoadOrDefaultJoinConfiguration.

Signed-off-by: Rostislav M. Georgiev <rostislavg@vmware.com>
pull/564/head
Rostislav M. Georgiev 2018-12-07 12:51:38 +02:00
parent 76722926ed
commit 09f753a94c
5 changed files with 66 additions and 44 deletions

View File

@ -201,7 +201,7 @@ func getDefaultInitConfigBytes() ([]byte, error) {
} }
func getDefaultNodeConfigBytes() ([]byte, error) { func getDefaultNodeConfigBytes() ([]byte, error) {
internalcfg, err := configutil.JoinConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1beta1.JoinConfiguration{ internalcfg, err := configutil.DefaultedJoinConfiguration(&kubeadmapiv1beta1.JoinConfiguration{
Discovery: kubeadmapiv1beta1.Discovery{ Discovery: kubeadmapiv1beta1.Discovery{
BootstrapToken: &kubeadmapiv1beta1.BootstrapTokenDiscovery{ BootstrapToken: &kubeadmapiv1beta1.BootstrapTokenDiscovery{
Token: placeholderToken.Token.String(), Token: placeholderToken.Token.String(),

View File

@ -353,7 +353,7 @@ func newJoinData(cmd *cobra.Command, args []string, options *joinOptions, out io
klog.V(1).Infoln("[join] found advertiseAddress empty; using default interface's IP address as advertiseAddress") klog.V(1).Infoln("[join] found advertiseAddress empty; using default interface's IP address as advertiseAddress")
} }
cfg, err := configutil.JoinConfigFileAndDefaultsToInternalConfig(options.cfgPath, options.externalcfg) cfg, err := configutil.LoadOrDefaultJoinConfiguration(options.cfgPath, options.externalcfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -167,7 +167,7 @@ func MigrateOldConfigFromFile(cfgPath string) ([]byte, error) {
// Migrate JoinConfiguration if there is any // Migrate JoinConfiguration if there is any
if kubeadmutil.GroupVersionKindsHasJoinConfiguration(gvks...) { if kubeadmutil.GroupVersionKindsHasJoinConfiguration(gvks...) {
o, err := JoinConfigFileAndDefaultsToInternalConfig(cfgPath, &kubeadmapiv1beta1.JoinConfiguration{}) o, err := LoadJoinConfigurationFromFile(cfgPath)
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }

View File

@ -54,59 +54,60 @@ func SetJoinControlPlaneDefaults(cfg *kubeadmapi.JoinControlPlane) error {
return nil return nil
} }
// JoinConfigFileAndDefaultsToInternalConfig takes a path to a config file and a versioned configuration that can serve as the default config // LoadOrDefaultJoinConfiguration takes a path to a config file and a versioned configuration that can serve as the default config
// If cfgPath is specified, defaultversionedcfg will always get overridden. Otherwise, the default config (often populated by flags) will be used. // If cfgPath is specified, defaultversionedcfg will always get overridden. Otherwise, the default config (often populated by flags) will be used.
// Then the external, versioned configuration is defaulted and converted to the internal type. // Then the external, versioned configuration is defaulted and converted to the internal type.
// Right thereafter, the configuration is defaulted again with dynamic values (like IP addresses of a machine, etc) // Right thereafter, the configuration is defaulted again with dynamic values (like IP addresses of a machine, etc)
// Lastly, the internal config is validated and returned. // Lastly, the internal config is validated and returned.
func JoinConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedcfg *kubeadmapiv1beta1.JoinConfiguration) (*kubeadmapi.JoinConfiguration, error) { func LoadOrDefaultJoinConfiguration(cfgPath string, defaultversionedcfg *kubeadmapiv1beta1.JoinConfiguration) (*kubeadmapi.JoinConfiguration, error) {
internalcfg := &kubeadmapi.JoinConfiguration{}
if cfgPath != "" { if cfgPath != "" {
// Loads configuration from config file, if provided // Loads configuration from config file, if provided
// Nb. --config overrides command line flags, TODO: fix this // Nb. --config overrides command line flags, TODO: fix this
klog.V(1).Infoln("loading configuration from the given file") return LoadJoinConfigurationFromFile(cfgPath)
}
b, err := ioutil.ReadFile(cfgPath) return DefaultedJoinConfiguration(defaultversionedcfg)
if err != nil { }
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
// LoadJoinConfigurationFromFile loads versioned JoinConfiguration from file, converts it to internal, defaults and validates it
func LoadJoinConfigurationFromFile(cfgPath string) (*kubeadmapi.JoinConfiguration, error) {
klog.V(1).Infof("loading configuration from %q", cfgPath)
b, err := ioutil.ReadFile(cfgPath)
if err != nil {
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
}
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
if err != nil {
return nil, err
}
joinBytes := []byte{}
for gvk, bytes := range gvkmap {
// not interested in anything other than JoinConfiguration
if gvk.Kind != constants.JoinConfigurationKind {
continue
} }
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b) // check if this version is supported one
if err != nil { if err := ValidateSupportedVersion(gvk.GroupVersion()); err != nil {
return nil, err return nil, err
} }
joinBytes := []byte{} // verify the validity of the YAML
for gvk, bytes := range gvkmap { strict.VerifyUnmarshalStrict(bytes, gvk)
// not interested in anything other than JoinConfiguration
if gvk.Kind != constants.JoinConfigurationKind {
continue
}
// check if this version is supported one joinBytes = bytes
if err := ValidateSupportedVersion(gvk.GroupVersion()); err != nil { }
return nil, err
}
// verify the validity of the YAML if len(joinBytes) == 0 {
strict.VerifyUnmarshalStrict(bytes, gvk) return nil, errors.Errorf("no %s found in config file %q", constants.JoinConfigurationKind, cfgPath)
}
joinBytes = bytes internalcfg := &kubeadmapi.JoinConfiguration{}
} if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), joinBytes, internalcfg); err != nil {
return nil, err
if len(joinBytes) == 0 {
return nil, errors.Errorf("no %s found in config file %q", constants.JoinConfigurationKind, cfgPath)
}
if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), joinBytes, internalcfg); err != nil {
return nil, err
}
} else {
// Takes passed flags into account; the defaulting is executed once again enforcing assignement of
// static default values to cfg only for values not provided with flags
kubeadmscheme.Scheme.Default(defaultversionedcfg)
kubeadmscheme.Scheme.Convert(defaultversionedcfg, internalcfg, nil)
} }
// Applies dynamic defaults to settings not provided with flags // Applies dynamic defaults to settings not provided with flags
@ -120,3 +121,24 @@ func JoinConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedc
return internalcfg, nil return internalcfg, nil
} }
// DefaultedJoinConfiguration takes a versioned JoinConfiguration (usually filled in by command line parameters), defaults it, converts it to internal and validates it
func DefaultedJoinConfiguration(defaultversionedcfg *kubeadmapiv1beta1.JoinConfiguration) (*kubeadmapi.JoinConfiguration, error) {
internalcfg := &kubeadmapi.JoinConfiguration{}
// Takes passed flags into account; the defaulting is executed once again enforcing assignment of
// static default values to cfg only for values not provided with flags
kubeadmscheme.Scheme.Default(defaultversionedcfg)
kubeadmscheme.Scheme.Convert(defaultversionedcfg, internalcfg, nil)
// Applies dynamic defaults to settings not provided with flags
if err := SetJoinDynamicDefaults(internalcfg); err != nil {
return nil, err
}
// Validates cfg (flags/configs + defaults)
if err := validation.ValidateJoinConfiguration(internalcfg).ToAggregate(); err != nil {
return nil, err
}
return internalcfg, nil
}

View File

@ -37,13 +37,13 @@ const (
node_invalidYAML = "testdata/validation/invalid_nodecfg.yaml" node_invalidYAML = "testdata/validation/invalid_nodecfg.yaml"
) )
func TestJoinConfigFileAndDefaultsToInternalConfig(t *testing.T) { func TestLoadJoinConfigurationFromFile(t *testing.T) {
var tests = []struct { var tests = []struct {
name, in, out string name, in, out string
groupVersion schema.GroupVersion groupVersion schema.GroupVersion
expectedErr bool expectedErr bool
}{ }{
// These tests are reading one file, loading it using JoinConfigFileAndDefaultsToInternalConfig that all of kubeadm is using for unmarshal of our API types, // These tests are reading one file, loading it using LoadJoinConfigurationFromFile that all of kubeadm is using for unmarshal of our API types,
// and then marshals the internal object to the expected groupVersion // and then marshals the internal object to the expected groupVersion
{ // v1alpha3 -> internal { // v1alpha3 -> internal
name: "v1alpha3ToInternal", name: "v1alpha3ToInternal",
@ -69,7 +69,7 @@ func TestJoinConfigFileAndDefaultsToInternalConfig(t *testing.T) {
out: node_v1beta1YAML, out: node_v1beta1YAML,
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion, groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
}, },
// These tests are reading one file that has only a subset of the fields populated, loading it using JoinConfigFileAndDefaultsToInternalConfig, // These tests are reading one file that has only a subset of the fields populated, loading it using LoadJoinConfigurationFromFile,
// and then marshals the internal object to the expected groupVersion // and then marshals the internal object to the expected groupVersion
{ // v1beta1 -> default -> validate -> internal -> v1beta1 { // v1beta1 -> default -> validate -> internal -> v1beta1
name: "incompleteYAMLToDefaultedv1beta1", name: "incompleteYAMLToDefaultedv1beta1",
@ -87,7 +87,7 @@ func TestJoinConfigFileAndDefaultsToInternalConfig(t *testing.T) {
for _, rt := range tests { for _, rt := range tests {
t.Run(rt.name, func(t2 *testing.T) { t.Run(rt.name, func(t2 *testing.T) {
internalcfg, err := JoinConfigFileAndDefaultsToInternalConfig(rt.in, &kubeadmapiv1beta1.JoinConfiguration{}) internalcfg, err := LoadJoinConfigurationFromFile(rt.in)
if err != nil { if err != nil {
if rt.expectedErr { if rt.expectedErr {
return return