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 }