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"""Useful commands for interacting with the Cloud Resource Management API.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import unicode_literals 20 21from apitools.base.py import list_pager 22 23from googlecloudsdk.api_lib.cloudresourcemanager import projects_util 24from googlecloudsdk.api_lib.resource_manager import folders 25from googlecloudsdk.command_lib.iam import iam_util 26 27DEFAULT_API_VERSION = projects_util.DEFAULT_API_VERSION 28 29 30def List(limit=None, 31 filter=None, # pylint: disable=redefined-builtin 32 batch_size=500, 33 api_version=DEFAULT_API_VERSION): 34 """Make API calls to List active projects. 35 36 Args: 37 limit: The number of projects to limit the results to. This limit is passed 38 to the server and the server does the limiting. 39 filter: The client side filter expression. 40 batch_size: the number of projects to get with each request. 41 api_version: the version of the api 42 43 Returns: 44 Generator that yields projects 45 """ 46 client = projects_util.GetClient(api_version) 47 messages = projects_util.GetMessages(api_version) 48 return list_pager.YieldFromList( 49 client.projects, 50 messages.CloudresourcemanagerProjectsListRequest( 51 filter=_AddActiveProjectFilterIfNotSpecified(filter)), 52 batch_size=batch_size, 53 limit=limit, 54 field='projects', 55 batch_size_attribute='pageSize') 56 57 58def _AddActiveProjectFilterIfNotSpecified(filter_expr): 59 if not filter_expr: 60 return 'lifecycleState:ACTIVE' 61 if 'lifecycleState' in filter_expr: 62 return filter_expr 63 return 'lifecycleState:ACTIVE AND ({})'.format(filter_expr) 64 65 66def Get(project_ref, api_version=DEFAULT_API_VERSION, 67 disable_api_enablement_check=False): 68 """Get project information.""" 69 client = projects_util.GetClient(api_version) 70 # disable_api_enablement_check added to handle special case of 71 # setting config value core/project, see b/133841504/ 72 if disable_api_enablement_check: 73 client.check_response_func = None 74 return client.projects.Get( 75 client.MESSAGES_MODULE.CloudresourcemanagerProjectsGetRequest( 76 projectId=project_ref.projectId)) 77 78 79def Create(project_ref, 80 display_name=None, 81 parent=None, 82 labels=None, 83 api_version=DEFAULT_API_VERSION): 84 """Create a new project. 85 86 Args: 87 project_ref: The identifier for the project 88 display_name: Optional display name for the project 89 parent: Optional for the project (ex. folders/123 or organizations/5231) 90 labels: Optional labels to apply to the project 91 api_version: the version of the api 92 93 Returns: 94 An Operation object which can be used to check on the progress of the 95 project creation. 96 """ 97 client = projects_util.GetClient(api_version) 98 messages = projects_util.GetMessages(api_version) 99 return client.projects.Create( 100 messages.Project( 101 projectId=project_ref.Name(), 102 name=display_name if display_name else project_ref.Name(), 103 parent=parent, 104 labels=labels)) 105 106 107def Delete(project_ref, api_version=DEFAULT_API_VERSION): 108 """Delete an existing project.""" 109 client = projects_util.GetClient(api_version) 110 messages = projects_util.GetMessages(api_version) 111 112 client.projects.Delete( 113 messages.CloudresourcemanagerProjectsDeleteRequest( 114 projectId=project_ref.Name())) 115 return projects_util.DeletedResource(project_ref.Name()) 116 117 118def Undelete(project_ref, api_version=DEFAULT_API_VERSION): 119 """Undelete a project that has been deleted.""" 120 client = projects_util.GetClient(api_version) 121 messages = projects_util.GetMessages(api_version) 122 123 client.projects.Undelete( 124 messages.CloudresourcemanagerProjectsUndeleteRequest( 125 projectId=project_ref.Name())) 126 return projects_util.DeletedResource(project_ref.Name()) 127 128 129def Update(project_ref, 130 name=None, 131 parent=None, 132 labels_diff=None, 133 api_version=DEFAULT_API_VERSION): 134 """Update project information.""" 135 client = projects_util.GetClient(api_version) 136 messages = projects_util.GetMessages(api_version) 137 138 project = client.projects.Get( 139 client.MESSAGES_MODULE.CloudresourcemanagerProjectsGetRequest( 140 projectId=project_ref.projectId)) 141 142 if name: 143 project.name = name 144 145 if parent: 146 project.parent = parent 147 148 if labels_diff: 149 labels_update = labels_diff.Apply(messages.Project.LabelsValue, 150 project.labels) 151 if labels_update.needs_update: 152 project.labels = labels_update.labels 153 154 return client.projects.Update(project) 155 156 157def GetIamPolicy(project_ref, api_version=DEFAULT_API_VERSION): 158 """Get IAM policy for a given project.""" 159 client = projects_util.GetClient(api_version) 160 messages = projects_util.GetMessages(api_version) 161 162 policy_request = messages.CloudresourcemanagerProjectsGetIamPolicyRequest( 163 getIamPolicyRequest=messages.GetIamPolicyRequest( 164 options=messages.GetPolicyOptions( 165 requestedPolicyVersion= 166 iam_util.MAX_LIBRARY_IAM_SUPPORTED_VERSION)), 167 resource=project_ref.Name(), 168 ) 169 return client.projects.GetIamPolicy(policy_request) 170 171 172def GetAncestry(project_id, api_version=DEFAULT_API_VERSION): 173 """Get ancestry for a given project.""" 174 client = projects_util.GetClient(api_version) 175 messages = projects_util.GetMessages(api_version) 176 177 ancestry_request = messages.CloudresourcemanagerProjectsGetAncestryRequest( 178 getAncestryRequest=messages.GetAncestryRequest(), 179 projectId=project_id, 180 ) 181 182 return client.projects.GetAncestry(ancestry_request) 183 184 185def SetIamPolicy(project_ref, 186 policy, 187 update_mask=None, 188 api_version=DEFAULT_API_VERSION): 189 """Set IAM policy, for a given project.""" 190 client = projects_util.GetClient(api_version) 191 messages = projects_util.GetMessages(api_version) 192 193 policy.version = iam_util.MAX_LIBRARY_IAM_SUPPORTED_VERSION 194 set_iam_policy_request = messages.SetIamPolicyRequest(policy=policy) 195 # Only include update_mask if provided, otherwise, leave the field unset. 196 if update_mask is not None: 197 set_iam_policy_request.updateMask = update_mask 198 199 policy_request = messages.CloudresourcemanagerProjectsSetIamPolicyRequest( 200 resource=project_ref.Name(), 201 setIamPolicyRequest=set_iam_policy_request) 202 return client.projects.SetIamPolicy(policy_request) 203 204 205def SetIamPolicyFromFile(project_ref, 206 policy_file, 207 api_version=DEFAULT_API_VERSION): 208 """Read projects IAM policy from a file, and set it.""" 209 messages = projects_util.GetMessages(api_version) 210 policy = iam_util.ParsePolicyFile(policy_file, messages.Policy) 211 update_mask = iam_util.ConstructUpdateMaskFromPolicy(policy_file) 212 213 # To preserve the existing set-iam-policy behavior of always overwriting 214 # bindings and etag, add bindings and etag to update_mask. 215 if 'bindings' not in update_mask: 216 update_mask += ',bindings' 217 if 'etag' not in update_mask: 218 update_mask += ',etag' 219 220 return SetIamPolicy(project_ref, policy, update_mask, api_version) 221 222 223def AddIamPolicyBinding(project_ref, 224 member, 225 role, 226 api_version=DEFAULT_API_VERSION): 227 return AddIamPolicyBindings(project_ref, [(member, role)], api_version) 228 229 230def AddIamPolicyBindings(project_ref, 231 member_roles, 232 api_version=DEFAULT_API_VERSION): 233 """Adds iam bindings to project_ref's iam policy. 234 235 Args: 236 project_ref: The project for the binding 237 member_roles: List of 2-tuples of the form [(member, role), ...]. 238 api_version: The version of the api 239 240 Returns: 241 The updated IAM Policy 242 """ 243 messages = projects_util.GetMessages(api_version) 244 245 policy = GetIamPolicy(project_ref, api_version) 246 for member, role in member_roles: 247 iam_util.AddBindingToIamPolicy(messages.Binding, policy, member, role) 248 return SetIamPolicy(project_ref, policy, api_version=api_version) 249 250 251def AddIamPolicyBindingWithCondition(project_ref, 252 member, 253 role, 254 condition, 255 api_version=DEFAULT_API_VERSION): 256 """Add iam binding with condition to project_ref's iam policy.""" 257 messages = projects_util.GetMessages(api_version) 258 259 policy = GetIamPolicy(project_ref, api_version=api_version) 260 iam_util.AddBindingToIamPolicyWithCondition(messages.Binding, messages.Expr, 261 policy, member, role, condition) 262 return SetIamPolicy(project_ref, policy, api_version=api_version) 263 264 265def RemoveIamPolicyBinding(project_ref, 266 member, 267 role, 268 api_version=DEFAULT_API_VERSION): 269 policy = GetIamPolicy(project_ref, api_version=api_version) 270 iam_util.RemoveBindingFromIamPolicy(policy, member, role) 271 return SetIamPolicy(project_ref, policy, api_version=api_version) 272 273 274def RemoveIamPolicyBindingWithCondition(project_ref, 275 member, 276 role, 277 condition, 278 all_conditions, 279 api_version=DEFAULT_API_VERSION): 280 """Remove iam binding with condition from project_ref's iam policy.""" 281 policy = GetIamPolicy(project_ref, api_version=api_version) 282 iam_util.RemoveBindingFromIamPolicyWithCondition(policy, member, role, 283 condition, all_conditions) 284 return SetIamPolicy(project_ref, policy, api_version=api_version) 285 286 287def TestIamPermissions(project_ref, 288 permissions, 289 api_version=DEFAULT_API_VERSION): 290 """Return a subset of the given permissions that a caller has on project_ref.""" 291 client = projects_util.GetClient(api_version) 292 messages = projects_util.GetMessages(api_version) 293 294 request = messages.CloudresourcemanagerProjectsTestIamPermissionsRequest( 295 resource=project_ref.Name(), 296 testIamPermissionsRequest=messages.TestIamPermissionsRequest( 297 permissions=permissions)) 298 return client.projects.TestIamPermissions(request) 299 300 301def ParentNameToResourceId(parent_name, api_version=DEFAULT_API_VERSION): 302 messages = projects_util.GetMessages(api_version) 303 if not parent_name: 304 return None 305 elif parent_name.startswith('folders/'): 306 return messages.ResourceId( 307 id=folders.FolderNameToId(parent_name), type='folder') 308 elif parent_name.startswith('organizations/'): 309 return messages.ResourceId( 310 id=parent_name[len('organizations/'):], type='organization') 311