Skip to content

Commit

Permalink
feat: added offboard option to delete proxy, gcp metrics and dashboards
Browse files Browse the repository at this point in the history
  • Loading branch information
payaljindal committed Mar 21, 2024
1 parent f8333ef commit 4e1b9cc
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 2 deletions.
13 changes: 12 additions & 1 deletion tools/target-server-validator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ Validation is done by deploying a sample proxy which check if HOST & PORT is ope
* Python3.x
* Java
* Maven >= 3.9.6

* If you are pushing the data to gcp metrics, you require `roles/monitoring.editor` role.

* Please install the required Python dependencies
```
python3 -m pip install -r requirements.txt
Expand Down Expand Up @@ -112,9 +115,10 @@ export APIGEE_ACCESS_TOKEN=$(gcloud auth print-access-token) # Access

The script supports the below arguments

* `--onboard` option to create validator proxy, custom metric descriptors and dashboard
* `--onboard` option to create validator proxy, custom metric descriptors, alerting policy and dashboard
* `--scan` option to fetch target servers from Environment target servers, api proxies & csv file
* `--monitor` option to check the status of target servers and generate report or push to GCP metrics
* `--offboard` option to delete validator proxy, custom metric descriptors, alerting policy and dashboard
* `--input` Path to input properties file

To onboard, run
Expand All @@ -133,6 +137,11 @@ To monitor, run
python3 main.py --input path/to/input_file --monitor
```

To offboard, run
```
python3 main.py --input path/to/input_file --offboard
```

You can also pass multiple arguments at the same time.

--onboard deploys an API proxy to validate if the target servers are reachable or not. To use the API proxy, make sure your payloads adhere to the following format:
Expand Down Expand Up @@ -197,6 +206,8 @@ Before running the pipeline script, ensure you have the following prerequisites

*NOTE*: This pipeline will create a test notification channel with type email and email_address as `no-reply@google.com`.

- **IAM Roles**: To set up the monitoring dashboard and alerts, make sure that you have `roles/monitoring.editor` role.

- **Input Properties Template**: This script requires an `input.properties` file for the necessary configuration parameters and will create a corresponding `generated.properties` file by replacing the environment variables with their values. Ensure that the values are set properly in this file before running the script.

## Running the Pipeline
Expand Down
42 changes: 42 additions & 0 deletions tools/target-server-validator/apigee_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,48 @@ def create_api(self, api_name, proxy_bundle_path):
logger.debug(response.text)
return False, None

def get_api_deployments(self, api_name):
headers = self.auth_header.copy()

deployed_revision_url = f"{self.baseurl}/apis/{api_name}/deployments"
deployed_revision_get_response = requests.request(
"GET", deployed_revision_url, headers=headers, data={}
)
deployments = deployed_revision_get_response.json()
revision_deployements = deployments.get('deployments')
return revision_deployements

def delete_api(self, api_name):
headers = self.auth_header.copy()
revision_deployements = self.get_api_deployments(api_name)

if revision_deployements:
for revision_deployement in revision_deployements:
deployed_env = revision_deployement.get('environment')
rev = revision_deployement.get('revision')

# delete api deployment
revision_delete_url = f"{self.baseurl}/environments/{deployed_env}/apis/{api_name}/revisions/{rev}/deployments" # noqa
revision_response = requests.request(
"DELETE",
revision_delete_url, headers=headers, data={}
)
if revision_response.status_code == 200:
logger.info(f"Successfully deleted {api_name} api proxy revision {rev} in env {deployed_env}") # noqa

# proxy deletion
url = f"{self.baseurl}/apis/{api_name}"
try:
response = requests.request(
"DELETE", url, headers=headers, data={}
)
if response.status_code == 200:
logger.info(f"Api proxy {api_name} deleted successfully.")
else:
logger.error(f"Error deleting Api proxy {api_name}. ERROR-INFO - {response.json()}") # noqa
except Exception as e:
logger.error(f"Couldn't delete api proxy {api_name}. ERROR-INFO- {e}") # noqa

