mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 04:06:03 +00:00
Merge pull request #64232 from luxas/kubeadm_config_migrate
Automatic merge from submit-queue (batch tested with PRs 64322, 64210, 64458, 64232, 64370). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. kubeadm: Add a 'kubeadm config migrate' command **What this PR does / why we need it**: This is an UX improvement so users may easier "upgrade" their **configuration files** from the an old version (e.g. `v1alpha1`) version to a new one (e.g. `v1alpha2`), can do this **locally and seamlessly without touching a cluster**. We talked about this in the SIG meeting; getting the users to be able to convert their checked-in configuration files to new API versions will be crucial. **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes # **Special notes for your reviewer**: **Release note**: ```release-note kubeadm: Add a 'kubeadm config migrate' command to convert old API types to their newer counterparts in the new, supported API types. This is just a client-side tool, it just executes locally without requiring a cluster to be running. You can think about this as an Unix pipe that upgrades config files. ``` @kubernetes/sig-cluster-lifecycle-pr-reviews @liztio
This commit is contained in:
commit
897a4b4968
@ -22,6 +22,7 @@ go_library(
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
||||
"//cmd/kubeadm/app/cmd/phases:go_default_library",
|
||||
|
@ -19,6 +19,7 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
@ -30,6 +31,7 @@ import (
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
@ -43,6 +45,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// TODO: Figure out how to get these constants from the API machinery
|
||||
masterConfig = "MasterConfiguration"
|
||||
nodeConfig = "NodeConfiguration"
|
||||
sillyToken = "abcdef.0123456789abcdef"
|
||||
@ -74,6 +77,7 @@ func NewCmdConfig(out io.Writer) *cobra.Command {
|
||||
cmd.PersistentFlags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use when talking to the cluster.")
|
||||
|
||||
cmd.AddCommand(NewCmdConfigPrintDefault(out))
|
||||
cmd.AddCommand(NewCmdConfigMigrate(out))
|
||||
cmd.AddCommand(NewCmdConfigUpload(out, &kubeConfigFile))
|
||||
cmd.AddCommand(NewCmdConfigView(out, &kubeConfigFile))
|
||||
cmd.AddCommand(NewCmdConfigImages(out))
|
||||
@ -138,6 +142,71 @@ func getDefaultAPIObjectBytes(apiObject string) ([]byte, error) {
|
||||
return []byte{}, fmt.Errorf("--api-object needs to be one of %v", availableAPIObjects)
|
||||
}
|
||||
|
||||
// NewCmdConfigMigrate returns cobra.Command for "kubeadm config migrate" command
|
||||
func NewCmdConfigMigrate(out io.Writer) *cobra.Command {
|
||||
var oldCfgPath, newCfgPath string
|
||||
cmd := &cobra.Command{
|
||||
Use: "migrate",
|
||||
Short: "Read an older version of the kubeadm configuration API types from a file, and output the similar config object for the newer version.",
|
||||
Long: fmt.Sprintf(dedent.Dedent(`
|
||||
This command lets you convert configuration objects of older versions to the latest supported version,
|
||||
locally in the CLI tool without ever touching anything in the cluster.
|
||||
In this version of kubeadm, the following API versions are supported:
|
||||
- %s
|
||||
- %s
|
||||
|
||||
Further, kubeadm can only write out config of version %q, but read both types.
|
||||
So regardless of what version you pass to the --old-config parameter here, the API object will be
|
||||
read, deserialized, defaulted, converted, validated, and re-serialized when written to stdout or
|
||||
--new-config if specified.
|
||||
|
||||
In other words, the output of this command is what kubeadm actually would read internally if you
|
||||
submitted this file to "kubeadm init"
|
||||
`), kubeadmapiv1alpha2.SchemeGroupVersion.String(), kubeadmapiv1alpha1.SchemeGroupVersion.String(), kubeadmapiv1alpha2.SchemeGroupVersion.String()),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(oldCfgPath) == 0 {
|
||||
kubeadmutil.CheckErr(fmt.Errorf("The --old-config flag is mandatory"))
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(oldCfgPath)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
var outputBytes []byte
|
||||
gvk, err := kubeadmutil.GroupVersionKindFromBytes(b, kubeadmscheme.Codecs)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
switch gvk.Kind {
|
||||
case masterConfig:
|
||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(oldCfgPath, &kubeadmapiv1alpha2.MasterConfiguration{})
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
outputBytes, err = kubeadmutil.MarshalToYamlForCodecs(internalcfg, kubeadmapiv1alpha2.SchemeGroupVersion, kubeadmscheme.Codecs)
|
||||
kubeadmutil.CheckErr(err)
|
||||
case nodeConfig:
|
||||
internalcfg, err := configutil.NodeConfigFileAndDefaultsToInternalConfig(oldCfgPath, &kubeadmapiv1alpha2.NodeConfiguration{})
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
// TODO: In the future we might not want to duplicate these two lines of code for every case here.
|
||||
outputBytes, err = kubeadmutil.MarshalToYamlForCodecs(internalcfg, kubeadmapiv1alpha2.SchemeGroupVersion, kubeadmscheme.Codecs)
|
||||
kubeadmutil.CheckErr(err)
|
||||
default:
|
||||
kubeadmutil.CheckErr(fmt.Errorf("Didn't recognize type with GroupVersionKind: %v", gvk))
|
||||
}
|
||||
|
||||
if newCfgPath == "" {
|
||||
fmt.Fprintf(out, string(outputBytes))
|
||||
} else {
|
||||
if err := ioutil.WriteFile(newCfgPath, outputBytes, 0644); err != nil {
|
||||
kubeadmutil.CheckErr(fmt.Errorf("failed to write the new configuration to the file %q: %v", newCfgPath, err))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&oldCfgPath, "old-config", "", "Path to the kubeadm config file that is using an old API version and should be converted. This flag is mandatory.")
|
||||
cmd.Flags().StringVar(&newCfgPath, "new-config", "", "Path to the resulting equivalent kubeadm config file using the new API version. Optional, if not specified output will be sent to STDOUT.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// NewCmdConfigUpload returns cobra.Command for "kubeadm config upload" command
|
||||
func NewCmdConfigUpload(out io.Writer, kubeConfigFile *string) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
|
@ -20,6 +20,7 @@ go_library(
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util",
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
||||
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||
)
|
||||
|
||||
// MarshalToYaml marshals an object into yaml.
|
||||
@ -34,6 +35,8 @@ func MarshalToYaml(obj runtime.Object, gv schema.GroupVersion) ([]byte, error) {
|
||||
}
|
||||
|
||||
// MarshalToYamlForCodecs marshals an object into yaml using the specified codec
|
||||
// TODO: Is specifying the gv really needed here?
|
||||
// TODO: Can we support json out of the box easily here?
|
||||
func MarshalToYamlForCodecs(obj runtime.Object, gv schema.GroupVersion, codecs serializer.CodecFactory) ([]byte, error) {
|
||||
mediaType := "application/yaml"
|
||||
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
|
||||
@ -51,6 +54,8 @@ func UnmarshalFromYaml(buffer []byte, gv schema.GroupVersion) (runtime.Object, e
|
||||
}
|
||||
|
||||
// UnmarshalFromYamlForCodecs unmarshals yaml into an object using the specified codec
|
||||
// TODO: Is specifying the gv really needed here?
|
||||
// TODO: Can we support json out of the box easily here?
|
||||
func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs serializer.CodecFactory) (runtime.Object, error) {
|
||||
mediaType := "application/yaml"
|
||||
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
|
||||
@ -62,6 +67,40 @@ func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs se
|
||||
return runtime.Decode(decoder, buffer)
|
||||
}
|
||||
|
||||
// GroupVersionKindFromBytes parses the bytes and returns the gvk
|
||||
func GroupVersionKindFromBytes(buffer []byte, codecs serializer.CodecFactory) (schema.GroupVersionKind, error) {
|
||||
|
||||
decoded, err := LoadYAML(buffer)
|
||||
if err != nil {
|
||||
return schema.EmptyObjectKind.GroupVersionKind(), fmt.Errorf("unable to decode config from bytes: %v", err)
|
||||
}
|
||||
kindStr, apiVersionStr := "", ""
|
||||
|
||||
// As there was a bug in kubeadm v1.10 and earlier that made the YAML uploaded to the cluster configmap NOT have metav1.TypeMeta information
|
||||
// we need to populate this here manually. If kind or apiVersion is empty, we know the apiVersion is v1alpha1, as by the time kubeadm had this bug,
|
||||
// it could only write
|
||||
// TODO: Remove this "hack" in v1.12 when we know the ConfigMap always contains v1alpha2 content written by kubeadm v1.11. Also, we will drop support for
|
||||
// v1alpha1 in v1.12
|
||||
kind := decoded["kind"]
|
||||
apiVersion := decoded["apiVersion"]
|
||||
if kind == nil || len(kind.(string)) == 0 {
|
||||
kindStr = "MasterConfiguration"
|
||||
} else {
|
||||
kindStr = kind.(string)
|
||||
}
|
||||
if apiVersion == nil || len(apiVersion.(string)) == 0 {
|
||||
apiVersionStr = kubeadmapiv1alpha1.SchemeGroupVersion.String()
|
||||
} else {
|
||||
apiVersionStr = apiVersion.(string)
|
||||
}
|
||||
gv, err := schema.ParseGroupVersion(apiVersionStr)
|
||||
if err != nil {
|
||||
return schema.EmptyObjectKind.GroupVersionKind(), fmt.Errorf("unable to parse apiVersion: %v", err)
|
||||
}
|
||||
|
||||
return gv.WithKind(kindStr), nil
|
||||
}
|
||||
|
||||
// LoadYAML is a small wrapper around go-yaml that ensures all nested structs are map[string]interface{} instead of map[interface{}]interface{}.
|
||||
func LoadYAML(bytes []byte) (map[string]interface{}, error) {
|
||||
var decoded map[interface{}]interface{}
|
||||
|
@ -61,6 +61,7 @@ docs/admin/kubeadm_config.md
|
||||
docs/admin/kubeadm_config_images.md
|
||||
docs/admin/kubeadm_config_images_list.md
|
||||
docs/admin/kubeadm_config_images_pull.md
|
||||
docs/admin/kubeadm_config_migrate.md
|
||||
docs/admin/kubeadm_config_print-default.md
|
||||
docs/admin/kubeadm_config_upload.md
|
||||
docs/admin/kubeadm_config_upload_from-file.md
|
||||
@ -140,6 +141,7 @@ docs/man/man1/kubeadm-completion.1
|
||||
docs/man/man1/kubeadm-config-images-list.1
|
||||
docs/man/man1/kubeadm-config-images-pull.1
|
||||
docs/man/man1/kubeadm-config-images.1
|
||||
docs/man/man1/kubeadm-config-migrate.1
|
||||
docs/man/man1/kubeadm-config-print-default.1
|
||||
docs/man/man1/kubeadm-config-upload-from-file.1
|
||||
docs/man/man1/kubeadm-config-upload-from-flags.1
|
||||
|
3
docs/admin/kubeadm_config_migrate.md
Normal file
3
docs/admin/kubeadm_config_migrate.md
Normal file
@ -0,0 +1,3 @@
|
||||
This file is autogenerated, but we've stopped checking such files into the
|
||||
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
|
||||
populate this file.
|
3
docs/man/man1/kubeadm-config-migrate.1
Normal file
3
docs/man/man1/kubeadm-config-migrate.1
Normal file
@ -0,0 +1,3 @@
|
||||
This file is autogenerated, but we've stopped checking such files into the
|
||||
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
|
||||
populate this file.
|
Loading…
Reference in New Issue
Block a user