# Copyright 2015 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Utility for managing projects via the Cloud Resource Manager API."""
from google.cloud.exceptions import NotFound
[docs]class Project(object):
"""Projects are containers for your work on Google Cloud Platform.
.. note::
A :class:`Project` can also be created via
:meth:`Client.new_project() \
<google.cloud.resource_manager.client.Client.new_project>`
To manage labels on a :class:`Project`::
>>> from google.cloud import resource_manager
>>> client = resource_manager.Client()
>>> project = client.new_project('purple-spaceship-123')
>>> project.labels = {'color': 'purple'}
>>> project.labels['environment'] = 'production'
>>> project.update()
See
https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects
:type project_id: str
:param project_id: The globally unique ID of the project.
:type client: :class:`google.cloud.resource_manager.client.Client`
:param client: The Client used with this project.
:type name: str
:param name: The display name of the project.
:type labels: dict
:param labels: A list of labels associated with the project.
"""
def __init__(self, project_id, client, name=None, labels=None):
self._client = client
self.project_id = project_id
self.name = name
self.number = None
self.labels = labels or {}
self.status = None
self.parent = None
def __repr__(self):
return "<Project: %r (%r)>" % (self.name, self.project_id)
[docs] @classmethod
def from_api_repr(cls, resource, client):
"""Factory: construct a project given its API representation.
:type resource: dict
:param resource: project resource representation returned from the API
:type client: :class:`google.cloud.resource_manager.client.Client`
:param client: The Client used with this project.
:rtype: :class:`google.cloud.resource_manager.project.Project`
:returns: The project created.
"""
project = cls(project_id=resource["projectId"], client=client)
project.set_properties_from_api_repr(resource)
return project
[docs] def set_properties_from_api_repr(self, resource):
"""Update specific properties from its API representation."""
self.name = resource.get("name")
self.number = resource["projectNumber"]
self.labels = resource.get("labels", {})
self.status = resource["lifecycleState"]
if "parent" in resource:
self.parent = resource["parent"]
@property
def full_name(self):
"""Fully-qualified name (ie, ``'projects/purple-spaceship-123'``)."""
if not self.project_id:
raise ValueError("Missing project ID.")
return "projects/%s" % (self.project_id)
@property
def path(self):
"""URL for the project (ie, ``'/projects/purple-spaceship-123'``)."""
return "/%s" % (self.full_name)
def _require_client(self, client):
"""Check client or verify over-ride.
:type client: :class:`google.cloud.resource_manager.client.Client` or
``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current project.
:rtype: :class:`google.cloud.resource_manager.client.Client`
:returns: The client passed in or the currently bound client.
"""
if client is None:
client = self._client
return client
[docs] def create(self, client=None):
"""API call: create the project via a ``POST`` request.
See
https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/create
:type client: :class:`google.cloud.resource_manager.client.Client` or
:data:`NoneType <types.NoneType>`
:param client: the client to use. If not passed, falls back to
the client stored on the current project.
"""
client = self._require_client(client)
data = {"projectId": self.project_id, "name": self.name, "labels": self.labels}
resp = client._connection.api_request(
method="POST", path="/projects", data=data
)
self.set_properties_from_api_repr(resource=resp)
[docs] def reload(self, client=None):
"""API call: reload the project via a ``GET`` request.
This method will reload the newest metadata for the project. If you've
created a new :class:`Project` instance via
:meth:`Client.new_project() \
<google.cloud.resource_manager.client.Client.new_project>`,
this method will retrieve project metadata.
.. warning::
This will overwrite any local changes you've made and not saved
via :meth:`update`.
See
https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/get
:type client: :class:`google.cloud.resource_manager.client.Client` or
:data:`NoneType <types.NoneType>`
:param client: the client to use. If not passed, falls back to
the client stored on the current project.
"""
client = self._require_client(client)
# We assume the project exists. If it doesn't it will raise a NotFound
# exception.
resp = client._connection.api_request(method="GET", path=self.path)
self.set_properties_from_api_repr(resource=resp)
[docs] def exists(self, client=None):
"""API call: test the existence of a project via a ``GET`` request.
See
https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/get
:type client: :class:`google.cloud.resource_manager.client.Client` or
:data:`NoneType <types.NoneType>`
:param client: the client to use. If not passed, falls back to
the client stored on the current project.
:rtype: bool
:returns: Boolean indicating existence of the project.
"""
client = self._require_client(client)
try:
# Note that we have to request the entire resource as the API
# doesn't provide a way tocheck for existence only.
client._connection.api_request(method="GET", path=self.path)
except NotFound:
return False
else:
return True
[docs] def update(self, client=None):
"""API call: update the project via a ``PUT`` request.
See
https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/update
:type client: :class:`google.cloud.resource_manager.client.Client` or
:data:`NoneType <types.NoneType>`
:param client: the client to use. If not passed, falls back to
the client stored on the current project.
"""
client = self._require_client(client)
data = {"name": self.name, "labels": self.labels, "parent": self.parent}
resp = client._connection.api_request(method="PUT", path=self.path, data=data)
self.set_properties_from_api_repr(resp)
[docs] def delete(self, client=None, reload_data=False):
"""API call: delete the project via a ``DELETE`` request.
See
https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/delete
This actually changes the status (``lifecycleState``) from ``ACTIVE``
to ``DELETE_REQUESTED``.
Later (it's not specified when), the project will move into the
``DELETE_IN_PROGRESS`` state, which means the deleting has actually
begun.
:type client: :class:`google.cloud.resource_manager.client.Client` or
:data:`NoneType <types.NoneType>`
:param client: the client to use. If not passed, falls back to
the client stored on the current project.
:type reload_data: bool
:param reload_data: Whether to reload the project with the latest
state. If you want to get the updated status,
you'll want this set to :data:`True` as the DELETE
method doesn't send back the updated project.
Default: :data:`False`.
"""
client = self._require_client(client)
client._connection.api_request(method="DELETE", path=self.path)
# If the reload flag is set, reload the project.
if reload_data:
self.reload()
[docs] def undelete(self, client=None, reload_data=False):
"""API call: undelete the project via a ``POST`` request.
See
https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/undelete
This actually changes the project status (``lifecycleState``) from
``DELETE_REQUESTED`` to ``ACTIVE``.
If the project has already reached a status of ``DELETE_IN_PROGRESS``,
this request will fail and the project cannot be restored.
:type client: :class:`google.cloud.resource_manager.client.Client` or
:data:`NoneType <types.NoneType>`
:param client: the client to use. If not passed, falls back to
the client stored on the current project.
:type reload_data: bool
:param reload_data: Whether to reload the project with the latest
state. If you want to get the updated status,
you'll want this set to :data:`True` as the DELETE
method doesn't send back the updated project.
Default: :data:`False`.
"""
client = self._require_client(client)
client._connection.api_request(method="POST", path=self.path + ":undelete")
# If the reload flag is set, reload the project.
if reload_data:
self.reload()