mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-09 21:21:14 +00:00
kubeadm: Add a 'kubeadm config migrate' command
This commit is contained in:
@@ -19,6 +19,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
@@ -30,6 +31,7 @@ import (
|
|||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
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"
|
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
@@ -43,6 +45,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// TODO: Figure out how to get these constants from the API machinery
|
||||||
masterConfig = "MasterConfiguration"
|
masterConfig = "MasterConfiguration"
|
||||||
nodeConfig = "NodeConfiguration"
|
nodeConfig = "NodeConfiguration"
|
||||||
sillyToken = "abcdef.0123456789abcdef"
|
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.PersistentFlags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use when talking to the cluster.")
|
||||||
|
|
||||||
cmd.AddCommand(NewCmdConfigPrintDefault(out))
|
cmd.AddCommand(NewCmdConfigPrintDefault(out))
|
||||||
|
cmd.AddCommand(NewCmdConfigMigrate(out))
|
||||||
cmd.AddCommand(NewCmdConfigUpload(out, &kubeConfigFile))
|
cmd.AddCommand(NewCmdConfigUpload(out, &kubeConfigFile))
|
||||||
cmd.AddCommand(NewCmdConfigView(out, &kubeConfigFile))
|
cmd.AddCommand(NewCmdConfigView(out, &kubeConfigFile))
|
||||||
cmd.AddCommand(NewCmdConfigImages(out))
|
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)
|
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
|
// NewCmdConfigUpload returns cobra.Command for "kubeadm config upload" command
|
||||||
func NewCmdConfigUpload(out io.Writer, kubeConfigFile *string) *cobra.Command {
|
func NewCmdConfigUpload(out io.Writer, kubeConfigFile *string) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
@@ -26,6 +26,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
|
kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MarshalToYaml marshals an object into yaml.
|
// 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
|
// 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) {
|
func MarshalToYamlForCodecs(obj runtime.Object, gv schema.GroupVersion, codecs serializer.CodecFactory) ([]byte, error) {
|
||||||
mediaType := "application/yaml"
|
mediaType := "application/yaml"
|
||||||
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
|
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
|
// 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) {
|
func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs serializer.CodecFactory) (runtime.Object, error) {
|
||||||
mediaType := "application/yaml"
|
mediaType := "application/yaml"
|
||||||
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
|
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)
|
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{}.
|
// 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) {
|
func LoadYAML(bytes []byte) (map[string]interface{}, error) {
|
||||||
var decoded map[interface{}]interface{}
|
var decoded map[interface{}]interface{}
|
||||||
|
Reference in New Issue
Block a user