def get_api_revisions_deployment(self, env, api_name, api_rev): # noqa
url = (
url
Expand Down
12 changes: 11 additions & 1 deletion tools/target-server-validator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
report_metric,
create_custom_dashboard,
get_metric_descriptor,
delete_alerting_policy,
delete_dashboard,
delete_metric_descriptor,
gcs_upload_json,
download_json_from_gcs,
write_json_to_file,
Expand All @@ -48,9 +51,10 @@ def main():
# Arguments
parser = argparse.ArgumentParser(description='details',
usage='use "%(prog)s --help" for more information',formatter_class=argparse.RawTextHelpFormatter) # noqa
parser.add_argument('--onboard', action='store_true', help='Toggle to onboard validator proxy, custom metric descriptors and dashboard') # noqa
parser.add_argument('--onboard', action='store_true', help='Toggle to onboard validator proxy, custom metric descriptors, dashboard and alerting policy') # noqa
parser.add_argument('--scan', action='store_true', help='Toggle to read all resources') # noqa
parser.add_argument('--monitor', action='store_true', help='Toggle to check the status of target servers and push to GCP Logging') # noqa
parser.add_argument('--offboard', action='store_true', help='Toggle to offboard validator proxy, custom metric descriptors, dashboard and alerting policy') # noqa
parser.add_argument('--input', default='input.properties', help='Path to input file', type=str) # noqa
args = parser.parse_args()

Expand Down Expand Up @@ -333,6 +337,12 @@ def main():
logger.info(f"Dumping report to file {report_file}")
write_csv_report(report_file, final_report)

if args.offboard:
target_apigee.delete_api(cfg["validation"]["api_name"])
alerting_policies = delete_alerting_policy(cfg["target"]["org"], metric_name) # noqa
delete_dashboard(cfg["target"]["org"], alerting_policies)
delete_metric_descriptor(metric_name, cfg["target"]["org"])


if __name__ == "__main__":
main()
5 changes: 5 additions & 0 deletions tools/target-server-validator/pipeline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,14 @@ python3 main.py --scan --input "$SCRIPTPATH/generated.properties"

python3 main.py --monitor --input "$SCRIPTPATH/generated.properties"

python3 main.py --offboard --input "$SCRIPTPATH/generated.properties"

# Display Report
cat "$SCRIPTPATH/report.md"

# delete notification channel
bash "$SCRIPTPATH/test/delete_notification_channel.sh" "$APIGEE_X_ORG" "$NOTIFICATION_CHANNEL_IDS"

# deactivate venv & cleanup
deactivate
rm -rf "$VENV_PATH"
24 changes: 24 additions & 0 deletions tools/target-server-validator/test/delete_notification_channel.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/sh

# Copyright 2024 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.

PROJECT_ID=$1
NOTIFICATION_CHANNEL_IDS=$2
IFS=','

# Loop through each notification channel ID and delete it
for CHANNEL_ID in $NOTIFICATION_CHANNEL_IDS; do
gcloud beta monitoring channels delete "$CHANNEL_ID" --project="$PROJECT_ID" --quiet
done
63 changes: 63 additions & 0 deletions tools/target-server-validator/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,69 @@ def create_custom_dashboard(project_id, dashboard_title, metric_name, policy_nam
logger.error("Dashboard could not be created, since alerting policy doesn't exist") # noqa


def delete_dashboard(project_id, alerting_policies):
logger.info("Deleting GCP Monitoring Dashboard")
client = monitoring_dashboard_v1.DashboardsServiceClient()

list_request = monitoring_dashboard_v1.ListDashboardsRequest(
parent=f"projects/{project_id}",)
list_response = client.list_dashboards(request=list_request)

try:
for dashboard in list_response.dashboards:
if 'grid_layout' in dashboard and 'widgets' in dashboard.grid_layout: # noqa
for widget in dashboard.grid_layout.widgets:
if 'alert_chart' in widget:
alerting_policy = widget.alert_chart.name
if alerting_policy in alerting_policies:
delete_request = monitoring_dashboard_v1.DeleteDashboardRequest( # noqa
name=dashboard.name
)
client.delete_dashboard(request=delete_request)
logger.info(f"Deleted monitoring dashboard {dashboard.name}") # noqa
except Exception as e:
logger.error(f"Error deleting dashboard: {e}")


def delete_alerting_policy(project_id, metric_name):
logger.info(f"Deleting alerting policy with metric {metric_name}")
try:
client = monitoring_v3.AlertPolicyServiceClient()
list_request = monitoring_v3.ListAlertPoliciesRequest(
name=f"projects/{project_id}",
)
policies_list = client.list_alert_policies(request=list_request)
alerting_policy = []
for alert_policy in policies_list.alert_policies:
if 'conditions' in alert_policy:
for condition in alert_policy.conditions:
if 'condition_threshold' in condition and 'filter' in condition.condition_threshold: # noqa
if f'metric.type = "{metric_name}"' in condition.condition_threshold.filter: # noqa
request = monitoring_v3.DeleteAlertPolicyRequest(
name=alert_policy.name,
)
client.delete_alert_policy(request=request)
alerting_policy.append(alert_policy.name)
logger.info(f"Deleted alerting policy {alert_policy.name}") # noqa
return alerting_policy
except Exception as e:
logger.error(f"Couldn't delete alerting policy {alert_policy.name}. ERROR-INFO: {e}") # noqa
return []


def delete_metric_descriptor(metric_name, project_id):
logger.info(f"Deleting metric descriptor {metric_name}")
client = monitoring_v3.MetricServiceClient()
request = monitoring_v3.DeleteMetricDescriptorRequest(
name=f"projects/{project_id}/metricDescriptors/{metric_name}",
)
try:
client.delete_metric_descriptor(request=request)
logger.info(f"Deleted Metric Descriptor - {metric_name}")
except Exception as e:
logger.error(f"Couldn't delete metric descriptor {metric_name}. ERROR-INFO - {e}") # noqa


def gcs_upload_json(project_id, bucket_name, destination_blob_name, json_data):
try:
storage_client = storage.Client(project=project_id)
Expand Down

0 comments on commit 4e1b9cc

Please sign in to comment.