Skip to content

Commit

Permalink
Merge branch 'main' into feature/adding-pre-commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Sachinbisht27 authored May 8, 2024
2 parents 6a3a440 + e048735 commit ade125f
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 75 deletions.
12 changes: 6 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Add env variables here
TESTING=
DATASET_ID=
DATASET_LOCATION=
DEBUG=
SLACK_TOKEN=
SLACK_CHANNEL=
ENVIRONMENT=
DATASET_ID=
TABLE_ID=
GOOGLE_APPLICATION_CREDENTIALS=
DATASET_LOCATION=
SLACK_CHANNEL=
SLACK_TOKEN=
TABLE_ID=
TESTING=
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Steps to reproduce the behavior:
2. Trigger webhook '....'
3. See the error


**Expected behavior**
A clear and concise description of what you expected to happen.

Expand Down
10 changes: 10 additions & 0 deletions .github/ISSUE_TEMPLATE/custom.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''

---


1 change: 0 additions & 1 deletion .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ A clear and concise description of any alternative solutions or features you've
**Additional context**
Add any other context or screenshots about the feature request here.


**Acceptance Criteria**
Add acceptance criteria here.

Expand Down
39 changes: 39 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
exclude: "^\
(third-party/.*)\
"

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
Expand Down Expand Up @@ -25,3 +29,38 @@ repos:
rev: v1.7.5
hooks:
- id: docformatter
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-added-large-files # prevents giant files from being committed.
- id: check-case-conflict # checks for files that would conflict in case-insensitive filesystems.
- id: check-merge-conflict # checks for files that contain merge conflict strings.
- id: check-yaml # checks yaml files for parseable syntax.
- id: detect-private-key # detects the presence of private keys.
- id: end-of-file-fixer # ensures that a file is either empty, or ends with one newline.
- id: fix-byte-order-marker # removes utf-8 byte order marker.
- id: mixed-line-ending # replaces or checks mixed line ending.
- id: requirements-txt-fixer # sorts entries in requirements.txt.
- id: trailing-whitespace # trims trailing whitespace.

- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
files: \.(js|ts|jsx|tsx|css|less|html|json|markdown|md|yaml|yml)$

- repo: https://github.com/psf/black
rev: 24.2.0
hooks:
- id: black

- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort
args: [--profile=black]

- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v17.0.6
hooks:
- id: clang-format
51 changes: 27 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,50 @@
# gcp-budget-alerts-service
The micro-service is for tracking and monitoring the spending amount at the GCP service. This micro-service elevates an alert whenever the overall budget surpasses or meets predefined thresholds.

The microservice is for tracking and monitoring the spending amount at the GCP service. This microservice elevates an alert whenever the overall budget surpasses or meets predefined thresholds.

## Architecture

