mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 06:27:05 +00:00
Merge pull request #108447 from pacoxu/kubeadm-json-upgrade-plan
`Kubeadm upgrade plan` support json/yaml output
This commit is contained in:
commit
9169f16841
@ -40,8 +40,12 @@ type Images struct {
|
|||||||
Images []string
|
Images []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
// ComponentUpgradePlan represents information about upgrade plan for one component
|
// ComponentUpgradePlan represents information about upgrade plan for one component
|
||||||
type ComponentUpgradePlan struct {
|
type ComponentUpgradePlan struct {
|
||||||
|
metav1.TypeMeta
|
||||||
|
|
||||||
Name string
|
Name string
|
||||||
CurrentVersion string
|
CurrentVersion string
|
||||||
NewVersion string
|
NewVersion string
|
||||||
|
@ -40,8 +40,12 @@ type Images struct {
|
|||||||
Images []string `json:"images"`
|
Images []string `json:"images"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
// ComponentUpgradePlan represents information about upgrade plan for one component
|
// ComponentUpgradePlan represents information about upgrade plan for one component
|
||||||
type ComponentUpgradePlan struct {
|
type ComponentUpgradePlan struct {
|
||||||
|
metav1.TypeMeta
|
||||||
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
CurrentVersion string `json:"currentVersion"`
|
CurrentVersion string `json:"currentVersion"`
|
||||||
NewVersion string `json:"newVersion"`
|
NewVersion string `json:"newVersion"`
|
||||||
|
@ -70,6 +70,7 @@ func (in *ComponentConfigVersionState) DeepCopy() *ComponentConfigVersionState {
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ComponentUpgradePlan) DeepCopyInto(out *ComponentUpgradePlan) {
|
func (in *ComponentUpgradePlan) DeepCopyInto(out *ComponentUpgradePlan) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +84,14 @@ func (in *ComponentUpgradePlan) DeepCopy() *ComponentUpgradePlan {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *ComponentUpgradePlan) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Images) DeepCopyInto(out *Images) {
|
func (in *Images) DeepCopyInto(out *Images) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -70,6 +70,7 @@ func (in *ComponentConfigVersionState) DeepCopy() *ComponentConfigVersionState {
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ComponentUpgradePlan) DeepCopyInto(out *ComponentUpgradePlan) {
|
func (in *ComponentUpgradePlan) DeepCopyInto(out *ComponentUpgradePlan) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +84,14 @@ func (in *ComponentUpgradePlan) DeepCopy() *ComponentUpgradePlan {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *ComponentUpgradePlan) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Images) DeepCopyInto(out *Images) {
|
func (in *Images) DeepCopyInto(out *Images) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -332,7 +332,7 @@ func getInternalCfg(cfgPath string, kubeconfigPath string, cfg kubeadmapiv1.Clus
|
|||||||
if cfgPath == "" {
|
if cfgPath == "" {
|
||||||
client, err := kubeconfigutil.ClientSetFromFile(kubeconfigPath)
|
client, err := kubeconfigutil.ClientSetFromFile(kubeconfigPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
internalcfg, err := configutil.FetchInitConfigurationFromCluster(client, out, logPrefix, false, false)
|
internalcfg, err := configutil.FetchInitConfigurationFromCluster(client, nil, logPrefix, false, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Println() // add empty line to separate the FetchInitConfigurationFromCluster output from the command output
|
fmt.Println() // add empty line to separate the FetchInitConfigurationFromCluster output from the command output
|
||||||
return internalcfg, nil
|
return internalcfg, nil
|
||||||
|
@ -371,7 +371,7 @@ func newCmdConfigImagesList(out io.Writer, mockK8sVersion *string) *cobra.Comman
|
|||||||
|
|
||||||
printer, err := outputFlags.ToPrinter()
|
printer, err := outputFlags.ToPrinter()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "could not construct output printer")
|
||||||
}
|
}
|
||||||
|
|
||||||
imagesList, err := NewImagesList(cfgPath, externalcfg)
|
imagesList, err := NewImagesList(cfgPath, externalcfg)
|
||||||
@ -424,7 +424,7 @@ func (itp *imageTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) erro
|
|||||||
// imageTextPrintFlags provides flags necessary for printing image in a text form.
|
// imageTextPrintFlags provides flags necessary for printing image in a text form.
|
||||||
type imageTextPrintFlags struct{}
|
type imageTextPrintFlags struct{}
|
||||||
|
|
||||||
// ToPrinter returns kubeadm printer for the text output format
|
// ToPrinter returns a kubeadm printer for the text output format
|
||||||
func (ipf *imageTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) {
|
func (ipf *imageTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) {
|
||||||
if outputFormat == output.TextOutput {
|
if outputFormat == output.TextOutput {
|
||||||
return &imageTextPrinter{}, nil
|
return &imageTextPrinter{}, nil
|
||||||
|
@ -618,7 +618,7 @@ func fetchInitConfiguration(tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.I
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetches the init configuration
|
// Fetches the init configuration
|
||||||
initConfiguration, err := configutil.FetchInitConfigurationFromCluster(tlsClient, os.Stdout, "preflight", true, false)
|
initConfiguration, err := configutil.FetchInitConfigurationFromCluster(tlsClient, nil, "preflight", true, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
|
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ func newResetData(cmd *cobra.Command, options *resetOptions, in io.Reader, out i
|
|||||||
client, err := getClientset(options.kubeconfigPath, false)
|
client, err := getClientset(options.kubeconfigPath, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
klog.V(1).Infof("[reset] Loaded client set from kubeconfig file: %s", options.kubeconfigPath)
|
klog.V(1).Infof("[reset] Loaded client set from kubeconfig file: %s", options.kubeconfigPath)
|
||||||
cfg, err = configutil.FetchInitConfigurationFromCluster(client, out, "reset", false, false)
|
cfg, err = configutil.FetchInitConfigurationFromCluster(client, nil, "reset", false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Warningf("[reset] Unable to fetch the kubeadm-config ConfigMap from cluster: %v", err)
|
klog.Warningf("[reset] Unable to fetch the kubeadm-config ConfigMap from cluster: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ func newCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
|||||||
|
|
||||||
printer, err := outputFlags.ToPrinter()
|
printer, err := outputFlags.ToPrinter()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "could not construct output printer")
|
||||||
}
|
}
|
||||||
|
|
||||||
return RunListTokens(out, errW, client, printer)
|
return RunListTokens(out, errW, client, printer)
|
||||||
@ -354,7 +354,7 @@ func (ttp *tokenTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) erro
|
|||||||
// tokenTextPrintFlags provides flags necessary for printing bootstrap token in a text form.
|
// tokenTextPrintFlags provides flags necessary for printing bootstrap token in a text form.
|
||||||
type tokenTextPrintFlags struct{}
|
type tokenTextPrintFlags struct{}
|
||||||
|
|
||||||
// ToPrinter returns kubeadm printer for the text output format
|
// ToPrinter returns a kubeadm printer for the text output format
|
||||||
func (tpf *tokenTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) {
|
func (tpf *tokenTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) {
|
||||||
if outputFormat == output.TextOutput {
|
if outputFormat == output.TextOutput {
|
||||||
return &tokenTextPrinter{columns: []string{"TOKEN", "TTL", "EXPIRES", "USAGES", "DESCRIPTION", "EXTRA GROUPS"}}, nil
|
return &tokenTextPrinter{columns: []string{"TOKEN", "TTL", "EXPIRES", "USAGES", "DESCRIPTION", "EXTRA GROUPS"}}, nil
|
||||||
|
@ -37,6 +37,7 @@ import (
|
|||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||||
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
|
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
// applyFlags holds the information about the flags that can be passed to apply
|
// applyFlags holds the information about the flags that can be passed to apply
|
||||||
@ -103,7 +104,7 @@ func runApply(flags *applyFlags, args []string) error {
|
|||||||
// Start with the basics, verify that the cluster is healthy and get the configuration from the cluster (using the ConfigMap)
|
// Start with the basics, verify that the cluster is healthy and get the configuration from the cluster (using the ConfigMap)
|
||||||
klog.V(1).Infoln("[upgrade/apply] verifying health of cluster")
|
klog.V(1).Infoln("[upgrade/apply] verifying health of cluster")
|
||||||
klog.V(1).Infoln("[upgrade/apply] retrieving configuration from cluster")
|
klog.V(1).Infoln("[upgrade/apply] retrieving configuration from cluster")
|
||||||
client, versionGetter, cfg, err := enforceRequirements(flags.applyPlanFlags, args, flags.dryRun, true)
|
client, versionGetter, cfg, err := enforceRequirements(flags.applyPlanFlags, args, flags.dryRun, true, &output.TextPrinter{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ import (
|
|||||||
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
|
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
|
||||||
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
|
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
|
||||||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
// isKubeadmConfigPresent checks if a kubeadm config type is found in the provided document map
|
// isKubeadmConfigPresent checks if a kubeadm config type is found in the provided document map
|
||||||
@ -63,14 +64,14 @@ func isKubeadmConfigPresent(docmap kubeadmapi.DocumentMap) bool {
|
|||||||
// 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
|
// 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
|
||||||
// in it (API group "kubeadm.kubernetes.io" present), then the supplied file is treaded as a legacy reconfiguration style "--config" use and the
|
// in it (API group "kubeadm.kubernetes.io" present), then the supplied file is treaded as a legacy reconfiguration style "--config" use and the
|
||||||
// returned bool value is set to true (the only case to be done so).
|
// returned bool value is set to true (the only case to be done so).
|
||||||
func loadConfig(cfgPath string, client clientset.Interface, skipComponentConfigs bool) (*kubeadmapi.InitConfiguration, bool, error) {
|
func loadConfig(cfgPath string, client clientset.Interface, skipComponentConfigs bool, printer output.Printer) (*kubeadmapi.InitConfiguration, bool, error) {
|
||||||
// Used for info logs here
|
// Used for info logs here
|
||||||
const logPrefix = "upgrade/config"
|
const logPrefix = "upgrade/config"
|
||||||
|
|
||||||
// The usual case here is to not have a config file, but rather load the config from the cluster.
|
// The usual case here is to not have a config file, but rather load the config from the cluster.
|
||||||
// This is probably 90% of the time. So we handle it first.
|
// This is probably 90% of the time. So we handle it first.
|
||||||
if cfgPath == "" {
|
if cfgPath == "" {
|
||||||
cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, logPrefix, false, skipComponentConfigs)
|
cfg, err := configutil.FetchInitConfigurationFromCluster(client, printer, logPrefix, false, skipComponentConfigs)
|
||||||
return cfg, false, err
|
return cfg, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +99,7 @@ func loadConfig(cfgPath string, client clientset.Interface, skipComponentConfigs
|
|||||||
|
|
||||||
// If no kubeadm config types are present, we assume that there are manually upgraded component configs in the file.
|
// If no kubeadm config types are present, we assume that there are manually upgraded component configs in the file.
|
||||||
// Hence, we load the kubeadm types from the cluster.
|
// Hence, we load the kubeadm types from the cluster.
|
||||||
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, logPrefix, false, true)
|
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, printer, logPrefix, false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
@ -121,27 +122,27 @@ func loadConfig(cfgPath string, client clientset.Interface, skipComponentConfigs
|
|||||||
}
|
}
|
||||||
|
|
||||||
// enforceRequirements verifies that it's okay to upgrade and then returns the variables needed for the rest of the procedure
|
// enforceRequirements verifies that it's okay to upgrade and then returns the variables needed for the rest of the procedure
|
||||||
func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgradeApply bool) (clientset.Interface, upgrade.VersionGetter, *kubeadmapi.InitConfiguration, error) {
|
func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgradeApply bool, printer output.Printer) (clientset.Interface, upgrade.VersionGetter, *kubeadmapi.InitConfiguration, error) {
|
||||||
client, err := getClient(flags.kubeConfigPath, dryRun)
|
client, err := getClient(flags.kubeConfigPath, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath)
|
return nil, nil, nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the configuration from a file or ConfigMap and validate it
|
// Fetch the configuration from a file or ConfigMap and validate it
|
||||||
fmt.Println("[upgrade/config] Making sure the configuration is correct:")
|
printer.Printf("[upgrade/config] Making sure the configuration is correct:\n")
|
||||||
|
|
||||||
var newK8sVersion string
|
var newK8sVersion string
|
||||||
cfg, legacyReconfigure, err := loadConfig(flags.cfgPath, client, !upgradeApply)
|
cfg, legacyReconfigure, err := loadConfig(flags.cfgPath, client, !upgradeApply, printer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if apierrors.IsNotFound(err) {
|
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)
|
printer.Printf("[upgrade/config] In order to upgrade, a ConfigMap called %q in the %s namespace must exist.\n", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
|
||||||
fmt.Println("[upgrade/config] Without this information, 'kubeadm upgrade' won't know how to configure your upgraded cluster.")
|
printer.Printf("[upgrade/config] Without this information, 'kubeadm upgrade' won't know how to configure your upgraded cluster.\n")
|
||||||
fmt.Println("")
|
printer.Println()
|
||||||
fmt.Println("[upgrade/config] Next steps:")
|
printer.Printf("[upgrade/config] Next steps:\n")
|
||||||
fmt.Printf("\t- OPTION 1: Run 'kubeadm config upload from-flags' and specify the same CLI arguments you passed to 'kubeadm init' when you created your control-plane.\n")
|
printer.Printf("\t- OPTION 1: Run 'kubeadm config upload from-flags' and specify the same CLI arguments you passed to 'kubeadm init' when you created your control-plane.\n")
|
||||||
fmt.Printf("\t- OPTION 2: Run 'kubeadm config upload from-file' and specify the same config file you passed to 'kubeadm init' when you created your control-plane.\n")
|
printer.Printf("\t- OPTION 2: Run 'kubeadm config upload from-file' and specify the same config file you passed to 'kubeadm init' when you created your control-plane.\n")
|
||||||
fmt.Printf("\t- OPTION 3: Pass a config file to 'kubeadm upgrade' using the --config flag.\n")
|
printer.Printf("\t- OPTION 3: Pass a config file to 'kubeadm upgrade' using the --config flag.\n")
|
||||||
fmt.Println("")
|
printer.Println()
|
||||||
err = errors.Errorf("the ConfigMap %q in the %s namespace used for getting configuration information was not found", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
|
err = errors.Errorf("the ConfigMap %q in the %s namespace used for getting configuration information was not found", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
|
||||||
}
|
}
|
||||||
return nil, nil, nil, errors.Wrap(err, "[upgrade/config] FATAL")
|
return nil, nil, nil, errors.Wrap(err, "[upgrade/config] FATAL")
|
||||||
@ -161,7 +162,7 @@ func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgr
|
|||||||
|
|
||||||
// Ensure the user is root
|
// Ensure the user is root
|
||||||
klog.V(1).Info("running preflight checks")
|
klog.V(1).Info("running preflight checks")
|
||||||
if err := runPreflightChecks(client, ignorePreflightErrorsSet, &cfg.ClusterConfiguration); err != nil {
|
if err := runPreflightChecks(client, ignorePreflightErrorsSet, &cfg.ClusterConfiguration, printer); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,14 +203,14 @@ func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgr
|
|||||||
// Check if feature gate flags used in the cluster are consistent with the set of features currently supported by kubeadm
|
// Check if feature gate flags used in the cluster are consistent with the set of features currently supported by kubeadm
|
||||||
if msg := features.CheckDeprecatedFlags(&features.InitFeatureGates, cfg.FeatureGates); len(msg) > 0 {
|
if msg := features.CheckDeprecatedFlags(&features.InitFeatureGates, cfg.FeatureGates); len(msg) > 0 {
|
||||||
for _, m := range msg {
|
for _, m := range msg {
|
||||||
fmt.Printf("[upgrade/config] %s\n", m)
|
printer.Printf("[upgrade/config] %s\n", m)
|
||||||
}
|
}
|
||||||
return nil, nil, nil, errors.New("[upgrade/config] FATAL. Unable to upgrade a cluster using deprecated feature-gate flags. Please see the release notes")
|
return nil, nil, nil, errors.New("[upgrade/config] FATAL. Unable to upgrade a cluster using deprecated feature-gate flags. Please see the release notes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user told us to print this information out; do it!
|
// If the user told us to print this information out; do it!
|
||||||
if flags.printConfig {
|
if flags.printConfig {
|
||||||
printConfiguration(&cfg.ClusterConfiguration, os.Stdout)
|
printConfiguration(&cfg.ClusterConfiguration, os.Stdout, printer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use a real version getter interface that queries the API server, the kubeadm client and the Kubernetes CI system for latest versions
|
// Use a real version getter interface that queries the API server, the kubeadm client and the Kubernetes CI system for latest versions
|
||||||
@ -217,7 +218,7 @@ func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// printConfiguration prints the external version of the API to yaml
|
// printConfiguration prints the external version of the API to yaml
|
||||||
func printConfiguration(clustercfg *kubeadmapi.ClusterConfiguration, w io.Writer) {
|
func printConfiguration(clustercfg *kubeadmapi.ClusterConfiguration, w io.Writer, printer output.Printer) {
|
||||||
// Short-circuit if cfg is nil, so we can safely get the value of the pointer below
|
// Short-circuit if cfg is nil, so we can safely get the value of the pointer below
|
||||||
if clustercfg == nil {
|
if clustercfg == nil {
|
||||||
return
|
return
|
||||||
@ -225,18 +226,18 @@ func printConfiguration(clustercfg *kubeadmapi.ClusterConfiguration, w io.Writer
|
|||||||
|
|
||||||
cfgYaml, err := configutil.MarshalKubeadmConfigObject(clustercfg)
|
cfgYaml, err := configutil.MarshalKubeadmConfigObject(clustercfg)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Fprintln(w, "[upgrade/config] Configuration used:")
|
printer.Fprintln(w, "[upgrade/config] Configuration used:")
|
||||||
|
|
||||||
scanner := bufio.NewScanner(bytes.NewReader(cfgYaml))
|
scanner := bufio.NewScanner(bytes.NewReader(cfgYaml))
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
fmt.Fprintf(w, "\t%s\n", scanner.Text())
|
printer.Fprintf(w, "\t%s\n", scanner.Text())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// runPreflightChecks runs the root preflight check
|
// runPreflightChecks runs the root preflight check
|
||||||
func runPreflightChecks(client clientset.Interface, ignorePreflightErrors sets.String, cfg *kubeadmapi.ClusterConfiguration) error {
|
func runPreflightChecks(client clientset.Interface, ignorePreflightErrors sets.String, cfg *kubeadmapi.ClusterConfiguration, printer output.Printer) error {
|
||||||
fmt.Println("[preflight] Running pre-flight checks.")
|
printer.Printf("[preflight] Running pre-flight checks.\n")
|
||||||
err := preflight.RunRootCheckOnly(ignorePreflightErrors)
|
err := preflight.RunRootCheckOnly(ignorePreflightErrors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEnforceRequirements(t *testing.T) {
|
func TestEnforceRequirements(t *testing.T) {
|
||||||
@ -54,7 +55,7 @@ func TestEnforceRequirements(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tcases {
|
for _, tt := range tcases {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
_, _, _, err := enforceRequirements(&tt.flags, nil, tt.dryRun, false)
|
_, _, _, err := enforceRequirements(&tt.flags, nil, tt.dryRun, false, &output.TextPrinter{})
|
||||||
|
|
||||||
if err == nil && tt.expectedErr {
|
if err == nil && tt.expectedErr {
|
||||||
t.Error("Expected error, but got success")
|
t.Error("Expected error, but got success")
|
||||||
@ -138,7 +139,7 @@ func TestPrintConfiguration(t *testing.T) {
|
|||||||
for _, rt := range tests {
|
for _, rt := range tests {
|
||||||
t.Run(rt.name, func(t *testing.T) {
|
t.Run(rt.name, func(t *testing.T) {
|
||||||
rt.buf = bytes.NewBufferString("")
|
rt.buf = bytes.NewBufferString("")
|
||||||
printConfiguration(rt.cfg, rt.buf)
|
printConfiguration(rt.cfg, rt.buf, &output.TextPrinter{})
|
||||||
actualBytes := rt.buf.Bytes()
|
actualBytes := rt.buf.Bytes()
|
||||||
if !bytes.Equal(actualBytes, rt.expectedBytes) {
|
if !bytes.Equal(actualBytes, rt.expectedBytes) {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
|
@ -118,7 +118,7 @@ func runDiff(flags *diffFlags, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath)
|
return errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath)
|
||||||
}
|
}
|
||||||
cfg, err = configutil.FetchInitConfigurationFromCluster(client, flags.out, "upgrade/diff", false, false)
|
cfg, err = configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade/diff", false, false)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -139,7 +139,7 @@ func newNodeData(cmd *cobra.Command, args []string, options *nodeOptions) (*node
|
|||||||
// Fetches the cluster configuration
|
// Fetches the cluster configuration
|
||||||
// NB in case of control-plane node, we are reading all the info for the node; in case of NOT control-plane node
|
// NB in case of control-plane node, we are reading all the info for the node; in case of NOT control-plane node
|
||||||
// (worker node), we are not reading local API address and the CRI socket from the node object
|
// (worker node), we are not reading local API address and the CRI socket from the node object
|
||||||
cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "upgrade", !isControlPlaneNode, false)
|
cfg, err := configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade", !isControlPlaneNode, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
|
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
|
||||||
}
|
}
|
||||||
|
@ -28,16 +28,21 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/version"
|
"k8s.io/apimachinery/pkg/util/version"
|
||||||
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
|
"k8s.io/cli-runtime/pkg/printers"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
outputapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/output"
|
outputapischeme "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/scheme"
|
||||||
|
outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
|
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
|
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
type planFlags struct {
|
type planFlags struct {
|
||||||
@ -50,25 +55,202 @@ func newCmdPlan(apf *applyPlanFlags) *cobra.Command {
|
|||||||
applyPlanFlags: apf,
|
applyPlanFlags: apf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputFlags := newUpgradePlanPrintFlags(output.TextOutput)
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "plan [version] [flags]",
|
Use: "plan [version] [flags]",
|
||||||
Short: "Check which versions are available to upgrade to and validate whether your current cluster is upgradeable. To skip the internet check, pass in the optional [version] parameter",
|
Short: "Check which versions are available to upgrade to and validate whether your current cluster is upgradeable. To skip the internet check, pass in the optional [version] parameter",
|
||||||
RunE: func(_ *cobra.Command, args []string) error {
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
return runPlan(flags, args)
|
printer, err := outputFlags.ToPrinter()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not construct output printer")
|
||||||
|
}
|
||||||
|
|
||||||
|
return runPlan(flags, args, printer)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputFlags.AddFlags(cmd)
|
||||||
|
|
||||||
// Register the common flags for apply and plan
|
// Register the common flags for apply and plan
|
||||||
addApplyPlanFlags(cmd.Flags(), flags.applyPlanFlags)
|
addApplyPlanFlags(cmd.Flags(), flags.applyPlanFlags)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newComponentUpgradePlan helper creates outputapiv1alpha2.ComponentUpgradePlan object
|
||||||
|
func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapiv1alpha2.ComponentUpgradePlan {
|
||||||
|
return outputapiv1alpha2.ComponentUpgradePlan{
|
||||||
|
Name: name,
|
||||||
|
CurrentVersion: currentVersion,
|
||||||
|
NewVersion: newVersion,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// upgradePlanPrintFlags defines a printer flag structure for the
|
||||||
|
// upgrade plan kubeadm command and provides a method
|
||||||
|
// of retrieving a known printer based on flag values provided.
|
||||||
|
type upgradePlanPrintFlags struct {
|
||||||
|
// JSONYamlPrintFlags provides default flags necessary for json/yaml printing
|
||||||
|
JSONYamlPrintFlags *upgradePlanJSONYamlPrintFlags
|
||||||
|
// TextPrintFlags provides default flags necessary for text printing
|
||||||
|
TextPrintFlags *upgradePlanTextPrintFlags
|
||||||
|
// TypeSetterPrinter is an implementation of ResourcePrinter that wraps another printer with types set on the objects
|
||||||
|
TypeSetterPrinter *printers.TypeSetterPrinter
|
||||||
|
// OutputFormat contains currently set output format
|
||||||
|
OutputFormat string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUpgradePlanPrintFlags(outputFormat string) *upgradePlanPrintFlags {
|
||||||
|
return &upgradePlanPrintFlags{
|
||||||
|
JSONYamlPrintFlags: &upgradePlanJSONYamlPrintFlags{},
|
||||||
|
TextPrintFlags: &upgradePlanTextPrintFlags{},
|
||||||
|
TypeSetterPrinter: printers.NewTypeSetter(outputapischeme.Scheme),
|
||||||
|
OutputFormat: strings.ToLower(outputFormat),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowedFormats returns a list of allowed output formats
|
||||||
|
func (pf *upgradePlanPrintFlags) AllowedFormats() []string {
|
||||||
|
ret := pf.TextPrintFlags.AllowedFormats()
|
||||||
|
return append(ret, pf.JSONYamlPrintFlags.AllowedFormats()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlags receives a *cobra.Command reference and binds
|
||||||
|
// flags related to Kubeadm printing to it
|
||||||
|
func (pf *upgradePlanPrintFlags) AddFlags(cmd *cobra.Command) {
|
||||||
|
pf.TextPrintFlags.AddFlags(cmd)
|
||||||
|
pf.JSONYamlPrintFlags.AddFlags(cmd)
|
||||||
|
// TODO: once we are confident the feature is graduated we should remove the EXPERIMENTAL text below:
|
||||||
|
// https://github.com/kubernetes/kubeadm/issues/494
|
||||||
|
cmd.Flags().StringVarP(&pf.OutputFormat, "output", "o", pf.OutputFormat, fmt.Sprintf("EXPERIMENTAL: Output format. One of: %s.", strings.Join(pf.AllowedFormats(), "|")))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPrinter receives an outputFormat and returns a printer capable of
|
||||||
|
// handling format printing.
|
||||||
|
// Returns error if the specified outputFormat does not match supported formats.
|
||||||
|
func (pf *upgradePlanPrintFlags) ToPrinter() (output.Printer, error) {
|
||||||
|
switch pf.OutputFormat {
|
||||||
|
case output.TextOutput:
|
||||||
|
return pf.TextPrintFlags.ToPrinter(pf.OutputFormat)
|
||||||
|
case output.JSONOutput:
|
||||||
|
return newUpgradePlanJSONYAMLPrinter(pf.TypeSetterPrinter.WrapToPrinter(pf.JSONYamlPrintFlags.ToPrinter(output.JSONOutput)))
|
||||||
|
case output.YAMLOutput:
|
||||||
|
return newUpgradePlanJSONYAMLPrinter(pf.TypeSetterPrinter.WrapToPrinter(pf.JSONYamlPrintFlags.ToPrinter(output.YAMLOutput)))
|
||||||
|
default:
|
||||||
|
return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &pf.OutputFormat, AllowedFormats: pf.AllowedFormats()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type upgradePlanJSONYamlPrintFlags struct {
|
||||||
|
genericclioptions.JSONYamlPrintFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowedFormats returns a list of allowed output formats
|
||||||
|
func (pf *upgradePlanJSONYamlPrintFlags) AllowedFormats() []string {
|
||||||
|
return []string{output.JSONOutput, output.YAMLOutput}
|
||||||
|
}
|
||||||
|
|
||||||
|
// upgradePlanJSONYAMLPrinter prints upgrade plan in a JSON or YAML format
|
||||||
|
type upgradePlanJSONYAMLPrinter struct {
|
||||||
|
output.ResourcePrinterWrapper
|
||||||
|
Buffer []outputapiv1alpha2.ComponentUpgradePlan
|
||||||
|
}
|
||||||
|
|
||||||
|
// newUpgradePlanJSONYAMLPrinter creates a new upgradePlanJSONYAMLPrinter object
|
||||||
|
func newUpgradePlanJSONYAMLPrinter(resourcePrinter printers.ResourcePrinter, err error) (output.Printer, error) {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &upgradePlanJSONYAMLPrinter{ResourcePrinterWrapper: output.ResourcePrinterWrapper{Printer: resourcePrinter}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintObj is an implementation of ResourcePrinter.PrintObj that adds object to the buffer
|
||||||
|
func (p *upgradePlanJSONYAMLPrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
|
||||||
|
item, ok := obj.(*outputapiv1alpha2.ComponentUpgradePlan)
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("expected ComponentUpgradePlan, but got %+v", obj)
|
||||||
|
}
|
||||||
|
p.Buffer = append(p.Buffer, *item)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush writes any buffered data once last object is added
|
||||||
|
func (p *upgradePlanJSONYAMLPrinter) Flush(writer io.Writer, last bool) {
|
||||||
|
if !last {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(p.Buffer) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
plan := &outputapiv1alpha2.UpgradePlan{Components: p.Buffer}
|
||||||
|
if err := p.Printer.PrintObj(plan, writer); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "could not flush output buffer: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close empties the list of buffered components
|
||||||
|
func (p *upgradePlanJSONYAMLPrinter) Close(writer io.Writer) {
|
||||||
|
p.Buffer = p.Buffer[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// upgradePlanTextPrinter prints upgrade plan in a text form
|
||||||
|
type upgradePlanTextPrinter struct {
|
||||||
|
output.TextPrinter
|
||||||
|
columns []string
|
||||||
|
tabwriter *tabwriter.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush writes any buffered data
|
||||||
|
func (p *upgradePlanTextPrinter) Flush(writer io.Writer, last bool) {
|
||||||
|
if p.tabwriter != nil {
|
||||||
|
p.tabwriter.Flush()
|
||||||
|
p.tabwriter = nil
|
||||||
|
p.Fprintln(writer, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintObj is an implementation of ResourcePrinter.PrintObj for upgrade plan plain text output
|
||||||
|
func (p *upgradePlanTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
|
||||||
|
if p.tabwriter == nil {
|
||||||
|
p.tabwriter = tabwriter.NewWriter(writer, 10, 4, 3, ' ', 0)
|
||||||
|
// Print header
|
||||||
|
fmt.Fprintln(p.tabwriter, strings.Join(p.columns, "\t"))
|
||||||
|
}
|
||||||
|
|
||||||
|
item, ok := obj.(*outputapiv1alpha2.ComponentUpgradePlan)
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("expected ComponentUpgradePlan, but got %+v", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print item
|
||||||
|
fmt.Fprintf(p.tabwriter, "%s\t%s\t%s\n", item.Name, item.CurrentVersion, item.NewVersion)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// upgradePlanTextPrintFlags provides flags necessary for printing upgrade plan in a text form
|
||||||
|
type upgradePlanTextPrintFlags struct{}
|
||||||
|
|
||||||
|
func (pf *upgradePlanTextPrintFlags) AddFlags(cmd *cobra.Command) {}
|
||||||
|
|
||||||
|
// AllowedFormats returns a list of allowed output formats
|
||||||
|
func (pf *upgradePlanTextPrintFlags) AllowedFormats() []string {
|
||||||
|
return []string{output.TextOutput}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPrinter returns a kubeadm printer for the text output format
|
||||||
|
func (pf *upgradePlanTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) {
|
||||||
|
if outputFormat == output.TextOutput {
|
||||||
|
return &upgradePlanTextPrinter{columns: []string{"COMPONENT", "CURRENT", "TARGET"}}, nil
|
||||||
|
}
|
||||||
|
return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: []string{output.JSONOutput, output.YAMLOutput, output.TextOutput}}
|
||||||
|
}
|
||||||
|
|
||||||
// runPlan takes care of outputting available versions to upgrade to for the user
|
// runPlan takes care of outputting available versions to upgrade to for the user
|
||||||
func runPlan(flags *planFlags, args []string) error {
|
func runPlan(flags *planFlags, args []string, printer output.Printer) error {
|
||||||
// Start with the basics, verify that the cluster is healthy, build a client and a versionGetter. Never dry-run when planning.
|
// Start with the basics, verify that the cluster is healthy, build a client and a versionGetter. Never dry-run when planning.
|
||||||
klog.V(1).Infoln("[upgrade/plan] verifying health of cluster")
|
klog.V(1).Infoln("[upgrade/plan] verifying health of cluster")
|
||||||
klog.V(1).Infoln("[upgrade/plan] retrieving configuration from cluster")
|
klog.V(1).Infoln("[upgrade/plan] retrieving configuration from cluster")
|
||||||
client, versionGetter, cfg, err := enforceRequirements(flags.applyPlanFlags, args, false, false)
|
client, versionGetter, cfg, err := enforceRequirements(flags.applyPlanFlags, args, false, false, printer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -79,7 +261,7 @@ func runPlan(flags *planFlags, args []string) error {
|
|||||||
|
|
||||||
// Compute which upgrade possibilities there are
|
// Compute which upgrade possibilities there are
|
||||||
klog.V(1).Infoln("[upgrade/plan] computing upgrade possibilities")
|
klog.V(1).Infoln("[upgrade/plan] computing upgrade possibilities")
|
||||||
availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, flags.allowExperimentalUpgrades, flags.allowRCUpgrades, isExternalEtcd, client, constants.GetStaticPodDirectory())
|
availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, flags.allowExperimentalUpgrades, flags.allowRCUpgrades, isExternalEtcd, client, constants.GetStaticPodDirectory(), printer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "[upgrade/versions] FATAL")
|
return errors.Wrap(err, "[upgrade/versions] FATAL")
|
||||||
}
|
}
|
||||||
@ -109,27 +291,17 @@ func runPlan(flags *planFlags, args []string) error {
|
|||||||
// in the human readable output if it did so
|
// in the human readable output if it did so
|
||||||
plan.ConfigVersions = configVersionStates
|
plan.ConfigVersions = configVersionStates
|
||||||
|
|
||||||
printUpgradePlan(&up, plan, unstableVersionFlag, isExternalEtcd, os.Stdout)
|
printUpgradePlan(&up, plan, unstableVersionFlag, isExternalEtcd, os.Stdout, printer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, print the component config state table
|
// Finally, print the component config state table
|
||||||
printComponentConfigVersionStates(configVersionStates, os.Stdout)
|
printComponentConfigVersionStates(configVersionStates, os.Stdout, printer)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newComponentUpgradePlan helper creates outputapi.ComponentUpgradePlan object
|
|
||||||
func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapi.ComponentUpgradePlan {
|
|
||||||
return outputapi.ComponentUpgradePlan{
|
|
||||||
Name: name,
|
|
||||||
CurrentVersion: currentVersion,
|
|
||||||
NewVersion: newVersion,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO There is currently no way to cleanly output upgrades that involve adding, removing, or changing components
|
// TODO There is currently no way to cleanly output upgrades that involve adding, removing, or changing components
|
||||||
// https://github.com/kubernetes/kubeadm/issues/810 was created to track addressing this.
|
// https://github.com/kubernetes/kubeadm/issues/810 was created to track addressing this.
|
||||||
func appendDNSComponent(components []outputapi.ComponentUpgradePlan, up *upgrade.Upgrade, name string) []outputapi.ComponentUpgradePlan {
|
func appendDNSComponent(components []outputapiv1alpha2.ComponentUpgradePlan, up *upgrade.Upgrade, name string) []outputapiv1alpha2.ComponentUpgradePlan {
|
||||||
beforeVersion := up.Before.DNSVersion
|
beforeVersion := up.Before.DNSVersion
|
||||||
afterVersion := up.After.DNSVersion
|
afterVersion := up.After.DNSVersion
|
||||||
|
|
||||||
@ -140,7 +312,7 @@ func appendDNSComponent(components []outputapi.ComponentUpgradePlan, up *upgrade
|
|||||||
}
|
}
|
||||||
|
|
||||||
// genUpgradePlan generates output-friendly upgrade plan out of upgrade.Upgrade structure
|
// genUpgradePlan generates output-friendly upgrade plan out of upgrade.Upgrade structure
|
||||||
func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapi.UpgradePlan, string, error) {
|
func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapiv1alpha2.UpgradePlan, string, error) {
|
||||||
newK8sVersion, err := version.ParseSemantic(up.After.KubeVersion)
|
newK8sVersion, err := version.ParseSemantic(up.After.KubeVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", errors.Wrapf(err, "Unable to parse normalized version %q as a semantic version", up.After.KubeVersion)
|
return nil, "", errors.Wrapf(err, "Unable to parse normalized version %q as a semantic version", up.After.KubeVersion)
|
||||||
@ -155,7 +327,7 @@ func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapi.Upgrad
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
components := []outputapi.ComponentUpgradePlan{}
|
components := []outputapiv1alpha2.ComponentUpgradePlan{}
|
||||||
|
|
||||||
if up.CanUpgradeKubelets() {
|
if up.CanUpgradeKubelets() {
|
||||||
// The map is of the form <old-version>:<node-count>. Here all the keys are put into a slice and sorted
|
// The map is of the form <old-version>:<node-count>. Here all the keys are put into a slice and sorted
|
||||||
@ -177,10 +349,10 @@ func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapi.Upgrad
|
|||||||
components = append(components, newComponentUpgradePlan(constants.Etcd, up.Before.EtcdVersion, up.After.EtcdVersion))
|
components = append(components, newComponentUpgradePlan(constants.Etcd, up.Before.EtcdVersion, up.After.EtcdVersion))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &outputapi.UpgradePlan{Components: components}, unstableVersionFlag, nil
|
return &outputapiv1alpha2.UpgradePlan{Components: components}, unstableVersionFlag, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getComponentConfigVersionStates(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, cfgPath string) ([]outputapi.ComponentConfigVersionState, error) {
|
func getComponentConfigVersionStates(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, cfgPath string) ([]outputapiv1alpha2.ComponentConfigVersionState, error) {
|
||||||
docmap := kubeadmapi.DocumentMap{}
|
docmap := kubeadmapi.DocumentMap{}
|
||||||
|
|
||||||
if cfgPath != "" {
|
if cfgPath != "" {
|
||||||
@ -199,16 +371,7 @@ func getComponentConfigVersionStates(cfg *kubeadmapi.ClusterConfiguration, clien
|
|||||||
}
|
}
|
||||||
|
|
||||||
// printUpgradePlan prints a UX-friendly overview of what versions are available to upgrade to
|
// printUpgradePlan prints a UX-friendly overview of what versions are available to upgrade to
|
||||||
func printUpgradePlan(up *upgrade.Upgrade, plan *outputapi.UpgradePlan, unstableVersionFlag string, isExternalEtcd bool, w io.Writer) {
|
func printUpgradePlan(up *upgrade.Upgrade, plan *outputapiv1alpha2.UpgradePlan, unstableVersionFlag string, isExternalEtcd bool, writer io.Writer, printer output.Printer) {
|
||||||
// The tab writer writes to the "real" writer w
|
|
||||||
tabw := tabwriter.NewWriter(w, 10, 4, 3, ' ', 0)
|
|
||||||
|
|
||||||
// endOfTable helper function flashes table writer
|
|
||||||
endOfTable := func() {
|
|
||||||
tabw.Flush()
|
|
||||||
fmt.Fprintln(w, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
printHeader := true
|
printHeader := true
|
||||||
printManualUpgradeHeader := true
|
printManualUpgradeHeader := true
|
||||||
for _, component := range plan.Components {
|
for _, component := range plan.Components {
|
||||||
@ -217,41 +380,40 @@ func printUpgradePlan(up *upgrade.Upgrade, plan *outputapi.UpgradePlan, unstable
|
|||||||
continue
|
continue
|
||||||
} else if component.Name == constants.Kubelet {
|
} else if component.Name == constants.Kubelet {
|
||||||
if printManualUpgradeHeader {
|
if printManualUpgradeHeader {
|
||||||
fmt.Fprintln(w, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':")
|
printer.Fprintln(writer, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':")
|
||||||
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tTARGET")
|
plan := newComponentUpgradePlan(component.Name, component.CurrentVersion, component.NewVersion)
|
||||||
fmt.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion)
|
printer.PrintObj(&plan, writer)
|
||||||
printManualUpgradeHeader = false
|
printManualUpgradeHeader = false
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(tabw, "%s\t%s\t%s\n", "", component.CurrentVersion, component.NewVersion)
|
plan := newComponentUpgradePlan("", component.CurrentVersion, component.NewVersion)
|
||||||
|
printer.PrintObj(&plan, writer)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if printHeader {
|
if printHeader {
|
||||||
// End of manual upgrades table
|
// End of manual upgrades table
|
||||||
endOfTable()
|
printer.Flush(writer, false)
|
||||||
|
printer.Fprintf(writer, "Upgrade to the latest %s:\n", up.Description)
|
||||||
fmt.Fprintf(w, "Upgrade to the latest %s:\n", up.Description)
|
printer.Fprintln(writer, "")
|
||||||
fmt.Fprintln(w, "")
|
|
||||||
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tTARGET")
|
|
||||||
printHeader = false
|
printHeader = false
|
||||||
}
|
}
|
||||||
fmt.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion)
|
plan := newComponentUpgradePlan(component.Name, component.CurrentVersion, component.NewVersion)
|
||||||
|
printer.PrintObj(&plan, writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// End of control plane table
|
printer.Flush(writer, true)
|
||||||
endOfTable()
|
|
||||||
|
|
||||||
//fmt.Fprintln(w, "")
|
printer.Fprintln(writer, "You can now apply the upgrade by executing the following command:")
|
||||||
fmt.Fprintln(w, "You can now apply the upgrade by executing the following command:")
|
printer.Fprintln(writer, "")
|
||||||
fmt.Fprintln(w, "")
|
printer.Fprintf(writer, "\tkubeadm upgrade apply %s%s\n", up.After.KubeVersion, unstableVersionFlag)
|
||||||
fmt.Fprintf(w, "\tkubeadm upgrade apply %s%s\n", up.After.KubeVersion, unstableVersionFlag)
|
printer.Fprintln(writer, "")
|
||||||
fmt.Fprintln(w, "")
|
|
||||||
|
|
||||||
if up.Before.KubeadmVersion != up.After.KubeadmVersion {
|
if up.Before.KubeadmVersion != up.After.KubeadmVersion {
|
||||||
fmt.Fprintf(w, "Note: Before you can perform this upgrade, you have to update kubeadm to %s.\n", up.After.KubeadmVersion)
|
printer.Fprintf(writer, "Note: Before you can perform this upgrade, you have to update kubeadm to %s.\n", up.After.KubeadmVersion)
|
||||||
fmt.Fprintln(w, "")
|
printer.Fprintln(writer, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
printLineSeparator(w)
|
printLineSeparator(writer, printer)
|
||||||
|
printer.Close(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically
|
// sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically
|
||||||
@ -278,18 +440,17 @@ func yesOrNo(b bool) string {
|
|||||||
return "no"
|
return "no"
|
||||||
}
|
}
|
||||||
|
|
||||||
func printLineSeparator(w io.Writer) {
|
func printLineSeparator(w io.Writer, printer output.Printer) {
|
||||||
fmt.Fprintln(w, "_____________________________________________________________________")
|
printer.Fprintf(w, "_____________________________________________________________________\n\n")
|
||||||
fmt.Fprintln(w, "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func printComponentConfigVersionStates(versionStates []outputapi.ComponentConfigVersionState, w io.Writer) {
|
func printComponentConfigVersionStates(versionStates []outputapiv1alpha2.ComponentConfigVersionState, w io.Writer, printer output.Printer) {
|
||||||
if len(versionStates) == 0 {
|
if len(versionStates) == 0 {
|
||||||
fmt.Fprintln(w, "No information available on component configs.")
|
printer.Fprintln(w, "No information available on component configs.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(w, dedent.Dedent(`
|
printer.Fprintln(w, dedent.Dedent(`
|
||||||
The table below shows the current state of component configs as understood by this version of kubeadm.
|
The table below shows the current state of component configs as understood by this version of kubeadm.
|
||||||
Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or
|
Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or
|
||||||
resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually
|
resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually
|
||||||
@ -297,10 +458,10 @@ func printComponentConfigVersionStates(versionStates []outputapi.ComponentConfig
|
|||||||
`))
|
`))
|
||||||
|
|
||||||
tabw := tabwriter.NewWriter(w, 10, 4, 3, ' ', 0)
|
tabw := tabwriter.NewWriter(w, 10, 4, 3, ' ', 0)
|
||||||
fmt.Fprintln(tabw, "API GROUP\tCURRENT VERSION\tPREFERRED VERSION\tMANUAL UPGRADE REQUIRED")
|
printer.Fprintln(tabw, "API GROUP\tCURRENT VERSION\tPREFERRED VERSION\tMANUAL UPGRADE REQUIRED")
|
||||||
|
|
||||||
for _, state := range versionStates {
|
for _, state := range versionStates {
|
||||||
fmt.Fprintf(tabw,
|
printer.Fprintf(tabw,
|
||||||
"%s\t%s\t%s\t%s\n",
|
"%s\t%s\t%s\t%s\n",
|
||||||
state.Group,
|
state.Group,
|
||||||
strOrDash(state.CurrentVersion),
|
strOrDash(state.CurrentVersion),
|
||||||
@ -310,5 +471,5 @@ func printComponentConfigVersionStates(versionStates []outputapi.ComponentConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
tabw.Flush()
|
tabw.Flush()
|
||||||
printLineSeparator(w)
|
printLineSeparator(w, printer)
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
|
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSortedSliceFromStringIntMap(t *testing.T) {
|
func TestSortedSliceFromStringIntMap(t *testing.T) {
|
||||||
@ -441,13 +442,19 @@ _____________________________________________________________________
|
|||||||
for _, rt := range tests {
|
for _, rt := range tests {
|
||||||
t.Run(rt.name, func(t *testing.T) {
|
t.Run(rt.name, func(t *testing.T) {
|
||||||
rt.buf = bytes.NewBufferString("")
|
rt.buf = bytes.NewBufferString("")
|
||||||
|
outputFlags := newUpgradePlanPrintFlags(output.TextOutput)
|
||||||
|
printer, err := outputFlags.ToPrinter()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed ToPrinter, err: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Generate and print upgrade plans
|
// Generate and print upgrade plans
|
||||||
for _, up := range rt.upgrades {
|
for _, up := range rt.upgrades {
|
||||||
plan, unstableVersionFlag, err := genUpgradePlan(&up, rt.externalEtcd)
|
plan, unstableVersionFlag, err := genUpgradePlan(&up, rt.externalEtcd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed genUpgradePlan, err: %+v", err)
|
t.Errorf("failed genUpgradePlan, err: %+v", err)
|
||||||
}
|
}
|
||||||
printUpgradePlan(&up, plan, unstableVersionFlag, rt.externalEtcd, rt.buf)
|
printUpgradePlan(&up, plan, unstableVersionFlag, rt.externalEtcd, rt.buf, printer)
|
||||||
}
|
}
|
||||||
actualBytes := rt.buf.Bytes()
|
actualBytes := rt.buf.Bytes()
|
||||||
if !bytes.Equal(actualBytes, rt.expectedBytes) {
|
if !bytes.Equal(actualBytes, rt.expectedBytes) {
|
||||||
@ -460,3 +467,164 @@ _____________________________________________________________________
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrintAvailableUpgradesStructured(t *testing.T) {
|
||||||
|
upgrades := []upgrade.Upgrade{
|
||||||
|
{
|
||||||
|
Description: "version in the v1.8 series",
|
||||||
|
Before: upgrade.ClusterState{
|
||||||
|
KubeVersion: "v1.8.1",
|
||||||
|
KubeletVersions: map[string]uint16{
|
||||||
|
"v1.8.1": 1,
|
||||||
|
},
|
||||||
|
KubeadmVersion: "v1.8.2",
|
||||||
|
DNSVersion: "1.14.5",
|
||||||
|
EtcdVersion: "3.0.17",
|
||||||
|
},
|
||||||
|
After: upgrade.ClusterState{
|
||||||
|
KubeVersion: "v1.8.3",
|
||||||
|
KubeadmVersion: "v1.8.3",
|
||||||
|
DNSVersion: "1.14.5",
|
||||||
|
EtcdVersion: "3.0.17",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
outputFormat string
|
||||||
|
buf *bytes.Buffer
|
||||||
|
expected string
|
||||||
|
externalEtcd bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "JSON output",
|
||||||
|
outputFormat: "json",
|
||||||
|
expected: `{
|
||||||
|
"kind": "UpgradePlan",
|
||||||
|
"apiVersion": "output.kubeadm.k8s.io/v1alpha2",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "kubelet",
|
||||||
|
"currentVersion": "1 x v1.8.1",
|
||||||
|
"newVersion": "v1.8.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kube-apiserver",
|
||||||
|
"currentVersion": "v1.8.1",
|
||||||
|
"newVersion": "v1.8.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kube-controller-manager",
|
||||||
|
"currentVersion": "v1.8.1",
|
||||||
|
"newVersion": "v1.8.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kube-scheduler",
|
||||||
|
"currentVersion": "v1.8.1",
|
||||||
|
"newVersion": "v1.8.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kube-proxy",
|
||||||
|
"currentVersion": "v1.8.1",
|
||||||
|
"newVersion": "v1.8.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CoreDNS",
|
||||||
|
"currentVersion": "1.14.5",
|
||||||
|
"newVersion": "1.14.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "etcd",
|
||||||
|
"currentVersion": "3.0.17",
|
||||||
|
"newVersion": "3.0.17"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configVersions": null
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "YAML output",
|
||||||
|
outputFormat: "yaml",
|
||||||
|
expected: `apiVersion: output.kubeadm.k8s.io/v1alpha2
|
||||||
|
components:
|
||||||
|
- currentVersion: 1 x v1.8.1
|
||||||
|
name: kubelet
|
||||||
|
newVersion: v1.8.3
|
||||||
|
- currentVersion: v1.8.1
|
||||||
|
name: kube-apiserver
|
||||||
|
newVersion: v1.8.3
|
||||||
|
- currentVersion: v1.8.1
|
||||||
|
name: kube-controller-manager
|
||||||
|
newVersion: v1.8.3
|
||||||
|
- currentVersion: v1.8.1
|
||||||
|
name: kube-scheduler
|
||||||
|
newVersion: v1.8.3
|
||||||
|
- currentVersion: v1.8.1
|
||||||
|
name: kube-proxy
|
||||||
|
newVersion: v1.8.3
|
||||||
|
- currentVersion: 1.14.5
|
||||||
|
name: CoreDNS
|
||||||
|
newVersion: 1.14.5
|
||||||
|
- currentVersion: 3.0.17
|
||||||
|
name: etcd
|
||||||
|
newVersion: 3.0.17
|
||||||
|
configVersions: null
|
||||||
|
kind: UpgradePlan
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Text output",
|
||||||
|
outputFormat: "Text",
|
||||||
|
expected: `Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
|
||||||
|
COMPONENT CURRENT TARGET
|
||||||
|
kubelet 1 x v1.8.1 v1.8.3
|
||||||
|
|
||||||
|
Upgrade to the latest version in the v1.8 series:
|
||||||
|
|
||||||
|
COMPONENT CURRENT TARGET
|
||||||
|
kube-apiserver v1.8.1 v1.8.3
|
||||||
|
kube-controller-manager v1.8.1 v1.8.3
|
||||||
|
kube-scheduler v1.8.1 v1.8.3
|
||||||
|
kube-proxy v1.8.1 v1.8.3
|
||||||
|
CoreDNS 1.14.5 1.14.5
|
||||||
|
etcd 3.0.17 3.0.17
|
||||||
|
|
||||||
|
You can now apply the upgrade by executing the following command:
|
||||||
|
|
||||||
|
kubeadm upgrade apply v1.8.3
|
||||||
|
|
||||||
|
Note: Before you can perform this upgrade, you have to update kubeadm to v1.8.3.
|
||||||
|
|
||||||
|
_____________________________________________________________________
|
||||||
|
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rt := range tests {
|
||||||
|
t.Run(rt.name, func(t *testing.T) {
|
||||||
|
rt.buf = bytes.NewBufferString("")
|
||||||
|
outputFlags := newUpgradePlanPrintFlags(rt.outputFormat)
|
||||||
|
printer, err := outputFlags.ToPrinter()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed ToPrinter, err: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate and print upgrade plans
|
||||||
|
for _, up := range upgrades {
|
||||||
|
plan, unstableVersionFlag, err := genUpgradePlan(&up, rt.externalEtcd)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed genUpgradePlan, err: %+v", err)
|
||||||
|
}
|
||||||
|
printUpgradePlan(&up, plan, unstableVersionFlag, rt.externalEtcd, rt.buf, printer)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := rt.buf.String()
|
||||||
|
if actual != rt.expected {
|
||||||
|
t.Errorf("failed PrintAvailableUpgrades:\n\nexpected:\n%s\n\nactual:\n%s", rt.expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -28,7 +28,7 @@ import (
|
|||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/output"
|
outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"
|
||||||
@ -289,7 +289,7 @@ func FetchFromClusterWithLocalOverwrites(clusterCfg *kubeadmapi.ClusterConfigura
|
|||||||
|
|
||||||
// GetVersionStates returns a slice of ComponentConfigVersionState structs
|
// GetVersionStates returns a slice of ComponentConfigVersionState structs
|
||||||
// describing all supported component config groups that were identified on the cluster
|
// describing all supported component config groups that were identified on the cluster
|
||||||
func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, docmap kubeadmapi.DocumentMap) ([]output.ComponentConfigVersionState, error) {
|
func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, docmap kubeadmapi.DocumentMap) ([]outputapiv1alpha2.ComponentConfigVersionState, error) {
|
||||||
// We don't want to modify clusterCfg so we make a working deep copy of it.
|
// We don't want to modify clusterCfg so we make a working deep copy of it.
|
||||||
// Also, we don't want the defaulted component configs so we get rid of them.
|
// Also, we don't want the defaulted component configs so we get rid of them.
|
||||||
scratchClusterCfg := clusterCfg.DeepCopy()
|
scratchClusterCfg := clusterCfg.DeepCopy()
|
||||||
@ -308,13 +308,13 @@ func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results := []output.ComponentConfigVersionState{}
|
results := []outputapiv1alpha2.ComponentConfigVersionState{}
|
||||||
for _, handler := range known {
|
for _, handler := range known {
|
||||||
group := handler.GroupVersion.Group
|
group := handler.GroupVersion.Group
|
||||||
if vererr, ok := multipleVerErrs[group]; ok {
|
if vererr, ok := multipleVerErrs[group]; ok {
|
||||||
// If there is an UnsupportedConfigVersionError then we are dealing with a case where the config was user
|
// If there is an UnsupportedConfigVersionError then we are dealing with a case where the config was user
|
||||||
// supplied and requires manual upgrade
|
// supplied and requires manual upgrade
|
||||||
results = append(results, output.ComponentConfigVersionState{
|
results = append(results, outputapiv1alpha2.ComponentConfigVersionState{
|
||||||
Group: group,
|
Group: group,
|
||||||
CurrentVersion: vererr.OldVersion.Version,
|
CurrentVersion: vererr.OldVersion.Version,
|
||||||
PreferredVersion: vererr.CurrentVersion.Version,
|
PreferredVersion: vererr.CurrentVersion.Version,
|
||||||
@ -322,7 +322,7 @@ func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client client
|
|||||||
})
|
})
|
||||||
} else if _, ok := scratchClusterCfg.ComponentConfigs[group]; ok {
|
} else if _, ok := scratchClusterCfg.ComponentConfigs[group]; ok {
|
||||||
// Normally loaded component config. No manual upgrade required on behalf of users.
|
// Normally loaded component config. No manual upgrade required on behalf of users.
|
||||||
results = append(results, output.ComponentConfigVersionState{
|
results = append(results, outputapiv1alpha2.ComponentConfigVersionState{
|
||||||
Group: group,
|
Group: group,
|
||||||
CurrentVersion: handler.GroupVersion.Version, // Currently kubeadm supports only one version per API
|
CurrentVersion: handler.GroupVersion.Version, // Currently kubeadm supports only one version per API
|
||||||
PreferredVersion: handler.GroupVersion.Version, // group so we can get away with these being the same
|
PreferredVersion: handler.GroupVersion.Version, // group so we can get away with these being the same
|
||||||
@ -331,7 +331,7 @@ func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client client
|
|||||||
// This config was either not present (user did not install an addon) or the config was unsupported kubeadm
|
// This config was either not present (user did not install an addon) or the config was unsupported kubeadm
|
||||||
// generated one and is therefore skipped so we can automatically re-generate it (no action required on
|
// generated one and is therefore skipped so we can automatically re-generate it (no action required on
|
||||||
// behalf of the user).
|
// behalf of the user).
|
||||||
results = append(results, output.ComponentConfigVersionState{
|
results = append(results, outputapiv1alpha2.ComponentConfigVersionState{
|
||||||
Group: group,
|
Group: group,
|
||||||
PreferredVersion: handler.GroupVersion.Version,
|
PreferredVersion: handler.GroupVersion.Version,
|
||||||
})
|
})
|
||||||
|
@ -35,7 +35,7 @@ import (
|
|||||||
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"
|
||||||
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||||
outputapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/output"
|
outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
)
|
)
|
||||||
@ -615,12 +615,12 @@ func TestFetchFromClusterWithLocalOverwrites(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetVersionStates(t *testing.T) {
|
func TestGetVersionStates(t *testing.T) {
|
||||||
fakeKnownContext(func() {
|
fakeKnownContext(func() {
|
||||||
versionStateCurrent := outputapi.ComponentConfigVersionState{
|
versionStateCurrent := outputapiv1alpha2.ComponentConfigVersionState{
|
||||||
Group: kubeadmapiv1.GroupName,
|
Group: kubeadmapiv1.GroupName,
|
||||||
CurrentVersion: currentClusterConfigVersion,
|
CurrentVersion: currentClusterConfigVersion,
|
||||||
PreferredVersion: currentClusterConfigVersion,
|
PreferredVersion: currentClusterConfigVersion,
|
||||||
}
|
}
|
||||||
versionStateOld := outputapi.ComponentConfigVersionState{
|
versionStateOld := outputapiv1alpha2.ComponentConfigVersionState{
|
||||||
Group: kubeadmapiv1.GroupName,
|
Group: kubeadmapiv1.GroupName,
|
||||||
CurrentVersion: oldClusterConfigVersion,
|
CurrentVersion: oldClusterConfigVersion,
|
||||||
PreferredVersion: currentClusterConfigVersion,
|
PreferredVersion: currentClusterConfigVersion,
|
||||||
@ -631,7 +631,7 @@ func TestGetVersionStates(t *testing.T) {
|
|||||||
desc string
|
desc string
|
||||||
obj runtime.Object
|
obj runtime.Object
|
||||||
config string
|
config string
|
||||||
expected outputapi.ComponentConfigVersionState
|
expected outputapiv1alpha2.ComponentConfigVersionState
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "appropriate cluster object without overwrite",
|
desc: "appropriate cluster object without overwrite",
|
||||||
@ -687,7 +687,7 @@ func TestGetVersionStates(t *testing.T) {
|
|||||||
{
|
{
|
||||||
desc: "old signed config without an overwrite",
|
desc: "old signed config without an overwrite",
|
||||||
obj: testClusterConfigMap(oldFooClusterConfig, true),
|
obj: testClusterConfigMap(oldFooClusterConfig, true),
|
||||||
expected: outputapi.ComponentConfigVersionState{
|
expected: outputapiv1alpha2.ComponentConfigVersionState{
|
||||||
Group: kubeadmapiv1.GroupName,
|
Group: kubeadmapiv1.GroupName,
|
||||||
CurrentVersion: "", // The config is treated as if it's missing
|
CurrentVersion: "", // The config is treated as if it's missing
|
||||||
PreferredVersion: currentClusterConfigVersion,
|
PreferredVersion: currentClusterConfigVersion,
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
|
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns"
|
"k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Upgrade defines an upgrade possibility to upgrade from a current version to a new one
|
// Upgrade defines an upgrade possibility to upgrade from a current version to a new one
|
||||||
@ -72,8 +73,8 @@ type ClusterState struct {
|
|||||||
|
|
||||||
// GetAvailableUpgrades fetches all versions from the specified VersionGetter and computes which
|
// GetAvailableUpgrades fetches all versions from the specified VersionGetter and computes which
|
||||||
// kinds of upgrades can be performed
|
// kinds of upgrades can be performed
|
||||||
func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed, externalEtcd bool, client clientset.Interface, manifestsDir string) ([]Upgrade, error) {
|
func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed, externalEtcd bool, client clientset.Interface, manifestsDir string, printer output.Printer) ([]Upgrade, error) {
|
||||||
fmt.Println("[upgrade] Fetching available versions to upgrade to")
|
printer.Printf("[upgrade] Fetching available versions to upgrade to\n")
|
||||||
|
|
||||||
// Collect the upgrades kubeadm can do in this list
|
// Collect the upgrades kubeadm can do in this list
|
||||||
upgrades := []Upgrade{}
|
upgrades := []Upgrade{}
|
||||||
@ -83,14 +84,14 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return upgrades, err
|
return upgrades, err
|
||||||
}
|
}
|
||||||
fmt.Printf("[upgrade/versions] Cluster version: %s\n", clusterVersionStr)
|
printer.Printf("[upgrade/versions] Cluster version: %s\n", clusterVersionStr)
|
||||||
|
|
||||||
// Get current kubeadm CLI version
|
// Get current kubeadm CLI version
|
||||||
kubeadmVersionStr, kubeadmVersion, err := versionGetterImpl.KubeadmVersion()
|
kubeadmVersionStr, kubeadmVersion, err := versionGetterImpl.KubeadmVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return upgrades, err
|
return upgrades, err
|
||||||
}
|
}
|
||||||
fmt.Printf("[upgrade/versions] kubeadm version: %s\n", kubeadmVersionStr)
|
printer.Printf("[upgrade/versions] kubeadm version: %s\n", kubeadmVersionStr)
|
||||||
|
|
||||||
// Get and output the current latest stable version
|
// Get and output the current latest stable version
|
||||||
stableVersionStr, stableVersion, err := versionGetterImpl.VersionFromCILabel("stable", "stable version")
|
stableVersionStr, stableVersion, err := versionGetterImpl.VersionFromCILabel("stable", "stable version")
|
||||||
@ -99,7 +100,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
|
|||||||
klog.Warningf("[upgrade/versions] WARNING: Falling back to current kubeadm version as latest stable version")
|
klog.Warningf("[upgrade/versions] WARNING: Falling back to current kubeadm version as latest stable version")
|
||||||
stableVersionStr, stableVersion = kubeadmVersionStr, kubeadmVersion
|
stableVersionStr, stableVersion = kubeadmVersionStr, kubeadmVersion
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("[upgrade/versions] Target version: %s\n", stableVersionStr)
|
printer.Printf("[upgrade/versions] Target version: %s\n", stableVersionStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the kubelet versions in the cluster
|
// Get the kubelet versions in the cluster
|
||||||
@ -148,7 +149,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Warningf("[upgrade/versions] WARNING: %v\n", err)
|
klog.Warningf("[upgrade/versions] WARNING: %v\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("[upgrade/versions] Latest %s: %s\n", description, patchVersionStr)
|
printer.Printf("[upgrade/versions] Latest %s: %s\n", description, patchVersionStr)
|
||||||
|
|
||||||
// Check if a minor version upgrade is possible when a patch release exists
|
// Check if a minor version upgrade is possible when a patch release exists
|
||||||
// It's only possible if the latest patch version is higher than the current patch version
|
// It's only possible if the latest patch version is higher than the current patch version
|
||||||
@ -267,7 +268,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a newline in the end of this output to leave some space to the next output section
|
// Add a newline in the end of this output to leave some space to the next output section
|
||||||
fmt.Println("")
|
printer.Println()
|
||||||
|
|
||||||
return upgrades, nil
|
return upgrades, nil
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
clientsetfake "k8s.io/client-go/kubernetes/fake"
|
clientsetfake "k8s.io/client-go/kubernetes/fake"
|
||||||
|
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeVersionGetter struct {
|
type fakeVersionGetter struct {
|
||||||
@ -652,7 +653,7 @@ func TestGetAvailableUpgrades(t *testing.T) {
|
|||||||
t.Fatalf("Unable to create test static pod manifest: %v", err)
|
t.Fatalf("Unable to create test static pod manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, rt.externalEtcd, client, manifestsDir)
|
actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, rt.externalEtcd, client, manifestsDir, &output.TextPrinter{})
|
||||||
if !reflect.DeepEqual(actualUpgrades, rt.expectedUpgrades) {
|
if !reflect.DeepEqual(actualUpgrades, rt.expectedUpgrades) {
|
||||||
t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades: %v\n\tgot: %v", rt.expectedUpgrades, actualUpgrades)
|
t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades: %v\n\tgot: %v", rt.expectedUpgrades, actualUpgrades)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -42,12 +41,16 @@ import (
|
|||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FetchInitConfigurationFromCluster fetches configuration from a ConfigMap in the cluster
|
// FetchInitConfigurationFromCluster fetches configuration from a ConfigMap in the cluster
|
||||||
func FetchInitConfigurationFromCluster(client clientset.Interface, w io.Writer, logPrefix string, newControlPlane, skipComponentConfigs bool) (*kubeadmapi.InitConfiguration, error) {
|
func FetchInitConfigurationFromCluster(client clientset.Interface, printer output.Printer, logPrefix string, newControlPlane, skipComponentConfigs bool) (*kubeadmapi.InitConfiguration, error) {
|
||||||
fmt.Fprintf(w, "[%s] Reading configuration from the cluster...\n", logPrefix)
|
if printer == nil {
|
||||||
fmt.Fprintf(w, "[%s] FYI: You can look at this config file with 'kubectl -n %s get cm %s -o yaml'\n", logPrefix, metav1.NamespaceSystem, constants.KubeadmConfigConfigMap)
|
printer = &output.TextPrinter{}
|
||||||
|
}
|
||||||
|
printer.Printf("[%s] Reading configuration from the cluster...\n", logPrefix)
|
||||||
|
printer.Printf("[%s] FYI: You can look at this config file with 'kubectl -n %s get cm %s -o yaml'\n", logPrefix, metav1.NamespaceSystem, constants.KubeadmConfigConfigMap)
|
||||||
|
|
||||||
// Fetch the actual config from cluster
|
// Fetch the actual config from cluster
|
||||||
cfg, err := getInitConfigurationFromCluster(constants.KubernetesDir, client, newControlPlane, skipComponentConfigs)
|
cfg, err := getInitConfigurationFromCluster(constants.KubernetesDir, client, newControlPlane, skipComponentConfigs)
|
||||||
|
@ -28,8 +28,16 @@ import (
|
|||||||
"k8s.io/cli-runtime/pkg/printers"
|
"k8s.io/cli-runtime/pkg/printers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TextOutput describes the plain text output
|
const (
|
||||||
const TextOutput = "text"
|
// TextOutput describes the plain text output
|
||||||
|
TextOutput = "text"
|
||||||
|
|
||||||
|
// JSONOutput describes the JSON output
|
||||||
|
JSONOutput = "json"
|
||||||
|
|
||||||
|
// YAMLOutput describes the YAML output
|
||||||
|
YAMLOutput = "yaml"
|
||||||
|
)
|
||||||
|
|
||||||
// TextPrintFlags is an interface to handle custom text output
|
// TextPrintFlags is an interface to handle custom text output
|
||||||
type TextPrintFlags interface {
|
type TextPrintFlags interface {
|
||||||
@ -44,7 +52,7 @@ type PrintFlags struct {
|
|||||||
JSONYamlPrintFlags *genericclioptions.JSONYamlPrintFlags
|
JSONYamlPrintFlags *genericclioptions.JSONYamlPrintFlags
|
||||||
// KubeTemplatePrintFlags composes print flags that provide both a JSONPath and a go-template printer.
|
// KubeTemplatePrintFlags composes print flags that provide both a JSONPath and a go-template printer.
|
||||||
KubeTemplatePrintFlags *genericclioptions.KubeTemplatePrintFlags
|
KubeTemplatePrintFlags *genericclioptions.KubeTemplatePrintFlags
|
||||||
// JSONYamlPrintFlags provides default flags necessary for kubeadm text printing.
|
// TextPrintFlags provides default flags necessary for kubeadm text printing.
|
||||||
TextPrintFlags TextPrintFlags
|
TextPrintFlags TextPrintFlags
|
||||||
// TypeSetterPrinter is an implementation of ResourcePrinter that wraps another printer with types set on the objects
|
// TypeSetterPrinter is an implementation of ResourcePrinter that wraps another printer with types set on the objects
|
||||||
TypeSetterPrinter *printers.TypeSetterPrinter
|
TypeSetterPrinter *printers.TypeSetterPrinter
|
||||||
@ -52,7 +60,7 @@ type PrintFlags struct {
|
|||||||
OutputFormat *string
|
OutputFormat *string
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllowedFormats returns list of allowed output formats
|
// AllowedFormats returns a list of allowed output formats
|
||||||
func (pf *PrintFlags) AllowedFormats() []string {
|
func (pf *PrintFlags) AllowedFormats() []string {
|
||||||
ret := []string{TextOutput}
|
ret := []string{TextOutput}
|
||||||
ret = append(ret, pf.JSONYamlPrintFlags.AllowedFormats()...)
|
ret = append(ret, pf.JSONYamlPrintFlags.AllowedFormats()...)
|
||||||
@ -133,7 +141,12 @@ func NewOutputFlags(textPrintFlags TextPrintFlags) *PrintFlags {
|
|||||||
type Printer interface {
|
type Printer interface {
|
||||||
PrintObj(obj runtime.Object, writer io.Writer) error
|
PrintObj(obj runtime.Object, writer io.Writer) error
|
||||||
Fprintf(writer io.Writer, format string, args ...interface{}) (n int, err error)
|
Fprintf(writer io.Writer, format string, args ...interface{}) (n int, err error)
|
||||||
|
Fprintln(writer io.Writer, args ...interface{}) (n int, err error)
|
||||||
Printf(format string, args ...interface{}) (n int, err error)
|
Printf(format string, args ...interface{}) (n int, err error)
|
||||||
|
Println(args ...interface{}) (n int, err error)
|
||||||
|
|
||||||
|
Flush(writer io.Writer, last bool)
|
||||||
|
Close(writer io.Writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TextPrinter implements Printer interface for generic text output
|
// TextPrinter implements Printer interface for generic text output
|
||||||
@ -151,11 +164,29 @@ func (tp *TextPrinter) Fprintf(writer io.Writer, format string, args ...interfac
|
|||||||
return fmt.Fprintf(writer, format, args...)
|
return fmt.Fprintf(writer, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fprintln is a wrapper around fmt.Fprintln
|
||||||
|
func (tp *TextPrinter) Fprintln(writer io.Writer, args ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintln(writer, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// Printf is a wrapper around fmt.Printf
|
// Printf is a wrapper around fmt.Printf
|
||||||
func (tp *TextPrinter) Printf(format string, args ...interface{}) (n int, err error) {
|
func (tp *TextPrinter) Printf(format string, args ...interface{}) (n int, err error) {
|
||||||
return fmt.Printf(format, args...)
|
return fmt.Printf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Println is a wrapper around fmt.Printf
|
||||||
|
func (tp *TextPrinter) Println(args ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Println(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush writes any buffered data
|
||||||
|
func (tp *TextPrinter) Flush(writer io.Writer, last bool) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close flushes any buffered data and closes the printer
|
||||||
|
func (tp *TextPrinter) Close(writer io.Writer) {
|
||||||
|
}
|
||||||
|
|
||||||
// ResourcePrinterWrapper wraps ResourcePrinter and implements Printer interface
|
// ResourcePrinterWrapper wraps ResourcePrinter and implements Printer interface
|
||||||
type ResourcePrinterWrapper struct {
|
type ResourcePrinterWrapper struct {
|
||||||
Printer printers.ResourcePrinter
|
Printer printers.ResourcePrinter
|
||||||
@ -169,6 +200,14 @@ func NewResourcePrinterWrapper(resourcePrinter printers.ResourcePrinter, err err
|
|||||||
return &ResourcePrinterWrapper{Printer: resourcePrinter}, nil
|
return &ResourcePrinterWrapper{Printer: resourcePrinter}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flush writes any buffered data
|
||||||
|
func (rpw *ResourcePrinterWrapper) Flush(writer io.Writer, last bool) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close flushes any buffered data and closes the printer
|
||||||
|
func (rpw *ResourcePrinterWrapper) Close(writer io.Writer) {
|
||||||
|
}
|
||||||
|
|
||||||
// PrintObj is an implementation of ResourcePrinter.PrintObj that calls underlying printer API
|
// PrintObj is an implementation of ResourcePrinter.PrintObj that calls underlying printer API
|
||||||
func (rpw *ResourcePrinterWrapper) PrintObj(obj runtime.Object, writer io.Writer) error {
|
func (rpw *ResourcePrinterWrapper) PrintObj(obj runtime.Object, writer io.Writer) error {
|
||||||
return rpw.Printer.PrintObj(obj, writer)
|
return rpw.Printer.PrintObj(obj, writer)
|
||||||
@ -181,9 +220,23 @@ func (rpw *ResourcePrinterWrapper) Fprintf(writer io.Writer, format string, args
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fprintln is an empty method to satisfy the Printer interface
|
||||||
|
// and silent info printing for structured output
|
||||||
|
// This method is usually redefined for the text output
|
||||||
|
func (rpw *ResourcePrinterWrapper) Fprintln(writer io.Writer, args ...interface{}) (n int, err error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Printf is an empty method to satisfy Printer interface
|
// Printf is an empty method to satisfy Printer interface
|
||||||
// and silent info printing for structured output
|
// and silent info printing for structured output
|
||||||
// This method is usually redefined for the text output
|
// This method is usually redefined for the text output
|
||||||
func (rpw *ResourcePrinterWrapper) Printf(format string, args ...interface{}) (n int, err error) {
|
func (rpw *ResourcePrinterWrapper) Printf(format string, args ...interface{}) (n int, err error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Println is an empty method to satisfy Printer interface
|
||||||
|
// and silent info printing for structured output
|
||||||
|
// This method is usually redefined for the text output
|
||||||
|
func (rpw *ResourcePrinterWrapper) Println(args ...interface{}) (n int, err error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user