Skip to content

Commit ad42b4e

Browse files
Chronicle Teamcopybara-github
authored andcommitted
Add sample code for DeleteGCPAssociation.
PiperOrigin-RevId: 406890769
1 parent 74da743 commit ad42b4e

2 files changed

Lines changed: 150 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright 2021 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
"""Executable and reusable sample for deleting a GCP association.
18+
19+
In Chronicle, customers can associate their GCP organizations with their
20+
Chronicle instances. This example provides a programmatic way to delete such
21+
association.
22+
"""
23+
24+
import argparse
25+
import sys
26+
from typing import Optional, Sequence
27+
28+
from google.auth.transport import requests
29+
30+
from common import chronicle_auth
31+
32+
SERVICE_MANAGEMENT_API_BASE_URL = "https://chronicleservicemanager.googleapis.com"
33+
34+
AUTHORIZATION_SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]
35+
36+
37+
def initialize_command_line_args(
38+
args: Optional[Sequence[str]] = None) -> Optional[argparse.Namespace]:
39+
"""Initializes and checks all the command-line arguments."""
40+
parser = argparse.ArgumentParser()
41+
chronicle_auth.add_argument_credentials_file(parser)
42+
parser.add_argument(
43+
"--organization_id",
44+
type=int,
45+
required=True,
46+
help="the GCP organization ID for the association")
47+
48+
# Sanity checks for the command-line arguments.
49+
parsed_args = parser.parse_args(args)
50+
if parsed_args.organization_id >= 2**64 or parsed_args.organization_id < 0:
51+
print("Error: organization ID should not be bigger than 2^64")
52+
return None
53+
54+
return parsed_args
55+
56+
57+
def delete_gcp_association(http_session: requests.AuthorizedSession,
58+
organization_id: int) -> None:
59+
"""Deletes a GCP association with Chronicle.
60+
61+
Args:
62+
http_session: Authorized session for HTTP requests.
63+
organization_id: GCP organization ID for the association.
64+
65+
Raises:
66+
requests.exceptions.HTTPError: HTTP request resulted in an error
67+
(response.status_code >= 400).
68+
"""
69+
name = f"organizations/{organization_id}/gcpAssociations/{organization_id}"
70+
url = f"{SERVICE_MANAGEMENT_API_BASE_URL}/v1/{name}"
71+
72+
response = http_session.request("DELETE", url)
73+
74+
if response.status_code >= 400:
75+
print(response.text)
76+
response.raise_for_status()
77+
78+
79+
if __name__ == "__main__":
80+
cli = initialize_command_line_args()
81+
if not cli:
82+
sys.exit(1) # A sanity check failed.
83+
84+
session = chronicle_auth.initialize_http_session(
85+
cli.credentials_file, scopes=AUTHORIZATION_SCOPES)
86+
delete_gcp_association(session, cli.organization_id)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
"""Tests for the "delete_gcp_association" module."""
16+
17+
import unittest
18+
from unittest import mock
19+
20+
from google.auth.transport import requests
21+
22+
from . import delete_gcp_association
23+
24+
25+
class DeleteGCPAssociationTest(unittest.TestCase):
26+
27+
def test_initialize_command_line_args(self):
28+
actual = delete_gcp_association.initialize_command_line_args(
29+
["--credentials_file=./foo.json", "--organization_id=123"])
30+
self.assertIsNotNone(actual)
31+
32+
def test_initialize_command_line_args_organization_id_too_big(self):
33+
invalid_organization_id = 2**64
34+
actual = delete_gcp_association.initialize_command_line_args(
35+
[f"--organization_id={invalid_organization_id}"])
36+
self.assertIsNone(actual)
37+
38+
def test_initialize_command_line_args_negative_organization_id(self):
39+
actual = delete_gcp_association.initialize_command_line_args(
40+
["--organization_id=-1"])
41+
self.assertIsNone(actual)
42+
43+
@mock.patch.object(requests, "AuthorizedSession", autospec=True)
44+
@mock.patch.object(requests.requests, "Response", autospec=True)
45+
def test_http_error(self, mock_response, mock_session):
46+
mock_session.request.return_value = mock_response
47+
type(mock_response).status_code = mock.PropertyMock(return_value=400)
48+
mock_response.raise_for_status.side_effect = (
49+
requests.requests.exceptions.HTTPError())
50+
51+
with self.assertRaises(requests.requests.exceptions.HTTPError):
52+
delete_gcp_association.delete_gcp_association(mock_session, 123)
53+
54+
@mock.patch.object(requests, "AuthorizedSession", autospec=True)
55+
@mock.patch.object(requests.requests, "Response", autospec=True)
56+
def test_happy_path(self, mock_response, mock_session):
57+
mock_session.request.return_value = mock_response
58+
type(mock_response).status_code = mock.PropertyMock(return_value=200)
59+
60+
delete_gcp_association.delete_gcp_association(mock_session, 123)
61+
62+
63+
if __name__ == "__main__":
64+
unittest.main()

0 commit comments

Comments
 (0)