diff --git a/.coveragerc b/.coveragerc index dd39c85..df3be7a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,35 +1,17 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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 -# -# https://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. - -# Generated by synthtool. DO NOT EDIT! [run] branch = True [report] fail_under = 100 show_missing = True +omit = google/cloud/devtools/containeranalysis/__init__.py exclude_lines = # Re-enable the standard pragma pragma: NO COVER # Ignore debug-only repr def __repr__ - # Ignore abstract methods - raise NotImplementedError -omit = - */gapic/*.py - */proto/*.py - */core/*.py - */site-packages/*.py \ No newline at end of file + # Ignore pkg_resources exceptions. + # This is added at the module level as a safeguard for if someone + # generates the code and tries to run it without pip installing. This + # makes it virtually impossible to test properly. + except pkg_resources.DistributionNotFound \ No newline at end of file diff --git a/README.rst b/README.rst index e758e04..e6655ec 100644 --- a/README.rst +++ b/README.rst @@ -48,6 +48,16 @@ dependencies. .. _`virtualenv`: https://virtualenv.pypa.io/en/latest/ +Supported Python Versions +^^^^^^^^^^^^^^^^^^^^^^^^^ +Python >= 3.6 + +Deprecated Python Versions +^^^^^^^^^^^^^^^^^^^^^^^^^^ +Python == 2.7. + +The last version of this library compatible with Python 2.7 is google-cloud-containeranalysis==1.0.2. + Mac/Linux ^^^^^^^^^ diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 0000000..3388b0c --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,142 @@ +# 2.0.0 Migration Guide + +The 2.0 release of the `google-cloud-containeranalysis` client is a significant upgrade based on a [next-gen code generator](https://github.com/googleapis/gapic-generator-python), and includes substantial interface changes. Existing code written for earlier versions of this library will likely require updates to use this version. This document describes the changes that have been made, and what you need to do to update your usage. + +If you experience issues or have questions, please file an [issue](https://github.com/googleapis/python-containeranalysis/issues). + +## Supported Python Versions + +> **WARNING**: Breaking change + +The 2.0.0 release requires Python 3.6+. + + +## Method Calls + +> **WARNING**: Breaking change + +Methods expect request objects. We provide a script that will convert most common use cases. + +* Install the library + +```py +python3 -m pip install google-cloud-containeranalysis +``` + +* The script `fixup_containeranalysis_v1_keywords.py` is shipped with the library. It expects +an input directory (with the code to convert) and an empty destination directory. + +```sh +$ fixup_containeranalysis_v1_keywords.py --input-directory .samples/ --output-directory samples/ +``` + +**Before:** +```py +from google.cloud.devtools import containeranalysis_v1 + +client = containeranalysis_v1.ContainerAnalysisClient() +resource = "projects/[PROJECT_ID]/notes/[NOTE_ID]" +policy = client.get_iam_policy(resource) +``` + + +**After:** +```py +from google.cloud.devtools import containeranalysis_v1 + +client = containeranalysis_v1.ContainerAnalysisClient() +request = {"resource": "projects/[PROJECT_ID]/notes/[NOTE_ID]"} +policy = client.get_iam_policy(request=request) +``` + +### More Details + +In `google-cloud-containeranalysis<2.0.0`, parameters required by the API were positional parameters and optional parameters were keyword parameters. + +**Before:** +```py + def get_iam_policy( + self, + resource, + options_=None, + retry=google.api_core.gapic_v1.method.DEFAULT, + timeout=google.api_core.gapic_v1.method.DEFAULT, + metadata=None, + ): +``` + +In the 2.0.0 release, all methods have a single positional parameter `request`. Method docstrings indicate whether a parameter is required or optional. + +Some methods have additional keyword only parameters. The available parameters depend on the [`google.api.method_signature` annotation](https://github.com/googleapis/googleapis/blob/b77cacf1ed06e0301a39d6328b599e24102f04be/google/devtools/containeranalysis/v1/containeranalysis.proto#L67) specified by the API producer. + + +**After:** +```py + def get_iam_policy( + self, + request: iam_policy.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy.Policy: +``` + +> **NOTE:** The `request` parameter and flattened keyword parameters for the API are mutually exclusive. +> Passing both will result in an error. + + +Both of these calls are valid: + +```py +response = client.test_iam_permissions( + request={ + "resource": resource, + "permissions": permissions, + } +) +``` + +```py +response = client.test_iam_permissions( + resource=resource, + permissions=permissions, +) +``` + +This call is invalid because it mixes `request` with a keyword argument `permissions`. Executing this code +will result in an error. + +```py +response = client.test_iam_permissions( + request={ + "resource": resource, + }, + permissions=permissions +) +``` + + + +## Enums and Types + + +> **WARNING**: Breaking change + +The submodule `types` has been removed. + +**Before:** +```py +from google.cloud.devtools import containeranalysis_v1 + +audit_config = containeranalysis_v1.types.AuditConfigDelta() +``` + + +**After:** +```py +from google.cloud.devtools import containeranalysis_v1 + +audit_config = containeranalysis_v1.AuditConfigDelta() +``` diff --git a/docs/UPGRADING.md b/docs/UPGRADING.md new file mode 120000 index 0000000..01097c8 --- /dev/null +++ b/docs/UPGRADING.md @@ -0,0 +1 @@ +../UPGRADING.md \ No newline at end of file diff --git a/docs/containeranalysis_v1/services.rst b/docs/containeranalysis_v1/services.rst new file mode 100644 index 0000000..1547128 --- /dev/null +++ b/docs/containeranalysis_v1/services.rst @@ -0,0 +1,6 @@ +Services for Google Cloud Devtools Containeranalysis v1 API +=========================================================== + +.. automodule:: google.cloud.devtools.containeranalysis_v1.services.container_analysis + :members: + :inherited-members: diff --git a/docs/containeranalysis_v1/types.rst b/docs/containeranalysis_v1/types.rst new file mode 100644 index 0000000..b779ea6 --- /dev/null +++ b/docs/containeranalysis_v1/types.rst @@ -0,0 +1,5 @@ +Types for Google Cloud Devtools Containeranalysis v1 API +======================================================== + +.. automodule:: google.cloud.devtools.containeranalysis_v1.types + :members: diff --git a/docs/gapic/v1/api.rst b/docs/gapic/v1/api.rst deleted file mode 100644 index 60a1b16..0000000 --- a/docs/gapic/v1/api.rst +++ /dev/null @@ -1,6 +0,0 @@ -Client for Container Analysis API -================================= - -.. automodule:: google.cloud.devtools.containeranalysis_v1 - :members: - :inherited-members: \ No newline at end of file diff --git a/docs/gapic/v1/types.rst b/docs/gapic/v1/types.rst deleted file mode 100644 index 3940e6a..0000000 --- a/docs/gapic/v1/types.rst +++ /dev/null @@ -1,5 +0,0 @@ -Types for Container Analysis API Client -======================================= - -.. automodule:: google.cloud.devtools.containeranalysis_v1.types - :members: \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index ec379e7..cbf09a5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,8 +7,8 @@ Api Reference .. toctree:: :maxdepth: 2 - gapic/v1/api - gapic/v1/types + containeranalysis_v1/services + containeranalysis_v1/types Changelog @@ -19,4 +19,14 @@ For all previous ``google-cloud-containeranalysis`` releases: .. toctree:: :maxdepth: 2 - changelog \ No newline at end of file + changelog + +Migration Guide +--------------- + +See the guide below for instructions on migrating to the 2.x release of this library. + +.. toctree:: + :maxdepth: 2 + + UPGRADING diff --git a/google/cloud/containeranalysis_v1/proto/containeranalysis.proto b/google/cloud/containeranalysis_v1/proto/containeranalysis.proto deleted file mode 100644 index 5c855ac..0000000 --- a/google/cloud/containeranalysis_v1/proto/containeranalysis.proto +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2019 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. -// - -syntax = "proto3"; - -package google.devtools.containeranalysis.v1; - -import "google/api/annotations.proto"; -import "google/iam/v1/iam_policy.proto"; -import "google/iam/v1/policy.proto"; -import "google/protobuf/timestamp.proto"; - -option csharp_namespace = "Google.Cloud.DevTools.ContainerAnalysis.V1"; -option go_package = "google.golang.org/genproto/googleapis/devtools/containeranalysis/v1;containeranalysis"; -option java_multiple_files = true; -option java_package = "com.google.containeranalysis.v1"; -option objc_class_prefix = "GCA"; - -// Retrieves analysis results of Cloud components such as Docker container -// images. The Container Analysis API is an implementation of the -// [Grafeas](grafeas.io) API. -// -// Analysis results are stored as a series of occurrences. An `Occurrence` -// contains information about a specific analysis instance on a resource. An -// occurrence refers to a `Note`. A note contains details describing the -// analysis and is generally stored in a separate project, called a `Provider`. -// Multiple occurrences can refer to the same note. -// -// For example, an SSL vulnerability could affect multiple images. In this case, -// there would be one note for the vulnerability and an occurrence for each -// image with the vulnerability referring to that note. -service ContainerAnalysis { - // Sets the access control policy on the specified note or occurrence. - // Requires `containeranalysis.notes.setIamPolicy` or - // `containeranalysis.occurrences.setIamPolicy` permission if the resource is - // a note or an occurrence, respectively. - // - // The resource takes the format `projects/[PROJECT_ID]/notes/[NOTE_ID]` for - // notes and `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]` for - // occurrences. - rpc SetIamPolicy(google.iam.v1.SetIamPolicyRequest) returns (google.iam.v1.Policy) { - option (google.api.http) = { - post: "/v1/{resource=projects/*/notes/*}:setIamPolicy" - body: "*" - additional_bindings { - post: "/v1/{resource=projects/*/occurrences/*}:setIamPolicy" - body: "*" - } - }; - } - - // Gets the access control policy for a note or an occurrence resource. - // Requires `containeranalysis.notes.setIamPolicy` or - // `containeranalysis.occurrences.setIamPolicy` permission if the resource is - // a note or occurrence, respectively. - // - // The resource takes the format `projects/[PROJECT_ID]/notes/[NOTE_ID]` for - // notes and `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]` for - // occurrences. - rpc GetIamPolicy(google.iam.v1.GetIamPolicyRequest) returns (google.iam.v1.Policy) { - option (google.api.http) = { - post: "/v1/{resource=projects/*/notes/*}:getIamPolicy" - body: "*" - additional_bindings { - post: "/v1/{resource=projects/*/occurrences/*}:getIamPolicy" - body: "*" - } - }; - } - - // Returns the permissions that a caller has on the specified note or - // occurrence. Requires list permission on the project (for example, - // `containeranalysis.notes.list`). - // - // The resource takes the format `projects/[PROJECT_ID]/notes/[NOTE_ID]` for - // notes and `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]` for - // occurrences. - rpc TestIamPermissions(google.iam.v1.TestIamPermissionsRequest) returns (google.iam.v1.TestIamPermissionsResponse) { - option (google.api.http) = { - post: "/v1/{resource=projects/*/notes/*}:testIamPermissions" - body: "*" - additional_bindings { - post: "/v1/{resource=projects/*/occurrences/*}:testIamPermissions" - body: "*" - } - }; - } -} diff --git a/google/cloud/devtools/containeranalysis/__init__.py b/google/cloud/devtools/containeranalysis/__init__.py new file mode 100644 index 0000000..fb5a9de --- /dev/null +++ b/google/cloud/devtools/containeranalysis/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from google.cloud.devtools.containeranalysis_v1.services.container_analysis.async_client import ( + ContainerAnalysisAsyncClient, +) +from google.cloud.devtools.containeranalysis_v1.services.container_analysis.client import ( + ContainerAnalysisClient, +) + +__all__ = ( + "ContainerAnalysisAsyncClient", + "ContainerAnalysisClient", +) diff --git a/google/cloud/devtools/containeranalysis/py.typed b/google/cloud/devtools/containeranalysis/py.typed new file mode 100644 index 0000000..73accaf --- /dev/null +++ b/google/cloud/devtools/containeranalysis/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-cloud-devtools-containeranalysis package uses inline types. diff --git a/google/cloud/devtools/containeranalysis_v1/__init__.py b/google/cloud/devtools/containeranalysis_v1/__init__.py index 0af72cb..66e394e 100644 --- a/google/cloud/devtools/containeranalysis_v1/__init__.py +++ b/google/cloud/devtools/containeranalysis_v1/__init__.py @@ -1,42 +1,21 @@ # -*- coding: utf-8 -*- -# + # Copyright 2020 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 # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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. +# - -from __future__ import absolute_import -import sys -import warnings - -from google.cloud.devtools.containeranalysis_v1 import types -from google.cloud.devtools.containeranalysis_v1.gapic import container_analysis_client - - -if sys.version_info[:2] == (2, 7): - message = ( - "A future version of this library will drop support for Python 2.7. " - "More details about Python 2 support for Google Cloud Client Libraries " - "can be found at https://cloud.google.com/python/docs/python2-sunset/" - ) - warnings.warn(message, DeprecationWarning) - - -class ContainerAnalysisClient(container_analysis_client.ContainerAnalysisClient): - __doc__ = container_analysis_client.ContainerAnalysisClient.__doc__ +from .services.container_analysis import ContainerAnalysisClient -__all__ = ( - "types", - "ContainerAnalysisClient", -) +__all__ = ("ContainerAnalysisClient",) diff --git a/google/cloud/devtools/containeranalysis_v1/gapic/__init__.py b/google/cloud/devtools/containeranalysis_v1/gapic/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/google/cloud/devtools/containeranalysis_v1/gapic/container_analysis_client.py b/google/cloud/devtools/containeranalysis_v1/gapic/container_analysis_client.py deleted file mode 100644 index e03791b..0000000 --- a/google/cloud/devtools/containeranalysis_v1/gapic/container_analysis_client.py +++ /dev/null @@ -1,481 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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 -# -# https://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. - -"""Accesses the google.devtools.containeranalysis.v1 ContainerAnalysis API.""" - -import pkg_resources -import warnings - -from google.oauth2 import service_account -import google.api_core.client_options -import google.api_core.gapic_v1.client_info -import google.api_core.gapic_v1.config -import google.api_core.gapic_v1.method -import google.api_core.gapic_v1.routing_header -import google.api_core.grpc_helpers -import grpc - -from google.cloud.devtools.containeranalysis_v1.gapic import ( - container_analysis_client_config, -) -from google.cloud.devtools.containeranalysis_v1.gapic.transports import ( - container_analysis_grpc_transport, -) -from google.cloud.devtools.containeranalysis_v1.proto import containeranalysis_pb2_grpc -from google.iam.v1 import iam_policy_pb2 -from google.iam.v1 import options_pb2 -from google.iam.v1 import policy_pb2 - - -from grafeas import grafeas_v1 -from grafeas.grafeas_v1.gapic.transports import grafeas_grpc_transport - -_GAPIC_LIBRARY_VERSION = pkg_resources.get_distribution( - "google-cloud-containeranalysis" -).version - - -class ContainerAnalysisClient(object): - """ - Retrieves analysis results of Cloud components such as Docker - container images. The Container Analysis API is an implementation of the - `Grafeas `__ API. - - Analysis results are stored as a series of occurrences. An - ``Occurrence`` contains information about a specific analysis instance - on a resource. An occurrence refers to a ``Note``. A note contains - details describing the analysis and is generally stored in a separate - project, called a ``Provider``. Multiple occurrences can refer to the - same note. - - For example, an SSL vulnerability could affect multiple images. In this - case, there would be one note for the vulnerability and an occurrence - for each image with the vulnerability referring to that note. - """ - - SERVICE_ADDRESS = "containeranalysis.googleapis.com:443" - """The default address of the service.""" - - # The name of the interface for this client. This is the key used to - # find the method configuration in the client_config dictionary. - _INTERFACE_NAME = "google.devtools.containeranalysis.v1.ContainerAnalysis" - - @classmethod - def from_service_account_file(cls, filename, *args, **kwargs): - """Creates an instance of this client using the provided credentials - file. - - Args: - filename (str): The path to the service account private key json - file. - args: Additional arguments to pass to the constructor. - kwargs: Additional arguments to pass to the constructor. - - Returns: - ContainerAnalysisClient: The constructed client. - """ - credentials = service_account.Credentials.from_service_account_file(filename) - kwargs["credentials"] = credentials - return cls(*args, **kwargs) - - from_service_account_json = from_service_account_file - - def __init__( - self, - transport=None, - channel=None, - credentials=None, - client_config=None, - client_info=None, - client_options=None, - ): - """Constructor. - - Args: - transport (Union[~.ContainerAnalysisGrpcTransport, - Callable[[~.Credentials, type], ~.ContainerAnalysisGrpcTransport]): A transport - instance, responsible for actually making the API calls. - The default transport uses the gRPC protocol. - This argument may also be a callable which returns a - transport instance. Callables will be sent the credentials - as the first argument and the default transport class as - the second argument. - channel (grpc.Channel): DEPRECATED. A ``Channel`` instance - through which to make calls. This argument is mutually exclusive - with ``credentials``; providing both will raise an exception. - credentials (google.auth.credentials.Credentials): The - authorization credentials to attach to requests. These - credentials identify this application to the service. If none - are specified, the client will attempt to ascertain the - credentials from the environment. - This argument is mutually exclusive with providing a - transport instance to ``transport``; doing so will raise - an exception. - client_config (dict): DEPRECATED. A dictionary of call options for - each method. If not specified, the default configuration is used. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing - your own client library. - client_options (Union[dict, google.api_core.client_options.ClientOptions]): - Client options used to set user options on the client. API Endpoint - should be set through client_options. - """ - # Raise deprecation warnings for things we want to go away. - if client_config is not None: - warnings.warn( - "The `client_config` argument is deprecated.", - PendingDeprecationWarning, - stacklevel=2, - ) - else: - client_config = container_analysis_client_config.config - - if channel: - warnings.warn( - "The `channel` argument is deprecated; use " "`transport` instead.", - PendingDeprecationWarning, - stacklevel=2, - ) - - api_endpoint = self.SERVICE_ADDRESS - if client_options: - if type(client_options) == dict: - client_options = google.api_core.client_options.from_dict( - client_options - ) - if client_options.api_endpoint: - api_endpoint = client_options.api_endpoint - - # Instantiate the transport. - # The transport is responsible for handling serialization and - # deserialization and actually sending data to the service. - if transport: - if callable(transport): - self.transport = transport( - credentials=credentials, - default_class=container_analysis_grpc_transport.ContainerAnalysisGrpcTransport, - address=api_endpoint, - ) - else: - if credentials: - raise ValueError( - "Received both a transport instance and " - "credentials; these are mutually exclusive." - ) - self.transport = transport - else: - self.transport = container_analysis_grpc_transport.ContainerAnalysisGrpcTransport( - address=api_endpoint, channel=channel, credentials=credentials, - ) - - if client_info is None: - client_info = google.api_core.gapic_v1.client_info.ClientInfo( - gapic_version=_GAPIC_LIBRARY_VERSION, - ) - else: - client_info.gapic_version = _GAPIC_LIBRARY_VERSION - self._client_info = client_info - - # Parse out the default settings for retry and timeout for each RPC - # from the client configuration. - # (Ordinarily, these are the defaults specified in the `*_config.py` - # file next to this one.) - self._method_configs = google.api_core.gapic_v1.config.parse_method_configs( - client_config["interfaces"][self._INTERFACE_NAME], - ) - - # Save a dictionary of cached API call functions. - # These are the actual callables which invoke the proper - # transport methods, wrapped with `wrap_method` to add retry, - # timeout, and the like. - self._inner_api_calls = {} - - def get_grafeas_client(self): - """Returns an equivalent grafeas client. - - Returns: - A :class:`~grafeas.grafeas_v1.GrafeasClient` instance. - """ - grafeas_transport = grafeas_grpc_transport.GrafeasGrpcTransport( - self.SERVICE_ADDRESS, self.transport._OAUTH_SCOPES - ) - - return grafeas_v1.GrafeasClient(grafeas_transport) - - # Service calls - def set_iam_policy( - self, - resource, - policy, - retry=google.api_core.gapic_v1.method.DEFAULT, - timeout=google.api_core.gapic_v1.method.DEFAULT, - metadata=None, - ): - """ - Sets the access control policy on the specified note or occurrence. - Requires ``containeranalysis.notes.setIamPolicy`` or - ``containeranalysis.occurrences.setIamPolicy`` permission if the - resource is a note or an occurrence, respectively. - - The resource takes the format ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` - for notes and ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for - occurrences. - - Example: - >>> from google.cloud.devtools import containeranalysis_v1 - >>> - >>> client = containeranalysis_v1.ContainerAnalysisClient() - >>> - >>> # TODO: Initialize `resource`: - >>> resource = '' - >>> - >>> # TODO: Initialize `policy`: - >>> policy = {} - >>> - >>> response = client.set_iam_policy(resource, policy) - - Args: - resource (str): REQUIRED: The resource for which the policy is being specified. - See the operation documentation for the appropriate value for this field. - policy (Union[dict, ~google.cloud.devtools.containeranalysis_v1.types.Policy]): REQUIRED: The complete policy to be applied to the ``resource``. The - size of the policy is limited to a few 10s of KB. An empty policy is a - valid policy but certain Cloud Platform services (such as Projects) - might reject them. - - If a dict is provided, it must be of the same form as the protobuf - message :class:`~google.cloud.devtools.containeranalysis_v1.types.Policy` - retry (Optional[google.api_core.retry.Retry]): A retry object used - to retry requests. If ``None`` is specified, requests will - be retried using a default configuration. - timeout (Optional[float]): The amount of time, in seconds, to wait - for the request to complete. Note that if ``retry`` is - specified, the timeout applies to each individual attempt. - metadata (Optional[Sequence[Tuple[str, str]]]): Additional metadata - that is provided to the method. - - Returns: - A :class:`~google.cloud.devtools.containeranalysis_v1.types.Policy` instance. - - Raises: - google.api_core.exceptions.GoogleAPICallError: If the request - failed for any reason. - google.api_core.exceptions.RetryError: If the request failed due - to a retryable error and retry attempts failed. - ValueError: If the parameters are invalid. - """ - # Wrap the transport method to add retry and timeout logic. - if "set_iam_policy" not in self._inner_api_calls: - self._inner_api_calls[ - "set_iam_policy" - ] = google.api_core.gapic_v1.method.wrap_method( - self.transport.set_iam_policy, - default_retry=self._method_configs["SetIamPolicy"].retry, - default_timeout=self._method_configs["SetIamPolicy"].timeout, - client_info=self._client_info, - ) - - request = iam_policy_pb2.SetIamPolicyRequest(resource=resource, policy=policy,) - if metadata is None: - metadata = [] - metadata = list(metadata) - try: - routing_header = [("resource", resource)] - except AttributeError: - pass - else: - routing_metadata = google.api_core.gapic_v1.routing_header.to_grpc_metadata( - routing_header - ) - metadata.append(routing_metadata) - - return self._inner_api_calls["set_iam_policy"]( - request, retry=retry, timeout=timeout, metadata=metadata - ) - - def get_iam_policy( - self, - resource, - options_=None, - retry=google.api_core.gapic_v1.method.DEFAULT, - timeout=google.api_core.gapic_v1.method.DEFAULT, - metadata=None, - ): - """ - Gets the access control policy for a note or an occurrence resource. - Requires ``containeranalysis.notes.setIamPolicy`` or - ``containeranalysis.occurrences.setIamPolicy`` permission if the - resource is a note or occurrence, respectively. - - The resource takes the format ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` - for notes and ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for - occurrences. - - Example: - >>> from google.cloud.devtools import containeranalysis_v1 - >>> - >>> client = containeranalysis_v1.ContainerAnalysisClient() - >>> - >>> # TODO: Initialize `resource`: - >>> resource = '' - >>> - >>> response = client.get_iam_policy(resource) - - Args: - resource (str): REQUIRED: The resource for which the policy is being requested. - See the operation documentation for the appropriate value for this field. - options_ (Union[dict, ~google.cloud.devtools.containeranalysis_v1.types.GetPolicyOptions]): OPTIONAL: A ``GetPolicyOptions`` object for specifying options to - ``GetIamPolicy``. This field is only used by Cloud IAM. - - If a dict is provided, it must be of the same form as the protobuf - message :class:`~google.cloud.devtools.containeranalysis_v1.types.GetPolicyOptions` - retry (Optional[google.api_core.retry.Retry]): A retry object used - to retry requests. If ``None`` is specified, requests will - be retried using a default configuration. - timeout (Optional[float]): The amount of time, in seconds, to wait - for the request to complete. Note that if ``retry`` is - specified, the timeout applies to each individual attempt. - metadata (Optional[Sequence[Tuple[str, str]]]): Additional metadata - that is provided to the method. - - Returns: - A :class:`~google.cloud.devtools.containeranalysis_v1.types.Policy` instance. - - Raises: - google.api_core.exceptions.GoogleAPICallError: If the request - failed for any reason. - google.api_core.exceptions.RetryError: If the request failed due - to a retryable error and retry attempts failed. - ValueError: If the parameters are invalid. - """ - # Wrap the transport method to add retry and timeout logic. - if "get_iam_policy" not in self._inner_api_calls: - self._inner_api_calls[ - "get_iam_policy" - ] = google.api_core.gapic_v1.method.wrap_method( - self.transport.get_iam_policy, - default_retry=self._method_configs["GetIamPolicy"].retry, - default_timeout=self._method_configs["GetIamPolicy"].timeout, - client_info=self._client_info, - ) - - request = iam_policy_pb2.GetIamPolicyRequest( - resource=resource, options=options_, - ) - if metadata is None: - metadata = [] - metadata = list(metadata) - try: - routing_header = [("resource", resource)] - except AttributeError: - pass - else: - routing_metadata = google.api_core.gapic_v1.routing_header.to_grpc_metadata( - routing_header - ) - metadata.append(routing_metadata) - - return self._inner_api_calls["get_iam_policy"]( - request, retry=retry, timeout=timeout, metadata=metadata - ) - - def test_iam_permissions( - self, - resource, - permissions, - retry=google.api_core.gapic_v1.method.DEFAULT, - timeout=google.api_core.gapic_v1.method.DEFAULT, - metadata=None, - ): - """ - Returns the permissions that a caller has on the specified note or - occurrence. Requires list permission on the project (for example, - ``containeranalysis.notes.list``). - - The resource takes the format ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` - for notes and ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for - occurrences. - - Example: - >>> from google.cloud.devtools import containeranalysis_v1 - >>> - >>> client = containeranalysis_v1.ContainerAnalysisClient() - >>> - >>> # TODO: Initialize `resource`: - >>> resource = '' - >>> - >>> # TODO: Initialize `permissions`: - >>> permissions = [] - >>> - >>> response = client.test_iam_permissions(resource, permissions) - - Args: - resource (str): REQUIRED: The resource for which the policy detail is being requested. - See the operation documentation for the appropriate value for this field. - permissions (list[str]): The set of permissions to check for the ``resource``. Permissions - with wildcards (such as '*' or 'storage.*') are not allowed. For more - information see `IAM - Overview `__. - retry (Optional[google.api_core.retry.Retry]): A retry object used - to retry requests. If ``None`` is specified, requests will - be retried using a default configuration. - timeout (Optional[float]): The amount of time, in seconds, to wait - for the request to complete. Note that if ``retry`` is - specified, the timeout applies to each individual attempt. - metadata (Optional[Sequence[Tuple[str, str]]]): Additional metadata - that is provided to the method. - - Returns: - A :class:`~google.cloud.devtools.containeranalysis_v1.types.TestIamPermissionsResponse` instance. - - Raises: - google.api_core.exceptions.GoogleAPICallError: If the request - failed for any reason. - google.api_core.exceptions.RetryError: If the request failed due - to a retryable error and retry attempts failed. - ValueError: If the parameters are invalid. - """ - # Wrap the transport method to add retry and timeout logic. - if "test_iam_permissions" not in self._inner_api_calls: - self._inner_api_calls[ - "test_iam_permissions" - ] = google.api_core.gapic_v1.method.wrap_method( - self.transport.test_iam_permissions, - default_retry=self._method_configs["TestIamPermissions"].retry, - default_timeout=self._method_configs["TestIamPermissions"].timeout, - client_info=self._client_info, - ) - - request = iam_policy_pb2.TestIamPermissionsRequest( - resource=resource, permissions=permissions, - ) - if metadata is None: - metadata = [] - metadata = list(metadata) - try: - routing_header = [("resource", resource)] - except AttributeError: - pass - else: - routing_metadata = google.api_core.gapic_v1.routing_header.to_grpc_metadata( - routing_header - ) - metadata.append(routing_metadata) - - return self._inner_api_calls["test_iam_permissions"]( - request, retry=retry, timeout=timeout, metadata=metadata - ) diff --git a/google/cloud/devtools/containeranalysis_v1/gapic/container_analysis_client_config.py b/google/cloud/devtools/containeranalysis_v1/gapic/container_analysis_client_config.py deleted file mode 100644 index 90c1ddb..0000000 --- a/google/cloud/devtools/containeranalysis_v1/gapic/container_analysis_client_config.py +++ /dev/null @@ -1,44 +0,0 @@ -config = { - "interfaces": { - "google.devtools.containeranalysis.v1.ContainerAnalysis": { - "retry_codes": {"no_retry_codes": [], "no_retry_1_codes": []}, - "retry_params": { - "no_retry_params": { - "initial_retry_delay_millis": 0, - "retry_delay_multiplier": 0.0, - "max_retry_delay_millis": 0, - "initial_rpc_timeout_millis": 0, - "rpc_timeout_multiplier": 1.0, - "max_rpc_timeout_millis": 0, - "total_timeout_millis": 0, - }, - "no_retry_1_params": { - "initial_retry_delay_millis": 0, - "retry_delay_multiplier": 0.0, - "max_retry_delay_millis": 0, - "initial_rpc_timeout_millis": 30000, - "rpc_timeout_multiplier": 1.0, - "max_rpc_timeout_millis": 30000, - "total_timeout_millis": 30000, - }, - }, - "methods": { - "SetIamPolicy": { - "timeout_millis": 30000, - "retry_codes_name": "no_retry_1_codes", - "retry_params_name": "no_retry_1_params", - }, - "GetIamPolicy": { - "timeout_millis": 30000, - "retry_codes_name": "no_retry_1_codes", - "retry_params_name": "no_retry_1_params", - }, - "TestIamPermissions": { - "timeout_millis": 30000, - "retry_codes_name": "no_retry_1_codes", - "retry_params_name": "no_retry_1_params", - }, - }, - } - } -} diff --git a/google/cloud/devtools/containeranalysis_v1/gapic/transports/__init__.py b/google/cloud/devtools/containeranalysis_v1/gapic/transports/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/google/cloud/devtools/containeranalysis_v1/gapic/transports/container_analysis_grpc_transport.py b/google/cloud/devtools/containeranalysis_v1/gapic/transports/container_analysis_grpc_transport.py deleted file mode 100644 index 8b3d81b..0000000 --- a/google/cloud/devtools/containeranalysis_v1/gapic/transports/container_analysis_grpc_transport.py +++ /dev/null @@ -1,172 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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 -# -# https://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. - - -import google.api_core.grpc_helpers - -from google.cloud.devtools.containeranalysis_v1.proto import containeranalysis_pb2_grpc - - -class ContainerAnalysisGrpcTransport(object): - """gRPC transport class providing stubs for - google.devtools.containeranalysis.v1 ContainerAnalysis API. - - The transport provides access to the raw gRPC stubs, - which can be used to take advantage of advanced - features of gRPC. - """ - - # The scopes needed to make gRPC calls to all of the methods defined - # in this service. - _OAUTH_SCOPES = ("https://www.googleapis.com/auth/cloud-platform",) - - def __init__( - self, - channel=None, - credentials=None, - address="containeranalysis.googleapis.com:443", - ): - """Instantiate the transport class. - - Args: - channel (grpc.Channel): A ``Channel`` instance through - which to make calls. This argument is mutually exclusive - with ``credentials``; providing both will raise an exception. - credentials (google.auth.credentials.Credentials): The - authorization credentials to attach to requests. These - credentials identify this application to the service. If none - are specified, the client will attempt to ascertain the - credentials from the environment. - address (str): The address where the service is hosted. - """ - # If both `channel` and `credentials` are specified, raise an - # exception (channels come with credentials baked in already). - if channel is not None and credentials is not None: - raise ValueError( - "The `channel` and `credentials` arguments are mutually " "exclusive.", - ) - - # Create the channel. - if channel is None: - channel = self.create_channel( - address=address, - credentials=credentials, - options={ - "grpc.max_send_message_length": -1, - "grpc.max_receive_message_length": -1, - }.items(), - ) - - self._channel = channel - - # gRPC uses objects called "stubs" that are bound to the - # channel and provide a basic method for each RPC. - self._stubs = { - "container_analysis_stub": containeranalysis_pb2_grpc.ContainerAnalysisStub( - channel - ), - } - - @classmethod - def create_channel( - cls, address="containeranalysis.googleapis.com:443", credentials=None, **kwargs - ): - """Create and return a gRPC channel object. - - Args: - address (str): The host for the channel to use. - credentials (~.Credentials): The - authorization credentials to attach to requests. These - credentials identify this application to the service. If - none are specified, the client will attempt to ascertain - the credentials from the environment. - kwargs (dict): Keyword arguments, which are passed to the - channel creation. - - Returns: - grpc.Channel: A gRPC channel object. - """ - return google.api_core.grpc_helpers.create_channel( - address, credentials=credentials, scopes=cls._OAUTH_SCOPES, **kwargs - ) - - @property - def channel(self): - """The gRPC channel used by the transport. - - Returns: - grpc.Channel: A gRPC channel object. - """ - return self._channel - - @property - def set_iam_policy(self): - """Return the gRPC stub for :meth:`ContainerAnalysisClient.set_iam_policy`. - - Sets the access control policy on the specified note or occurrence. - Requires ``containeranalysis.notes.setIamPolicy`` or - ``containeranalysis.occurrences.setIamPolicy`` permission if the - resource is a note or an occurrence, respectively. - - The resource takes the format ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` - for notes and ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for - occurrences. - - Returns: - Callable: A callable which accepts the appropriate - deserialized request object and returns a - deserialized response object. - """ - return self._stubs["container_analysis_stub"].SetIamPolicy - - @property - def get_iam_policy(self): - """Return the gRPC stub for :meth:`ContainerAnalysisClient.get_iam_policy`. - - Gets the access control policy for a note or an occurrence resource. - Requires ``containeranalysis.notes.setIamPolicy`` or - ``containeranalysis.occurrences.setIamPolicy`` permission if the - resource is a note or occurrence, respectively. - - The resource takes the format ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` - for notes and ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for - occurrences. - - Returns: - Callable: A callable which accepts the appropriate - deserialized request object and returns a - deserialized response object. - """ - return self._stubs["container_analysis_stub"].GetIamPolicy - - @property - def test_iam_permissions(self): - """Return the gRPC stub for :meth:`ContainerAnalysisClient.test_iam_permissions`. - - Returns the permissions that a caller has on the specified note or - occurrence. Requires list permission on the project (for example, - ``containeranalysis.notes.list``). - - The resource takes the format ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` - for notes and ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for - occurrences. - - Returns: - Callable: A callable which accepts the appropriate - deserialized request object and returns a - deserialized response object. - """ - return self._stubs["container_analysis_stub"].TestIamPermissions diff --git a/google/cloud/devtools/containeranalysis_v1/proto/__init__.py b/google/cloud/devtools/containeranalysis_v1/proto/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/google/cloud/devtools/containeranalysis_v1/proto/containeranalysis_pb2.py b/google/cloud/devtools/containeranalysis_v1/proto/containeranalysis_pb2.py deleted file mode 100644 index a3b3ba4..0000000 --- a/google/cloud/devtools/containeranalysis_v1/proto/containeranalysis_pb2.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: google/cloud/devtools/containeranalysis_v1/proto/containeranalysis.proto - -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -from google.api import client_pb2 as google_dot_api_dot_client__pb2 -from google.iam.v1 import iam_policy_pb2 as google_dot_iam_dot_v1_dot_iam__policy__pb2 -from google.iam.v1 import policy_pb2 as google_dot_iam_dot_v1_dot_policy__pb2 -from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name="google/cloud/devtools/containeranalysis_v1/proto/containeranalysis.proto", - package="google.devtools.containeranalysis.v1", - syntax="proto3", - serialized_options=b"\n\037com.google.containeranalysis.v1P\001ZUgoogle.golang.org/genproto/googleapis/devtools/containeranalysis/v1;containeranalysis\242\002\003GCA\252\002*Google.Cloud.DevTools.ContainerAnalysis.V1\352\002$Google::Cloud::ContainerAnalysis::V1", - create_key=_descriptor._internal_create_key, - serialized_pb=b'\nHgoogle/cloud/devtools/containeranalysis_v1/proto/containeranalysis.proto\x12$google.devtools.containeranalysis.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1egoogle/iam/v1/iam_policy.proto\x1a\x1agoogle/iam/v1/policy.proto\x1a\x1fgoogle/protobuf/timestamp.proto2\x91\x06\n\x11\x43ontainerAnalysis\x12\xd2\x01\n\x0cSetIamPolicy\x12".google.iam.v1.SetIamPolicyRequest\x1a\x15.google.iam.v1.Policy"\x86\x01\x82\xd3\xe4\x93\x02n"./v1/{resource=projects/*/notes/*}:setIamPolicy:\x01*Z9"4/v1/{resource=projects/*/occurrences/*}:setIamPolicy:\x01*\xda\x41\x0fresource,policy\x12\xca\x01\n\x0cGetIamPolicy\x12".google.iam.v1.GetIamPolicyRequest\x1a\x15.google.iam.v1.Policy"\x7f\x82\xd3\xe4\x93\x02n"./v1/{resource=projects/*/notes/*}:getIamPolicy:\x01*Z9"4/v1/{resource=projects/*/occurrences/*}:getIamPolicy:\x01*\xda\x41\x08resource\x12\x83\x02\n\x12TestIamPermissions\x12(.google.iam.v1.TestIamPermissionsRequest\x1a).google.iam.v1.TestIamPermissionsResponse"\x97\x01\x82\xd3\xe4\x93\x02z"4/v1/{resource=projects/*/notes/*}:testIamPermissions:\x01*Z?":/v1/{resource=projects/*/occurrences/*}:testIamPermissions:\x01*\xda\x41\x14resource,permissions\x1aT\xca\x41 containeranalysis.googleapis.com\xd2\x41.https://www.googleapis.com/auth/cloud-platformB\xd4\x01\n\x1f\x63om.google.containeranalysis.v1P\x01ZUgoogle.golang.org/genproto/googleapis/devtools/containeranalysis/v1;containeranalysis\xa2\x02\x03GCA\xaa\x02*Google.Cloud.DevTools.ContainerAnalysis.V1\xea\x02$Google::Cloud::ContainerAnalysis::V1b\x06proto3', - dependencies=[ - google_dot_api_dot_annotations__pb2.DESCRIPTOR, - google_dot_api_dot_client__pb2.DESCRIPTOR, - google_dot_iam_dot_v1_dot_iam__policy__pb2.DESCRIPTOR, - google_dot_iam_dot_v1_dot_policy__pb2.DESCRIPTOR, - google_dot_protobuf_dot_timestamp__pb2.DESCRIPTOR, - ], -) - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -DESCRIPTOR._options = None - -_CONTAINERANALYSIS = _descriptor.ServiceDescriptor( - name="ContainerAnalysis", - full_name="google.devtools.containeranalysis.v1.ContainerAnalysis", - file=DESCRIPTOR, - index=0, - serialized_options=b"\312A containeranalysis.googleapis.com\322A.https://www.googleapis.com/auth/cloud-platform", - create_key=_descriptor._internal_create_key, - serialized_start=263, - serialized_end=1048, - methods=[ - _descriptor.MethodDescriptor( - name="SetIamPolicy", - full_name="google.devtools.containeranalysis.v1.ContainerAnalysis.SetIamPolicy", - index=0, - containing_service=None, - input_type=google_dot_iam_dot_v1_dot_iam__policy__pb2._SETIAMPOLICYREQUEST, - output_type=google_dot_iam_dot_v1_dot_policy__pb2._POLICY, - serialized_options=b'\202\323\344\223\002n"./v1/{resource=projects/*/notes/*}:setIamPolicy:\001*Z9"4/v1/{resource=projects/*/occurrences/*}:setIamPolicy:\001*\332A\017resource,policy', - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name="GetIamPolicy", - full_name="google.devtools.containeranalysis.v1.ContainerAnalysis.GetIamPolicy", - index=1, - containing_service=None, - input_type=google_dot_iam_dot_v1_dot_iam__policy__pb2._GETIAMPOLICYREQUEST, - output_type=google_dot_iam_dot_v1_dot_policy__pb2._POLICY, - serialized_options=b'\202\323\344\223\002n"./v1/{resource=projects/*/notes/*}:getIamPolicy:\001*Z9"4/v1/{resource=projects/*/occurrences/*}:getIamPolicy:\001*\332A\010resource', - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name="TestIamPermissions", - full_name="google.devtools.containeranalysis.v1.ContainerAnalysis.TestIamPermissions", - index=2, - containing_service=None, - input_type=google_dot_iam_dot_v1_dot_iam__policy__pb2._TESTIAMPERMISSIONSREQUEST, - output_type=google_dot_iam_dot_v1_dot_iam__policy__pb2._TESTIAMPERMISSIONSRESPONSE, - serialized_options=b'\202\323\344\223\002z"4/v1/{resource=projects/*/notes/*}:testIamPermissions:\001*Z?":/v1/{resource=projects/*/occurrences/*}:testIamPermissions:\001*\332A\024resource,permissions', - create_key=_descriptor._internal_create_key, - ), - ], -) -_sym_db.RegisterServiceDescriptor(_CONTAINERANALYSIS) - -DESCRIPTOR.services_by_name["ContainerAnalysis"] = _CONTAINERANALYSIS - -# @@protoc_insertion_point(module_scope) diff --git a/google/cloud/devtools/containeranalysis_v1/proto/containeranalysis_pb2_grpc.py b/google/cloud/devtools/containeranalysis_v1/proto/containeranalysis_pb2_grpc.py deleted file mode 100644 index e6963a6..0000000 --- a/google/cloud/devtools/containeranalysis_v1/proto/containeranalysis_pb2_grpc.py +++ /dev/null @@ -1,226 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" -import grpc - -from google.iam.v1 import iam_policy_pb2 as google_dot_iam_dot_v1_dot_iam__policy__pb2 -from google.iam.v1 import policy_pb2 as google_dot_iam_dot_v1_dot_policy__pb2 - - -class ContainerAnalysisStub(object): - """Retrieves analysis results of Cloud components such as Docker container - images. The Container Analysis API is an implementation of the - [Grafeas](https://grafeas.io) API. - - Analysis results are stored as a series of occurrences. An `Occurrence` - contains information about a specific analysis instance on a resource. An - occurrence refers to a `Note`. A note contains details describing the - analysis and is generally stored in a separate project, called a `Provider`. - Multiple occurrences can refer to the same note. - - For example, an SSL vulnerability could affect multiple images. In this case, - there would be one note for the vulnerability and an occurrence for each - image with the vulnerability referring to that note. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.SetIamPolicy = channel.unary_unary( - "/google.devtools.containeranalysis.v1.ContainerAnalysis/SetIamPolicy", - request_serializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.SetIamPolicyRequest.SerializeToString, - response_deserializer=google_dot_iam_dot_v1_dot_policy__pb2.Policy.FromString, - ) - self.GetIamPolicy = channel.unary_unary( - "/google.devtools.containeranalysis.v1.ContainerAnalysis/GetIamPolicy", - request_serializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.GetIamPolicyRequest.SerializeToString, - response_deserializer=google_dot_iam_dot_v1_dot_policy__pb2.Policy.FromString, - ) - self.TestIamPermissions = channel.unary_unary( - "/google.devtools.containeranalysis.v1.ContainerAnalysis/TestIamPermissions", - request_serializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsRequest.SerializeToString, - response_deserializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsResponse.FromString, - ) - - -class ContainerAnalysisServicer(object): - """Retrieves analysis results of Cloud components such as Docker container - images. The Container Analysis API is an implementation of the - [Grafeas](https://grafeas.io) API. - - Analysis results are stored as a series of occurrences. An `Occurrence` - contains information about a specific analysis instance on a resource. An - occurrence refers to a `Note`. A note contains details describing the - analysis and is generally stored in a separate project, called a `Provider`. - Multiple occurrences can refer to the same note. - - For example, an SSL vulnerability could affect multiple images. In this case, - there would be one note for the vulnerability and an occurrence for each - image with the vulnerability referring to that note. - """ - - def SetIamPolicy(self, request, context): - """Sets the access control policy on the specified note or occurrence. - Requires `containeranalysis.notes.setIamPolicy` or - `containeranalysis.occurrences.setIamPolicy` permission if the resource is - a note or an occurrence, respectively. - - The resource takes the format `projects/[PROJECT_ID]/notes/[NOTE_ID]` for - notes and `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]` for - occurrences. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def GetIamPolicy(self, request, context): - """Gets the access control policy for a note or an occurrence resource. - Requires `containeranalysis.notes.setIamPolicy` or - `containeranalysis.occurrences.setIamPolicy` permission if the resource is - a note or occurrence, respectively. - - The resource takes the format `projects/[PROJECT_ID]/notes/[NOTE_ID]` for - notes and `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]` for - occurrences. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def TestIamPermissions(self, request, context): - """Returns the permissions that a caller has on the specified note or - occurrence. Requires list permission on the project (for example, - `containeranalysis.notes.list`). - - The resource takes the format `projects/[PROJECT_ID]/notes/[NOTE_ID]` for - notes and `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]` for - occurrences. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - -def add_ContainerAnalysisServicer_to_server(servicer, server): - rpc_method_handlers = { - "SetIamPolicy": grpc.unary_unary_rpc_method_handler( - servicer.SetIamPolicy, - request_deserializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.SetIamPolicyRequest.FromString, - response_serializer=google_dot_iam_dot_v1_dot_policy__pb2.Policy.SerializeToString, - ), - "GetIamPolicy": grpc.unary_unary_rpc_method_handler( - servicer.GetIamPolicy, - request_deserializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.GetIamPolicyRequest.FromString, - response_serializer=google_dot_iam_dot_v1_dot_policy__pb2.Policy.SerializeToString, - ), - "TestIamPermissions": grpc.unary_unary_rpc_method_handler( - servicer.TestIamPermissions, - request_deserializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsRequest.FromString, - response_serializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - "google.devtools.containeranalysis.v1.ContainerAnalysis", rpc_method_handlers - ) - server.add_generic_rpc_handlers((generic_handler,)) - - -# This class is part of an EXPERIMENTAL API. -class ContainerAnalysis(object): - """Retrieves analysis results of Cloud components such as Docker container - images. The Container Analysis API is an implementation of the - [Grafeas](https://grafeas.io) API. - - Analysis results are stored as a series of occurrences. An `Occurrence` - contains information about a specific analysis instance on a resource. An - occurrence refers to a `Note`. A note contains details describing the - analysis and is generally stored in a separate project, called a `Provider`. - Multiple occurrences can refer to the same note. - - For example, an SSL vulnerability could affect multiple images. In this case, - there would be one note for the vulnerability and an occurrence for each - image with the vulnerability referring to that note. - """ - - @staticmethod - def SetIamPolicy( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/google.devtools.containeranalysis.v1.ContainerAnalysis/SetIamPolicy", - google_dot_iam_dot_v1_dot_iam__policy__pb2.SetIamPolicyRequest.SerializeToString, - google_dot_iam_dot_v1_dot_policy__pb2.Policy.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) - - @staticmethod - def GetIamPolicy( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/google.devtools.containeranalysis.v1.ContainerAnalysis/GetIamPolicy", - google_dot_iam_dot_v1_dot_iam__policy__pb2.GetIamPolicyRequest.SerializeToString, - google_dot_iam_dot_v1_dot_policy__pb2.Policy.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) - - @staticmethod - def TestIamPermissions( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/google.devtools.containeranalysis.v1.ContainerAnalysis/TestIamPermissions", - google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsRequest.SerializeToString, - google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsResponse.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) diff --git a/google/cloud/devtools/containeranalysis_v1/py.typed b/google/cloud/devtools/containeranalysis_v1/py.typed new file mode 100644 index 0000000..73accaf --- /dev/null +++ b/google/cloud/devtools/containeranalysis_v1/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-cloud-devtools-containeranalysis package uses inline types. diff --git a/google/cloud/devtools/__init__.py b/google/cloud/devtools/containeranalysis_v1/services/__init__.py similarity index 67% rename from google/cloud/devtools/__init__.py rename to google/cloud/devtools/containeranalysis_v1/services/__init__.py index 8fcc60e..42ffdf2 100644 --- a/google/cloud/devtools/__init__.py +++ b/google/cloud/devtools/containeranalysis_v1/services/__init__.py @@ -1,24 +1,16 @@ # -*- coding: utf-8 -*- -# -# Copyright 2019 Google LLC + +# Copyright 2020 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 # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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. - -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - - __path__ = pkgutil.extend_path(__path__, __name__) +# diff --git a/google/cloud/devtools/containeranalysis.py b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/__init__.py similarity index 71% rename from google/cloud/devtools/containeranalysis.py rename to google/cloud/devtools/containeranalysis_v1/services/container_analysis/__init__.py index 4707e72..8b5ee62 100644 --- a/google/cloud/devtools/containeranalysis.py +++ b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/__init__.py @@ -1,27 +1,24 @@ # -*- coding: utf-8 -*- -# + # Copyright 2020 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 # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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. +# - -from __future__ import absolute_import - -from google.cloud.devtools.containeranalysis_v1 import ContainerAnalysisClient -from google.cloud.devtools.containeranalysis_v1 import types - +from .client import ContainerAnalysisClient +from .async_client import ContainerAnalysisAsyncClient __all__ = ( - "types", "ContainerAnalysisClient", + "ContainerAnalysisAsyncClient", ) diff --git a/google/cloud/devtools/containeranalysis_v1/services/container_analysis/async_client.py b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/async_client.py new file mode 100644 index 0000000..8365efe --- /dev/null +++ b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/async_client.py @@ -0,0 +1,530 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from collections import OrderedDict +import functools +import re +from typing import Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.iam.v1 import iam_policy_pb2 as iam_policy # type: ignore +from google.iam.v1 import policy_pb2 as policy # type: ignore + +from .transports.base import ContainerAnalysisTransport +from .transports.grpc_asyncio import ContainerAnalysisGrpcAsyncIOTransport +from .client import ContainerAnalysisClient + + +from grafeas import grafeas_v1 +from grafeas.grafeas_v1.services.grafeas import transports + + +class ContainerAnalysisAsyncClient: + """Retrieves analysis results of Cloud components such as Docker + container images. The Container Analysis API is an implementation of + the `Grafeas `__ API. + + Analysis results are stored as a series of occurrences. An + ``Occurrence`` contains information about a specific analysis + instance on a resource. An occurrence refers to a ``Note``. A note + contains details describing the analysis and is generally stored in + a separate project, called a ``Provider``. Multiple occurrences can + refer to the same note. + + For example, an SSL vulnerability could affect multiple images. In + this case, there would be one note for the vulnerability and an + occurrence for each image with the vulnerability referring to that + note. + """ + + _client: ContainerAnalysisClient + + DEFAULT_ENDPOINT = ContainerAnalysisClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = ContainerAnalysisClient.DEFAULT_MTLS_ENDPOINT + + from_service_account_file = ContainerAnalysisClient.from_service_account_file + from_service_account_json = from_service_account_file + + get_transport_class = functools.partial( + type(ContainerAnalysisClient).get_transport_class, type(ContainerAnalysisClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, ContainerAnalysisTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + ) -> None: + """Instantiate the container analysis client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.ContainerAnalysisTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint, this is the default value for + the environment variable) and "auto" (auto switch to the default + mTLS endpoint if client SSL credentials is present). However, + the ``api_endpoint`` property takes precedence if provided. + (2) The ``client_cert_source`` property is used to provide client + SSL credentials for mutual TLS transport. If not provided, the + default SSL credentials will be used if present. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + """ + + self._client = ContainerAnalysisClient( + credentials=credentials, transport=transport, client_options=client_options, + ) + + def get_grafeas_client(self) -> grafeas_v1.GrafeasClient: + transport = type(self).get_transport_class("grpc_asyncio")() + grafeas_transport = grafeas_v1.services.grafeas.transports.GrafeasGrpcTransport( + host=transport._host, scopes=transport.AUTH_SCOPES + ) + return grafeas_v1.GrafeasClient(transport=grafeas_transport) + + # Service calls + + async def set_iam_policy( + self, + request: iam_policy.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy.Policy: + r"""Sets the access control policy on the specified note or + occurrence. Requires ``containeranalysis.notes.setIamPolicy`` or + ``containeranalysis.occurrences.setIamPolicy`` permission if the + resource is a note or an occurrence, respectively. + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Args: + request (:class:`~.iam_policy.SetIamPolicyRequest`): + The request object. Request message for `SetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.policy.Policy: + Defines an Identity and Access Management (IAM) policy. + It is used to specify access control policies for Cloud + Platform resources. + + A ``Policy`` is a collection of ``bindings``. A + ``binding`` binds one or more ``members`` to a single + ``role``. Members can be user accounts, service + accounts, Google groups, and domains (such as G Suite). + A ``role`` is a named list of permissions (defined by + IAM or configured by users). A ``binding`` can + optionally specify a ``condition``, which is a logic + expression that further constrains the role binding + based on attributes about the request and/or target + resource. + + **JSON Example** + + :: + + { + "bindings": [ + { + "role": "roles/resourcemanager.organizationAdmin", + "members": [ + "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + }, + { + "role": "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { + "title": "expirable access", + "description": "Does not grant access after Sep 2020", + "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", + } + } + ] + } + + **YAML Example** + + :: + + bindings: + - members: + - user:mike@example.com + - group:admins@example.com + - domain:google.com + - serviceAccount:my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin + - members: + - user:eve@example.com + role: roles/resourcemanager.organizationViewer + condition: + title: expirable access + description: Does not grant access after Sep 2020 + expression: request.time < timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the `IAM + developer's + guide `__. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([resource]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy.SetIamPolicyRequest(**request) + + elif not request: + request = iam_policy.SetIamPolicyRequest() + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.set_iam_policy, + default_timeout=30.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def get_iam_policy( + self, + request: iam_policy.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy.Policy: + r"""Gets the access control policy for a note or an occurrence + resource. Requires ``containeranalysis.notes.setIamPolicy`` or + ``containeranalysis.occurrences.setIamPolicy`` permission if the + resource is a note or occurrence, respectively. + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Args: + request (:class:`~.iam_policy.GetIamPolicyRequest`): + The request object. Request message for `GetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.policy.Policy: + Defines an Identity and Access Management (IAM) policy. + It is used to specify access control policies for Cloud + Platform resources. + + A ``Policy`` is a collection of ``bindings``. A + ``binding`` binds one or more ``members`` to a single + ``role``. Members can be user accounts, service + accounts, Google groups, and domains (such as G Suite). + A ``role`` is a named list of permissions (defined by + IAM or configured by users). A ``binding`` can + optionally specify a ``condition``, which is a logic + expression that further constrains the role binding + based on attributes about the request and/or target + resource. + + **JSON Example** + + :: + + { + "bindings": [ + { + "role": "roles/resourcemanager.organizationAdmin", + "members": [ + "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + }, + { + "role": "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { + "title": "expirable access", + "description": "Does not grant access after Sep 2020", + "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", + } + } + ] + } + + **YAML Example** + + :: + + bindings: + - members: + - user:mike@example.com + - group:admins@example.com + - domain:google.com + - serviceAccount:my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin + - members: + - user:eve@example.com + role: roles/resourcemanager.organizationViewer + condition: + title: expirable access + description: Does not grant access after Sep 2020 + expression: request.time < timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the `IAM + developer's + guide `__. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([resource]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy.GetIamPolicyRequest(**request) + + elif not request: + request = iam_policy.GetIamPolicyRequest() + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_iam_policy, + default_timeout=30.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def test_iam_permissions( + self, + request: iam_policy.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy.TestIamPermissionsResponse: + r"""Returns the permissions that a caller has on the specified note + or occurrence. Requires list permission on the project (for + example, ``containeranalysis.notes.list``). + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Args: + request (:class:`~.iam_policy.TestIamPermissionsRequest`): + The request object. Request message for + `TestIamPermissions` method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (:class:`Sequence[str]`): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.iam_policy.TestIamPermissionsResponse: + Response message for ``TestIamPermissions`` method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([resource, permissions]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy.TestIamPermissionsRequest(**request) + + elif not request: + request = iam_policy.TestIamPermissionsRequest() + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if resource is not None: + request.resource = resource + + if permissions: + request.permissions.extend(permissions) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.test_iam_permissions, + default_timeout=30.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + _client_info = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-containeranalysis", + ).version, + ) +except pkg_resources.DistributionNotFound: + _client_info = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ContainerAnalysisAsyncClient",) diff --git a/google/cloud/devtools/containeranalysis_v1/services/container_analysis/client.py b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/client.py new file mode 100644 index 0000000..67bc8ad --- /dev/null +++ b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/client.py @@ -0,0 +1,649 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from collections import OrderedDict +import os +import re +from typing import Callable, Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport import mtls # type: ignore +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.iam.v1 import iam_policy_pb2 as iam_policy # type: ignore +from google.iam.v1 import policy_pb2 as policy # type: ignore + +from .transports.base import ContainerAnalysisTransport +from .transports.grpc import ContainerAnalysisGrpcTransport +from .transports.grpc_asyncio import ContainerAnalysisGrpcAsyncIOTransport + + +from grafeas import grafeas_v1 +from grafeas.grafeas_v1.services.grafeas import transports + + +class ContainerAnalysisClientMeta(type): + """Metaclass for the ContainerAnalysis client. + + This provides class-level methods for building and retrieving + support objects (e.g. transport) without polluting the client instance + objects. + """ + + _transport_registry = ( + OrderedDict() + ) # type: Dict[str, Type[ContainerAnalysisTransport]] + _transport_registry["grpc"] = ContainerAnalysisGrpcTransport + _transport_registry["grpc_asyncio"] = ContainerAnalysisGrpcAsyncIOTransport + + def get_transport_class( + cls, label: str = None, + ) -> Type[ContainerAnalysisTransport]: + """Return an appropriate transport class. + + Args: + label: The name of the desired transport. If none is + provided, then the first transport in the registry is used. + + Returns: + The transport class to use. + """ + # If a specific transport is requested, return that one. + if label: + return cls._transport_registry[label] + + # No transport is requested; return the default (that is, the first one + # in the dictionary). + return next(iter(cls._transport_registry.values())) + + +class ContainerAnalysisClient(metaclass=ContainerAnalysisClientMeta): + """Retrieves analysis results of Cloud components such as Docker + container images. The Container Analysis API is an implementation of + the `Grafeas `__ API. + + Analysis results are stored as a series of occurrences. An + ``Occurrence`` contains information about a specific analysis + instance on a resource. An occurrence refers to a ``Note``. A note + contains details describing the analysis and is generally stored in + a separate project, called a ``Provider``. Multiple occurrences can + refer to the same note. + + For example, an SSL vulnerability could affect multiple images. In + this case, there would be one note for the vulnerability and an + occurrence for each image with the vulnerability referring to that + note. + """ + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Convert api endpoint to mTLS endpoint. + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + str: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + DEFAULT_ENDPOINT = "containeranalysis.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + {@api.name}: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_file(filename) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + from_service_account_json = from_service_account_file + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, ContainerAnalysisTransport] = None, + client_options: ClientOptions = None, + ) -> None: + """Instantiate the container analysis client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.ContainerAnalysisTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint, this is the default value for + the environment variable) and "auto" (auto switch to the default + mTLS endpoint if client SSL credentials is present). However, + the ``api_endpoint`` property takes precedence if provided. + (2) The ``client_cert_source`` property is used to provide client + SSL credentials for mutual TLS transport. If not provided, the + default SSL credentials will be used if present. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = ClientOptions.from_dict(client_options) + if client_options is None: + client_options = ClientOptions.ClientOptions() + + if client_options.api_endpoint is None: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + if use_mtls_env == "never": + client_options.api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + has_client_cert_source = ( + client_options.client_cert_source is not None + or mtls.has_default_client_cert_source() + ) + client_options.api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT + if has_client_cert_source + else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, ContainerAnalysisTransport): + # transport is a ContainerAnalysisTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=client_options.api_endpoint, + scopes=client_options.scopes, + api_mtls_endpoint=client_options.api_endpoint, + client_cert_source=client_options.client_cert_source, + quota_project_id=client_options.quota_project_id, + ) + + def get_grafeas_client(self) -> grafeas_v1.GrafeasClient: + transport = type(self).get_transport_class("grpc")() + grafeas_transport = grafeas_v1.services.grafeas.transports.GrafeasGrpcTransport( + host=transport._host, scopes=transport.AUTH_SCOPES + ) + return grafeas_v1.GrafeasClient(transport=grafeas_transport) + + # Service calls + + def set_iam_policy( + self, + request: iam_policy.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy.Policy: + r"""Sets the access control policy on the specified note or + occurrence. Requires ``containeranalysis.notes.setIamPolicy`` or + ``containeranalysis.occurrences.setIamPolicy`` permission if the + resource is a note or an occurrence, respectively. + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Args: + request (:class:`~.iam_policy.SetIamPolicyRequest`): + The request object. Request message for `SetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.policy.Policy: + Defines an Identity and Access Management (IAM) policy. + It is used to specify access control policies for Cloud + Platform resources. + + A ``Policy`` is a collection of ``bindings``. A + ``binding`` binds one or more ``members`` to a single + ``role``. Members can be user accounts, service + accounts, Google groups, and domains (such as G Suite). + A ``role`` is a named list of permissions (defined by + IAM or configured by users). A ``binding`` can + optionally specify a ``condition``, which is a logic + expression that further constrains the role binding + based on attributes about the request and/or target + resource. + + **JSON Example** + + :: + + { + "bindings": [ + { + "role": "roles/resourcemanager.organizationAdmin", + "members": [ + "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + }, + { + "role": "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { + "title": "expirable access", + "description": "Does not grant access after Sep 2020", + "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", + } + } + ] + } + + **YAML Example** + + :: + + bindings: + - members: + - user:mike@example.com + - group:admins@example.com + - domain:google.com + - serviceAccount:my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin + - members: + - user:eve@example.com + role: roles/resourcemanager.organizationViewer + condition: + title: expirable access + description: Does not grant access after Sep 2020 + expression: request.time < timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the `IAM + developer's + guide `__. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy.SetIamPolicyRequest(**request) + + elif not request: + request = iam_policy.SetIamPolicyRequest() + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.set_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def get_iam_policy( + self, + request: iam_policy.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy.Policy: + r"""Gets the access control policy for a note or an occurrence + resource. Requires ``containeranalysis.notes.setIamPolicy`` or + ``containeranalysis.occurrences.setIamPolicy`` permission if the + resource is a note or occurrence, respectively. + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Args: + request (:class:`~.iam_policy.GetIamPolicyRequest`): + The request object. Request message for `GetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.policy.Policy: + Defines an Identity and Access Management (IAM) policy. + It is used to specify access control policies for Cloud + Platform resources. + + A ``Policy`` is a collection of ``bindings``. A + ``binding`` binds one or more ``members`` to a single + ``role``. Members can be user accounts, service + accounts, Google groups, and domains (such as G Suite). + A ``role`` is a named list of permissions (defined by + IAM or configured by users). A ``binding`` can + optionally specify a ``condition``, which is a logic + expression that further constrains the role binding + based on attributes about the request and/or target + resource. + + **JSON Example** + + :: + + { + "bindings": [ + { + "role": "roles/resourcemanager.organizationAdmin", + "members": [ + "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + }, + { + "role": "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { + "title": "expirable access", + "description": "Does not grant access after Sep 2020", + "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", + } + } + ] + } + + **YAML Example** + + :: + + bindings: + - members: + - user:mike@example.com + - group:admins@example.com + - domain:google.com + - serviceAccount:my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin + - members: + - user:eve@example.com + role: roles/resourcemanager.organizationViewer + condition: + title: expirable access + description: Does not grant access after Sep 2020 + expression: request.time < timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the `IAM + developer's + guide `__. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy.GetIamPolicyRequest(**request) + + elif not request: + request = iam_policy.GetIamPolicyRequest() + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def test_iam_permissions( + self, + request: iam_policy.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy.TestIamPermissionsResponse: + r"""Returns the permissions that a caller has on the specified note + or occurrence. Requires list permission on the project (for + example, ``containeranalysis.notes.list``). + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Args: + request (:class:`~.iam_policy.TestIamPermissionsRequest`): + The request object. Request message for + `TestIamPermissions` method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (:class:`Sequence[str]`): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.iam_policy.TestIamPermissionsResponse: + Response message for ``TestIamPermissions`` method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy.TestIamPermissionsRequest(**request) + + elif not request: + request = iam_policy.TestIamPermissionsRequest() + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if resource is not None: + request.resource = resource + + if permissions: + request.permissions.extend(permissions) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + _client_info = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-containeranalysis", + ).version, + ) +except pkg_resources.DistributionNotFound: + _client_info = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ContainerAnalysisClient",) diff --git a/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/__init__.py b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/__init__.py new file mode 100644 index 0000000..68e64ee --- /dev/null +++ b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import ContainerAnalysisTransport +from .grpc import ContainerAnalysisGrpcTransport +from .grpc_asyncio import ContainerAnalysisGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[ContainerAnalysisTransport]] +_transport_registry["grpc"] = ContainerAnalysisGrpcTransport +_transport_registry["grpc_asyncio"] = ContainerAnalysisGrpcAsyncIOTransport + + +__all__ = ( + "ContainerAnalysisTransport", + "ContainerAnalysisGrpcTransport", + "ContainerAnalysisGrpcAsyncIOTransport", +) diff --git a/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/base.py b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/base.py new file mode 100644 index 0000000..3e33d8b --- /dev/null +++ b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/base.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import abc +import typing +import pkg_resources + +from google import auth +from google.api_core import exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials # type: ignore + +from google.iam.v1 import iam_policy_pb2 as iam_policy # type: ignore +from google.iam.v1 import policy_pb2 as policy # type: ignore + + +try: + _client_info = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-devtools-containeranalysis", + ).version, + ) +except pkg_resources.DistributionNotFound: + _client_info = gapic_v1.client_info.ClientInfo() + + +class ContainerAnalysisTransport(abc.ABC): + """Abstract transport class for ContainerAnalysis.""" + + AUTH_SCOPES = ("https://www.googleapis.com/auth/cloud-platform",) + + def __init__( + self, + *, + host: str = "containeranalysis.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: typing.Optional[str] = None, + scopes: typing.Optional[typing.Sequence[str]] = AUTH_SCOPES, + quota_project_id: typing.Optional[str] = None, + **kwargs, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scope (Optional[Sequence[str]]): A list of scopes. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = auth.load_credentials_from_file( + credentials_file, scopes=scopes, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = auth.default( + scopes=scopes, quota_project_id=quota_project_id + ) + + # Save the credentials. + self._credentials = credentials + + # Lifted into its own function so it can be stubbed out during tests. + self._prep_wrapped_messages() + + def _prep_wrapped_messages(self): + # Precompute the wrapped methods. + self._wrapped_methods = { + self.set_iam_policy: gapic_v1.method.wrap_method( + self.set_iam_policy, default_timeout=30.0, client_info=_client_info, + ), + self.get_iam_policy: gapic_v1.method.wrap_method( + self.get_iam_policy, default_timeout=30.0, client_info=_client_info, + ), + self.test_iam_permissions: gapic_v1.method.wrap_method( + self.test_iam_permissions, + default_timeout=30.0, + client_info=_client_info, + ), + } + + @property + def set_iam_policy( + self, + ) -> typing.Callable[ + [iam_policy.SetIamPolicyRequest], + typing.Union[policy.Policy, typing.Awaitable[policy.Policy]], + ]: + raise NotImplementedError() + + @property + def get_iam_policy( + self, + ) -> typing.Callable[ + [iam_policy.GetIamPolicyRequest], + typing.Union[policy.Policy, typing.Awaitable[policy.Policy]], + ]: + raise NotImplementedError() + + @property + def test_iam_permissions( + self, + ) -> typing.Callable[ + [iam_policy.TestIamPermissionsRequest], + typing.Union[ + iam_policy.TestIamPermissionsResponse, + typing.Awaitable[iam_policy.TestIamPermissionsResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("ContainerAnalysisTransport",) diff --git a/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/grpc.py b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/grpc.py new file mode 100644 index 0000000..dd846b9 --- /dev/null +++ b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/grpc.py @@ -0,0 +1,325 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from typing import Callable, Dict, Optional, Sequence, Tuple + +from google.api_core import grpc_helpers # type: ignore +from google import auth # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + + +import grpc # type: ignore + +from google.iam.v1 import iam_policy_pb2 as iam_policy # type: ignore +from google.iam.v1 import policy_pb2 as policy # type: ignore + +from .base import ContainerAnalysisTransport + + +class ContainerAnalysisGrpcTransport(ContainerAnalysisTransport): + """gRPC backend transport for ContainerAnalysis. + + Retrieves analysis results of Cloud components such as Docker + container images. The Container Analysis API is an implementation of + the `Grafeas `__ API. + + Analysis results are stored as a series of occurrences. An + ``Occurrence`` contains information about a specific analysis + instance on a resource. An occurrence refers to a ``Note``. A note + contains details describing the analysis and is generally stored in + a separate project, called a ``Provider``. Multiple occurrences can + refer to the same note. + + For example, an SSL vulnerability could affect multiple images. In + this case, there would be one note for the vulnerability and an + occurrence for each image with the vulnerability referring to that + note. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _stubs: Dict[str, Callable] + + def __init__( + self, + *, + host: str = "containeranalysis.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + channel: grpc.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If + provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A + callback to provide client SSL certificate bytes and private key + bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` + is None. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + @classmethod + def create_channel( + cls, + host: str = "containeranalysis.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: str = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs + ) -> grpc.Channel: + """Create and return a gRPC channel object. + Args: + address (Optionsl[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Sanity check: Only create a new channel if we do not already + # have one. + if not hasattr(self, "_grpc_channel"): + self._grpc_channel = self.create_channel( + self._host, credentials=self._credentials, + ) + + # Return the channel from cache. + return self._grpc_channel + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy.SetIamPolicyRequest], policy.Policy]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the access control policy on the specified note or + occurrence. Requires ``containeranalysis.notes.setIamPolicy`` or + ``containeranalysis.occurrences.setIamPolicy`` permission if the + resource is a note or an occurrence, respectively. + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Returns: + Callable[[~.SetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.devtools.containeranalysis.v1.ContainerAnalysis/SetIamPolicy", + request_serializer=iam_policy.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy.GetIamPolicyRequest], policy.Policy]: + r"""Return a callable for the get iam policy method over gRPC. + + Gets the access control policy for a note or an occurrence + resource. Requires ``containeranalysis.notes.setIamPolicy`` or + ``containeranalysis.occurrences.setIamPolicy`` permission if the + resource is a note or occurrence, respectively. + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Returns: + Callable[[~.GetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.devtools.containeranalysis.v1.ContainerAnalysis/GetIamPolicy", + request_serializer=iam_policy.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy.TestIamPermissionsRequest], iam_policy.TestIamPermissionsResponse + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns the permissions that a caller has on the specified note + or occurrence. Requires list permission on the project (for + example, ``containeranalysis.notes.list``). + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Returns: + Callable[[~.TestIamPermissionsRequest], + ~.TestIamPermissionsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.devtools.containeranalysis.v1.ContainerAnalysis/TestIamPermissions", + request_serializer=iam_policy.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("ContainerAnalysisGrpcTransport",) diff --git a/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/grpc_asyncio.py b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/grpc_asyncio.py new file mode 100644 index 0000000..24bad10 --- /dev/null +++ b/google/cloud/devtools/containeranalysis_v1/services/container_analysis/transports/grpc_asyncio.py @@ -0,0 +1,319 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple + +from google.api_core import grpc_helpers_async # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.iam.v1 import iam_policy_pb2 as iam_policy # type: ignore +from google.iam.v1 import policy_pb2 as policy # type: ignore + +from .base import ContainerAnalysisTransport +from .grpc import ContainerAnalysisGrpcTransport + + +class ContainerAnalysisGrpcAsyncIOTransport(ContainerAnalysisTransport): + """gRPC AsyncIO backend transport for ContainerAnalysis. + + Retrieves analysis results of Cloud components such as Docker + container images. The Container Analysis API is an implementation of + the `Grafeas `__ API. + + Analysis results are stored as a series of occurrences. An + ``Occurrence`` contains information about a specific analysis + instance on a resource. An occurrence refers to a ``Note``. A note + contains details describing the analysis and is generally stored in + a separate project, called a ``Provider``. Multiple occurrences can + refer to the same note. + + For example, an SSL vulnerability could affect multiple images. In + this case, there would be one note for the vulnerability and an + occurrence for each image with the vulnerability referring to that + note. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _grpc_channel: aio.Channel + _stubs: Dict[str, Callable] = {} + + @classmethod + def create_channel( + cls, + host: str = "containeranalysis.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> aio.Channel: + """Create and return a gRPC AsyncIO channel object. + Args: + address (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + aio.Channel: A gRPC AsyncIO channel object. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers_async.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "containeranalysis.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + channel: aio.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If + provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A + callback to provide client SSL certificate bytes and private key + bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` + is None. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Sanity check: Only create a new channel if we do not already + # have one. + if not hasattr(self, "_grpc_channel"): + self._grpc_channel = self.create_channel( + self._host, credentials=self._credentials, + ) + + # Return the channel from cache. + return self._grpc_channel + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy.SetIamPolicyRequest], Awaitable[policy.Policy]]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the access control policy on the specified note or + occurrence. Requires ``containeranalysis.notes.setIamPolicy`` or + ``containeranalysis.occurrences.setIamPolicy`` permission if the + resource is a note or an occurrence, respectively. + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Returns: + Callable[[~.SetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.devtools.containeranalysis.v1.ContainerAnalysis/SetIamPolicy", + request_serializer=iam_policy.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy.GetIamPolicyRequest], Awaitable[policy.Policy]]: + r"""Return a callable for the get iam policy method over gRPC. + + Gets the access control policy for a note or an occurrence + resource. Requires ``containeranalysis.notes.setIamPolicy`` or + ``containeranalysis.occurrences.setIamPolicy`` permission if the + resource is a note or occurrence, respectively. + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Returns: + Callable[[~.GetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.devtools.containeranalysis.v1.ContainerAnalysis/GetIamPolicy", + request_serializer=iam_policy.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy.TestIamPermissionsRequest], + Awaitable[iam_policy.TestIamPermissionsResponse], + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns the permissions that a caller has on the specified note + or occurrence. Requires list permission on the project (for + example, ``containeranalysis.notes.list``). + + The resource takes the format + ``projects/[PROJECT_ID]/notes/[NOTE_ID]`` for notes and + ``projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`` for + occurrences. + + Returns: + Callable[[~.TestIamPermissionsRequest], + Awaitable[~.TestIamPermissionsResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.devtools.containeranalysis.v1.ContainerAnalysis/TestIamPermissions", + request_serializer=iam_policy.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("ContainerAnalysisGrpcAsyncIOTransport",) diff --git a/google/cloud/devtools/containeranalysis_v1/types.py b/google/cloud/devtools/containeranalysis_v1/types.py deleted file mode 100644 index 8b4c214..0000000 --- a/google/cloud/devtools/containeranalysis_v1/types.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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 -# -# https://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. - - -from __future__ import absolute_import -import sys - -from google.api_core.protobuf_helpers import get_messages - -from google.iam.v1 import iam_policy_pb2 -from google.iam.v1 import options_pb2 -from google.iam.v1 import policy_pb2 -from google.type import expr_pb2 - - -_shared_modules = [ - iam_policy_pb2, - options_pb2, - policy_pb2, - expr_pb2, -] - -_local_modules = [] - -names = [] - -for module in _shared_modules: # pragma: NO COVER - for name, message in get_messages(module).items(): - setattr(sys.modules[__name__], name, message) - names.append(name) -for module in _local_modules: - for name, message in get_messages(module).items(): - message.__module__ = "google.cloud.devtools.containeranalysis_v1.types" - setattr(sys.modules[__name__], name, message) - names.append(name) - - -__all__ = tuple(sorted(names)) diff --git a/google/__init__.py b/google/cloud/devtools/containeranalysis_v1/types/__init__.py similarity index 71% rename from google/__init__.py rename to google/cloud/devtools/containeranalysis_v1/types/__init__.py index 9a1b64a..e1d07d2 100644 --- a/google/__init__.py +++ b/google/cloud/devtools/containeranalysis_v1/types/__init__.py @@ -1,24 +1,19 @@ # -*- coding: utf-8 -*- -# + # Copyright 2020 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 # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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. +# -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - __path__ = pkgutil.extend_path(__path__, __name__) +__all__ = () diff --git a/google/cloud/__init__.py b/google/cloud/devtools/containeranalysis_v1/types/containeranalysis.py similarity index 71% rename from google/cloud/__init__.py rename to google/cloud/devtools/containeranalysis_v1/types/containeranalysis.py index 9a1b64a..0d29807 100644 --- a/google/cloud/__init__.py +++ b/google/cloud/devtools/containeranalysis_v1/types/containeranalysis.py @@ -1,24 +1,24 @@ # -*- coding: utf-8 -*- -# + # Copyright 2020 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 # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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. +# + -try: - import pkg_resources +__protobuf__ = proto.module( + package="google.devtools.containeranalysis.v1", manifest={}, +) - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - __path__ = pkgutil.extend_path(__path__, __name__) +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..4505b48 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,3 @@ +[mypy] +python_version = 3.6 +namespace_packages = True diff --git a/noxfile.py b/noxfile.py index fcfde61..b3ff7a2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -27,8 +27,8 @@ BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] DEFAULT_PYTHON_VERSION = "3.8" -SYSTEM_TEST_PYTHON_VERSIONS = ["2.7", "3.8"] -UNIT_TEST_PYTHON_VERSIONS = ["2.7", "3.5", "3.6", "3.7", "3.8"] +SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] +UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8"] @nox.session(python=DEFAULT_PYTHON_VERSION) @@ -70,6 +70,8 @@ def lint_setup_py(session): def default(session): # Install all test dependencies, then install this package in-place. + session.install("asyncmock", "pytest-asyncio") + session.install("mock", "pytest", "pytest-cov") session.install("-e", ".") @@ -139,7 +141,7 @@ def cover(session): test runs (not system test runs), and then erases coverage data. """ session.install("coverage", "pytest-cov") - session.run("coverage", "report", "--show-missing", "--fail-under=45") + session.run("coverage", "report", "--show-missing", "--fail-under=98") session.run("coverage", "erase") @@ -149,7 +151,7 @@ def docs(session): """Build the docs for this library.""" session.install("-e", ".") - session.install("sphinx<3.0.0", "alabaster", "recommonmark") + session.install("sphinx", "alabaster", "recommonmark") shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) session.run( @@ -171,7 +173,7 @@ def docfx(session): """Build the docfx yaml files for this library.""" session.install("-e", ".") - session.install("sphinx<3.0.0", "alabaster", "recommonmark", "sphinx-docfx-yaml") + session.install("sphinx", "alabaster", "recommonmark", "sphinx-docfx-yaml") shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) session.run( diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 459f9c9..af8750f 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,6 +1,6 @@ google-cloud-pubsub==1.7.0 google-cloud-containeranalysis==1.0.3 -grafeas==0.4.1 +grafeas==1.0.0 pytest==5.3.0; python_version > "3.0" pytest==4.6.6; python_version < "3.0" flaky==3.7.0 diff --git a/samples/snippets/samples.py b/samples/snippets/samples.py index 7d1348c..caf0899 100644 --- a/samples/snippets/samples.py +++ b/samples/snippets/samples.py @@ -20,12 +20,12 @@ def create_note(note_id, project_id): # note_id = 'my-note' # project_id = 'my-gcp-project' - from grafeas.grafeas_v1.gapic.enums import Version + from grafeas.grafeas_v1 import Version from google.cloud.devtools import containeranalysis_v1 client = containeranalysis_v1.ContainerAnalysisClient() grafeas_client = client.get_grafeas_client() - project_name = grafeas_client.project_path(project_id) + project_name = f"projects/{project_id}" note = { 'vulnerability': { 'details': [ @@ -42,7 +42,7 @@ def create_note(note_id, project_id): ] } } - response = grafeas_client.create_note(project_name, note_id, note) + response = grafeas_client.create_note(parent=project_name, note_id=note_id, note=note) return response # [END containeranalysis_create_note] @@ -59,7 +59,7 @@ def delete_note(note_id, project_id): grafeas_client = client.get_grafeas_client() note_name = f"projects/{project_id}/notes/{note_id}" - grafeas_client.delete_note(note_name) + grafeas_client.delete_note(name=note_name) # [END containeranalysis_delete_note] @@ -72,13 +72,13 @@ def create_occurrence(resource_url, note_id, occurrence_project, note_project): # occurrence_project = 'my-gcp-project' # note_project = 'my-gcp-project' - from grafeas.grafeas_v1.gapic.enums import Version + from grafeas.grafeas_v1 import Version from google.cloud.devtools import containeranalysis_v1 client = containeranalysis_v1.ContainerAnalysisClient() grafeas_client = client.get_grafeas_client() formatted_note = f"projects/{note_project}/notes/{note_id}" - formatted_project = grafeas_client.project_path(occurrence_project) + formatted_project = f"projects/{occurrence_project}" occurrence = { 'note_name': formatted_note, @@ -99,7 +99,7 @@ def create_occurrence(resource_url, note_id, occurrence_project, note_project): } } - return grafeas_client.create_occurrence(formatted_project, occurrence) + return grafeas_client.create_occurrence(parent=formatted_project, occurrence=occurrence) # [END containeranalysis_create_occurrence] @@ -114,7 +114,7 @@ def delete_occurrence(occurrence_id, project_id): client = containeranalysis_v1.ContainerAnalysisClient() grafeas_client = client.get_grafeas_client() parent = f"projects/{project_id}/occurrences/{occurrence_id}" - grafeas_client.delete_occurrence(parent) + grafeas_client.delete_occurrence(name=parent) # [END containeranalysis_delete_occurrence] @@ -129,7 +129,7 @@ def get_note(note_id, project_id): client = containeranalysis_v1.ContainerAnalysisClient() grafeas_client = client.get_grafeas_client() note_name = f"projects/{project_id}/notes/{note_id}" - response = grafeas_client.get_note(note_name) + response = grafeas_client.get_note(name=note_name) return response # [END containeranalysis_get_note] @@ -145,7 +145,7 @@ def get_occurrence(occurrence_id, project_id): client = containeranalysis_v1.ContainerAnalysisClient() grafeas_client = client.get_grafeas_client() parent = f"projects/{project_id}/occurrences/{occurrence_id}" - return grafeas_client.get_occurrence(parent) + return grafeas_client.get_occurrence(name=parent) # [END containeranalysis_get_occurrence] @@ -162,8 +162,8 @@ def get_discovery_info(resource_url, project_id): filter_str = 'kind="DISCOVERY" AND resourceUrl="{}"'.format(resource_url) client = containeranalysis_v1.ContainerAnalysisClient() grafeas_client = client.get_grafeas_client() - project_name = grafeas_client.project_path(project_id) - response = grafeas_client.list_occurrences(project_name, + project_name = f"projects/{project_id}" + response = grafeas_client.list_occurrences(parent=project_name, filter_=filter_str) for occ in response: print(occ) @@ -183,7 +183,7 @@ def get_occurrences_for_note(note_id, project_id): grafeas_client = client.get_grafeas_client() note_name = f"projects/{project_id}/notes/{note_id}" - response = grafeas_client.list_note_occurrences(note_name) + response = grafeas_client.list_note_occurrences(name=note_name) count = 0 for o in response: # do something with the retrieved occurrence @@ -205,10 +205,10 @@ def get_occurrences_for_image(resource_url, project_id): filter_str = 'resourceUrl="{}"'.format(resource_url) client = containeranalysis_v1.ContainerAnalysisClient() grafeas_client = client.get_grafeas_client() - project_name = grafeas_client.project_path(project_id) + project_name = f"projects/{project_id}" - response = grafeas_client.list_occurrences(project_name, - filter_=filter_str) + response = grafeas_client.list_occurrences(parent=project_name, + filter=filter_str) count = 0 for o in response: # do something with the retrieved occurrence @@ -288,14 +288,14 @@ def poll_discovery_finished(resource_url, timeout_seconds, project_id): # project_id = 'my-gcp-project' import time - from grafeas.grafeas_v1.gapic.enums import DiscoveryOccurrence + from grafeas.grafeas_v1 import DiscoveryOccurrence from google.cloud.devtools import containeranalysis_v1 deadline = time.time() + timeout_seconds client = containeranalysis_v1.ContainerAnalysisClient() grafeas_client = client.get_grafeas_client() - project_name = grafeas_client.project_path(project_id) + project_name = f"projects/{project_id}" discovery_occurrence = None while discovery_occurrence is None: @@ -309,7 +309,7 @@ def poll_discovery_finished(resource_url, timeout_seconds, project_id): filter_str = 'kind="DISCOVERY" AND resourceUrl="{}"'\ .format(resource_url) # [START containeranalysis_poll_discovery_occurrence_finished] - result = grafeas_client.list_occurrences(project_name, filter_str) + result = grafeas_client.list_occurrences(parent=project_name, filter=filter_str) # only one occurrence should ever be returned by ListOccurrences # and the given filter for item in result: @@ -322,7 +322,7 @@ def poll_discovery_finished(resource_url, timeout_seconds, project_id): and status != DiscoveryOccurrence.AnalysisStatus.FINISHED_FAILED \ and status != DiscoveryOccurrence.AnalysisStatus.FINISHED_SUCCESS: time.sleep(1) - updated = grafeas_client.get_occurrence(discovery_occurrence.name) + updated = grafeas_client.get_occurrence(name=discovery_occurrence.name) status = updated.discovery.analysis_status if time.time() > deadline: raise RuntimeError('timeout while waiting for terminal state') @@ -340,11 +340,11 @@ def find_vulnerabilities_for_image(resource_url, project_id): client = containeranalysis_v1.ContainerAnalysisClient() grafeas_client = client.get_grafeas_client() - project_name = grafeas_client.project_path(project_id) + project_name = f"projects/{project_id}" filter_str = 'kind="VULNERABILITY" AND resourceUrl="{}"'\ .format(resource_url) - return list(grafeas_client.list_occurrences(project_name, filter_str)) + return list(grafeas_client.list_occurrences(parent=project_name, filter=filter_str)) # [END containeranalysis_vulnerability_occurrences_for_image] @@ -355,16 +355,16 @@ def find_high_severity_vulnerabilities_for_image(resource_url, project_id): # resource_url = 'https://gcr.io/my-project/my-image@sha256:123' # project_id = 'my-gcp-project' - from grafeas.grafeas_v1.gapic.enums import Severity + from grafeas.grafeas_v1 import Severity from google.cloud.devtools import containeranalysis_v1 client = containeranalysis_v1.ContainerAnalysisClient() grafeas_client = client.get_grafeas_client() - project_name = grafeas_client.project_path(project_id) + project_name = f"projects/{project_id}" filter_str = 'kind="VULNERABILITY" AND resourceUrl="{}"'\ .format(resource_url) - vulnerabilities = grafeas_client.list_occurrences(project_name, filter_str) + vulnerabilities = grafeas_client.list_occurrences(parent=project_name, filter=filter_str) filtered_list = [] for v in vulnerabilities: if v.effective_severity == Severity.HIGH or v.effective_severity == Severity.CRITICAL: diff --git a/samples/snippets/samples_test.py b/samples/snippets/samples_test.py index 56ac9ab..7a1bd81 100644 --- a/samples/snippets/samples_test.py +++ b/samples/snippets/samples_test.py @@ -25,10 +25,10 @@ from google.cloud.devtools import containeranalysis_v1 from google.cloud.pubsub import PublisherClient, SubscriberClient -from grafeas.grafeas_v1.gapic.enums import DiscoveryOccurrence -from grafeas.grafeas_v1.gapic.enums import NoteKind -from grafeas.grafeas_v1.gapic.enums import Severity -from grafeas.grafeas_v1.gapic.enums import Version +from grafeas.grafeas_v1 import DiscoveryOccurrence +from grafeas.grafeas_v1 import NoteKind +from grafeas.grafeas_v1 import Severity +from grafeas.grafeas_v1 import Version import pytest import samples @@ -207,7 +207,7 @@ def test_poll_discovery_occurrence(self): } } grafeas_client.\ - create_note(grafeas_client.project_path(PROJECT_ID), note_id, note) + create_note(parent=f"projects/{PROJECT_ID}", note_id=note_id, note=note) occurrence = { 'note_name': f"projects/{PROJECT_ID}/notes/{note_id}", 'resource_uri': self.image_url, @@ -217,8 +217,8 @@ def test_poll_discovery_occurrence(self): } } created = grafeas_client.\ - create_occurrence(grafeas_client.project_path(PROJECT_ID), - occurrence) + create_occurrence(parent=f"projects/{PROJECT_ID}", + occurrence=occurrence) # poll again disc = samples.poll_discovery_finished(self.image_url, 10, PROJECT_ID) @@ -278,7 +278,7 @@ def test_find_high_severity_vulnerabilities(self): } } grafeas_client.\ - create_note(grafeas_client.project_path(PROJECT_ID), note_id, note) + create_note(parent=f"projects/{PROJECT_ID}", note_id=note_id, note=note) occurrence = { 'note_name': f"projects/{PROJECT_ID}/notes/{note_id}", 'resource_uri': self.image_url, @@ -299,8 +299,8 @@ def test_find_high_severity_vulnerabilities(self): } } created = grafeas_client.\ - create_occurrence(grafeas_client.project_path(PROJECT_ID), - occurrence) + create_occurrence(parent=f"projects/{PROJECT_ID}", + occurrence=occurrence) # query again tries = 0 count = 0 diff --git a/scripts/fixup_containeranalysis_v1_keywords.py b/scripts/fixup_containeranalysis_v1_keywords.py new file mode 100644 index 0000000..5129740 --- /dev/null +++ b/scripts/fixup_containeranalysis_v1_keywords.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import argparse +import os +import libcst as cst +import pathlib +import sys +from typing import (Any, Callable, Dict, List, Sequence, Tuple) + + +def partition( + predicate: Callable[[Any], bool], + iterator: Sequence[Any] +) -> Tuple[List[Any], List[Any]]: + """A stable, out-of-place partition.""" + results = ([], []) + + for i in iterator: + results[int(predicate(i))].append(i) + + # Returns trueList, falseList + return results[1], results[0] + + +class containeranalysisCallTransformer(cst.CSTTransformer): + CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') + METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { + 'get_iam_policy': ('resource', 'options', ), + 'set_iam_policy': ('resource', 'policy', ), + 'test_iam_permissions': ('resource', 'permissions', ), + + } + + def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: + try: + key = original.func.attr.value + kword_params = self.METHOD_TO_PARAMS[key] + except (AttributeError, KeyError): + # Either not a method from the API or too convoluted to be sure. + return updated + + # If the existing code is valid, keyword args come after positional args. + # Therefore, all positional args must map to the first parameters. + args, kwargs = partition(lambda a: not bool(a.keyword), updated.args) + if any(k.keyword.value == "request" for k in kwargs): + # We've already fixed this file, don't fix it again. + return updated + + kwargs, ctrl_kwargs = partition( + lambda a: not a.keyword.value in self.CTRL_PARAMS, + kwargs + ) + + args, ctrl_args = args[:len(kword_params)], args[len(kword_params):] + ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl)) + for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS)) + + request_arg = cst.Arg( + value=cst.Dict([ + cst.DictElement( + cst.SimpleString("'{}'".format(name)), + cst.Element(value=arg.value) + ) + # Note: the args + kwargs looks silly, but keep in mind that + # the control parameters had to be stripped out, and that + # those could have been passed positionally or by keyword. + for name, arg in zip(kword_params, args + kwargs)]), + keyword=cst.Name("request") + ) + + return updated.with_changes( + args=[request_arg] + ctrl_kwargs + ) + + +def fix_files( + in_dir: pathlib.Path, + out_dir: pathlib.Path, + *, + transformer=containeranalysisCallTransformer(), +): + """Duplicate the input dir to the output dir, fixing file method calls. + + Preconditions: + * in_dir is a real directory + * out_dir is a real, empty directory + """ + pyfile_gen = ( + pathlib.Path(os.path.join(root, f)) + for root, _, files in os.walk(in_dir) + for f in files if os.path.splitext(f)[1] == ".py" + ) + + for fpath in pyfile_gen: + with open(fpath, 'r') as f: + src = f.read() + + # Parse the code and insert method call fixes. + tree = cst.parse_module(src) + updated = tree.visit(transformer) + + # Create the path and directory structure for the new file. + updated_path = out_dir.joinpath(fpath.relative_to(in_dir)) + updated_path.parent.mkdir(parents=True, exist_ok=True) + + # Generate the updated source file at the corresponding path. + with open(updated_path, 'w') as f: + f.write(updated.code) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description="""Fix up source that uses the containeranalysis client library. + +The existing sources are NOT overwritten but are copied to output_dir with changes made. + +Note: This tool operates at a best-effort level at converting positional + parameters in client method calls to keyword based parameters. + Cases where it WILL FAIL include + A) * or ** expansion in a method call. + B) Calls via function or method alias (includes free function calls) + C) Indirect or dispatched calls (e.g. the method is looked up dynamically) + + These all constitute false negatives. The tool will also detect false + positives when an API method shares a name with another method. +""") + parser.add_argument( + '-d', + '--input-directory', + required=True, + dest='input_dir', + help='the input directory to walk for python files to fix up', + ) + parser.add_argument( + '-o', + '--output-directory', + required=True, + dest='output_dir', + help='the directory to output files fixed via un-flattening', + ) + args = parser.parse_args() + input_dir = pathlib.Path(args.input_dir) + output_dir = pathlib.Path(args.output_dir) + if not input_dir.is_dir(): + print( + f"input directory '{input_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if not output_dir.is_dir(): + print( + f"output directory '{output_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if os.listdir(output_dir): + print( + f"output directory '{output_dir}' is not empty", + file=sys.stderr, + ) + sys.exit(-1) + + fix_files(input_dir, output_dir) diff --git a/setup.py b/setup.py index 3899f26..ca26d48 100644 --- a/setup.py +++ b/setup.py @@ -25,10 +25,11 @@ version = "1.0.3" release_status = "Development Status :: 5 - Production/Stable" dependencies = [ - "google-api-core[grpc] >= 1.14.0, < 2.0.0dev", + "google-api-core[grpc] >= 1.21.0, < 2.0.0dev", "grpc-google-iam-v1 >= 0.12.3, < 0.13dev", - 'enum34; python_version < "3.4"', - "grafeas < 1.0.0dev", + "proto-plus >= 1.4.0", + "libcst >= 0.2.5", + "grafeas >=1.0.0, <2.0dev", ] @@ -39,7 +40,9 @@ readme = readme_file.read() packages = [ - package for package in setuptools.find_packages() if package.startswith("google") + package + for package in setuptools.PEP420PackageFinder.find() + if package.startswith("google") ] namespaces = ["google"] @@ -62,12 +65,10 @@ "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Operating System :: OS Independent", "Topic :: Internet", ], @@ -75,7 +76,8 @@ packages=packages, namespace_packages=namespaces, install_requires=dependencies, - python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", + python_requires=">=3.6", + scripts=["scripts/fixup_containeranalysis_v1_keywords.py"], include_package_data=True, zip_safe=False, ) diff --git a/synth.metadata b/synth.metadata index dcb8b0f..2386c34 100644 --- a/synth.metadata +++ b/synth.metadata @@ -3,30 +3,30 @@ { "git": { "name": ".", - "remote": "git@github.com:busunkim96/python-containeranalysis.git", - "sha": "d8ee9edbfa95d596d76d1de5770dc00e094c8fc7" + "remote": "git@github.com:googleapis/python-containeranalysis", + "sha": "1a93756ed78656cbbdadf164b3de3ab4a3ba6232" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "fb84629a56703d04f0b5304c4a9ade7313ebd92d", - "internalRef": "325339219" + "sha": "43a62a87b70010d9cf9be31e99ea230a535e1b47", + "internalRef": "326109811" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "5f2f711c91199ba2f609d3f06a2fe22aee4e5be3" + "sha": "cd886e8e4be8311aaaffcdcbae44d2cb57d06f8f" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "5f2f711c91199ba2f609d3f06a2fe22aee4e5be3" + "sha": "cd886e8e4be8311aaaffcdcbae44d2cb57d06f8f" } } ], diff --git a/synth.py b/synth.py index c2edaab..d343fe8 100644 --- a/synth.py +++ b/synth.py @@ -36,60 +36,86 @@ ) excludes = [ - "nox.py", "setup.py", - "google/cloud/containeranalysis_v1/proto", - "google/cloud/devtools/__init__.py", # other packages also use this namespace "README.rst", "docs/index.rst", ] s.move(library, excludes=excludes) + +s.replace( + "google/**/*client.py", + r"""google-cloud-devtools-containeranalysis""", + r"""google-cloud-containeranalysis""", +) + # Insert helper method to get grafeas client + s.replace( - "google/**/container_analysis_client.py", - r"""_GAPIC_LIBRARY_VERSION = pkg_resources\.get_distribution\( - 'google-cloud-devtools-containeranalysis', -\)\.version""", - r"""from grafeas import grafeas_v1 -from grafeas.grafeas_v1.gapic.transports import grafeas_grpc_transport - -_GAPIC_LIBRARY_VERSION = pkg_resources.get_distribution( - "google-cloud-containeranalysis" -).version -""", + "google/**/client.py", + "class ContainerAnalysisClientMeta\(type\):", + """from grafeas import grafeas_v1 +from grafeas.grafeas_v1.services.grafeas import transports + +class ContainerAnalysisClientMeta(type):""", ) s.replace( - "google/**/container_analysis_client.py", - r""" \# Service calls - def set_iam_policy\(""", - r''' def get_grafeas_client(self): - """Returns an equivalent grafeas client. - - Returns: - A :class:`~grafeas.grafeas_v1.GrafeasClient` instance. - """ - grafeas_transport = grafeas_grpc_transport.GrafeasGrpcTransport( - self.SERVICE_ADDRESS, - self.transport._OAUTH_SCOPES) - - return grafeas_v1.GrafeasClient(grafeas_transport) - - # Service calls - def set_iam_policy(''', + "google/**/async_client.py", + "class ContainerAnalysisAsyncClient:", + """from grafeas import grafeas_v1 +from grafeas.grafeas_v1.services.grafeas import transports + +class ContainerAnalysisAsyncClient:""", ) + + +s.replace( + "google/**/client.py", + r"""(\s+)def set_iam_policy\(""", + r'''\n\g<1>def get_grafeas_client( + self + ) -> grafeas_v1.GrafeasClient: + transport = type(self).get_transport_class("grpc")() + grafeas_transport = grafeas_v1.services.grafeas.transports.GrafeasGrpcTransport( + host=transport._host, + scopes=transport.AUTH_SCOPES + ) + return grafeas_v1.GrafeasClient(transport=grafeas_transport) + +\g<1># Service calls +\g<1>def set_iam_policy(''', +) + +s.replace( + "google/**/async_client.py", + r"""(\s+)async def set_iam_policy\(""", + r'''\n\g<1>def get_grafeas_client( + self + ) -> grafeas_v1.GrafeasClient: + transport = type(self).get_transport_class("grpc_asyncio")() + grafeas_transport = grafeas_v1.services.grafeas.transports.GrafeasGrpcTransport( + host=transport._host, + scopes=transport.AUTH_SCOPES + ) + return grafeas_v1.GrafeasClient(transport=grafeas_transport) + +\g<1># Service calls +\g<1>async def set_iam_policy(''', +) + # ---------------------------------------------------------------------------- # Add templated files # ---------------------------------------------------------------------------- -templated_files = common.py_library(unit_cov_level=45, cov_level=45) -s.move(templated_files) +templated_files = common.py_library( + samples=False, # set to True only if there are samples + microgenerator=True, + cov_level=98, +) +s.move(templated_files, excludes=[".coveragerc"]) # microgenerator has a good coveragerc python.py_samples(skip_readmes=True) -# TODO(busunkim): Use latest sphinx after microgenerator transition -s.replace("noxfile.py", """['"]sphinx['"]""", '"sphinx<3.0.0"') - s.shell.run(["nox", "-s", "blacken"], hide_output=False) diff --git a/tests/unit/gapic/containeranalysis_v1/__init__.py b/tests/unit/gapic/containeranalysis_v1/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/unit/gapic/containeranalysis_v1/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/unit/gapic/containeranalysis_v1/test_container_analysis.py b/tests/unit/gapic/containeranalysis_v1/test_container_analysis.py new file mode 100644 index 0000000..8edd78f --- /dev/null +++ b/tests/unit/gapic/containeranalysis_v1/test_container_analysis.py @@ -0,0 +1,1348 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import os +import mock + +import grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + +from google import auth +from google.api_core import client_options +from google.api_core import exceptions +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.auth import credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.devtools.containeranalysis_v1.services.container_analysis import ( + ContainerAnalysisAsyncClient, +) +from google.cloud.devtools.containeranalysis_v1.services.container_analysis import ( + ContainerAnalysisClient, +) +from google.cloud.devtools.containeranalysis_v1.services.container_analysis import ( + transports, +) +from google.iam.v1 import iam_policy_pb2 as iam_policy # type: ignore +from google.iam.v1 import options_pb2 as options # type: ignore +from google.iam.v1 import policy_pb2 as policy # type: ignore +from google.oauth2 import service_account +from google.type import expr_pb2 as expr # type: ignore + + +def client_cert_source_callback(): + return b"cert bytes", b"key bytes" + + +# If default endpoint is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint(client): + return ( + "foo.googleapis.com" + if ("localhost" in client.DEFAULT_ENDPOINT) + else client.DEFAULT_ENDPOINT + ) + + +def test__get_default_mtls_endpoint(): + api_endpoint = "example.googleapis.com" + api_mtls_endpoint = "example.mtls.googleapis.com" + sandbox_endpoint = "example.sandbox.googleapis.com" + sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" + non_googleapi = "api.example.com" + + assert ContainerAnalysisClient._get_default_mtls_endpoint(None) is None + assert ( + ContainerAnalysisClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + ContainerAnalysisClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + ContainerAnalysisClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ContainerAnalysisClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ContainerAnalysisClient._get_default_mtls_endpoint(non_googleapi) + == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [ContainerAnalysisClient, ContainerAnalysisAsyncClient] +) +def test_container_analysis_client_from_service_account_file(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_file" + ) as factory: + factory.return_value = creds + client = client_class.from_service_account_file("dummy/file/path.json") + assert client._transport._credentials == creds + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client._transport._credentials == creds + + assert client._transport._host == "containeranalysis.googleapis.com:443" + + +def test_container_analysis_client_get_transport_class(): + transport = ContainerAnalysisClient.get_transport_class() + assert transport == transports.ContainerAnalysisGrpcTransport + + transport = ContainerAnalysisClient.get_transport_class("grpc") + assert transport == transports.ContainerAnalysisGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ContainerAnalysisClient, transports.ContainerAnalysisGrpcTransport, "grpc"), + ( + ContainerAnalysisAsyncClient, + transports.ContainerAnalysisGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + ContainerAnalysisClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ContainerAnalysisClient), +) +@mock.patch.object( + ContainerAnalysisAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ContainerAnalysisAsyncClient), +) +def test_container_analysis_client_client_options( + client_class, transport_class, transport_name +): + # Check that if channel is provided we won't create a new one. + with mock.patch.object(ContainerAnalysisClient, "get_transport_class") as gtc: + transport = transport_class(credentials=credentials.AnonymousCredentials()) + client = client_class(transport=transport) + gtc.assert_not_called() + + # Check that if channel is provided via str we will create a new one. + with mock.patch.object(ContainerAnalysisClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + api_mtls_endpoint="squid.clam.whelk", + client_cert_source=None, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is + # "auto", and client_cert_source is provided. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, + client_cert_source=client_cert_source_callback, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is + # "auto", and default_client_cert_source is provided. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is + # "auto", but client_cert_source and default_client_cert_source are None. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_ENDPOINT, + client_cert_source=None, + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ContainerAnalysisClient, transports.ContainerAnalysisGrpcTransport, "grpc"), + ( + ContainerAnalysisAsyncClient, + transports.ContainerAnalysisGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_container_analysis_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + api_mtls_endpoint=client.DEFAULT_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ContainerAnalysisClient, transports.ContainerAnalysisGrpcTransport, "grpc"), + ( + ContainerAnalysisAsyncClient, + transports.ContainerAnalysisGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_container_analysis_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + +def test_container_analysis_client_client_options_from_dict(): + with mock.patch( + "google.cloud.devtools.containeranalysis_v1.services.container_analysis.transports.ContainerAnalysisGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = ContainerAnalysisClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + api_mtls_endpoint="squid.clam.whelk", + client_cert_source=None, + quota_project_id=None, + ) + + +def test_set_iam_policy( + transport: str = "grpc", request_type=iam_policy.SetIamPolicyRequest +): + client = ContainerAnalysisClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy.Policy(version=774, etag=b"etag_blob",) + + response = client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == iam_policy.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy.Policy) + + assert response.version == 774 + + assert response.etag == b"etag_blob" + + +def test_set_iam_policy_from_dict(): + test_set_iam_policy(request_type=dict) + + +@pytest.mark.asyncio +async def test_set_iam_policy_async(transport: str = "grpc_asyncio"): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = iam_policy.SetIamPolicyRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.set_iam_policy), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy.Policy(version=774, etag=b"etag_blob",) + ) + + response = await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, policy.Policy) + + assert response.version == 774 + + assert response.etag == b"etag_blob" + + +def test_set_iam_policy_field_headers(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy.SetIamPolicyRequest() + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.set_iam_policy), "__call__") as call: + call.return_value = policy.Policy() + + client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_set_iam_policy_field_headers_async(): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy.SetIamPolicyRequest() + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.set_iam_policy), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy.Policy()) + + await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_set_iam_policy_from_dict(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy.Policy() + + response = client.set_iam_policy( + request={ + "resource": "resource_value", + "policy": policy.Policy(version=774), + } + ) + call.assert_called() + + +def test_set_iam_policy_flattened(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy.Policy() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].resource == "resource_value" + + +def test_set_iam_policy_flattened_error(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.set_iam_policy( + iam_policy.SetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_async(): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.set_iam_policy), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = policy.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_error_async(): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.set_iam_policy( + iam_policy.SetIamPolicyRequest(), resource="resource_value", + ) + + +def test_get_iam_policy( + transport: str = "grpc", request_type=iam_policy.GetIamPolicyRequest +): + client = ContainerAnalysisClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy.Policy(version=774, etag=b"etag_blob",) + + response = client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == iam_policy.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy.Policy) + + assert response.version == 774 + + assert response.etag == b"etag_blob" + + +def test_get_iam_policy_from_dict(): + test_get_iam_policy(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_iam_policy_async(transport: str = "grpc_asyncio"): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = iam_policy.GetIamPolicyRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.get_iam_policy), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy.Policy(version=774, etag=b"etag_blob",) + ) + + response = await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, policy.Policy) + + assert response.version == 774 + + assert response.etag == b"etag_blob" + + +def test_get_iam_policy_field_headers(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy.GetIamPolicyRequest() + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.get_iam_policy), "__call__") as call: + call.return_value = policy.Policy() + + client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_iam_policy_field_headers_async(): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy.GetIamPolicyRequest() + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.get_iam_policy), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy.Policy()) + + await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_get_iam_policy_from_dict(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy.Policy() + + response = client.get_iam_policy( + request={ + "resource": "resource_value", + "options": options.GetPolicyOptions(requested_policy_version=2598), + } + ) + call.assert_called() + + +def test_get_iam_policy_flattened(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy.Policy() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].resource == "resource_value" + + +def test_get_iam_policy_flattened_error(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_iam_policy( + iam_policy.GetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_async(): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.get_iam_policy), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = policy.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_error_async(): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_iam_policy( + iam_policy.GetIamPolicyRequest(), resource="resource_value", + ) + + +def test_test_iam_permissions( + transport: str = "grpc", request_type=iam_policy.TestIamPermissionsRequest +): + client = ContainerAnalysisClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + + response = client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == iam_policy.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy.TestIamPermissionsResponse) + + assert response.permissions == ["permissions_value"] + + +def test_test_iam_permissions_from_dict(): + test_test_iam_permissions(request_type=dict) + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async(transport: str = "grpc_asyncio"): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = iam_policy.TestIamPermissionsRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy.TestIamPermissionsResponse(permissions=["permissions_value"],) + ) + + response = await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy.TestIamPermissionsResponse) + + assert response.permissions == ["permissions_value"] + + +def test_test_iam_permissions_field_headers(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy.TestIamPermissionsRequest() + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = iam_policy.TestIamPermissionsResponse() + + client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_field_headers_async(): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy.TestIamPermissionsRequest() + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy.TestIamPermissionsResponse() + ) + + await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_test_iam_permissions_from_dict(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy.TestIamPermissionsResponse() + + response = client.test_iam_permissions( + request={ + "resource": "resource_value", + "permissions": ["permissions_value"], + } + ) + call.assert_called() + + +def test_test_iam_permissions_flattened(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy.TestIamPermissionsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].resource == "resource_value" + + assert args[0].permissions == ["permissions_value"] + + +def test_test_iam_permissions_flattened_error(): + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.test_iam_permissions( + iam_policy.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_async(): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy.TestIamPermissionsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy.TestIamPermissionsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].resource == "resource_value" + + assert args[0].permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_error_async(): + client = ContainerAnalysisAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.test_iam_permissions( + iam_policy.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.ContainerAnalysisGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ContainerAnalysisClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.ContainerAnalysisGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ContainerAnalysisClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.ContainerAnalysisGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ContainerAnalysisClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.ContainerAnalysisGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = ContainerAnalysisClient(transport=transport) + assert client._transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.ContainerAnalysisGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.ContainerAnalysisGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = ContainerAnalysisClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client._transport, transports.ContainerAnalysisGrpcTransport,) + + +def test_container_analysis_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.ContainerAnalysisTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_container_analysis_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.devtools.containeranalysis_v1.services.container_analysis.transports.ContainerAnalysisTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.ContainerAnalysisTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "set_iam_policy", + "get_iam_policy", + "test_iam_permissions", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_container_analysis_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + auth, "load_credentials_from_file" + ) as load_creds, mock.patch( + "google.cloud.devtools.containeranalysis_v1.services.container_analysis.transports.ContainerAnalysisTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ContainerAnalysisTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id="octopus", + ) + + +def test_container_analysis_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + ContainerAnalysisClient() + adc.assert_called_once_with( + scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id=None, + ) + + +def test_container_analysis_transport_auth_adc(): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transports.ContainerAnalysisGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id="octopus", + ) + + +def test_container_analysis_host_no_port(): + client = ContainerAnalysisClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="containeranalysis.googleapis.com" + ), + ) + assert client._transport._host == "containeranalysis.googleapis.com:443" + + +def test_container_analysis_host_with_port(): + client = ContainerAnalysisClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="containeranalysis.googleapis.com:8000" + ), + ) + assert client._transport._host == "containeranalysis.googleapis.com:8000" + + +def test_container_analysis_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that if channel is provided, mtls endpoint and client_cert_source + # won't be used. + callback = mock.MagicMock() + transport = transports.ContainerAnalysisGrpcTransport( + host="squid.clam.whelk", + channel=channel, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=callback, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert not callback.called + + +def test_container_analysis_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that if channel is provided, mtls endpoint and client_cert_source + # won't be used. + callback = mock.MagicMock() + transport = transports.ContainerAnalysisGrpcAsyncIOTransport( + host="squid.clam.whelk", + channel=channel, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=callback, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert not callback.called + + +@mock.patch("grpc.ssl_channel_credentials", autospec=True) +@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) +def test_container_analysis_grpc_transport_channel_mtls_with_client_cert_source( + grpc_create_channel, grpc_ssl_channel_cred +): + # Check that if channel is None, but api_mtls_endpoint and client_cert_source + # are provided, then a mTLS channel will be created. + mock_cred = mock.Mock() + + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + transport = transports.ContainerAnalysisGrpcTransport( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +@mock.patch("grpc.ssl_channel_credentials", autospec=True) +@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) +def test_container_analysis_grpc_asyncio_transport_channel_mtls_with_client_cert_source( + grpc_create_channel, grpc_ssl_channel_cred +): + # Check that if channel is None, but api_mtls_endpoint and client_cert_source + # are provided, then a mTLS channel will be created. + mock_cred = mock.Mock() + + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + transport = transports.ContainerAnalysisGrpcAsyncIOTransport( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +@pytest.mark.parametrize( + "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] +) +@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) +def test_container_analysis_grpc_transport_channel_mtls_with_adc( + grpc_create_channel, api_mtls_endpoint +): + # Check that if channel and client_cert_source are None, but api_mtls_endpoint + # is provided, then a mTLS channel will be created with SSL ADC. + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + # Mock google.auth.transport.grpc.SslCredentials class. + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + mock_cred = mock.Mock() + transport = transports.ContainerAnalysisGrpcTransport( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint=api_mtls_endpoint, + client_cert_source=None, + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +@pytest.mark.parametrize( + "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] +) +@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) +def test_container_analysis_grpc_asyncio_transport_channel_mtls_with_adc( + grpc_create_channel, api_mtls_endpoint +): + # Check that if channel and client_cert_source are None, but api_mtls_endpoint + # is provided, then a mTLS channel will be created with SSL ADC. + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + # Mock google.auth.transport.grpc.SslCredentials class. + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + mock_cred = mock.Mock() + transport = transports.ContainerAnalysisGrpcAsyncIOTransport( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint=api_mtls_endpoint, + client_cert_source=None, + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel diff --git a/tests/unit/gapic/v1/test_container_analysis_client_v1.py b/tests/unit/gapic/v1/test_container_analysis_client_v1.py deleted file mode 100644 index 9febe50..0000000 --- a/tests/unit/gapic/v1/test_container_analysis_client_v1.py +++ /dev/null @@ -1,187 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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 -# -# https://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. - -"""Unit tests.""" - -import mock -import pytest - -from google.cloud.devtools import containeranalysis_v1 -from google.iam.v1 import iam_policy_pb2 -from google.iam.v1 import policy_pb2 - - -class MultiCallableStub(object): - """Stub for the grpc.UnaryUnaryMultiCallable interface.""" - - def __init__(self, method, channel_stub): - self.method = method - self.channel_stub = channel_stub - - def __call__(self, request, timeout=None, metadata=None, credentials=None): - self.channel_stub.requests.append((self.method, request)) - - response = None - if self.channel_stub.responses: - response = self.channel_stub.responses.pop() - - if isinstance(response, Exception): - raise response - - if response: - return response - - -class ChannelStub(object): - """Stub for the grpc.Channel interface.""" - - def __init__(self, responses=[]): - self.responses = responses - self.requests = [] - - def unary_unary(self, method, request_serializer=None, response_deserializer=None): - return MultiCallableStub(method, self) - - -class CustomException(Exception): - pass - - -class TestContainerAnalysisClient(object): - def test_set_iam_policy(self): - # Setup Expected Response - version = 351608024 - etag = b"21" - expected_response = {"version": version, "etag": etag} - expected_response = policy_pb2.Policy(**expected_response) - - # Mock the API response - channel = ChannelStub(responses=[expected_response]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = containeranalysis_v1.ContainerAnalysisClient() - - # Setup Request - resource = "resource-341064690" - policy = {} - - response = client.set_iam_policy(resource, policy) - assert expected_response == response - - assert len(channel.requests) == 1 - expected_request = iam_policy_pb2.SetIamPolicyRequest( - resource=resource, policy=policy - ) - actual_request = channel.requests[0][1] - assert expected_request == actual_request - - def test_set_iam_policy_exception(self): - # Mock the API response - channel = ChannelStub(responses=[CustomException()]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = containeranalysis_v1.ContainerAnalysisClient() - - # Setup request - resource = "resource-341064690" - policy = {} - - with pytest.raises(CustomException): - client.set_iam_policy(resource, policy) - - def test_get_iam_policy(self): - # Setup Expected Response - version = 351608024 - etag = b"21" - expected_response = {"version": version, "etag": etag} - expected_response = policy_pb2.Policy(**expected_response) - - # Mock the API response - channel = ChannelStub(responses=[expected_response]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = containeranalysis_v1.ContainerAnalysisClient() - - # Setup Request - resource = "resource-341064690" - - response = client.get_iam_policy(resource) - assert expected_response == response - - assert len(channel.requests) == 1 - expected_request = iam_policy_pb2.GetIamPolicyRequest(resource=resource) - actual_request = channel.requests[0][1] - assert expected_request == actual_request - - def test_get_iam_policy_exception(self): - # Mock the API response - channel = ChannelStub(responses=[CustomException()]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = containeranalysis_v1.ContainerAnalysisClient() - - # Setup request - resource = "resource-341064690" - - with pytest.raises(CustomException): - client.get_iam_policy(resource) - - def test_test_iam_permissions(self): - # Setup Expected Response - expected_response = {} - expected_response = iam_policy_pb2.TestIamPermissionsResponse( - **expected_response - ) - - # Mock the API response - channel = ChannelStub(responses=[expected_response]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = containeranalysis_v1.ContainerAnalysisClient() - - # Setup Request - resource = "resource-341064690" - permissions = [] - - response = client.test_iam_permissions(resource, permissions) - assert expected_response == response - - assert len(channel.requests) == 1 - expected_request = iam_policy_pb2.TestIamPermissionsRequest( - resource=resource, permissions=permissions - ) - actual_request = channel.requests[0][1] - assert expected_request == actual_request - - def test_test_iam_permissions_exception(self): - # Mock the API response - channel = ChannelStub(responses=[CustomException()]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = containeranalysis_v1.ContainerAnalysisClient() - - # Setup request - resource = "resource-341064690" - permissions = [] - - with pytest.raises(CustomException): - client.test_iam_permissions(resource, permissions) diff --git a/tests/unit/test_get_grafeas_client.py b/tests/unit/test_get_grafeas_client.py new file mode 100644 index 0000000..66a1652 --- /dev/null +++ b/tests/unit/test_get_grafeas_client.py @@ -0,0 +1,16 @@ +from google.cloud.devtools.containeranalysis_v1.services.container_analysis import ( + ContainerAnalysisAsyncClient, +) +from google.cloud.devtools.containeranalysis_v1.services.container_analysis import ( + ContainerAnalysisClient, +) + + +def test_get_grafeas_client(): + client = ContainerAnalysisClient() + client.get_grafeas_client() + + +def test_get_grafeas_client_async(): + async_client = ContainerAnalysisAsyncClient() + async_client.get_grafeas_client()