mirror of https://github.com/k3s-io/k3s
kubeadm: Refactor InitConfiguration init APIs
Currently ConfigFileAndDefaultsToInternalConfig and FetchConfigFromFileOrCluster are used to default and load InitConfiguration from file or cluster. These two APIs do a couple of completely separate things depending on how they were invoked. In the case of ConfigFileAndDefaultsToInternalConfig, an InitConfiguration could be either defaulted with external override parameters, or loaded from file. With FetchConfigFromFileOrCluster an InitConfiguration is either loaded from file or from the config map in the cluster. The two share both some functionality, but not enough code. They are also quite difficult to use and sometimes even error prone. To solve the issues, the following steps were taken: - Introduce DefaultedInitConfiguration which returns defaulted version agnostic InitConfiguration. The function takes InitConfiguration for overriding the defaults. - Introduce LoadInitConfigurationFromFile, which loads, converts, validates and defaults an InitConfiguration from file. - Introduce FetchInitConfigurationFromCluster that fetches InitConfiguration from the config map. - Reduce, when possible, the usage of ConfigFileAndDefaultsToInternalConfig by replacing it with DefaultedInitConfiguration or LoadInitConfigurationFromFile invocations. - Replace all usages of FetchConfigFromFileOrCluster with calls to LoadInitConfigurationFromFile or FetchInitConfigurationFromCluster. - Delete FetchConfigFromFileOrCluster as it's no longer used. - Rename ConfigFileAndDefaultsToInternalConfig to LoadOrDefaultInitConfiguration in order to better describe what the function is actually doing. Signed-off-by: Rostislav M. Georgiev <rostislavg@vmware.com>pull/564/head
parent
196047fc28
commit
51197e4393
|
@ -135,7 +135,7 @@ func addFlags(cmd *cobra.Command, cfg *renewConfig) {
|
|||
|
||||
func generateRenewalFunction(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert, cfg *renewConfig) func() {
|
||||
return func() {
|
||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfg.cfgPath, &cfg.cfg)
|
||||
internalcfg, err := configutil.LoadOrDefaultInitConfiguration(cfg.cfgPath, &cfg.cfg)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
if cfg.useCSR {
|
||||
|
|
|
@ -79,8 +79,8 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command {
|
|||
kubeadmutil.CheckErr(errors.New("missing required argument --client-name"))
|
||||
}
|
||||
|
||||
// This call returns the ready-to-use configuration based on the configuration file that might or might not exist and the default cfg populated by flags
|
||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig("", cfg)
|
||||
// This call returns the ready-to-use configuration based on the default cfg populated by flags
|
||||
internalcfg, err := configutil.DefaultedInitConfiguration(cfg)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
// if the kubeconfig file for an additional user has to use a token, use it
|
||||
|
|
|
@ -124,11 +124,11 @@ func getSelfhostingSubCommand(in io.Reader) *cobra.Command {
|
|||
kubeadmutil.CheckErr(err)
|
||||
|
||||
// KubernetesVersion is not used, but we set it explicitly to avoid the lookup
|
||||
// of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
||||
// of the version from the internet when executing LoadOrDefaultInitConfiguration
|
||||
phases.SetKubernetesVersion(&cfg.ClusterConfiguration)
|
||||
|
||||
// This call returns the ready-to-use configuration based on the configuration file that might or might not exist and the default cfg populated by flags
|
||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
|
||||
internalcfg, err := configutil.LoadOrDefaultInitConfiguration(cfgPath, cfg)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
// Converts the Static Pod-hosted control plane into a self-hosted one
|
||||
|
|
|
@ -178,7 +178,7 @@ func getSupportedComponentConfigAPIObjects() []string {
|
|||
}
|
||||
|
||||
func getDefaultedInitConfig() (*kubeadmapi.InitConfiguration, error) {
|
||||
return configutil.ConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1beta1.InitConfiguration{
|
||||
return configutil.DefaultedInitConfiguration(&kubeadmapiv1beta1.InitConfiguration{
|
||||
// TODO: Probably move to getDefaultedClusterConfig?
|
||||
LocalAPIEndpoint: kubeadmapiv1beta1.APIEndpoint{AdvertiseAddress: "1.2.3.4"},
|
||||
ClusterConfiguration: kubeadmapiv1beta1.ClusterConfiguration{
|
||||
|
@ -320,12 +320,13 @@ func NewCmdConfigUploadFromFile(out io.Writer, kubeConfigFile *string) *cobra.Co
|
|||
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
// The default configuration is empty; everything should come from the file on disk
|
||||
klog.V(1).Infoln("[config] creating empty default configuration")
|
||||
defaultcfg := &kubeadmapiv1beta1.InitConfiguration{}
|
||||
// Upload the configuration using the file; don't care about the defaultcfg really
|
||||
// Default both statically and dynamically, convert to internal API type, and validate everything
|
||||
internalcfg, err := configutil.LoadInitConfigurationFromFile(cfgPath)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
// Upload the configuration using the file
|
||||
klog.V(1).Infof("[config] uploading configuration")
|
||||
err = uploadConfiguration(client, cfgPath, defaultcfg)
|
||||
err = uploadconfig.UploadConfiguration(internalcfg, client)
|
||||
kubeadmutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
@ -360,10 +361,18 @@ func NewCmdConfigUploadFromFlags(out io.Writer, kubeConfigFile *string) *cobra.C
|
|||
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
// KubernetesVersion is not used, but we set it explicitly to avoid the lookup
|
||||
// of the version from the internet when executing DefaultedInitConfiguration
|
||||
phaseutil.SetKubernetesVersion(&cfg.ClusterConfiguration)
|
||||
|
||||
// Default both statically and dynamically, convert to internal API type, and validate everything
|
||||
// The cfgPath argument is unset here as we shouldn't load a config file from disk, just go with cfg
|
||||
klog.V(1).Infoln("[config] converting to internal API type")
|
||||
internalcfg, err := configutil.DefaultedInitConfiguration(cfg)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
// Finally, upload the configuration
|
||||
klog.V(1).Infof("[config] uploading configuration")
|
||||
err = uploadConfiguration(client, "", cfg)
|
||||
err = uploadconfig.UploadConfiguration(internalcfg, client)
|
||||
kubeadmutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
@ -384,24 +393,6 @@ func RunConfigView(out io.Writer, client clientset.Interface) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// uploadConfiguration handles the uploading of the configuration internally
|
||||
func uploadConfiguration(client clientset.Interface, cfgPath string, defaultcfg *kubeadmapiv1beta1.InitConfiguration) error {
|
||||
// KubernetesVersion is not used, but we set it explicitly to avoid the lookup
|
||||
// of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
||||
phaseutil.SetKubernetesVersion(&defaultcfg.ClusterConfiguration)
|
||||
|
||||
// Default both statically and dynamically, convert to internal API type, and validate everything
|
||||
// First argument is unset here as we shouldn't load a config file from disk
|
||||
klog.V(1).Infoln("[config] converting to internal API type")
|
||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, defaultcfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Then just call the uploadconfig phase to do the rest of the work
|
||||
return uploadconfig.UploadConfiguration(internalcfg, client)
|
||||
}
|
||||
|
||||
// NewCmdConfigImages returns the "kubeadm config images" command
|
||||
func NewCmdConfigImages(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
|
@ -427,7 +418,7 @@ func NewCmdConfigImagesPull() *cobra.Command {
|
|||
Run: func(_ *cobra.Command, _ []string) {
|
||||
externalcfg.ClusterConfiguration.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, featureGatesString)
|
||||
kubeadmutil.CheckErr(err)
|
||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, externalcfg)
|
||||
internalcfg, err := configutil.LoadOrDefaultInitConfiguration(cfgPath, externalcfg)
|
||||
kubeadmutil.CheckErr(err)
|
||||
containerRuntime, err := utilruntime.NewContainerRuntime(utilsexec.New(), internalcfg.GetCRISocket())
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
@ -496,7 +487,7 @@ func NewCmdConfigImagesList(out io.Writer, mockK8sVersion *string) *cobra.Comman
|
|||
|
||||
// NewImagesList returns the underlying struct for the "kubeadm config images list" command
|
||||
func NewImagesList(cfgPath string, cfg *kubeadmapiv1beta1.InitConfiguration) (*ImagesList, error) {
|
||||
initcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
|
||||
initcfg, err := configutil.LoadOrDefaultInitConfiguration(cfgPath, cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not convert cfg to an internal cfg")
|
||||
}
|
||||
|
|
|
@ -275,7 +275,7 @@ func TestMigrate(t *testing.T) {
|
|||
t.Fatalf("failed to set new-config flag")
|
||||
}
|
||||
command.Run(nil, nil)
|
||||
if _, err := configutil.ConfigFileAndDefaultsToInternalConfig(newConfigPath, &kubeadmapiv1beta1.InitConfiguration{}); err != nil {
|
||||
if _, err := configutil.LoadInitConfigurationFromFile(newConfigPath); err != nil {
|
||||
t.Fatalf("Could not read output back into internal type: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -302,7 +302,7 @@ func newInitData(cmd *cobra.Command, args []string, options *initOptions, out io
|
|||
|
||||
// Either use the config file if specified, or convert public kubeadm API to the internal InitConfiguration
|
||||
// and validates InitConfiguration
|
||||
cfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(options.cfgPath, options.externalcfg)
|
||||
cfg, err := configutil.LoadOrDefaultInitConfiguration(options.cfgPath, options.externalcfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -530,7 +530,7 @@ func fetchInitConfiguration(tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.I
|
|||
}
|
||||
|
||||
// Fetches the init configuration
|
||||
initConfiguration, err := configutil.FetchConfigFromFileOrCluster(tlsClient, os.Stdout, "join", "", true)
|
||||
initConfiguration, err := configutil.FetchInitConfigurationFromCluster(tlsClient, os.Stdout, "join", true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
|
||||
}
|
||||
|
|
|
@ -208,7 +208,7 @@ func getEtcdDataDir(manifestPath string, client clientset.Interface) (string, er
|
|||
var dataDir string
|
||||
|
||||
if client != nil {
|
||||
cfg, err := configutil.FetchConfigFromFileOrCluster(client, os.Stdout, "reset", "", false)
|
||||
cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "reset", false)
|
||||
if err == nil && cfg.Etcd.Local != nil {
|
||||
return cfg.Etcd.Local.DataDir, nil
|
||||
}
|
||||
|
@ -299,7 +299,7 @@ func resetConfigDir(configPathDir, pkiPathDir string) {
|
|||
|
||||
func resetDetectCRISocket(client clientset.Interface) (string, error) {
|
||||
// first try to connect to the cluster for the CRI socket
|
||||
cfg, err := configutil.FetchConfigFromFileOrCluster(client, os.Stdout, "reset", "", false)
|
||||
cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "reset", false)
|
||||
if err == nil {
|
||||
return cfg.NodeRegistration.CRISocket, nil
|
||||
}
|
||||
|
|
|
@ -211,12 +211,12 @@ func NewCmdTokenGenerate(out io.Writer) *cobra.Command {
|
|||
// RunCreateToken generates a new bootstrap token and stores it as a secret on the server.
|
||||
func RunCreateToken(out io.Writer, client clientset.Interface, cfgPath string, cfg *kubeadmapiv1beta1.InitConfiguration, printJoinCommand bool, kubeConfigFile string) error {
|
||||
// KubernetesVersion is not used, but we set it explicitly to avoid the lookup
|
||||
// of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
||||
// of the version from the internet when executing LoadOrDefaultInitConfiguration
|
||||
phaseutil.SetKubernetesVersion(&cfg.ClusterConfiguration)
|
||||
|
||||
// This call returns the ready-to-use configuration based on the configuration file that might or might not exist and the default cfg populated by flags
|
||||
klog.V(1).Infoln("[token] loading configurations")
|
||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
|
||||
internalcfg, err := configutil.LoadOrDefaultInitConfiguration(cfgPath, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ func TestRunCreateToken(t *testing.T) {
|
|||
cfg := &kubeadmapiv1beta1.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapiv1beta1.ClusterConfiguration{
|
||||
// KubernetesVersion is not used, but we set this explicitly to avoid
|
||||
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
||||
// the lookup of the version from the internet when executing LoadOrDefaultInitConfiguration
|
||||
KubernetesVersion: constants.MinimumControlPlaneVersion.String(),
|
||||
},
|
||||
BootstrapTokens: []kubeadmapiv1beta1.BootstrapToken{
|
||||
|
|
|
@ -14,7 +14,6 @@ go_library(
|
|||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
||||
"//cmd/kubeadm/app/cmd/options:go_default_library",
|
||||
"//cmd/kubeadm/app/cmd/util:go_default_library",
|
||||
|
|
|
@ -27,7 +27,6 @@ import (
|
|||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
|
||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||
|
@ -90,9 +89,8 @@ func NewCmdApply(apf *applyPlanFlags) *cobra.Command {
|
|||
|
||||
// If the version is specified in config file, pick up that value.
|
||||
if flags.cfgPath != "" {
|
||||
klog.V(1).Infof("fetching configuration from file %s", flags.cfgPath)
|
||||
// Note that cfg isn't preserved here, it's just an one-off to populate flags.newK8sVersionStr based on --config
|
||||
cfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(flags.cfgPath, &kubeadmapiv1beta1.InitConfiguration{})
|
||||
cfg, err := configutil.LoadInitConfigurationFromFile(flags.cfgPath)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
if cfg.KubernetesVersion != "" {
|
||||
|
|
|
@ -71,7 +71,14 @@ func enforceRequirements(flags *applyPlanFlags, dryRun bool, newK8sVersion strin
|
|||
|
||||
// Fetch the configuration from a file or ConfigMap and validate it
|
||||
fmt.Println("[upgrade/config] Making sure the configuration is correct:")
|
||||
cfg, err := configutil.FetchConfigFromFileOrCluster(client, os.Stdout, "upgrade/config", flags.cfgPath, false)
|
||||
|
||||
var cfg *kubeadmapi.InitConfiguration
|
||||
if flags.cfgPath != "" {
|
||||
cfg, err = configutil.LoadInitConfigurationFromFile(flags.cfgPath)
|
||||
} else {
|
||||
cfg, err = configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "upgrade/config", false)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Printf("[upgrade/config] In order to upgrade, a ConfigMap called %q in the %s namespace must exist.\n", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
|
||||
|
|
|
@ -26,7 +26,6 @@ import (
|
|||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
"k8s.io/klog"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
|
||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
|
@ -78,8 +77,7 @@ func NewCmdDiff(out io.Writer) *cobra.Command {
|
|||
func runDiff(flags *diffFlags, args []string) error {
|
||||
|
||||
// If the version is specified in config file, pick up that value.
|
||||
klog.V(1).Infof("fetching configuration from file %s", flags.cfgPath)
|
||||
cfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(flags.cfgPath, &kubeadmapiv1beta1.InitConfiguration{})
|
||||
cfg, err := configutil.LoadInitConfigurationFromFile(flags.cfgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -229,7 +229,7 @@ func RunUpgradeControlPlane(flags *controlplaneUpgradeFlags) error {
|
|||
waiter := apiclient.NewKubeWaiter(client, upgrade.UpgradeManifestTimeout, os.Stdout)
|
||||
|
||||
// Fetches the cluster configuration
|
||||
cfg, err := configutil.FetchConfigFromFileOrCluster(client, os.Stdout, "upgrade", "", false)
|
||||
cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "upgrade", false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/version"
|
||||
"k8s.io/klog"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
|
@ -62,8 +61,7 @@ func NewCmdPlan(apf *applyPlanFlags) *cobra.Command {
|
|||
|
||||
// If the version is specified in config file, pick up that value.
|
||||
if flags.cfgPath != "" {
|
||||
klog.V(1).Infof("fetching configuration from file %s", flags.cfgPath)
|
||||
cfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(flags.cfgPath, &kubeadmapiv1beta1.InitConfiguration{})
|
||||
cfg, err := configutil.LoadInitConfigurationFromFile(flags.cfgPath)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
if cfg.KubernetesVersion != "" {
|
||||
|
|
|
@ -201,7 +201,7 @@ func TestEnsureProxyAddon(t *testing.T) {
|
|||
masterConfig.Networking.PodSubnet = "2001:101::/96"
|
||||
}
|
||||
|
||||
intMaster, err := configutil.ConfigFileAndDefaultsToInternalConfig("", masterConfig)
|
||||
intMaster, err := configutil.DefaultedInitConfiguration(masterConfig)
|
||||
if err != nil {
|
||||
t.Errorf("test failed to convert external to internal version")
|
||||
break
|
||||
|
|
|
@ -84,7 +84,7 @@ func TestUploadConfiguration(t *testing.T) {
|
|||
CRISocket: "/var/run/custom-cri.sock",
|
||||
},
|
||||
}
|
||||
cfg, err := configutil.ConfigFileAndDefaultsToInternalConfig("", initialcfg)
|
||||
cfg, err := configutil.DefaultedInitConfiguration(initialcfg)
|
||||
|
||||
// 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{}
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
|
@ -39,54 +38,26 @@ import (
|
|||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
)
|
||||
|
||||
// FetchConfigFromFileOrCluster fetches configuration required for upgrading your cluster from a file (which has precedence) or a ConfigMap in the cluster
|
||||
func FetchConfigFromFileOrCluster(client clientset.Interface, w io.Writer, logPrefix, cfgPath string, newControlPlane bool) (*kubeadmapi.InitConfiguration, error) {
|
||||
// Load the configuration from a file or the cluster
|
||||
initcfg, err := loadConfiguration(client, w, logPrefix, cfgPath, newControlPlane)
|
||||
// FetchInitConfigurationFromCluster fetches configuration from a ConfigMap in the cluster
|
||||
func FetchInitConfigurationFromCluster(client clientset.Interface, w io.Writer, logPrefix string, newControlPlane bool) (*kubeadmapi.InitConfiguration, error) {
|
||||
fmt.Fprintf(w, "[%s] Reading configuration from the cluster...\n", logPrefix)
|
||||
fmt.Fprintf(w, "[%s] FYI: You can look at this config file with 'kubectl -n %s get cm %s -oyaml'\n", logPrefix, metav1.NamespaceSystem, constants.KubeadmConfigConfigMap)
|
||||
|
||||
// Fetch the actual config from cluster
|
||||
cfg, err := getInitConfigurationFromCluster(constants.KubernetesDir, client, newControlPlane)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply dynamic defaults
|
||||
if err := SetInitDynamicDefaults(initcfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return initcfg, err
|
||||
}
|
||||
|
||||
// loadConfiguration loads the configuration byte slice from either a file or the cluster ConfigMap
|
||||
func loadConfiguration(client clientset.Interface, w io.Writer, logPrefix, cfgPath string, newControlPlane bool) (*kubeadmapi.InitConfiguration, error) {
|
||||
// The config file has the highest priority
|
||||
if cfgPath != "" {
|
||||
fmt.Fprintf(w, "[%s] Reading configuration options from a file: %s\n", logPrefix, cfgPath)
|
||||
return loadInitConfigurationFromFile(cfgPath)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "[%s] Reading configuration from the cluster...\n", logPrefix)
|
||||
fmt.Fprintf(w, "[%s] FYI: You can look at this config file with 'kubectl -n %s get cm %s -oyaml'\n", logPrefix, metav1.NamespaceSystem, constants.KubeadmConfigConfigMap)
|
||||
return getInitConfigurationFromCluster(constants.KubernetesDir, client, newControlPlane)
|
||||
}
|
||||
|
||||
func loadInitConfigurationFromFile(cfgPath string) (*kubeadmapi.InitConfiguration, error) {
|
||||
configBytes, err := ioutil.ReadFile(cfgPath)
|
||||
if err != nil {
|
||||
if err := SetInitDynamicDefaults(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Unmarshal the versioned configuration populated from the file,
|
||||
// convert it to the internal API types, then default and validate
|
||||
// NB the file contains multiple YAML, with a combination of
|
||||
// - a YAML with a InitConfiguration object
|
||||
// - a YAML with a ClusterConfiguration object (without embedded component configs)
|
||||
// - separated YAML for components configs
|
||||
initcfg, err := BytesToInternalConfig(configBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return initcfg, nil
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// getInitConfigurationFromCluster is separate only for testing purposes, don't call it directly, use FetchInitConfigurationFromCluster instead
|
||||
func getInitConfigurationFromCluster(kubeconfigDir string, client clientset.Interface, newControlPlane bool) (*kubeadmapi.InitConfiguration, error) {
|
||||
// TODO: This code should support reading the MasterConfiguration key as well for backwards-compat
|
||||
// Also, the config map really should be KubeadmConfigConfigMap...
|
||||
|
@ -124,7 +95,6 @@ func getInitConfigurationFromCluster(kubeconfigDir string, client clientset.Inte
|
|||
return nil, errors.Wrap(err, "failed to getAPIEndpoint")
|
||||
}
|
||||
}
|
||||
|
||||
return initcfg, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -172,76 +171,6 @@ G+2/lm8TaVjoU7Fi5Ka5G5HY2GLaR7P+IxYcrMHCl62Y7Rqcrnc=
|
|||
`),
|
||||
}
|
||||
|
||||
func TestLoadInitConfigurationFromFile(t *testing.T) {
|
||||
// Create temp folder for the test case
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
fileContents []byte
|
||||
}{
|
||||
{
|
||||
name: "v1beta1.partial1",
|
||||
fileContents: cfgFiles["InitConfiguration_v1beta1"],
|
||||
},
|
||||
{
|
||||
name: "v1beta1.partial2",
|
||||
fileContents: cfgFiles["ClusterConfiguration_v1beta1"],
|
||||
},
|
||||
{
|
||||
name: "v1beta1.full",
|
||||
fileContents: bytes.Join([][]byte{
|
||||
cfgFiles["InitConfiguration_v1beta1"],
|
||||
cfgFiles["ClusterConfiguration_v1beta1"],
|
||||
cfgFiles["Kube-proxy_componentconfig"],
|
||||
cfgFiles["Kubelet_componentconfig"],
|
||||
}, []byte(kubeadmconstants.YAMLDocumentSeparator)),
|
||||
},
|
||||
{
|
||||
name: "v1alpha3.partial1",
|
||||
fileContents: cfgFiles["InitConfiguration_v1alpha3"],
|
||||
},
|
||||
{
|
||||
name: "v1alpha3.partial2",
|
||||
fileContents: cfgFiles["ClusterConfiguration_v1alpha3"],
|
||||
},
|
||||
{
|
||||
name: "v1alpha3.full",
|
||||
fileContents: bytes.Join([][]byte{
|
||||
cfgFiles["InitConfiguration_v1alpha3"],
|
||||
cfgFiles["ClusterConfiguration_v1alpha3"],
|
||||
cfgFiles["Kube-proxy_componentconfig"],
|
||||
cfgFiles["Kubelet_componentconfig"],
|
||||
}, []byte(kubeadmconstants.YAMLDocumentSeparator)),
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
cfgPath := filepath.Join(tmpdir, rt.name)
|
||||
err := ioutil.WriteFile(cfgPath, rt.fileContents, 0644)
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create file")
|
||||
return
|
||||
}
|
||||
|
||||
obj, err := loadInitConfigurationFromFile(cfgPath)
|
||||
if err != nil {
|
||||
t.Errorf("Error reading file: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if obj == nil {
|
||||
t.Errorf("Unexpected nil return value")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNodeNameFromKubeletConfig(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
|
|
|
@ -154,7 +154,7 @@ func MigrateOldConfigFromFile(cfgPath string) ([]byte, error) {
|
|||
|
||||
// Migrate InitConfiguration and ClusterConfiguration if there are any in the config
|
||||
if kubeadmutil.GroupVersionKindsHasInitConfiguration(gvks...) || kubeadmutil.GroupVersionKindsHasClusterConfiguration(gvks...) {
|
||||
o, err := ConfigFileAndDefaultsToInternalConfig(cfgPath, &kubeadmapiv1beta1.InitConfiguration{})
|
||||
o, err := LoadInitConfigurationFromFile(cfgPath)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
|
|
@ -161,33 +161,39 @@ func SetClusterDynamicDefaults(cfg *kubeadmapi.ClusterConfiguration, advertiseAd
|
|||
return nil
|
||||
}
|
||||
|
||||
// ConfigFileAndDefaultsToInternalConfig 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.
|
||||
// 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)
|
||||
// Lastly, the internal config is validated and returned.
|
||||
func ConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedcfg *kubeadmapiv1beta1.InitConfiguration) (*kubeadmapi.InitConfiguration, error) {
|
||||
// DefaultedInitConfiguration takes a versioned init config (often populated by flags), defaults it and converts it into internal InitConfiguration
|
||||
func DefaultedInitConfiguration(defaultversionedcfg *kubeadmapiv1beta1.InitConfiguration) (*kubeadmapi.InitConfiguration, error) {
|
||||
internalcfg := &kubeadmapi.InitConfiguration{}
|
||||
|
||||
if cfgPath != "" {
|
||||
// Loads configuration from config file, if provided
|
||||
// Nb. --config overrides command line flags
|
||||
klog.V(1).Infoln("loading configuration from the given file")
|
||||
// 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 := SetInitDynamicDefaults(internalcfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Validates cfg (flags/configs + defaults + dynamic defaults)
|
||||
if err := validation.ValidateInitConfiguration(internalcfg).ToAggregate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return internalcfg, nil
|
||||
}
|
||||
|
||||
// LoadInitConfigurationFromFile loads a supported versioned InitConfiguration from a file, converts it into internal config, defaults it and verifies it.
|
||||
func LoadInitConfigurationFromFile(cfgPath string) (*kubeadmapi.InitConfiguration, 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)
|
||||
}
|
||||
internalcfg, err = BytesToInternalConfig(b)
|
||||
|
||||
internalcfg, err := BytesToInternalConfig(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// 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 := SetInitDynamicDefaults(internalcfg); err != nil {
|
||||
|
@ -200,6 +206,21 @@ func ConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedcfg *
|
|||
return internalcfg, nil
|
||||
}
|
||||
|
||||
// LoadOrDefaultInitConfiguration 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.
|
||||
// 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)
|
||||
// Lastly, the internal config is validated and returned.
|
||||
func LoadOrDefaultInitConfiguration(cfgPath string, defaultversionedcfg *kubeadmapiv1beta1.InitConfiguration) (*kubeadmapi.InitConfiguration, error) {
|
||||
if cfgPath != "" {
|
||||
// Loads configuration from config file, if provided
|
||||
// Nb. --config overrides command line flags
|
||||
return LoadInitConfigurationFromFile(cfgPath)
|
||||
}
|
||||
|
||||
return DefaultedInitConfiguration(defaultversionedcfg)
|
||||
}
|
||||
|
||||
// BytesToInternalConfig converts a byte slice to an internal, defaulted and validated configuration object.
|
||||
// The byte slice may contain one or many different YAML documents. These YAML documents are parsed one-by-one
|
||||
// and well-known ComponentConfig GroupVersionKinds are stored inside of the internal InitConfiguration struct
|
||||
|
|
|
@ -19,6 +19,8 @@ package config
|
|||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
@ -27,6 +29,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -51,13 +54,84 @@ func diff(expected, actual []byte) string {
|
|||
return diffBytes.String()
|
||||
}
|
||||
|
||||
func TestConfigFileAndDefaultsToInternalConfig(t *testing.T) {
|
||||
func TestLoadInitConfigurationFromFile(t *testing.T) {
|
||||
// Create temp folder for the test case
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
// cfgFiles is in cluster_test.go
|
||||
var tests = []struct {
|
||||
name string
|
||||
fileContents []byte
|
||||
}{
|
||||
{
|
||||
name: "v1beta1.partial1",
|
||||
fileContents: cfgFiles["InitConfiguration_v1beta1"],
|
||||
},
|
||||
{
|
||||
name: "v1beta1.partial2",
|
||||
fileContents: cfgFiles["ClusterConfiguration_v1beta1"],
|
||||
},
|
||||
{
|
||||
name: "v1beta1.full",
|
||||
fileContents: bytes.Join([][]byte{
|
||||
cfgFiles["InitConfiguration_v1beta1"],
|
||||
cfgFiles["ClusterConfiguration_v1beta1"],
|
||||
cfgFiles["Kube-proxy_componentconfig"],
|
||||
cfgFiles["Kubelet_componentconfig"],
|
||||
}, []byte(constants.YAMLDocumentSeparator)),
|
||||
},
|
||||
{
|
||||
name: "v1alpha3.partial1",
|
||||
fileContents: cfgFiles["InitConfiguration_v1alpha3"],
|
||||
},
|
||||
{
|
||||
name: "v1alpha3.partial2",
|
||||
fileContents: cfgFiles["ClusterConfiguration_v1alpha3"],
|
||||
},
|
||||
{
|
||||
name: "v1alpha3.full",
|
||||
fileContents: bytes.Join([][]byte{
|
||||
cfgFiles["InitConfiguration_v1alpha3"],
|
||||
cfgFiles["ClusterConfiguration_v1alpha3"],
|
||||
cfgFiles["Kube-proxy_componentconfig"],
|
||||
cfgFiles["Kubelet_componentconfig"],
|
||||
}, []byte(constants.YAMLDocumentSeparator)),
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
cfgPath := filepath.Join(tmpdir, rt.name)
|
||||
err := ioutil.WriteFile(cfgPath, rt.fileContents, 0644)
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create file")
|
||||
return
|
||||
}
|
||||
|
||||
obj, err := LoadInitConfigurationFromFile(cfgPath)
|
||||
if err != nil {
|
||||
t.Errorf("Error reading file: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if obj == nil {
|
||||
t.Errorf("Unexpected nil return value")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitConfigurationMarshallingFromFile(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name, in, out string
|
||||
groupVersion schema.GroupVersion
|
||||
expectedErr bool
|
||||
}{
|
||||
// These tests are reading one file, loading it using ConfigFileAndDefaultsToInternalConfig that all of kubeadm is using for unmarshal of our API types,
|
||||
// These tests are reading one file, loading it using LoadInitConfigurationFromFile that all of kubeadm is using for unmarshal of our API types,
|
||||
// and then marshals the internal object to the expected groupVersion
|
||||
{ // v1alpha3 -> internal
|
||||
name: "v1alpha3ToInternal",
|
||||
|
@ -83,7 +157,7 @@ func TestConfigFileAndDefaultsToInternalConfig(t *testing.T) {
|
|||
out: master_v1beta1YAML,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
},
|
||||
// These tests are reading one file that has only a subset of the fields populated, loading it using ConfigFileAndDefaultsToInternalConfig,
|
||||
// These tests are reading one file that has only a subset of the fields populated, loading it using LoadInitConfigurationFromFile,
|
||||
// and then marshals the internal object to the expected groupVersion
|
||||
{ // v1beta1 -> default -> validate -> internal -> v1beta1
|
||||
name: "incompleteYAMLToDefaultedv1beta1",
|
||||
|
@ -101,7 +175,7 @@ func TestConfigFileAndDefaultsToInternalConfig(t *testing.T) {
|
|||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
internalcfg, err := ConfigFileAndDefaultsToInternalConfig(rt.in, &kubeadmapiv1beta1.InitConfiguration{})
|
||||
internalcfg, err := LoadInitConfigurationFromFile(rt.in)
|
||||
if err != nil {
|
||||
if rt.expectedErr {
|
||||
return
|
||||
|
|
|
@ -157,7 +157,7 @@ func AssertError(t *testing.T, err error, expected string) {
|
|||
|
||||
// GetDefaultInternalConfig returns a defaulted kubeadmapi.InitConfiguration
|
||||
func GetDefaultInternalConfig(t *testing.T) *kubeadmapi.InitConfiguration {
|
||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1beta1.InitConfiguration{})
|
||||
internalcfg, err := configutil.DefaultedInitConfiguration(&kubeadmapiv1beta1.InitConfiguration{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting default config: %v", err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue