@@ -21,6 +21,7 @@ import (
21
21
"bytes"
22
22
"fmt"
23
23
"io"
24
+ "io/ioutil"
24
25
"os"
25
26
"strings"
26
27
"time"
@@ -36,47 +37,91 @@ import (
36
37
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
37
38
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
38
39
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
40
+ "k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
39
41
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
40
42
"k8s.io/kubernetes/cmd/kubeadm/app/features"
41
43
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
42
44
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
45
+ kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
43
46
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
44
47
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
45
48
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
46
49
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
47
50
)
48
51
49
- func getK8sVersionFromUserInput (flags * applyPlanFlags , args []string , versionIsMandatory bool ) (string , error ) {
50
- var userVersion string
51
-
52
- // If the version is specified in config file, pick up that value.
53
- if flags .cfgPath != "" {
54
- // Note that cfg isn't preserved here, it's just an one-off to populate userVersion based on --config
55
- cfg , err := configutil .LoadInitConfigurationFromFile (flags .cfgPath )
56
- if err != nil {
57
- return "" , err
52
+ // isKubeadmConfigPresent checks if a kubeadm config type is found in the provided document map
53
+ func isKubeadmConfigPresent (docmap kubeadmapi.DocumentMap ) bool {
54
+ for gvk := range docmap {
55
+ if gvk .Group == kubeadmapi .GroupName {
56
+ return true
58
57
}
58
+ }
59
+ return false
60
+ }
59
61
60
- userVersion = cfg .KubernetesVersion
62
+ // loadConfig loads configuration from a file and/or the cluster. InitConfiguration, ClusterConfiguration and (optionally) component configs
63
+ // are loaded. This function allows the component configs to be loaded from a file that contains only them. If the file contains any kubeadm types
64
+ // in it (API group "kubeadm.kubernetes.io" present), then the supplied file is treaded as a legacy reconfiguration style "--config" use and the
65
+ // returned bool value is set to true (the only case to be done so).
66
+ func loadConfig (cfgPath string , client clientset.Interface , skipComponentConfigs bool ) (* kubeadmapi.InitConfiguration , bool , error ) {
67
+ // Used for info logs here
68
+ const logPrefix = "upgrade/config"
69
+
70
+ // The usual case here is to not have a config file, but rather load the config from the cluster.
71
+ // This is probably 90% of the time. So we handle it first.
72
+ if cfgPath == "" {
73
+ cfg , err := configutil .FetchInitConfigurationFromCluster (client , os .Stdout , logPrefix , false , skipComponentConfigs )
74
+ return cfg , false , err
61
75
}
62
76
63
- // the version arg is mandatory unless version is specified in the config file
64
- if versionIsMandatory && userVersion == "" {
65
- if err := cmdutil .ValidateExactArgNumber (args , []string {"version" }); err != nil {
66
- return "" , err
67
- }
77
+ // Otherwise, we have a config file. Let's load it.
78
+ configBytes , err := ioutil .ReadFile (cfgPath )
79
+ if err != nil {
80
+ return nil , false , errors .Wrapf (err , "unable to load config from file %q" , cfgPath )
68
81
}
69
82
70
- // If option was specified in both args and config file, args will overwrite the config file.
71
- if len (args ) == 1 {
72
- userVersion = args [0 ]
83
+ // Split the YAML documents in the file into a DocumentMap
84
+ docmap , err := kubeadmutil .SplitYAMLDocuments (configBytes )
85
+ if err != nil {
86
+ return nil , false , err
87
+ }
88
+
89
+ // If there are kubeadm types (API group kubeadm.kubernetes.io) present, we need to keep the existing behavior
90
+ // here. Basically, we have to load all of the configs from the file and none from the cluster. Configs that are
91
+ // missing from the file will be automatically regenerated by kubeadm even if they are present in the cluster.
92
+ // The resulting configs overwrite the existing cluster ones at the end of a successful upgrade apply operation.
93
+ if isKubeadmConfigPresent (docmap ) {
94
+ klog .Warning ("WARNING: Usage of the --config flag with kubeadm config types for reconfiguring the cluster during upgrade is not recommended!" )
95
+ cfg , err := configutil .BytesToInitConfiguration (configBytes )
96
+ return cfg , true , err
97
+ }
98
+
99
+ // If no kubeadm config types are present, we assume that there are manually upgraded component configs in the file.
100
+ // Hence, we load the kubeadm types from the cluster.
101
+ initCfg , err := configutil .FetchInitConfigurationFromCluster (client , os .Stdout , logPrefix , false , true )
102
+ if err != nil {
103
+ return nil , false , err
73
104
}
74
105
75
- return userVersion , nil
106
+ // Stop here if the caller does not want us to load the component configs
107
+ if ! skipComponentConfigs {
108
+ // Load the component configs with upgrades
109
+ if err := componentconfigs .FetchFromClusterWithLocalOverwrites (& initCfg .ClusterConfiguration , client , docmap ); err != nil {
110
+ return nil , false , err
111
+ }
112
+
113
+ // Now default and validate the configs
114
+ componentconfigs .Default (& initCfg .ClusterConfiguration , & initCfg .LocalAPIEndpoint , & initCfg .NodeRegistration )
115
+ if errs := componentconfigs .Validate (& initCfg .ClusterConfiguration ); len (errs ) != 0 {
116
+ return nil , false , errs .ToAggregate ()
117
+ }
118
+ }
119
+
120
+ return initCfg , false , nil
76
121
}
77
122
78
123
// enforceRequirements verifies that it's okay to upgrade and then returns the variables needed for the rest of the procedure
79
- func enforceRequirements (flags * applyPlanFlags , dryRun bool , newK8sVersion string ) (clientset.Interface , upgrade.VersionGetter , * kubeadmapi.InitConfiguration , error ) {
124
+ func enforceRequirements (flags * applyPlanFlags , args [] string , dryRun bool , upgradeApply bool ) (clientset.Interface , upgrade.VersionGetter , * kubeadmapi.InitConfiguration , error ) {
80
125
client , err := getClient (flags .kubeConfigPath , dryRun )
81
126
if err != nil {
82
127
return nil , nil , nil , errors .Wrapf (err , "couldn't create a Kubernetes client from file %q" , flags .kubeConfigPath )
@@ -90,14 +135,8 @@ func enforceRequirements(flags *applyPlanFlags, dryRun bool, newK8sVersion strin
90
135
// Fetch the configuration from a file or ConfigMap and validate it
91
136
fmt .Println ("[upgrade/config] Making sure the configuration is correct:" )
92
137
93
- var cfg * kubeadmapi.InitConfiguration
94
- if flags .cfgPath != "" {
95
- klog .Warning ("WARNING: Usage of the --config flag for reconfiguring the cluster during upgrade is not recommended!" )
96
- cfg , err = configutil .LoadInitConfigurationFromFile (flags .cfgPath )
97
- } else {
98
- cfg , err = configutil .FetchInitConfigurationFromCluster (client , os .Stdout , "upgrade/config" , false )
99
- }
100
-
138
+ var newK8sVersion string
139
+ cfg , legacyReconfigure , err := loadConfig (flags .cfgPath , client , ! upgradeApply )
101
140
if err != nil {
102
141
if apierrors .IsNotFound (err ) {
103
142
fmt .Printf ("[upgrade/config] In order to upgrade, a ConfigMap called %q in the %s namespace must exist.\n " , constants .KubeadmConfigConfigMap , metav1 .NamespaceSystem )
@@ -111,6 +150,11 @@ func enforceRequirements(flags *applyPlanFlags, dryRun bool, newK8sVersion strin
111
150
err = errors .Errorf ("the ConfigMap %q in the %s namespace used for getting configuration information was not found" , constants .KubeadmConfigConfigMap , metav1 .NamespaceSystem )
112
151
}
113
152
return nil , nil , nil , errors .Wrap (err , "[upgrade/config] FATAL" )
153
+ } else if legacyReconfigure {
154
+ // Set the newK8sVersion to the value in the ClusterConfiguration. This is done, so that users who use the --config option
155
+ // to supply a new ClusterConfiguration don't have to specify the Kubernetes version twice,
156
+ // if they don't want to upgrade but just change a setting.
157
+ newK8sVersion = cfg .KubernetesVersion
114
158
}
115
159
116
160
ignorePreflightErrorsSet , err := validation .ValidateIgnorePreflightErrors (flags .ignorePreflightErrors , cfg .NodeRegistration .IgnorePreflightErrors )
@@ -131,8 +175,16 @@ func enforceRequirements(flags *applyPlanFlags, dryRun bool, newK8sVersion strin
131
175
return nil , nil , nil , errors .Wrap (err , "[upgrade/health] FATAL" )
132
176
}
133
177
134
- // If a new k8s version should be set, apply the change before printing the config
135
- if len (newK8sVersion ) != 0 {
178
+ // The version arg is mandatory, during upgrade apply, unless it's specified in the config file
179
+ if upgradeApply && newK8sVersion == "" {
180
+ if err := cmdutil .ValidateExactArgNumber (args , []string {"version" }); err != nil {
181
+ return nil , nil , nil , err
182
+ }
183
+ }
184
+
185
+ // If option was specified in both args and config file, args will overwrite the config file.
186
+ if len (args ) == 1 {
187
+ newK8sVersion = args [0 ]
136
188
cfg .KubernetesVersion = newK8sVersion
137
189
}
138
190
0 commit comments