From 3feed14566ce289ac1d98f65fafc2e78fc64db87 Mon Sep 17 00:00:00 2001 From: Sijun Liu Date: Sat, 20 May 2023 22:02:35 -0700 Subject: [PATCH 1/4] feat: add metrics (part 1) --- google/auth/credentials.py | 17 ++++ google/auth/metrics.py | 165 +++++++++++++++++++++++++++++++++++++ tests/test_credentials.py | 17 ++++ tests/test_metrics.py | 74 +++++++++++++++++ 4 files changed, 273 insertions(+) create mode 100644 google/auth/metrics.py create mode 100644 tests/test_metrics.py diff --git a/google/auth/credentials.py b/google/auth/credentials.py index 4c0af7a6b..134c182d3 100644 --- a/google/auth/credentials.py +++ b/google/auth/credentials.py @@ -22,6 +22,7 @@ from google.auth import _helpers, environment_vars from google.auth import exceptions +from google.auth import metrics @six.add_metaclass(abc.ABCMeta) @@ -100,6 +101,21 @@ def refresh(self, request): # (pylint doesn't recognize that this is abstract) raise NotImplementedError("Refresh must be implemented") + def _metric_header_for_usage(self): + """The x-goog-api-client header for token usage metric. + + This header will be added to the API service requests in before_request + method. For example, "cred-type/sa-jwt" means service account self + signed jwt access token is used in the API service request + authorization header. Children credentials classes need to override + this method to provide the header value, if the token usage metric is + needed. + + Returns: + str: The x-goog-api-client header value. + """ + return None + def apply(self, headers, token=None): """Apply the token to the authentication header. @@ -133,6 +149,7 @@ def before_request(self, request, method, url, headers): # the http request.) if not self.valid: self.refresh(request) + metrics.add_metric_header(headers, self._metric_header_for_usage()) self.apply(headers) diff --git a/google/auth/metrics.py b/google/auth/metrics.py new file mode 100644 index 000000000..132932bec --- /dev/null +++ b/google/auth/metrics.py @@ -0,0 +1,165 @@ +# Copyright 2023 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. + +""" We use x-goog-api-client header to report metrics. This module provides +the constants and helper methods to construct x-goog-api-client header. +""" + +import platform + +from google.auth import version + + +API_CLIENT_HEADER = "x-goog-api-client" + +# Auth request type +REQUEST_TYPE_ACCESS_TOKEN = "auth-request-type/at" +REQUEST_TYPE_ID_TOKEN = "auth-request-type/it" +REQUEST_TYPE_MDS_PING = "auth-request-type/mds" +REQUEST_TYPE_REAUTH_START = "auth-request-type/re-start" +REQUEST_TYPE_REAUTH_CONTINUE = "auth-request-type/re-cont" + +# Credential type +CRED_TYPE_USER = "cred-type/u" +CRED_TYPE_SA_ASSERTION = "cred-type/sa" +CRED_TYPE_SA_JWT = "cred-type/jwt" +CRED_TYPE_SA_MDS = "cred-type/mds" +CRED_TYPE_SA_IMPERSONATE = "cred-type/imp" + + +# Versions +def auth_lib_version(): + return "auth/" + version.__version__ + + +def python_version(): + return "gl-python/" + platform.python_version() + + +# Token request metric header values + +# x-goog-api-client header value for access token request via metadata server. +# Example: "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/mds" +def token_request_access_token_mds(): + return "{} {} {} {}".format( + python_version(), + auth_lib_version(), + REQUEST_TYPE_ACCESS_TOKEN, + CRED_TYPE_SA_MDS, + ) + + +# x-goog-api-client header value for ID token request via metadata server. +# Example: "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/mds" +def token_request_id_token_mds(): + return "{} {} {} {}".format( + python_version(), auth_lib_version(), REQUEST_TYPE_ID_TOKEN, CRED_TYPE_SA_MDS + ) + + +# x-goog-api-client header value for impersonated credentials access token request. +# Example: "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/imp" +def token_request_access_token_impersonate(): + return "{} {} {} {}".format( + python_version(), + auth_lib_version(), + REQUEST_TYPE_ACCESS_TOKEN, + CRED_TYPE_SA_IMPERSONATE, + ) + + +# x-goog-api-client header value for impersonated credentials ID token request. +# Example: "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/imp" +def token_request_id_token_impersonate(): + return "{} {} {} {}".format( + python_version(), + auth_lib_version(), + REQUEST_TYPE_ID_TOKEN, + CRED_TYPE_SA_IMPERSONATE, + ) + + +# x-goog-api-client header value for service account credentials access token +# request (assertion flow). +# Example: "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/sa" +def token_request_access_token_sa_assertion(): + return "{} {} {} {}".format( + python_version(), + auth_lib_version(), + REQUEST_TYPE_ACCESS_TOKEN, + CRED_TYPE_SA_ASSERTION, + ) + + +# x-goog-api-client header value for service account credentials ID token +# request (assertion flow). +# Example: "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/sa" +def token_request_id_token_sa_assertion(): + return "{} {} {} {}".format( + python_version(), + auth_lib_version(), + REQUEST_TYPE_ID_TOKEN, + CRED_TYPE_SA_ASSERTION, + ) + + +# x-goog-api-client header value for user credentials token request. +# Example: "gl-python/3.7 auth/1.1 cred-type/u" +def token_request_user(): + return "{} {} {}".format(python_version(), auth_lib_version(), CRED_TYPE_USER) + + +# Miscellenous metrics + +# x-goog-api-client header value for metadata server ping. +# Example: "gl-python/3.7 auth/1.1 auth-request-type/mds" +def mds_ping(): + return "{} {} {}".format( + python_version(), auth_lib_version(), REQUEST_TYPE_MDS_PING + ) + + +# x-goog-api-client header value for reauth start endpoint calls. +# Example: "gl-python/3.7 auth/1.1 auth-request-type/re-start" +def reauth_start(): + return "{} {} {}".format( + python_version(), auth_lib_version(), REQUEST_TYPE_REAUTH_START + ) + + +# x-goog-api-client header value for reauth continue endpoint calls. +# Example: "gl-python/3.7 auth/1.1 cred-type/re-cont" +def reauth_continue(): + return "{} {} {}".format( + python_version(), auth_lib_version(), REQUEST_TYPE_REAUTH_CONTINUE + ) + + +def add_metric_header(headers, metric_header_value): + """Add x-goog-api-client header with the given value. + + Args: + headers (Mapping[str, str]): The headers to which we will add the + metric header. + metric_header_value (Optional[str]): If value is None, do nothing; + if headers already has a x-goog-api-client header, append the value + to the existing header; otherwise add a new x-goog-api-client + header with the given value. + """ + if not metric_header_value: + return + if API_CLIENT_HEADER not in headers: + headers[API_CLIENT_HEADER] = metric_header_value + else: + headers[API_CLIENT_HEADER] += " " + metric_header_value diff --git a/tests/test_credentials.py b/tests/test_credentials.py index da074143a..d1f391806 100644 --- a/tests/test_credentials.py +++ b/tests/test_credentials.py @@ -28,6 +28,14 @@ def with_quota_project(self, quota_project_id): raise NotImplementedError() +class CredentialsImplWithMetrics(credentials.Credentials): + def refresh(self, request): + self.token = request + + def _metric_header_for_usage(self): + return "foo" + + def test_credentials_constructor(): credentials = CredentialsImpl() assert not credentials.token @@ -83,6 +91,15 @@ def test_before_request(): assert headers["authorization"] == "Bearer token" +def test_before_request_metrics(): + credentials = CredentialsImplWithMetrics() + request = "token" + headers = {} + + credentials.before_request(request, "http://example.com", "GET", headers) + assert headers["x-goog-api-client"] == "foo" + + def test_anonymous_credentials_ctor(): anon = credentials.AnonymousCredentials() assert anon.token is None diff --git a/tests/test_metrics.py b/tests/test_metrics.py new file mode 100644 index 000000000..e2498ef13 --- /dev/null +++ b/tests/test_metrics.py @@ -0,0 +1,74 @@ +# Copyright 2014 Google Inc. +# +# 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 platform + +import mock + +from google.auth import metrics +from google.auth import version + + +def test_add_metric_header(): + headers = {} + metrics.add_metric_header(headers, None) + assert headers == {} + + headers = {"x-goog-api-client": "foo"} + metrics.add_metric_header(headers, "bar") + assert headers == {"x-goog-api-client": "foo bar"} + + headers = {} + metrics.add_metric_header(headers, "bar") + assert headers == {"x-goog-api-client": "bar"} + + +def test_versions(): + assert metrics.auth_lib_version() == "auth/" + version.__version__ + assert metrics.python_version() == "gl-python/" + platform.python_version() + + +@mock.patch("google.auth.metrics.auth_lib_version", return_value="auth/1.1") +@mock.patch("google.auth.metrics.python_version", return_value="gl-python/3.7") +def test_metric_values(mock_python_version, mock_auth_lib_version): + assert ( + metrics.token_request_access_token_mds() + == "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/mds" + ) + assert ( + metrics.token_request_id_token_mds() + == "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/mds" + ) + assert ( + metrics.token_request_access_token_impersonate() + == "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/imp" + ) + assert ( + metrics.token_request_id_token_impersonate() + == "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/imp" + ) + assert ( + metrics.token_request_access_token_sa_assertion() + == "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/sa" + ) + assert ( + metrics.token_request_id_token_sa_assertion() + == "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/sa" + ) + assert metrics.token_request_user() == "gl-python/3.7 auth/1.1 cred-type/u" + assert metrics.mds_ping() == "gl-python/3.7 auth/1.1 auth-request-type/mds" + assert metrics.reauth_start() == "gl-python/3.7 auth/1.1 auth-request-type/re-start" + assert ( + metrics.reauth_continue() == "gl-python/3.7 auth/1.1 auth-request-type/re-cont" + ) From 4af1732ff04ecc20da99676d93e1acf14ae52ac0 Mon Sep 17 00:00:00 2001 From: Sijun Liu Date: Mon, 22 May 2023 14:40:32 -0700 Subject: [PATCH 2/4] update --- google/auth/metrics.py | 59 +++++++++++++----------------------------- tests/test_metrics.py | 13 ++++++---- 2 files changed, 26 insertions(+), 46 deletions(-) diff --git a/google/auth/metrics.py b/google/auth/metrics.py index 132932bec..f7303282c 100644 --- a/google/auth/metrics.py +++ b/google/auth/metrics.py @@ -39,12 +39,8 @@ # Versions -def auth_lib_version(): - return "auth/" + version.__version__ - - -def python_version(): - return "gl-python/" + platform.python_version() +def python_and_auth_lib_version(): + return "gl-python/{} auth/{}".format(platform.python_version(), version.__version__) # Token request metric header values @@ -52,28 +48,24 @@ def python_version(): # x-goog-api-client header value for access token request via metadata server. # Example: "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/mds" def token_request_access_token_mds(): - return "{} {} {} {}".format( - python_version(), - auth_lib_version(), - REQUEST_TYPE_ACCESS_TOKEN, - CRED_TYPE_SA_MDS, + return "{} {} {}".format( + python_and_auth_lib_version(), REQUEST_TYPE_ACCESS_TOKEN, CRED_TYPE_SA_MDS ) # x-goog-api-client header value for ID token request via metadata server. # Example: "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/mds" def token_request_id_token_mds(): - return "{} {} {} {}".format( - python_version(), auth_lib_version(), REQUEST_TYPE_ID_TOKEN, CRED_TYPE_SA_MDS + return "{} {} {}".format( + python_and_auth_lib_version(), REQUEST_TYPE_ID_TOKEN, CRED_TYPE_SA_MDS ) # x-goog-api-client header value for impersonated credentials access token request. # Example: "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/imp" def token_request_access_token_impersonate(): - return "{} {} {} {}".format( - python_version(), - auth_lib_version(), + return "{} {} {}".format( + python_and_auth_lib_version(), REQUEST_TYPE_ACCESS_TOKEN, CRED_TYPE_SA_IMPERSONATE, ) @@ -82,11 +74,8 @@ def token_request_access_token_impersonate(): # x-goog-api-client header value for impersonated credentials ID token request. # Example: "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/imp" def token_request_id_token_impersonate(): - return "{} {} {} {}".format( - python_version(), - auth_lib_version(), - REQUEST_TYPE_ID_TOKEN, - CRED_TYPE_SA_IMPERSONATE, + return "{} {} {}".format( + python_and_auth_lib_version(), REQUEST_TYPE_ID_TOKEN, CRED_TYPE_SA_IMPERSONATE ) @@ -94,11 +83,8 @@ def token_request_id_token_impersonate(): # request (assertion flow). # Example: "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/sa" def token_request_access_token_sa_assertion(): - return "{} {} {} {}".format( - python_version(), - auth_lib_version(), - REQUEST_TYPE_ACCESS_TOKEN, - CRED_TYPE_SA_ASSERTION, + return "{} {} {}".format( + python_and_auth_lib_version(), REQUEST_TYPE_ACCESS_TOKEN, CRED_TYPE_SA_ASSERTION ) @@ -106,18 +92,15 @@ def token_request_access_token_sa_assertion(): # request (assertion flow). # Example: "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/sa" def token_request_id_token_sa_assertion(): - return "{} {} {} {}".format( - python_version(), - auth_lib_version(), - REQUEST_TYPE_ID_TOKEN, - CRED_TYPE_SA_ASSERTION, + return "{} {} {}".format( + python_and_auth_lib_version(), REQUEST_TYPE_ID_TOKEN, CRED_TYPE_SA_ASSERTION ) # x-goog-api-client header value for user credentials token request. # Example: "gl-python/3.7 auth/1.1 cred-type/u" def token_request_user(): - return "{} {} {}".format(python_version(), auth_lib_version(), CRED_TYPE_USER) + return "{} {}".format(python_and_auth_lib_version(), CRED_TYPE_USER) # Miscellenous metrics @@ -125,25 +108,19 @@ def token_request_user(): # x-goog-api-client header value for metadata server ping. # Example: "gl-python/3.7 auth/1.1 auth-request-type/mds" def mds_ping(): - return "{} {} {}".format( - python_version(), auth_lib_version(), REQUEST_TYPE_MDS_PING - ) + return "{} {}".format(python_and_auth_lib_version(), REQUEST_TYPE_MDS_PING) # x-goog-api-client header value for reauth start endpoint calls. # Example: "gl-python/3.7 auth/1.1 auth-request-type/re-start" def reauth_start(): - return "{} {} {}".format( - python_version(), auth_lib_version(), REQUEST_TYPE_REAUTH_START - ) + return "{} {}".format(python_and_auth_lib_version(), REQUEST_TYPE_REAUTH_START) # x-goog-api-client header value for reauth continue endpoint calls. # Example: "gl-python/3.7 auth/1.1 cred-type/re-cont" def reauth_continue(): - return "{} {} {}".format( - python_version(), auth_lib_version(), REQUEST_TYPE_REAUTH_CONTINUE - ) + return "{} {}".format(python_and_auth_lib_version(), REQUEST_TYPE_REAUTH_CONTINUE) def add_metric_header(headers, metric_header_value): diff --git a/tests/test_metrics.py b/tests/test_metrics.py index e2498ef13..7a733cbaf 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -35,13 +35,16 @@ def test_add_metric_header(): def test_versions(): - assert metrics.auth_lib_version() == "auth/" + version.__version__ - assert metrics.python_version() == "gl-python/" + platform.python_version() + assert metrics.python_and_auth_lib_version() == "gl-python/{} auth/{}".format( + platform.python_version(), version.__version__ + ) -@mock.patch("google.auth.metrics.auth_lib_version", return_value="auth/1.1") -@mock.patch("google.auth.metrics.python_version", return_value="gl-python/3.7") -def test_metric_values(mock_python_version, mock_auth_lib_version): +@mock.patch( + "google.auth.metrics.python_and_auth_lib_version", + return_value="gl-python/3.7 auth/1.1", +) +def test_metric_values(mock_python_and_auth_lib_version): assert ( metrics.token_request_access_token_mds() == "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/mds" From db624a4f6aa76a63adfda2b942ce42725b3dfd8c Mon Sep 17 00:00:00 2001 From: Sijun Liu Date: Tue, 23 May 2023 10:39:14 -0700 Subject: [PATCH 3/4] update --- tests/test_metrics.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 7a733cbaf..535b65451 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -34,10 +34,12 @@ def test_add_metric_header(): assert headers == {"x-goog-api-client": "bar"} -def test_versions(): - assert metrics.python_and_auth_lib_version() == "gl-python/{} auth/{}".format( - platform.python_version(), version.__version__ - ) +@mock.patch.object(platform, "python_version", return_value="3.7") +def test_versions(mock_python_version): + version_save = version.__version__ + version.__version__ = "1.1" + assert metrics.python_and_auth_lib_version() == "gl-python/3.7 auth/1.1" + version.__version__ = version_save @mock.patch( From d4fc92c1651ef86e6d304b3551452291b03a4750 Mon Sep 17 00:00:00 2001 From: Sijun Liu Date: Tue, 23 May 2023 13:56:53 -0700 Subject: [PATCH 4/4] update sys test cred --- system_tests/secrets.tar.enc | Bin 10324 -> 10324 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/system_tests/secrets.tar.enc b/system_tests/secrets.tar.enc index 033c2df24d9e356914d0baf3270af31014e68dc7..2f20ce4078f7b94e1eba5662d26e5f147aef5f03 100644 GIT binary patch literal 10324 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 mIS9h9r&p5?WQwpd+Um0A