Skip to content

Commit e338fb7

Browse files
committed
Implemented CI/CD pipeline for automatically deploying our Function App to Azure
1 parent fd85e9c commit e338fb7

File tree

8 files changed

+177
-1
lines changed

8 files changed

+177
-1
lines changed

.github/workflows/deploy_to_azure.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: CI/CD
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
env:
10+
AZURE_FUNCTIONAPP_PACKAGE_PATH: '.'
11+
PYTHON_VERSION: '3.10'
12+
13+
jobs:
14+
build:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
20+
- name: Setup Python version
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version: ${{ env.PYTHON_VERSION }}
24+
25+
- name: Create and start virtual environment
26+
run: |
27+
python -m venv venv
28+
source venv/bin/activate
29+
30+
- name: Install dependencies
31+
run: pip install -r requirements.txt
32+
33+
- name: Zip artifact for deployment
34+
run: cd hvalfangst_function && zip -r ../release.zip ./
35+
36+
- name: Upload artifact for deployment job
37+
uses: actions/upload-artifact@v3
38+
with:
39+
name: hvalfangst-function-app
40+
path: |
41+
release.zip
42+
!venv/
43+
44+
deploy:
45+
runs-on: ubuntu-latest
46+
needs: build
47+
environment:
48+
name: 'Production'
49+
url: ${{ steps.deploy-to-function.outputs.webapp-url }}
50+
51+
steps:
52+
- name: Download artifact from build job
53+
uses: actions/download-artifact@v3
54+
with:
55+
name: hvalfangst-function-app
56+
57+
- name: Unzip artifact for deployment
58+
run: unzip release.zip
59+
60+
- name: 'Deploy to Azure Functions'
61+
uses: Azure/functions-action@v1
62+
id: deploy-to-function
63+
with:
64+
app-name: 'hvalfangstlinuxfunctionapp'
65+
slot-name: 'Production'
66+
package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}
67+
scm-do-build-during-deployment: true
68+
enable-oryx-build: true
69+
publish-profile: ${{ secrets.PUBLISH_PROFILE }}

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,16 @@ APP_INSIGHTS_NAME=hvalfangstappinsights
4949

5050
## Deallocate resources
5151

52-
The shell script [deallocate_resources](infra/deallocate_resources.sh) deletes our Azure service bus queue, namespace and resource group.
52+
The shell script [deallocate_resources](infra/deallocate_resources.sh) deletes our Azure service bus queue, namespace and resource group.
53+
54+
# CI/CD
55+
56+
A CI/CD pipeline for deploying our [Function App](hvalfangst_function/function_app.py) to Azure has been set up using a GitHub Actions workflows [script](.github/workflows/deploy_to_azure.yml). The pipeline is either triggered by a push to the main branch or by manually running the workflow.
57+
In order for the pipeline to work, the following secrets must be set in the repository settings:
58+
59+
![img.png](img.png)
60+
61+
The associated values of the aforementioned secret can be retrieved from the Azure portal, under our deployed Function App.
62+
Click on the **Get publish profile** button and copy/paste the file content into the secret value field.
63+
64+
![img_1.png](img_1.png)

hvalfangst_function/function_app.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import logging
2+
import json
3+
import pandas as pd
4+
import azure.functions as func
5+
from io import StringIO
6+
from sklearn.preprocessing import LabelEncoder
7+
8+
# Decree and declare our project as an Azure Function App subsidiary
9+
app = func.FunctionApp()
10+
11+
# Configure logging
12+
logging.basicConfig(level=logging.DEBUG)
13+
logger = logging.getLogger(__name__)
14+
15+
16+
@app.blob_trigger(arg_name="inbound", path="hvalfangstcontainer/in/input.csv", connection="")
17+
@app.blob_output(arg_name="outbound", path="hvalfangstcontainer/out/statistics.json", connection="")
18+
def blob_trigger(inbound: func.InputStream, outbound: func.Out[str]):
19+
try:
20+
# Read CSV content from the blob
21+
csv_content = inbound.read().decode("utf-8")
22+
23+
# Convert CSV content to a pandas DataFrame
24+
df = pd.read_csv(StringIO(csv_content))
25+
26+
# Label encode 'Gender' and 'State' columns
27+
label_encoder = LabelEncoder()
28+
df['Gender'] = label_encoder.fit_transform(df['Gender'])
29+
df['State'] = label_encoder.fit_transform(df['State'])
30+
31+
# Calculate correlations
32+
gender_to_income_corr = df[['Gender', 'Income']].corr().iloc[0, 1]
33+
experience_to_income_corr = df[['Experience', 'Income']].corr().iloc[0, 1]
34+
state_to_income_corr = df[['State', 'Income']].corr().iloc[0, 1]
35+
36+
# Create statistics dictionary
37+
statistics = {
38+
"gender_to_income_corr": gender_to_income_corr,
39+
"experience_to_income_corr": experience_to_income_corr,
40+
"state_to_income_corr": state_to_income_corr
41+
}
42+
43+
# Convert statistics to JSON format
44+
statistics_json = json.dumps(statistics, indent=2)
45+
46+
# Upload statistics JSON file to storage account container blob
47+
outbound.set(statistics_json)
48+
logging.info("- - - - - |File 'statistics.json' was uploaded| - - - - - ")
49+
50+
except Exception as e:
51+
logging.error(f"An error occurred: {str(e)}")
52+
return f"Error: {str(e)}"
53+
54+
55+
@app.route(route="upload_csv", auth_level=func.AuthLevel.ANONYMOUS)
56+
@app.blob_output(arg_name="outbound", path="hvalfangstcontainer/in/input.csv", connection="")
57+
def upload_csv(req: func.HttpRequest, outbound: func.Out[str]) -> str:
58+
try:
59+
# Parse raw bytes derived from request body to string
60+
string_body = req.get_body().decode("utf-8")
61+
62+
# Upload parsed string body, which conforms to CSV format
63+
outbound.set(string_body)
64+
logging.info("- - - - - |Successfully uploaded CSV content| - - - - - ")
65+
return "Successfully uploaded CSV content"
66+
67+
except Exception as e:
68+
logging.error(f"An error occurred: {str(e)}")
69+
return f"Error: {str(e)}"

hvalfangst_function/host.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"version": "2.0",
3+
"logging": {
4+
"applicationInsights": {
5+
"samplingSettings": {
6+
"isEnabled": true,
7+
"excludedTypes": "Request"
8+
}
9+
}
10+
},
11+
"extensionBundle": {
12+
"id": "Microsoft.Azure.Functions.ExtensionBundle",
13+
"version": "[3.*, 4.0.0)"
14+
}
15+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"IsEncrypted": false,
3+
"Values": {
4+
"FUNCTIONS_WORKER_RUNTIME": "python",
5+
"AzureWebJobsFeatureFlags": "EnableWorkerIndexing",
6+
"AzureWebJobsStorage": "UseDevelopmentStorage=true"
7+
}
8+
}

hvalfangst_function/reqiurements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
azure-functions==1.7.0
2+
pandas~=2.1.4
3+
scikit-learn~=1.3.2

img.png

13.3 KB
Loading

img_1.png

53.6 KB
Loading

0 commit comments

Comments
 (0)