![_ Expected architecture for the Alert service](https://github.com/Sachinbisht27/gcp-budget-alerts-service/assets/96137915/86e87e12-824a-46a9-ba95-07edca694285)
<h6 align="center">Architecture for the Alert Service</h6>

<h6 align="center">Architecture for the Alert Service</h6>

## Installation Guideline

### Prerequisite

1. pyenv
2. python 3.8
3. A Slack App for delivering messages on the channel. [Setup Guideline](./SLACKAPP.md)

### Steps

1. Clone the repository
```sh
git clone https://github.com/Sachinbisht27/gcp-budget-alerts-service.git
```
```sh
git clone https://github.com/Sachinbisht27/gcp-budget-alerts-service.git
```
2. Switch to project folder and setup the vertual environment
```sh
cd gcp-alerts
python -m venv venv
```
```sh
cd gcp-alerts
python -m venv venv
```
3. Activate the virtual environment
```sh
source ./venv/bin/activate
```
```sh
source ./venv/bin/activate
```
4. Install the dependencies:
```sh
pip install -r requirements-dev.txt
```
```sh
pip install -r requirements-dev.txt
```
5. Set up your .env file by copying .env.example
```sh
cp .env.example .env
```
```sh
cp .env.example .env
```
6. Add/update variables in your `.env` file for your environment.
7. Run the following command to get started with pre-commit
```sh
pre-commit install
```
```sh
pre-commit install
```
8. Start the server by following command
```sh
functions_framework --target=handle --debug
```
```sh
functions_framework --target=handle --debug
```
11 changes: 1 addition & 10 deletions SLACKAPP.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# Create a Slack App

# Steps to create an app on Slack
## Steps to create an app on Slack

1. Go to the channel on which you want to install the app
2. Go to the view all members icon on the top right of the channel

![image](https://github.com/Sachinbisht27/gcp-budget-alerts-service/assets/96137915/13cc0d92-076e-4fd5-b985-fb5102efac79)


3. Click on **add app** option
4. Click on **View app directory**
5. Click on the **build** option at the top right of the menu
Expand All @@ -17,42 +16,34 @@

![image (9)](https://github.com/Sachinbisht27/gcp-budget-alerts-service/assets/96137915/252b96f9-ce02-4c74-bcdc-0527b01c3742)


9. Under the scope section, give the app permission to write messages

![image (10)](https://github.com/Sachinbisht27/gcp-budget-alerts-service/assets/96137915/6183d4aa-4577-4fe8-af35-5c8baf28d93c)


10. Install the app to the workspace

![image (11)](https://github.com/Sachinbisht27/gcp-budget-alerts-service/assets/96137915/4b75f0a7-0c34-4abe-b31d-120ed99d8679)


11. Give permission to the app

![image (12)](https://github.com/Sachinbisht27/gcp-budget-alerts-service/assets/96137915/1ef99851-9258-4c87-88f3-22dd7971d665)


12. Once the app is installed copy the OAuth Token as it will be used for authentication.

![image (14)](https://github.com/Sachinbisht27/gcp-budget-alerts-service/assets/96137915/355d7d13-9cef-4d6b-9a6d-894c96685e95)


13. Go to the channel where you want to add app.
14. Click on the `channel name` .
15. Click on the integrations tab here.

![Untitled](https://github.com/Sachinbisht27/gcp-budget-alerts-service/assets/96137915/cc301091-2403-4541-ad55-305224bc7f7c)


16. Then click on the `Add apps`.

![Untitled (2)](https://github.com/Sachinbisht27/gcp-budget-alerts-service/assets/96137915/1cf34c76-a38a-4f85-86c7-dd4ef0241246)


17. Then recognize the newly created app and add to the channel.

![Untitled (3)](https://github.com/Sachinbisht27/gcp-budget-alerts-service/assets/96137915/17cf0b6f-d8bb-4033-81f4-8bc64e543a8e)


You are done installing the app. Enjoy!
1 change: 0 additions & 1 deletion app/helpers/bigquery_helper/bigquery_helper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from google.cloud import bigquery


client = bigquery.Client()


Expand Down
6 changes: 6 additions & 0 deletions app/helpers/notification_helper.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
"""
Notification Helper Module
This module provides functions for sending notifications.
"""

from app import services


Expand Down
2 changes: 1 addition & 1 deletion app/services/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .slack_service import *
from .budget_service import *
from .slack_service import *
58 changes: 35 additions & 23 deletions app/services/budget_service.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,31 @@
import config
import datetime
from google.cloud import bigquery


from app.services import templates
from app.helpers import bigquery_helper
from app.helpers import notification_helper
from app.services.queries import budget_table
from google.cloud import bigquery
from services import templates
from services.queries import budget_table

import config
from app.helpers import bigquery_helper, notification_helper

client = bigquery.Client()


class BudgetService:
"""Handles budget-related operations."""

def __init__(self):
self.dataset_id = config.DATASET_ID
self.table_id = config.ALERT_THRESHOLD_TABLE_ID

def handle(self, alert_attrs, alert_data):
threshold = float(alert_data.get("alertThresholdExceeded")) * 100
if not self.is_new_threshold_greater(threshold):
"""Handles budget alerts."""
threshold = self._get_threshold(alert_data)
if not self._is_new_threshold_greater(threshold):
return

interval = datetime.datetime.strptime(
alert_data.get("costIntervalStart"), "%Y-%m-%dT%H:%M:%S%z"
)
interval_str = interval.strftime("%Y-%m-%d %H:%M")

current_year_month = datetime.datetime.utcnow().strftime("%Y-%m")
year_month_of_budget_interval = interval.strftime("%Y-%m")
interval_str = self._parse_interval(alert_data)

# Check if the alert is for the previous month
if year_month_of_budget_interval != current_year_month:
# The pub/sub is sending us alerts for the previous month on the first day of the new month.
# To handle this condition, we compare the Month-Year of the budget alert interval with the current Month-Year.
if not self._is_current_month(interval_str):
return

billing_id = alert_attrs.get("billingAccountId")
Expand All @@ -45,12 +37,31 @@ def handle(self, alert_attrs, alert_data):
billing_id, threshold, budget, budget_name, interval_str
)

notify = notification_helper.notify(slack_block)
if notification_helper.notify(slack_block):
self._insert_new_threshold(cost, budget, budget_name, threshold)

def _get_threshold(self, alert_data):
"""Extracts and converts threshold from alert data."""
return float(alert_data.get("alertThresholdExceeded")) * 100

def _parse_interval(self, alert_data):
"""Parses interval from alert data."""
interval = datetime.datetime.strptime(
alert_data.get("costIntervalStart"), "%Y-%m-%dT%H:%M:%S%z"
)
return interval.strftime("%Y-%m-%d %H:%M")

if notify:
self.insert_new_threshold(cost, budget, budget_name, threshold)

def is_new_threshold_greater(self, threshold):
def _is_current_month(self, interval_str):
"""Checks if the interval falls within the current month."""
current_year_month = datetime.datetime.utcnow().strftime("%Y-%m")
year_month_of_budget_interval = interval_str[:7] # Extract year-month
return year_month_of_budget_interval == current_year_month

def _is_new_threshold_greater(self, threshold):
"""Checks if the new threshold is greater."""
query_to_get_existing_threshold = budget_table.get_existing_threshold_query(
client, self.dataset_id, self.table_id
)
Expand All @@ -59,7 +70,8 @@ def is_new_threshold_greater(self, threshold):
)
return last_existing_threshold is None or threshold > last_existing_threshold

def insert_new_threshold(self, cost, budget, budget_name, threshold):
def _insert_new_threshold(self, cost, budget, budget_name, threshold):
"""Inserts a new threshold into the database."""
query_to_insert_threshold = budget_table.get_insert_threshold_query(
client, self.dataset_id, self.table_id, cost, budget, budget_name, threshold
)
Expand Down
8 changes: 5 additions & 3 deletions app/services/slack_service.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import config
from ssl import SSLContext

import slack

import config
from utils import logger
from ssl import SSLContext


class SlackService:
Expand All @@ -16,7 +18,7 @@ def __init__(self):

def send_alert(self, slack_block):
try:
response = self.slack_client.chat_postMessage(
self.slack_client.chat_postMessage(
channel=self.channel_name,
blocks=slack_block["blocks"],
)
Expand Down
1 change: 1 addition & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

if ENVIRONMENT == "development":
from os import path

from dotenv import load_dotenv

basedir = path.abspath(path.dirname(__file__))
Expand Down
Loading

0 comments on commit ade125f

Please sign in to comment.