def GetCluster(self, cluster_ref): """Get a running cluster. Args: cluster_ref: cluster Resource to describe. Returns: Cluster message. Raises: Error: if cluster cannot be found. """ try: return self.client.projects_zones_clusters.Get(cluster_ref.Request()) except apitools_exceptions.HttpError as error: api_error = util.GetError(error) if api_error.code != 404: raise api_error # Cluster couldn't be found, maybe user got zone wrong? try: clusters = self.ListClusters(cluster_ref.projectId).clusters except apitools_exceptions.HttpError as error: raise exceptions.HttpException(util.GetError(error)) for cluster in clusters: if cluster.name == cluster_ref.clusterId: # User likely got zone wrong. raise util.Error(WRONG_ZONE_ERROR_MSG.format( error=api_error, name=cluster_ref.clusterId, wrong_zone=self.Zone(cluster_ref), zone=cluster.zone)) # Couldn't find a cluster with that name. raise util.Error(NO_SUCH_CLUSTER_ERROR_MSG.format( error=api_error, name=cluster_ref.clusterId, project=cluster_ref.projectId))
def PopulateMembership(ref, args, request): """Populate membership object with metadata read from the cluster. Args: ref: reference to the membership object. args: command line arguments. request: API request to be issued Returns: modified request """ kubeconfig, context = GetKubeconfigAndContext(args) cmd = [ 'get', 'namespace', 'kube-system', '-o', 'jsonpath=\'{.metadata.uid}\'' ] membership = GetMembership(ref) # Warn if kubectl is not installed. if not c_util.CheckKubectlInstalled(): raise c_util.Error('kubectl not installed.') out, err, returncode = RunKubectl(kubeconfig, context, cmd, '') if returncode != 0: raise c_util.Error('Failed to get the UID of cluster: {0}'.format(err)) uuid = out.replace("'", '') membership.name = uuid request.membershipId = membership.name request.membership = membership return request
def PopulateMembership(ref, args, request): """Populate membership object with metadata read from the cluster. Args: ref: reference to the membership object. args: command line arguments. request: API request to be issued Returns: modified request """ kubeconfig, context = GetKubeconfigAndContext(args) cmd = [ 'get', 'namespace', 'kube-system', '-o', 'jsonpath=\'{.metadata.uid}\'' ] membership = GetMembership(ref) # Warn if kubectl is not installed and ask user to manually fill in # resource_id field. if not c_util.CheckKubectlInstalled(): raise c_util.Error('kubectl not installed') out, err, returncode = RunKubectl(kubeconfig, context, cmd, '') if returncode != 0: raise c_util.Error('Failed to get the UID of cluster: {0}'.format(err)) membership.name = out membership.endpoint = core_apis.GetMessagesModule( 'gkehub', 'v1beta1').MembershipEndpoint(resourceId=out) request.membershipId = membership.name request.membership = membership args.membershipsId = membership.name return request
def RemoveLabels(self, cluster_ref, remove_labels): """Removes labels from a cluster. Args: cluster_ref: cluster to update. remove_labels: labels to remove. Returns: Operation ref for label set operation. """ clus = None try: clus = self.client.projects_zones_clusters.Get( self.messages.ContainerProjectsZonesClustersGetRequest( projectId=cluster_ref.projectId, zone=cluster_ref.zone, clusterId=cluster_ref.clusterId)) except apitools_exceptions.HttpError as error: api_error = exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) if api_error.payload.status_code != 404: raise api_error clus_labels = {} if clus.resourceLabels: for item in clus.resourceLabels.additionalProperties: clus_labels[item.key] = str(item.value) # if clusLabels empty, nothing to do if not clus_labels: raise util.Error( NO_LABELS_ON_CLUSTER_ERROR_MSG.format(cluster=clus.name)) for k in remove_labels: try: clus_labels.pop(k) except KeyError as error: # if at least one label not found on cluster, raise error raise util.Error( NO_SUCH_LABEL_ERROR_MSG.format(cluster=clus.name, name=k)) labels = self.messages.SetLabelsRequest.ResourceLabelsValue() for k, v in sorted(clus_labels.iteritems()): labels.additionalProperties.append( labels.AdditionalProperty(key=k, value=v)) req = self.messages.SetLabelsRequest( resourceLabels=labels, labelFingerprint=clus.labelFingerprint) operation = self.client.projects_zones_clusters.ResourceLabels( self.messages.ContainerProjectsZonesClustersResourceLabelsRequest( clusterId=cluster_ref.clusterId, zone=cluster_ref.zone, projectId=cluster_ref.projectId, setLabelsRequest=req)) return self.ParseOperation(operation.name)
def WaitForOperation(self, operation_ref, message, timeout_s=1200, poll_period_s=5): """Poll container Operation until its status is done or timeout reached. Args: operation_ref: operation resource. message: str, message to display to user while polling. timeout_s: number, seconds to poll with retries before timing out. poll_period_s: number, delay in seconds between requests. Returns: Operation: the return value of the last successful operations.get request. Raises: Error: if the operation times out or finishes with an error. """ detail_message = None with progress_tracker.ProgressTracker( message, autotick=True, detail_message_callback=lambda: detail_message): start_time = time.clock() while timeout_s > (time.clock() - start_time): try: operation = self.GetOperation(operation_ref) if self.IsOperationFinished(operation): # Success! log.info('Operation %s succeeded after %.3f seconds', operation, (time.clock() - start_time)) break detail_message = operation.detail except apitools_exceptions.HttpError as error: log.debug('GetOperation failed: %s', error) # Keep trying until we timeout in case error is transient. # TODO(user): add additional backoff if server is returning 500s time.sleep(poll_period_s) if not self.IsOperationFinished(operation): log.err.Print( 'Timed out waiting for operation {0}'.format(operation)) raise util.Error( 'Operation [{0}] is still running'.format(operation)) if self.GetOperationError(operation): raise util.Error('Operation [{0}] finished with error: {1}'.format( operation, self.GetOperationError(operation))) return operation
def ParseCreateOptionsBase(args): flags.MungeBasicAuthFlags(args) if (args.IsSpecified('enable_cloud_endpoints') and properties.VALUES.container.new_scopes_behavior.GetBool()): raise util.Error( 'Flag --[no-]enable-cloud-endpoints is not allowed if ' 'property container/ new_scopes_behavior is set to true.') flags.WarnForUnspecifiedAutorepair(args) cluster_ipv4_cidr = args.cluster_ipv4_cidr enable_master_authorized_networks = args.enable_master_authorized_networks return api_adapter.CreateClusterOptions( accelerators=args.accelerator, additional_zones=args.additional_zones, addons=args.addons, cluster_ipv4_cidr=cluster_ipv4_cidr, cluster_secondary_range_name=args.cluster_secondary_range_name, cluster_version=args.cluster_version, node_version=args.node_version, create_subnetwork=args.create_subnetwork, disk_type=args.disk_type, enable_autorepair=args.enable_autorepair, enable_autoscaling=args.enable_autoscaling, enable_autoupgrade=args.enable_autoupgrade, enable_cloud_endpoints=args.enable_cloud_endpoints, enable_cloud_logging=args.enable_cloud_logging, enable_cloud_monitoring=args.enable_cloud_monitoring, enable_ip_alias=args.enable_ip_alias, enable_kubernetes_alpha=args.enable_kubernetes_alpha, enable_legacy_authorization=args.enable_legacy_authorization, enable_master_authorized_networks=enable_master_authorized_networks, enable_network_policy=args.enable_network_policy, image_type=args.image_type, image=args.image, image_project=args.image_project, image_family=args.image_family, issue_client_certificate=args.issue_client_certificate, labels=args.labels, local_ssd_count=args.local_ssd_count, maintenance_window=args.maintenance_window, master_authorized_networks=args.master_authorized_networks, max_nodes=args.max_nodes, max_nodes_per_pool=args.max_nodes_per_pool, min_cpu_platform=args.min_cpu_platform, min_nodes=args.min_nodes, network=args.network, node_disk_size_gb=args.disk_size, node_labels=args.node_labels, node_locations=args.node_locations, node_machine_type=args.machine_type, node_taints=args.node_taints, num_nodes=args.num_nodes, password=args.password, preemptible=args.preemptible, scopes=args.scopes, service_account=args.service_account, services_ipv4_cidr=args.services_ipv4_cidr, services_secondary_range_name=args.services_secondary_range_name, subnetwork=args.subnetwork, tags=args.tags, user=args.username)
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Raises: util.Error: if the cluster is unreachable or not running. """ util.CheckKubectlInstalled() adapter = self.context['api_adapter'] location_get = self.context['location_get'] location = location_get(args) cluster_ref = adapter.ParseCluster(args.name, location) log.status.Print('Fetching cluster endpoint and auth data.') # Call DescribeCluster to get auth info and cache for next time cluster = adapter.GetCluster(cluster_ref) auth = cluster.masterAuth # TODO(b/70856999) Make this consistent with the checks in # api_lib/container/kubeconfig.py. missing_creds = not (auth and auth.clientCertificate and auth.clientKey) if missing_creds and not util.ClusterConfig.UseGCPAuthProvider(): raise util.Error( 'get-credentials requires edit permission on {0}'.format( cluster_ref.projectId)) if not adapter.IsRunning(cluster): log.warning(NOT_RUNNING_MSG.format(cluster_ref.clusterId)) util.ClusterConfig.Persist(cluster, cluster_ref.projectId, args.internal_ip)
def GetUseV1APIProperty(): """Returns whether v1 API should be used.""" new_set = properties.VALUES.container.use_v1_api.IsExplicitlySet() if new_set: new_val = properties.VALUES.container.use_v1_api.GetBool() old_set = properties.VALUES.container.use_v1_api_client.IsExplicitlySet() if old_set: old_val = properties.VALUES.container.use_v1_api_client.GetBool() # use_v1_api is set but use_v1_api_client is not set if new_set and not old_set: return new_val # use_v1_api is not set but use_v1_api_client is set elif not new_set and old_set: return old_val # both use_v1_api and use_v1_api_client are not set elif not new_set and not old_set: # default behavior is using v1 api return True # both use_v1_api and use_v1_api_client are set else: # if the values of use_v1_api and use_v1_api match, return either one if new_val == old_val: return new_val else: raise util.Error( constants. CANNOT_SET_BOTH_USE_V1_API_PROPERTIES_WITH_DIFF_VALUES)
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Raises: util.Error: if the cluster is unreachable or not running. """ util.CheckKubectlInstalled() adapter = self.context['api_adapter'] cluster_ref = adapter.ParseCluster(args.name) log.status.Print('Fetching cluster endpoint and auth data.') # Call DescribeCluster to get auth info and cache for next time cluster = adapter.GetCluster(cluster_ref) auth = cluster.masterAuth has_creds = (auth and ((auth.clientCertificate and auth.clientKey) or (auth.username and auth.password))) if not has_creds and not util.ClusterConfig.UseGCPAuthProvider( cluster): raise util.Error( 'get-credentials requires edit permission on {0}'.format( cluster_ref.projectId)) if not adapter.IsRunning(cluster): log.error( 'cluster %s is not running. The kubernetes API will probably be ' 'unreachable.' % cluster_ref.clusterId) util.ClusterConfig.Persist(cluster, cluster_ref.projectId)
def ParseCreateNodePoolOptionsBase(args): if (args.IsSpecified('enable_cloud_endpoints') and properties.VALUES.container.new_scopes_behavior.GetBool()): raise util.Error( 'Flag --[no-]enable-cloud-endpoints is not allowed if ' 'property container/ new_scopes_behavior is set to true.') flags.WarnForUnspecifiedAutorepair(args) return api_adapter.CreateNodePoolOptions( accelerators=args.accelerator, machine_type=args.machine_type, disk_size_gb=args.disk_size, scopes=args.scopes, node_version=args.node_version, enable_cloud_endpoints=args.enable_cloud_endpoints, num_nodes=args.num_nodes, local_ssd_count=args.local_ssd_count, tags=args.tags, node_labels=args.node_labels, node_taints=args.node_taints, enable_autoscaling=args.enable_autoscaling, max_nodes=args.max_nodes, min_cpu_platform=args.min_cpu_platform, min_nodes=args.min_nodes, image_type=args.image_type, image=args.image, image_project=args.image_project, image_family=args.image_family, preemptible=args.preemptible, enable_autorepair=args.enable_autorepair, enable_autoupgrade=args.enable_autoupgrade, service_account=args.service_account, disk_type=args.disk_type)
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Raises: util.Error: if the cluster is unreachable or not running. """ util.CheckKubectlInstalled() adapter = self.context['api_adapter'] location_get = self.context['location_get'] location = location_get(args) cluster_ref = adapter.ParseCluster(args.name, location) if getattr(args, 'region', None): message = messages.NonGAFeatureUsingV1APIWarning( self._release_track) if message: console_io.PromptContinue(message=message, cancel_on_no=True) log.status.Print('Fetching cluster endpoint and auth data.') # Call DescribeCluster to get auth info and cache for next time cluster = adapter.GetCluster(cluster_ref) auth = cluster.masterAuth has_creds = (auth and ((auth.clientCertificate and auth.clientKey) or (auth.username and auth.password))) if not has_creds and not util.ClusterConfig.UseGCPAuthProvider( cluster): raise util.Error( 'get-credentials requires edit permission on {0}'.format( cluster_ref.projectId)) if not adapter.IsRunning(cluster): log.warn(NOT_RUNNING_MSG.format(cluster_ref.clusterId)) util.ClusterConfig.Persist(cluster, cluster_ref.projectId)
def GetKubeconfigAndContext(args): """Get kubeconfig and context of the cluster from arguments. Args: args: command line arguments Returns: the kubeconfig and context name Raises: calliope_exceptions.MinimumArgumentException: if $KUBECONFIG is not set and --kubeconfig is not provided. c_util.Error: if the context provided in args (or the current context in the kubeconfig file if a context is not provided) does not exist. """ kubeconfig = args.kubeconfig_file or os.environ.get('KUBECONFIG') if not kubeconfig: raise calliope_exceptions.MinimumArgumentException([ '--from-kubeconfig' ], 'Please specify kubeconfig or set $KUBECONFIG environment variable') kc = kconfig.Kubeconfig.LoadFromFile(kubeconfig) context_name = args.context if not context_name: context_name = kc.current_context if not kc.contexts[context_name]: raise c_util.Error( 'context {0} does not exist in kubeconfig {1}'.format( context_name, kubeconfig)) return kubeconfig, context_name
def WaitForComputeOperations(self, project, zone, operation_ids, message, timeout_s=1200, poll_period_s=5): """Poll Compute Operations until their status is done or timeout reached. Args: project: project on which the operation is performed zone: zone on which the operation is performed operation_ids: list/set of ids of the compute operations to wait for message: str, message to display to user while polling. timeout_s: number, seconds to poll with retries before timing out. poll_period_s: number, delay in seconds between requests. Returns: Operations: list of the last successful operations.getrequest for each op. Raises: Error: if the operation times out or finishes with an error. """ operation_ids = deque(operation_ids) operations = {} errors = [] with console_io.ProgressTracker(message, autotick=True): start_time = time.clock() ops_to_retry = [] while timeout_s > (time.clock() - start_time) and operation_ids: op_id = operation_ids.popleft() try: operation = self.GetComputeOperation(project, zone, op_id) operations[op_id] = operation if not self.IsComputeOperationFinished(operation): # Operation is still in progress. ops_to_retry.append(op_id) continue log.debug('Operation %s succeeded after %.3f seconds', operation, (time.clock() - start_time)) error = self.GetOperationError(operation) if error: # Operation Failed! msg = 'Operation [{0}] finished with error: {1}'.format(op_id, error) log.debug(msg) errors.append(msg) except apitools_exceptions.HttpError as error: log.debug('GetComputeOperation failed: %s', error) # Keep trying until we timeout in case error is transient. # TODO(user): add additional backoff if server is returning 500s if not operation_ids and ops_to_retry: operation_ids = deque(ops_to_retry) ops_to_retry = [] time.sleep(poll_period_s) operation_ids.extend(ops_to_retry) for op_id in operation_ids: errors.append('Operation [{0}] is still running'.format(op_id)) if errors: raise util.Error(linesep.join(errors)) return operations.values()
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Cluster message for the successfully created cluster. Raises: util.Error, if creation failed. """ util.CheckKubectlInstalled() if not args.password: args.password = ''.join( random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(16)) adapter = self.context['api_adapter'] if not args.scopes: args.scopes = [] cluster_ref = adapter.ParseCluster(args.name) options = self.ParseCreateOptions(args) try: operation_ref = adapter.CreateCluster(cluster_ref, options) if not args.wait: return adapter.GetCluster(cluster_ref) adapter.WaitForOperation(operation_ref, 'Creating cluster {0}'.format( cluster_ref.clusterId), timeout_s=args.timeout) cluster = adapter.GetCluster(cluster_ref) except apitools_base.HttpError as error: raise exceptions.HttpException(util.GetError(error)) log.CreatedResource(cluster_ref) # Persist cluster config current_context = kconfig.Kubeconfig.Default().current_context c_config = util.ClusterConfig.Persist(cluster, cluster_ref.projectId, self.cli) if not c_config.has_certs: # Purge config so we retry the cert fetch on next kubectl command util.ClusterConfig.Purge(cluster.name, cluster.zone, cluster_ref.projectId) # reset current context if current_context: kubeconfig = kconfig.Kubeconfig.Default() kubeconfig.SetCurrentContext(current_context) kubeconfig.SaveToFile() raise util.Error( NO_CERTS_ERROR_FMT.format( command=' '.join(args.command_path[:-1] + ['get-credentials', cluster.name]))) return cluster
def _GetGKEKubeconfig(api_adapter, project, location_id, cluster_id, temp_kubeconfig_dir, internal_ip, cross_connect_subnetwork, private_endpoint_fqdn): """The kubeconfig of GKE Cluster is fetched using the GKE APIs. The 'KUBECONFIG' value in `os.environ` will be temporarily updated with the temporary kubeconfig's path if the kubeconfig arg is not None. Consequently, subprocesses started with googlecloudsdk.core.execution_utils.Exec will see the temporary KUBECONFIG environment variable. Using GKE APIs the GKE cluster is validated, and the ClusterConfig object, is persisted in the temporarily updated 'KUBECONFIG'. Args: api_adapter: the GKE api adapter used for running kubernetes commands project: string, the project id of the cluster for which kube config is to be fetched location_id: string, the id of the location to which the cluster belongs cluster_id: string, the id of the cluster temp_kubeconfig_dir: TemporaryDirectory object internal_ip: whether to persist the internal IP of the endpoint. cross_connect_subnetwork: full path of the cross connect subnet whose endpoint to persist (optional) private_endpoint_fqdn: whether to persist the private fqdn. Raises: Error: If unable to get credentials for kubernetes cluster. Returns: the path to the kubeconfig file """ kubeconfig = os.path.join(temp_kubeconfig_dir.path, 'kubeconfig') old_kubeconfig = encoding.GetEncodedValue(os.environ, 'KUBECONFIG') try: encoding.SetEncodedValue(os.environ, 'KUBECONFIG', kubeconfig) if api_adapter is None: api_adapter = gke_api_adapter.NewAPIAdapter('v1') cluster_ref = api_adapter.ParseCluster(cluster_id, location_id, project) cluster = api_adapter.GetCluster(cluster_ref) auth = cluster.masterAuth valid_creds = auth and auth.clientCertificate and auth.clientKey # c_util.ClusterConfig.UseGCPAuthProvider() checks for # container/use_client_certificate setting if not valid_creds and not c_util.ClusterConfig.UseGCPAuthProvider(): raise c_util.Error( 'Unable to get cluster credentials. User must have edit ' 'permission on {}'.format(cluster_ref.projectId)) c_util.ClusterConfig.Persist(cluster, cluster_ref.projectId, internal_ip, cross_connect_subnetwork, private_endpoint_fqdn) finally: if old_kubeconfig: encoding.SetEncodedValue(os.environ, 'KUBECONFIG', old_kubeconfig) else: del os.environ['KUBECONFIG'] return kubeconfig
def CreateCluster(self, cluster_ref, options): node_config = self.messages.NodeConfig() if options.node_machine_type: node_config.machineType = options.node_machine_type if options.node_disk_size_gb: node_config.diskSizeGb = options.node_disk_size_gb if options.node_source_image: raise util.Error('cannot specify node source image in container v1 api') scope_uris = ExpandScopeURIs(options.scopes) if options.enable_cloud_endpoints: scope_uris += _ENDPOINTS_SCOPES node_config.oauthScopes = sorted(set(scope_uris + _REQUIRED_SCOPES)) if options.local_ssd_count: node_config.localSsdCount = options.local_ssd_count if options.tags: node_config.tags = options.tags else: node_config.tags = [] if options.image_family: node_config.imageFamily = options.image_family cluster = self.messages.Cluster( name=cluster_ref.clusterId, initialNodeCount=options.num_nodes, nodeConfig=node_config, masterAuth=self.messages.MasterAuth(username=options.user, password=options.password)) if options.additional_zones: cluster.locations = options.additional_zones + [cluster_ref.zone] if options.cluster_version: cluster.initialClusterVersion = options.cluster_version if options.network: cluster.network = options.network if options.cluster_ipv4_cidr: cluster.clusterIpv4Cidr = options.cluster_ipv4_cidr if not options.enable_cloud_logging: cluster.loggingService = 'none' if not options.enable_cloud_monitoring: cluster.monitoringService = 'none' if options.subnetwork: cluster.subnetwork = options.subnetwork if options.disable_addons: addons = self._AddonsConfig( disable_ingress=INGRESS in options.disable_addons or None, disable_hpa=HPA in options.disable_addons or None) cluster.addonsConfig = addons create_cluster_req = self.messages.CreateClusterRequest(cluster=cluster) req = self.messages.ContainerProjectsZonesClustersCreateRequest( createClusterRequest=create_cluster_req, projectId=cluster_ref.projectId, zone=cluster_ref.zone) operation = self.client.projects_zones_clusters.Create(req) return self.ParseOperation(operation.name)
def UpdateCluster(self, cluster_ref, options): if not options.version: options.version = '-' if options.update_nodes: update = self.messages.ClusterUpdate( desiredNodeVersion=options.version, desiredNodePoolId=options.node_pool, desiredImageType=options.image_type) elif options.update_master: update = self.messages.ClusterUpdate( desiredMasterVersion=options.version) elif options.monitoring_service: update = self.messages.ClusterUpdate( desiredMonitoringService=options.monitoring_service) elif options.disable_addons: addons = self._AddonsConfig( disable_ingress=options.disable_addons.get(INGRESS), disable_hpa=options.disable_addons.get(HPA)) update = self.messages.ClusterUpdate(desiredAddonsConfig=addons) elif options.enable_autoscaling is not None: # For update, we can either enable or disable. autoscaling = self.messages.NodePoolAutoscaling( enabled=options.enable_autoscaling) if options.enable_autoscaling: autoscaling.minNodeCount = options.min_nodes autoscaling.maxNodeCount = options.max_nodes update = self.messages.ClusterUpdate( desiredNodePoolId=options.node_pool, desiredNodePoolAutoscaling=autoscaling) elif options.locations: update = self.messages.ClusterUpdate( desiredLocations=options.locations) elif options.enable_master_authorized_networks is not None: # For update, we can either enable or disable. authorized_networks = self.messages.MasterAuthorizedNetworks( enabled=options.enable_master_authorized_networks) if options.master_authorized_networks: for network in options.master_authorized_networks: authorized_networks.cidrs.append( self.messages.CIDR(network=network)) update = self.messages.ClusterUpdate( desiredMasterAuthorizedNetworks=authorized_networks) if (options.master_authorized_networks and not options.enable_master_authorized_networks): # Raise error if use --master-authorized-networks without # --enable-master-authorized-networks. raise util.Error(MISMATCH_AUTHORIZED_NETWORKS_ERROR_MSG) op = self.client.projects_zones_clusters.Update( self.messages.ContainerProjectsZonesClustersUpdateRequest( clusterId=cluster_ref.clusterId, zone=cluster_ref.zone, projectId=cluster_ref.projectId, updateClusterRequest=self.messages.UpdateClusterRequest( update=update))) return self.ParseOperation(op.name)
def ValidateBasicAuthFlags(args): """Validates flags associated with basic auth. Check that args don't conflict, but only if they're both specified; overwrites username if enable_basic_auth is specified; and checks that password is set only if username is non-empty. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Raises: util.Error, if flags conflict. """ if args.IsSpecified('enable_basic_auth'): if args.IsSpecified('username'): raise util.Error(constants.USERNAME_ENABLE_BASIC_AUTH_ERROR_MSG) if not args.enable_basic_auth: args.username = '' # Right now, enable_basic_auth is a no-op because username defaults to # admin. if not args.username and args.IsSpecified('password'): raise util.Error(constants.USERNAME_PASSWORD_ERROR_MSG)
def FindNodePool(self, cluster, pool_name=None): """Find the node pool with the given name in the cluster.""" msg = '' if pool_name: for np in cluster.nodePools: if np.name == pool_name: return np msg = NO_SUCH_NODE_POOL_ERROR_MSG.format(cluster=cluster.name, name=pool_name) + linesep elif len(cluster.nodePools) == 1: return cluster.nodePools[0] # Couldn't find a node pool with that name or a node pool was not specified. msg += NO_NODE_POOL_SELECTED_ERROR_MSG + linesep.join( [np.name for np in cluster.nodePools]) raise util.Error(msg)
def _GetGKEKubeconfig(location_id, cluster_id, temp_kubeconfig_dir): """The kubeconfig of GKE Cluster is fetched using the GKE APIs. The 'KUBECONFIG' value in `os.environ` will be temporarily updated with the temporary kubeconfig's path if the kubeconfig arg is not None. Consequently, subprocesses started with googlecloudsdk.core.execution_utils.Exec will see the temporary KUBECONFIG environment variable. Using GKE APIs the GKE cluster is validated, and the ClusterConfig object, is persisted in the temporarily updated 'KUBECONFIG'. Args: location_id: string, the id of the location to which the cluster belongs cluster_id: string, the id of the cluster temp_kubeconfig_dir: TemporaryDirectory object Raises: Error: If unable to get credentials for kubernetes cluster. Returns: the path to the kubeconfig file """ kubeconfig = os.path.join(temp_kubeconfig_dir.path, 'kubeconfig') old_kubeconfig = encoding.GetEncodedValue(os.environ, 'KUBECONFIG') try: encoding.SetEncodedValue(os.environ, 'KUBECONFIG', kubeconfig) gke_api = gke_api_adapter.NewAPIAdapter('v1') cluster_ref = gke_api.ParseCluster(cluster_id, location_id) cluster = gke_api.GetCluster(cluster_ref) auth = cluster.masterAuth valid_creds = auth and auth.clientCertificate and auth.clientKey # c_util.ClusterConfig.UseGCPAuthProvider() checks for # container/use_client_certificate setting if not valid_creds and not c_util.ClusterConfig.UseGCPAuthProvider(): raise c_util.Error( 'Unable to get cluster credentials. User must have edit ' 'permission on {}'.format(cluster_ref.projectId)) c_util.ClusterConfig.Persist(cluster, cluster_ref.projectId) finally: if old_kubeconfig: encoding.SetEncodedValue(os.environ, 'KUBECONFIG', old_kubeconfig) else: del os.environ['KUBECONFIG'] return kubeconfig
def ParseCreateNodePoolOptionsBase(args): """Parses the flags provided with the node pool creation command.""" if (args.IsSpecified('enable_cloud_endpoints') and properties.VALUES.container.new_scopes_behavior.GetBool()): raise util.Error( 'Flag --[no-]enable-cloud-endpoints is not allowed if ' 'property container/ new_scopes_behavior is set to true.') if args.IsSpecified('enable_autorepair'): enable_autorepair = args.enable_autorepair else: # Node pools using COS support auto repairs, enable it for them by default. # Other node pools using (Ubuntu, custom images) don't support node auto # repairs, attempting to enable autorepair for them will result in API call # failing so don't do it. enable_autorepair = ((args.image_type or '').lower() in ['', 'cos']) flags.WarnForNodeModification(args, enable_autorepair) metadata = metadata_utils.ConstructMetadataDict(args.metadata, args.metadata_from_file) return api_adapter.CreateNodePoolOptions( accelerators=args.accelerator, machine_type=args.machine_type, disk_size_gb=utils.BytesToGb(args.disk_size), scopes=args.scopes, node_version=args.node_version, enable_cloud_endpoints=args.enable_cloud_endpoints, num_nodes=args.num_nodes, local_ssd_count=args.local_ssd_count, tags=args.tags, node_labels=args.node_labels, node_taints=args.node_taints, enable_autoscaling=args.enable_autoscaling, max_nodes=args.max_nodes, min_cpu_platform=args.min_cpu_platform, min_nodes=args.min_nodes, image_type=args.image_type, image=args.image, image_project=args.image_project, image_family=args.image_family, preemptible=args.preemptible, enable_autorepair=enable_autorepair, enable_autoupgrade=args.enable_autoupgrade, service_account=args.service_account, disk_type=args.disk_type, metadata=metadata, max_pods_per_node=args.max_pods_per_node)
def ValidateBasicAuthFlags(args): """Validates flags associated with basic auth. Overwrites username if enable_basic_auth is specified; checks that password is set if username is non-empty. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Raises: util.Error, if username is non-empty and password is not set. """ if args.IsSpecified('enable_basic_auth'): if not args.enable_basic_auth: args.username = '' # Right now, enable_basic_auth is a no-op because username defaults to # admin. if not args.username and args.IsSpecified('password'): raise util.Error(constants.USERNAME_PASSWORD_ERROR_MSG)
def CreateCluster(self, cluster_ref, options): node_config = self.messages.NodeConfig() if options.node_machine_type: node_config.machineType = options.node_machine_type # TODO(user): support disk size via flag if options.node_disk_size_gb: node_config.diskSizeGb = options.node_disk_size_gb if options.node_source_image: raise util.Error('cannot specify node source image in container v1 api') scope_uris = ExpandScopeURIs(options.scopes) if options.enable_cloud_endpoints: scope_uris += _ENDPOINTS_SCOPES node_config.oauthScopes = sorted(set(scope_uris + _REQUIRED_SCOPES)) cluster = self.messages.Cluster( name=cluster_ref.clusterId, initialNodeCount=options.num_nodes, nodeConfig=node_config, masterAuth=self.messages.MasterAuth(username=options.user, password=options.password)) if options.cluster_version: cluster.initialClusterVersion = options.cluster_version if options.network: cluster.network = options.network if options.cluster_ipv4_cidr: cluster.clusterIpv4Cidr = options.cluster_ipv4_cidr if not options.enable_cloud_logging: cluster.loggingService = 'none' if not options.enable_cloud_monitoring: cluster.monitoringService = 'none' if options.subnetwork: cluster.subnetwork = options.subnetwork create_cluster_req = self.messages.CreateClusterRequest(cluster=cluster) req = self.messages.ContainerProjectsZonesClustersCreateRequest( createClusterRequest=create_cluster_req, projectId=cluster_ref.projectId, zone=cluster_ref.zone) operation = self.client.projects_zones_clusters.Create(req) return self.ParseOperation(operation.name)
def _BaseRun(args, context): """Base operations for `get-credentials` run command.""" util.CheckKubectlInstalled() adapter = context['api_adapter'] location_get = context['location_get'] location = location_get(args) cluster_ref = adapter.ParseCluster(args.name, location) log.status.Print('Fetching cluster endpoint and auth data.') # Call DescribeCluster to get auth info and cache for next time cluster = adapter.GetCluster(cluster_ref) auth = cluster.masterAuth # TODO(b/70856999) Make this consistent with the checks in # api_lib/container/kubeconfig.py. missing_creds = not (auth and auth.clientCertificate and auth.clientKey) if missing_creds and not util.ClusterConfig.UseGCPAuthProvider(): raise util.Error('get-credentials requires edit permission on {0}'.format( cluster_ref.projectId)) if not adapter.IsRunning(cluster): log.warning(NOT_RUNNING_MSG.format(cluster_ref.clusterId)) return cluster, cluster_ref
def ValidateBasicAuthFlags(args): """Validates flags associated with basic auth. Overwrites username if enable_basic_auth is specified; checks that password is set if username is non-empty. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Raises: util.Error, if username is non-empty and password is not set. """ if args.IsSpecified('enable_basic_auth'): if not args.enable_basic_auth: args.username = '' # `enable_basic_auth == true` is a no-op defaults are resoved server-side # based on the version of the cluster. For versions before 1.12, this is # 'admin', otherwise '' (disabled). if not args.username and args.IsSpecified('password'): raise util.Error(constants.USERNAME_PASSWORD_ERROR_MSG)
def MungeBasicAuthFlags(args): """Munges flags associated with basic auth. If --enable-basic-auth is specified, converts it --username value, and checks that --password is only specified if it makes sense. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Raises: util.Error, if flags conflict. """ if args.IsSpecified('enable_basic_auth'): if not args.enable_basic_auth: args.username = '' else: # Even though this is the default for `clusters create`, we still need to # set it for `clusters update`. args.username = '******' if not args.username and args.IsSpecified('password'): raise util.Error(constants.USERNAME_PASSWORD_ERROR_MSG)
def ParseCreateNodePoolOptionsBase(args): """Parses the flags provided with the node pool creation command.""" if (args.IsSpecified('enable_cloud_endpoints') and properties.VALUES.container.new_scopes_behavior.GetBool()): raise util.Error( 'Flag --[no-]enable-cloud-endpoints is not allowed if ' 'property container/ new_scopes_behavior is set to true.') enable_autorepair = cmd_util.GetAutoRepair(args) flags.WarnForNodeModification(args, enable_autorepair) metadata = metadata_utils.ConstructMetadataDict(args.metadata, args.metadata_from_file) return api_adapter.CreateNodePoolOptions( accelerators=args.accelerator, machine_type=args.machine_type, disk_size_gb=utils.BytesToGb(args.disk_size), scopes=args.scopes, node_version=args.node_version, enable_cloud_endpoints=args.enable_cloud_endpoints, num_nodes=args.num_nodes, local_ssd_count=args.local_ssd_count, tags=args.tags, node_labels=args.node_labels, node_taints=args.node_taints, enable_autoscaling=args.enable_autoscaling, max_nodes=args.max_nodes, min_cpu_platform=args.min_cpu_platform, min_nodes=args.min_nodes, image_type=args.image_type, image=args.image, image_project=args.image_project, image_family=args.image_family, preemptible=args.preemptible, enable_autorepair=enable_autorepair, enable_autoupgrade=cmd_util.GetAutoUpgrade(args), service_account=args.service_account, disk_type=args.disk_type, metadata=metadata, max_pods_per_node=args.max_pods_per_node)
def DeployConnectAgent(response, args): """Python hook to deploy connect agent. Args: response: response to be returned. args: arguments of the command. Returns: modified response """ if args.agent_deployed: return response # project = properties.VALUES.core.project.GetOrFail() # Exit if kubectl is not installed. if not c_util.CheckKubectlInstalled(): log.warning( 'kubectl not installed, could not install the connect agent. ') return image = args.docker_image if not image: # Get the SHA for the default image. try: digest = ImageDigestForContainerImage(DEFAULT_CONNECT_AGENT_IMAGE, DEFAULT_CONNECT_AGENT_TAG) image = '{}@{}'.format(DEFAULT_CONNECT_AGENT_IMAGE, digest) except Exception as exp: raise c_util.Error( 'could not determine image digest for {}:{}: {}'.format( DEFAULT_CONNECT_AGENT_IMAGE, DEFAULT_CONNECT_AGENT_TAG, exp)) log.status.Print('The agent image that would be used is {}'.format(image)) # TODO(b/123907152): implement the manifest after Docker image is ready. return response
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Some value that we want to have printed later. """ adapter = self.context['api_adapter'] location_get = self.context['location_get'] location = location_get(args) if getattr(args, 'region', None): message = messages.NonGAFeatureUsingV1APIWarning( self._release_track) if message: console_io.PromptContinue(message=message, cancel_on_no=True) cluster_refs = [] for name in args.names: cluster_refs.append(adapter.ParseCluster(name, location)) console_io.PromptContinue(message=util.ConstructList( 'The following clusters will be deleted.', [ '[{name}] in [{zone}]'.format(name=ref.clusterId, zone=adapter.Zone(ref)) for ref in cluster_refs ]), throw_if_unattended=True, cancel_on_no=True) operations = [] errors = [] # Issue all deletes first for cluster_ref in cluster_refs: try: # Make sure it exists (will raise appropriate error if not) adapter.GetCluster(cluster_ref) op_ref = adapter.DeleteCluster(cluster_ref) operations.append((op_ref, cluster_ref)) except apitools_exceptions.HttpError as error: errors.append( str(exceptions.HttpException(error, util.HTTP_ERROR_FORMAT))) except util.Error as error: errors.append(error) if not args. async: # Poll each operation for completion for operation_ref, cluster_ref in operations: try: adapter.WaitForOperation(operation_ref, 'Deleting cluster {0}'.format( cluster_ref.clusterId), timeout_s=args.timeout) # Purge cached config files util.ClusterConfig.Purge(cluster_ref.clusterId, adapter.Zone(cluster_ref), cluster_ref.projectId) if properties.VALUES.container.cluster.Get( ) == cluster_ref.clusterId: properties.PersistProperty( properties.VALUES.container.cluster, None) log.DeletedResource(cluster_ref) except apitools_exceptions.HttpError as error: errors.append( exceptions.HttpException(error, util.HTTP_ERROR_FORMAT)) except util.Error as error: errors.append(error) if errors: raise util.Error( util.ConstructList('Some requests did not succeed:', errors))
def CreateCluster(self, cluster_ref, options): node_config = self.messages.NodeConfig() if options.node_machine_type: node_config.machineType = options.node_machine_type if options.node_disk_size_gb: node_config.diskSizeGb = options.node_disk_size_gb if options.node_source_image: raise util.Error( 'cannot specify node source image in container v1 api') scope_uris = ExpandScopeURIs(options.scopes) if options.enable_cloud_endpoints: scope_uris += _ENDPOINTS_SCOPES node_config.oauthScopes = sorted(set(scope_uris + _REQUIRED_SCOPES)) if options.local_ssd_count: node_config.localSsdCount = options.local_ssd_count if options.tags: node_config.tags = options.tags else: node_config.tags = [] if options.image_type: node_config.imageType = options.image_type _AddNodeLabelsToNodeConfig(node_config, options) max_nodes_per_pool = options.max_nodes_per_pool or MAX_NODES_PER_POOL pools = (options.num_nodes + max_nodes_per_pool - 1) / max_nodes_per_pool if pools == 1: pool_names = ['default-pool' ] # pool consistency with server default else: # default-pool-0, -1, ... pool_names = [ 'default-pool-{0}'.format(i) for i in range(0, pools) ] pools = [] per_pool = (options.num_nodes + len(pool_names) - 1) / len(pool_names) to_add = options.num_nodes for name in pool_names: nodes = per_pool if (to_add > per_pool) else to_add autoscaling = None if options.enable_autoscaling: autoscaling = self.messages.NodePoolAutoscaling( enabled=options.enable_autoscaling, minNodeCount=options.min_nodes, maxNodeCount=options.max_nodes) pools.append( self.messages.NodePool(name=name, initialNodeCount=nodes, config=node_config, autoscaling=autoscaling)) to_add -= nodes cluster = self.messages.Cluster(name=cluster_ref.clusterId, nodePools=pools, masterAuth=self.messages.MasterAuth( username=options.user, password=options.password)) if options.additional_zones: cluster.locations = sorted([cluster_ref.zone] + options.additional_zones) if options.cluster_version: cluster.initialClusterVersion = options.cluster_version if options.network: cluster.network = options.network if options.cluster_ipv4_cidr: cluster.clusterIpv4Cidr = options.cluster_ipv4_cidr if not options.enable_cloud_logging: cluster.loggingService = 'none' if not options.enable_cloud_monitoring: cluster.monitoringService = 'none' if options.subnetwork: cluster.subnetwork = options.subnetwork if options.disable_addons: addons = self._AddonsConfig( disable_ingress=INGRESS in options.disable_addons or None, disable_hpa=HPA in options.disable_addons or None) cluster.addonsConfig = addons if options.enable_kubernetes_alpha: cluster.enableKubernetesAlpha = options.enable_kubernetes_alpha create_cluster_req = self.messages.CreateClusterRequest( cluster=cluster) req = self.messages.ContainerProjectsZonesClustersCreateRequest( createClusterRequest=create_cluster_req, projectId=cluster_ref.projectId, zone=cluster_ref.zone) operation = self.client.projects_zones_clusters.Create(req) return self.ParseOperation(operation.name)