1# -*- coding: utf-8 -*- # 2# Copyright 2017 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"""Command to set service account and scopes for an instance resource.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import unicode_literals 20 21from googlecloudsdk.api_lib.compute import base_classes 22from googlecloudsdk.api_lib.compute import constants 23from googlecloudsdk.calliope import base 24from googlecloudsdk.command_lib.compute import flags as compute_flags 25from googlecloudsdk.command_lib.compute import scope as compute_scope 26from googlecloudsdk.command_lib.compute.instances import exceptions 27from googlecloudsdk.command_lib.compute.instances import flags 28 29 30@base.ReleaseTracks(base.ReleaseTrack.GA) 31class SetServiceAccount(base.SilentCommand): 32 """Set service account and scopes for a Compute Engine instance.""" 33 34 detailed_help = { 35 'EXAMPLES': """ 36 To set a service account with `pubsub` and `trace` scopes, run: 37 38 $ {command} example-instance --scopes=pubsub,trace --zone=us-central1-b --service-account=example-account 39 """} 40 41 def __init__(self, *args, **kwargs): 42 super(self.__class__, self).__init__(*args, **kwargs) 43 self._instance = None 44 45 @staticmethod 46 def Args(parser): 47 flags.INSTANCE_ARG.AddArgument(parser) 48 flags.AddServiceAccountAndScopeArgs(parser, True) 49 50 def _get_instance(self, instance_ref, client): 51 """Return cached instance if there isn't one fetch referrenced one.""" 52 if not self._instance: 53 request = (client.apitools_client.instances, 'Get', 54 client.messages.ComputeInstancesGetRequest( 55 **instance_ref.AsDict())) 56 instance = client.MakeRequests(requests=[request]) 57 58 self._instance = instance[0] 59 60 return self._instance 61 62 def _original_email(self, instance_ref, client): 63 """Return email of service account instance is using.""" 64 instance = self._get_instance(instance_ref, client) 65 if instance is None: 66 return None 67 orignal_service_accounts = instance.serviceAccounts 68 if orignal_service_accounts: 69 return orignal_service_accounts[0].email 70 return None 71 72 def _original_scopes(self, instance_ref, client): 73 """Return scopes instance is using.""" 74 instance = self._get_instance(instance_ref, client) 75 if instance is None: 76 return [] 77 orignal_service_accounts = instance.serviceAccounts 78 result = [] 79 for accounts in orignal_service_accounts: 80 result += accounts.scopes 81 return result 82 83 def _email(self, args, instance_ref, client): 84 """Return email to set as service account for the instance.""" 85 if args.no_service_account: 86 return None 87 if args.service_account: 88 return args.service_account 89 return self._original_email(instance_ref, client) 90 91 def _unprocessed_scopes(self, args, instance_ref, client): 92 """Return scopes to set for the instance.""" 93 if args.no_scopes: 94 return [] 95 if args.scopes is not None: # Empty list goes here 96 return args.scopes 97 return self._original_scopes(instance_ref, client) 98 99 def _scopes(self, args, instance_ref, client): 100 """Get list of scopes to be assigned to the instance. 101 102 Args: 103 args: parsed command line arguments. 104 instance_ref: reference to the instance to which scopes will be assigned. 105 client: a compute_holder.client instance 106 107 Returns: 108 List of scope urls extracted from args, with scope aliases expanded. 109 """ 110 result = [] 111 for unprocessed_scope in self._unprocessed_scopes(args, 112 instance_ref, client): 113 scope = constants.SCOPES.get(unprocessed_scope, [unprocessed_scope]) 114 result.extend(scope) 115 return result 116 117 def Run(self, args): 118 compute_holder = base_classes.ComputeApiHolder(self.ReleaseTrack()) 119 client = compute_holder.client 120 121 flags.ValidateServiceAccountAndScopeArgs(args) 122 123 instance_ref = flags.INSTANCE_ARG.ResolveAsResource( 124 args, compute_holder.resources, 125 default_scope=compute_scope.ScopeEnum.ZONE, 126 scope_lister=compute_flags.GetDefaultScopeLister(client)) 127 128 email = self._email(args, instance_ref, client) 129 scopes = self._scopes(args, instance_ref, client) 130 131 if scopes and not email: 132 raise exceptions.ScopesWithoutServiceAccountException( 133 'Can not set scopes when there is no service acoount.') 134 135 request = client.messages.ComputeInstancesSetServiceAccountRequest( 136 instancesSetServiceAccountRequest=( 137 client.messages.InstancesSetServiceAccountRequest( 138 email=email, 139 scopes=scopes, 140 ) 141 ), 142 project=instance_ref.project, 143 zone=instance_ref.zone, 144 instance=instance_ref.Name() 145 ) 146 147 return client.MakeRequests([( 148 client.apitools_client.instances, 149 'SetServiceAccount', 150 request)]) 151