1# -*- coding: utf-8 -*- # 2# Copyright 2015 Google LLC. All Rights Reserved. 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"""Upgrade cluster command.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import unicode_literals 20 21from apitools.base.py import exceptions as apitools_exceptions 22 23from googlecloudsdk.api_lib.container import api_adapter 24from googlecloudsdk.api_lib.container import util 25from googlecloudsdk.calliope import base 26from googlecloudsdk.calliope import exceptions 27from googlecloudsdk.command_lib.container import container_command_util 28from googlecloudsdk.command_lib.container import flags 29from googlecloudsdk.core import log 30from googlecloudsdk.core import properties 31from googlecloudsdk.core.console import console_attr 32from googlecloudsdk.core.console import console_io 33from googlecloudsdk.core.util.semver import SemVer 34 35 36class UpgradeHelpText(object): 37 """Upgrade available help text messages.""" 38 UPGRADE_AVAILABLE = """ 39* - There is an upgrade available for your cluster(s). 40""" 41 42 SUPPORT_ENDING = """ 43** - The current version of your cluster(s) will soon be out of support, please upgrade. 44""" 45 46 UNSUPPORTED = """ 47*** - The current version of your cluster(s) is unsupported, please upgrade. 48""" 49 50 UPGRADE_COMMAND = """ 51To upgrade nodes to the latest available version, run 52 $ gcloud container clusters upgrade {name}""" 53 54 55class VersionVerifier(object): 56 """Compares the cluster and master versions for upgrade availablity.""" 57 UP_TO_DATE = 0 58 UPGRADE_AVAILABLE = 1 59 SUPPORT_ENDING = 2 60 UNSUPPORTED = 3 61 62 def Compare(self, current_master_version, current_cluster_version): 63 """Compares the cluster and master versions and returns an enum.""" 64 if current_master_version == current_cluster_version: 65 return self.UP_TO_DATE 66 master_version = SemVer(current_master_version) 67 cluster_version = SemVer(current_cluster_version) 68 major, minor, _ = master_version.Distance(cluster_version) 69 if major != 0 or minor > 2: 70 return self.UNSUPPORTED 71 elif minor > 1: 72 return self.SUPPORT_ENDING 73 else: 74 return self.UPGRADE_AVAILABLE 75 76 77def ParseUpgradeOptionsBase(args): 78 """Parses the flags provided with the cluster upgrade command.""" 79 return api_adapter.UpdateClusterOptions( 80 version=args.cluster_version, 81 update_master=args.master, 82 update_nodes=(not args.master), 83 node_pool=args.node_pool, 84 image_type=args.image_type, 85 image=args.image, 86 image_project=args.image_project) 87 88 89def _Args(parser): 90 """Register flags for this command. 91 92 Args: 93 parser: An argparse.ArgumentParser-like object. It is mocked out in order to 94 capture some information, but behaves like an ArgumentParser. 95 """ 96 parser.add_argument( 97 'name', metavar='NAME', help='The name of the cluster to upgrade.') 98 flags.AddClusterVersionFlag( 99 parser, 100 help="""\ 101The Kubernetes release version to which to upgrade the cluster's nodes. 102 103If desired cluster version is omitted, *node* upgrades default to the current 104*master* version and *master* upgrades default to the default cluster version, 105which can be found in the server config. 106 107You can find the list of allowed versions for upgrades by running: 108 109 $ gcloud container get-server-config 110""") 111 parser.add_argument('--node-pool', help='The node pool to upgrade.') 112 parser.add_argument( 113 '--master', 114 help='Upgrade the cluster\'s master to the latest version of Kubernetes' 115 ' supported on Kubernetes Engine. Nodes cannot be upgraded at the same' 116 ' time as the master.', 117 action='store_true') 118 # Timeout in seconds for the operation, default 3600 seconds (60 minutes) 119 parser.add_argument( 120 '--timeout', 121 type=int, 122 default=3600, 123 hidden=True, 124 help='Timeout (seconds) for waiting on the operation to complete.') 125 flags.AddAsyncFlag(parser) 126 flags.AddImageTypeFlag(parser, 'cluster/node pool') 127 flags.AddImageFlag(parser, hidden=True) 128 flags.AddImageProjectFlag(parser, hidden=True) 129 130 131@base.ReleaseTracks(base.ReleaseTrack.GA) 132class Upgrade(base.Command): 133 """Upgrade the Kubernetes version of an existing container cluster.""" 134 135 @staticmethod 136 def Args(parser): 137 _Args(parser) 138 139 def ParseUpgradeOptions(self, args): 140 return ParseUpgradeOptionsBase(args) 141 142 def Run(self, args): 143 """This is what gets called when the user runs this command. 144 145 Args: 146 args: an argparse namespace. All the arguments that were provided to this 147 command invocation. 148 149 Returns: 150 Some value that we want to have printed later. 151 """ 152 adapter = self.context['api_adapter'] 153 location_get = self.context['location_get'] 154 location = location_get(args) 155 cluster_ref = adapter.ParseCluster(args.name, location) 156 project_id = properties.VALUES.core.project.Get(required=True) 157 158 try: 159 cluster = adapter.GetCluster(cluster_ref) 160 except (exceptions.HttpException, apitools_exceptions.HttpForbiddenError, 161 util.Error) as error: 162 log.warning(('Problem loading details of cluster to upgrade:\n\n{}\n\n' 163 'You can still attempt to upgrade the cluster.\n').format( 164 console_attr.SafeText(error))) 165 cluster = None 166 167 try: 168 server_conf = adapter.GetServerConfig(project_id, location) 169 except (exceptions.HttpException, apitools_exceptions.HttpForbiddenError, 170 util.Error) as error: 171 log.warning(('Problem loading server config:\n\n{}\n\n' 172 'You can still attempt to upgrade the cluster.\n').format( 173 console_attr.SafeText(error))) 174 server_conf = None 175 176 upgrade_message = container_command_util.ClusterUpgradeMessage( 177 name=args.name, 178 server_conf=server_conf, 179 cluster=cluster, 180 master=args.master, 181 node_pool_name=args.node_pool, 182 new_version=args.cluster_version) 183 184 console_io.PromptContinue( 185 message=upgrade_message, throw_if_unattended=True, cancel_on_no=True) 186 187 options = self.ParseUpgradeOptions(args) 188 189 try: 190 op_ref = adapter.UpdateCluster(cluster_ref, options) 191 except apitools_exceptions.HttpError as error: 192 raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) 193 194 if not args.async_: 195 adapter.WaitForOperation( 196 op_ref, 197 'Upgrading {0}'.format(cluster_ref.clusterId), 198 timeout_s=args.timeout) 199 200 log.UpdatedResource(cluster_ref) 201 202 203Upgrade.detailed_help = { 204 'DESCRIPTION': 205 """\ 206 Upgrades the Kubernetes version of an existing container cluster. 207 208 This command upgrades the Kubernetes version of the *nodes* or *master* of 209 a cluster. Note that the Kubernetes version of the cluster's *master* is 210 also periodically upgraded automatically as new releases are available. 211 212 If desired cluster version is omitted, *node* upgrades default to the 213 current *master* version and *master* upgrades default to the default 214 cluster version, which can be found in the server config. 215 216 *By running this command, all of the cluster's nodes will be deleted and* 217 *recreated one at a time.* While persistent Kubernetes resources, such as 218 pods backed by replication controllers, will be rescheduled onto new 219 nodes, a small cluster may experience a few minutes where there are 220 insufficient nodes available to run all of the scheduled Kubernetes 221 resources. 222 223 *Please ensure that any data you wish to keep is stored on a persistent* 224 *disk before upgrading the cluster.* Ephemeral Kubernetes resources--in 225 particular, pods without replication controllers--will be lost, while 226 persistent Kubernetes resources will get rescheduled. 227 """, 228 'EXAMPLES': 229 """\ 230 Upgrade the nodes of sample-cluster to the Kubernetes version of the 231 cluster's master. 232 233 $ {command} sample-cluster 234 235 Upgrade the nodes of sample-cluster to Kubernetes version 1.14.7-gke.14: 236 237 $ {command} sample-cluster --cluster-version="1.14.7-gke.14" 238 239 Upgrade the master of sample-cluster to the default cluster version: 240 241 $ {command} sample-cluster --master 242""", 243} 244 245 246@base.ReleaseTracks(base.ReleaseTrack.BETA) 247class UpgradeBeta(Upgrade): 248 """Upgrade the Kubernetes version of an existing container cluster.""" 249 250 @staticmethod 251 def Args(parser): 252 _Args(parser) 253 254 255@base.ReleaseTracks(base.ReleaseTrack.ALPHA) 256class UpgradeAlpha(Upgrade): 257 """Upgrade the Kubernetes version of an existing container cluster.""" 258 259 @staticmethod 260 def Args(parser): 261 _Args(parser) 262 flags.AddSecurityProfileForUpgradeFlags(parser) 263 264 def ParseUpgradeOptions(self, args): 265 ops = ParseUpgradeOptionsBase(args) 266 ops.security_profile = args.security_profile 267 ops.security_profile_runtime_rules = args.security_profile_runtime_rules 268 return ops 269