github.com/SUSE/skuba@v1.4.17/pkg/skuba/actions/node/upgrade/apply.go (about)

     1  /*
     2   * Copyright (c) 2019 SUSE LLC.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package upgrade
    19  
    20  import (
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"strings"
    25  
    26  	"k8s.io/apimachinery/pkg/runtime/schema"
    27  	clientset "k8s.io/client-go/kubernetes"
    28  	kubeadmconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
    29  
    30  	"github.com/pkg/errors"
    31  
    32  	"github.com/SUSE/skuba/internal/pkg/skuba/deployments"
    33  	"github.com/SUSE/skuba/internal/pkg/skuba/kubeadm"
    34  	"github.com/SUSE/skuba/internal/pkg/skuba/kubernetes"
    35  	"github.com/SUSE/skuba/internal/pkg/skuba/kured"
    36  	"github.com/SUSE/skuba/internal/pkg/skuba/node"
    37  	upgradenode "github.com/SUSE/skuba/internal/pkg/skuba/upgrade/node"
    38  	"github.com/SUSE/skuba/pkg/skuba"
    39  )
    40  
    41  func Apply(client clientset.Interface, target *deployments.Target) error {
    42  	if err := fillTargetWithNodeNameAndRole(client, target); err != nil {
    43  		return err
    44  	}
    45  
    46  	currentClusterVersion, err := kubeadm.GetCurrentClusterVersion(client)
    47  	if err != nil {
    48  		return err
    49  	}
    50  	currentVersion := currentClusterVersion.String()
    51  	latestVersion := kubernetes.LatestVersion().String()
    52  	nodeVersionInfoUpdate, err := upgradenode.UpdateStatus(client, target.Nodename)
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	fmt.Printf("Current Kubernetes cluster version: %s\n", currentVersion)
    58  	fmt.Printf("Latest Kubernetes version: %s\n", latestVersion)
    59  	fmt.Printf("Current Node version: %s\n", nodeVersionInfoUpdate.Current.KubeletVersion.String())
    60  	fmt.Println()
    61  
    62  	if nodeVersionInfoUpdate.IsUpdated() {
    63  		fmt.Printf("Node %s is up to date\n", target.Nodename)
    64  		return nil
    65  	}
    66  
    67  	// Check if the node is upgradeable (matches preconditions)
    68  	if err := nodeVersionInfoUpdate.NodeUpgradeableCheck(client, currentClusterVersion); err != nil {
    69  		fmt.Println()
    70  		return err
    71  	}
    72  
    73  	// Check if skuba-update.timer is already disabled
    74  	skubaUpdateWasEnabled, err := target.IsServiceEnabled("skuba-update.timer")
    75  	if err != nil {
    76  		return err
    77  	}
    78  
    79  	// Check if a lock on kured already exists
    80  	kuredWasLocked, err := kured.LockExists(client)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	var initCfgContents []byte
    86  
    87  	// Check if it's the first control plane node to be upgraded
    88  	isFirstControlPlaneNodeToBeUpgraded, err := nodeVersionInfoUpdate.IsFirstControlPlaneNodeToBeUpgraded(client)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	if isFirstControlPlaneNodeToBeUpgraded {
    93  		fmt.Println("Fetching the cluster configuration...")
    94  
    95  		initCfg, err := kubeadm.GetClusterConfiguration(client)
    96  		if err != nil {
    97  			return err
    98  		}
    99  		if err := node.AddTargetInformationToInitConfigurationWithClusterVersion(target, initCfg, nodeVersionInfoUpdate.Update.APIServerVersion); err != nil {
   100  			return errors.Wrap(err, "error adding target information to init configuration")
   101  		}
   102  
   103  		kubeadm.UpdateClusterConfigurationWithClusterVersion(initCfg, nodeVersionInfoUpdate.Update.APIServerVersion)
   104  		initCfgContents, err = kubeadmconfigutil.MarshalInitConfigurationToBytes(initCfg, schema.GroupVersion{
   105  			Group:   "kubeadm.k8s.io",
   106  			Version: kubeadm.GetKubeadmApisVersion(nodeVersionInfoUpdate.Update.APIServerVersion),
   107  		})
   108  		if err != nil {
   109  			return err
   110  		}
   111  	}
   112  
   113  	fmt.Printf("Performing node %s (%s) upgrade, please wait...\n", target.Nodename, target.Target)
   114  
   115  	if skubaUpdateWasEnabled {
   116  		err = target.Apply(nil, "skuba-update-timer.disable")
   117  		if err != nil {
   118  			return err
   119  		}
   120  	}
   121  	if !kuredWasLocked {
   122  		if err := kured.Lock(client); err != nil {
   123  			return err
   124  		}
   125  	}
   126  
   127  	/*
   128  		Always upload crio files, regardless of the version (allows to
   129  		enforce user behavior during patch updates).
   130  		During the upgrade from 1.16 to 1.18 crio, the cri-o package will
   131  		handle overriding the old crio sysconfig to an "empty" sysconfig,
   132  		and the cri.configure action will be enough.
   133  		We can remove the conditionals and only
   134  		keep the cri.configure action when caasp 4.2.0 is not supported
   135  		anymore (everyone has updated crio to 1.18)
   136  	*/
   137  	if _, err := os.Stat(skuba.CriDefaultsConfFile()); err == nil {
   138  		err = target.Apply(nil, "cri.configure")
   139  		if err != nil {
   140  			return err
   141  		}
   142  	} else if _, err := os.Stat(skuba.CriDockerDefaultsConfFile()); err == nil {
   143  		err = target.Apply(nil, "cri.sysconfig")
   144  		if err != nil {
   145  			return err
   146  		}
   147  	}
   148  
   149  	if nodeVersionInfoUpdate.HasMajorOrMinorUpdate() {
   150  		err = target.Apply(deployments.KubernetesBaseOSConfiguration{
   151  			UpdatedVersion: nodeVersionInfoUpdate.Update.KubeletVersion.String(),
   152  			CurrentVersion: nodeVersionInfoUpdate.Current.KubeletVersion.String(),
   153  		}, "kubernetes.install-node-pattern")
   154  		if err != nil {
   155  			return err
   156  		}
   157  	}
   158  	if isFirstControlPlaneNodeToBeUpgraded {
   159  		err = target.Apply(deployments.UpgradeConfiguration{
   160  			KubeadmConfigContents: string(initCfgContents),
   161  		}, "kubeadm.upgrade.apply")
   162  		if err != nil {
   163  			return err
   164  		}
   165  		err = downloadAdminConf(target)
   166  		if err != nil {
   167  			return err
   168  		}
   169  	} else if err := target.Apply(nil, "kubeadm.upgrade.node"); err != nil {
   170  		return err
   171  	}
   172  	err = target.Apply(deployments.KubernetesBaseOSConfiguration{
   173  		CurrentVersion: nodeVersionInfoUpdate.Update.KubeletVersion.String(),
   174  	}, "kubernetes.install-node-pattern")
   175  	if err != nil {
   176  		return err
   177  	}
   178  
   179  	// bsc#1155810: generate cluster-wide kubelet root certificate, and generate/rotate kuberlet server certificate
   180  	if err := kubernetes.GenerateKubeletRootCert(); err != nil {
   181  		return err
   182  	}
   183  	err = target.Apply(nil,
   184  		"kubelet.rootcert.upload",
   185  		"kubelet.servercert.create-and-upload",
   186  		"kubernetes.restart-services",
   187  	)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	if skubaUpdateWasEnabled {
   193  		err = target.Apply(nil,
   194  			"skuba-update.start.no-block",
   195  			"skuba-update-timer.enable",
   196  		)
   197  		if err != nil {
   198  			return err
   199  		}
   200  	}
   201  	if !kuredWasLocked {
   202  		if err := kured.Unlock(client); err != nil {
   203  			return err
   204  		}
   205  	}
   206  
   207  	fmt.Printf("Node %s (%s) successfully upgraded\n", target.Nodename, target.Target)
   208  
   209  	return nil
   210  }
   211  
   212  func fillTargetWithNodeNameAndRole(client clientset.Interface, target *deployments.Target) error {
   213  	machineID, err := target.DownloadFileContents("/etc/machine-id")
   214  	if err != nil {
   215  		return err
   216  	}
   217  	node, err := kubernetes.GetNodeWithMachineID(client, strings.TrimSuffix(machineID, "\n"))
   218  	if err != nil {
   219  		return err
   220  	}
   221  	target.Nodename = node.ObjectMeta.Name
   222  
   223  	var role deployments.Role
   224  	if kubernetes.IsControlPlane(node) {
   225  		role = deployments.MasterRole
   226  	} else {
   227  		role = deployments.WorkerRole
   228  	}
   229  	target.Role = &role
   230  
   231  	return nil
   232  }
   233  
   234  func downloadAdminConf(target *deployments.Target) error {
   235  	fmt.Printf("Downloading admin.conf from upgrade node %q\n", target.Target)
   236  	secretData, err := target.DownloadFileContents("/etc/kubernetes/admin.conf")
   237  	if err != nil {
   238  		return err
   239  	}
   240  	if err := ioutil.WriteFile("admin.conf", []byte(secretData), 0600); err != nil {
   241  		return err
   242  	}
   243  	return nil
   244  }