refactor terraform modules, add clustering lambda (#4)

This commit is contained in:
Will Freeman
2024-11-25 17:57:15 -06:00
committed by GitHub
parent cf4b93092f
commit a13b48af5b
16 changed files with 293 additions and 1057 deletions

1
.gitignore vendored
View File

@@ -44,6 +44,7 @@ terraform.tfstate*
serverless/*/lambda.zip
serverless/*/src/*
!serverless/*/src/alpr_counts.py
!serverless/*/src/alpr_clusters.py
!serverless/*/src/requirements.txt
# TODO: need a better way to handle python packages

View File

@@ -1,974 +0,0 @@
{
"clusters": [
{
"lat": 52.732404423221325,
"lon": -1.5764258991106712,
"id": 212781
},
{
"lat": 47.56745800097088,
"lon": 18.019815189320383,
"id": 26733510
},
{
"lat": 51.08509535957224,
"lon": 4.623003680320856,
"id": 26748849
},
{
"lat": 52.189532893750005,
"lon": 10.392907087500001,
"id": 30144501
},
{
"lat": 37.709913511656445,
"lon": -122.30694874969323,
"id": 53092228
},
{
"lat": 42.43266967878788,
"lon": -71.21201071111106,
"id": 61400195
},
{
"lat": 42.38934587522125,
"lon": -83.23396026017703,
"id": 62506088
},
{
"lat": 38.94347505,
"lon": -122.61952615,
"id": 85867866
},
{
"lat": 40.66668550571429,
"lon": -74.08348858857143,
"id": 104633967
},
{
"lat": -34.59345612395832,
"lon": -58.492578049479164,
"id": 241975905
},
{
"lat": 56.899951924999996,
"lon": 53.16952525,
"id": 255856115
},
{
"lat": 12.144520700000001,
"lon": -68.27292433333334,
"id": 278303396
},
{
"lat": 45.4610852375,
"lon": 12.0556567,
"id": 290025426
},
{
"lat": 40.78654777741936,
"lon": 72.34269644193549,
"id": 304747721
},
{
"lat": 42.43366232957746,
"lon": 19.25682327464788,
"id": 304836826
},
{
"lat": 40.692390192,
"lon": 16.682913912,
"id": 388279506
},
{
"lat": 45.14034804146342,
"lon": 10.177569271138205,
"id": 402291931
},
{
"lat": 42.545926,
"lon": -6.59531462857143,
"id": 486799226
},
{
"lat": 38.23947571666667,
"lon": 27.168448366666667,
"id": 506196190
},
{
"lat": 53.1019164,
"lon": 8.8266571,
"id": 550390297
},
{
"lat": 52.45130200428572,
"lon": 13.735323098571431,
"id": 559557998
},
{
"lat": 55.614296776712365,
"lon": 10.688850217808223,
"id": 583850576
},
{
"lat": -32.5698644,
"lon": -65.2043375,
"id": 598031064
},
{
"lat": 49.18208440909091,
"lon": 9.374031084090909,
"id": 653333122
},
{
"lat": 40.445664855555556,
"lon": -3.6551794444444443,
"id": 992003841
},
{
"lat": 46.5518581,
"lon": 0.3211684,
"id": 1481601113
},
{
"lat": -37.1105355,
"lon": 146.3996596,
"id": 1775755949
},
{
"lat": 41.88948087106107,
"lon": -87.69727232765273,
"id": 1931296905
},
{
"lat": 42.78282511249999,
"lon": -1.8327840625,
"id": 2161463239
},
{
"lat": -33.8872546,
"lon": 151.19508455,
"id": 2471675882
},
{
"lat": 47.82578458421053,
"lon": 11.277529773684211,
"id": 2540954475
},
{
"lat": -32.92934838461538,
"lon": -60.78882008461539,
"id": 2575002589
},
{
"lat": 47.5551079,
"lon": 7.7896264,
"id": 2712961963
},
{
"lat": 54.3215178,
"lon": 22.2975114,
"id": 2791180330
},
{
"lat": 49.678914500000005,
"lon": 25.124377799999998,
"id": 2990489748
},
{
"lat": 53.41295146666666,
"lon": 58.99338063333334,
"id": 3049183315
},
{
"lat": 54.19036753333333,
"lon": 15.795299,
"id": 3150691290
},
{
"lat": 55.7899815,
"lon": 38.4308137,
"id": 3151851060
},
{
"lat": 35.7246815,
"lon": -83.5101922,
"id": 3358378465
},
{
"lat": 42.0126763,
"lon": -4.5150433,
"id": 3379654702
},
{
"lat": 35.7279372375,
"lon": 139.98782023750002,
"id": 3818433757
},
{
"lat": 24.48206279375,
"lon": 54.54188122500001,
"id": 4003632176
},
{
"lat": 63.7669597,
"lon": 11.4547029,
"id": 4040253751
},
{
"lat": 40.5069511,
"lon": 68.7573655,
"id": 4167811245
},
{
"lat": 14.255630155555556,
"lon": 121.04478345000001,
"id": 4279277342
},
{
"lat": 42.458649771929814,
"lon": 59.61558534385965,
"id": 4375850761
},
{
"lat": 41.294033222222225,
"lon": 69.28546985555556,
"id": 4792739369
},
{
"lat": 39.6690496,
"lon": -77.7176339,
"id": 4795640515
},
{
"lat": 41.893131344444456,
"lon": 12.494813403703704,
"id": 4807332468
},
{
"lat": 44.6982733,
"lon": 17.1944327,
"id": 4902551036
},
{
"lat": 44.53316085,
"lon": 33.552830549999996,
"id": 4984788122
},
{
"lat": -31.3829959,
"lon": -52.6918941,
"id": 5043054317
},
{
"lat": 32.6570023,
"lon": 51.6663904,
"id": 5058232608
},
{
"lat": 44.5544037,
"lon": 38.1119744,
"id": 5139068722
},
{
"lat": 56.833408933333324,
"lon": 24.133528466666665,
"id": 5208722723
},
{
"lat": 52.498303250000006,
"lon": 103.936603475,
"id": 5316459794
},
{
"lat": 43.66505204,
"lon": 4.65540847,
"id": 5551987715
},
{
"lat": 43.18043762857143,
"lon": 2.745652242857143,
"id": 5671457593
},
{
"lat": 60.21154923333333,
"lon": 24.852057883333334,
"id": 5739209507
},
{
"lat": -27.57210675,
"lon": 153.10341286666667,
"id": 5746205033
},
{
"lat": 45.46046806666667,
"lon": 130.97369166666667,
"id": 5816874438
},
{
"lat": -34.7808275,
"lon": -65.2600588,
"id": 6016769883
},
{
"lat": -34.8867194,
"lon": -56.1385995,
"id": 6018626055
},
{
"lat": 48.7798547,
"lon": 5.1663791,
"id": 6153884138
},
{
"lat": 35.52809005,
"lon": -78.31248415,
"id": 6174245977
},
{
"lat": 60.13572106666666,
"lon": 11.032399925,
"id": 6188791947
},
{
"lat": 53.29233903333334,
"lon": -6.4701217,
"id": 6262199245
},
{
"lat": 45.028034357142865,
"lon": 7.674925185714286,
"id": 6281644435
},
{
"lat": 43.64369324285714,
"lon": 1.3643660857142856,
"id": 6361216535
},
{
"lat": 39.05453263,
"lon": -0.10385178999999997,
"id": 6514284851
},
{
"lat": 12.8699376,
"lon": 77.6533944,
"id": 6539731572
},
{
"lat": 59.99463045,
"lon": 30.35622135,
"id": 6713062800
},
{
"lat": 35.80318377142857,
"lon": 51.25868582857144,
"id": 6737053121
},
{
"lat": 43.69937303333334,
"lon": 7.2819903,
"id": 6745958839
},
{
"lat": 30.3164844,
"lon": 111.4785194,
"id": 6781732096
},
{
"lat": 46.8367024,
"lon": 29.624909,
"id": 6892206595
},
{
"lat": 46.14686631428571,
"lon": 6.172505428571428,
"id": 6928121637
},
{
"lat": -15.7748474,
"lon": -47.8901917,
"id": 6933101521
},
{
"lat": 43.369522,
"lon": -8.3996189,
"id": 6953949352
},
{
"lat": 39.27053958333333,
"lon": -84.48775338333333,
"id": 7105924326
},
{
"lat": 43.7658291,
"lon": 59.0272368,
"id": 7127665150
},
{
"lat": 51.14479765,
"lon": 34.3135918,
"id": 7176878182
},
{
"lat": 34.18648089666667,
"lon": -118.31329852499998,
"id": 7428089366
},
{
"lat": 48.978202499999995,
"lon": 14.474031100000001,
"id": 7526182056
},
{
"lat": 45.05926715,
"lon": 38.98956765,
"id": 7653161419
},
{
"lat": -33.44328448450705,
"lon": -70.6181848830986,
"id": 7705911244
},
{
"lat": -21.6908403,
"lon": -45.2390777,
"id": 7716567940
},
{
"lat": 46.66588165,
"lon": 32.68906265,
"id": 7757063873
},
{
"lat": 44.7902348,
"lon": 20.4538768,
"id": 7777750239
},
{
"lat": -38.94850192222222,
"lon": -68.0492845,
"id": 7795756969
},
{
"lat": 48.34348296666667,
"lon": 29.866569433333336,
"id": 7815050702
},
{
"lat": 49.198327500000005,
"lon": 16.6036377,
"id": 7956380414
},
{
"lat": -26.6066294,
"lon": -53.058778399999994,
"id": 7968782457
},
{
"lat": 50.74850008,
"lon": 25.31996091,
"id": 8247564853
},
{
"lat": 38.09874255,
"lon": 15.140991283333335,
"id": 8294434964
},
{
"lat": 10.9597213,
"lon": 106.6884558,
"id": 8359734753
},
{
"lat": 39.9521153,
"lon": 116.3294079,
"id": 8725102416
},
{
"lat": 55.06267,
"lon": 83.2075264,
"id": 8902753938
},
{
"lat": 17.1068462,
"lon": 42.6550979,
"id": 8928447784
},
{
"lat": 48.85871435714286,
"lon": 2.3348782357142857,
"id": 8929093241
},
{
"lat": 41.354143101119405,
"lon": -81.35892472201489,
"id": 8948981708
},
{
"lat": -30.0454996,
"lon": -51.0828317,
"id": 8957729203
},
{
"lat": 35.649095,
"lon": -105.96741025,
"id": 8982689960
},
{
"lat": 36.58238344,
"lon": -121.92447173000001,
"id": 9097102379
},
{
"lat": 42.8634008,
"lon": 0.7488745,
"id": 9103892625
},
{
"lat": 55.95849660000001,
"lon": -3.2760497,
"id": 9148459635
},
{
"lat": 32.6202096,
"lon": -85.404587,
"id": 9180837873
},
{
"lat": 49.7838112,
"lon": 13.3845051,
"id": 9315915453
},
{
"lat": 35.759663775,
"lon": 115.0691568,
"id": 9348368733
},
{
"lat": 36.96401410000001,
"lon": -76.63336733333334,
"id": 9366175968
},
{
"lat": 45.0490455,
"lon": 41.9839839,
"id": 9370917038
},
{
"lat": 41.0718644,
"lon": 14.336202,
"id": 9397537407
},
{
"lat": 59.0514187,
"lon": 10.0212121,
"id": 9445528897
},
{
"lat": 38.7411816,
"lon": -90.3648156,
"id": 9449104386
},
{
"lat": 33.94000148965517,
"lon": -84.49053553793105,
"id": 9559274111
},
{
"lat": -27.4200355,
"lon": -55.8794206,
"id": 9643363333
},
{
"lat": 30.20073885,
"lon": 115.0153711,
"id": 9668726265
},
{
"lat": 47.61664426666667,
"lon": -122.17338390000002,
"id": 9681269303
},
{
"lat": 35.4986225875,
"lon": 129.2936829,
"id": 9768814056
},
{
"lat": 46.06916597777778,
"lon": 13.889542333333331,
"id": 9772521365
},
{
"lat": -37.6716382,
"lon": 176.220261,
"id": 9786923901
},
{
"lat": 36.39862252272728,
"lon": 34.00519811818181,
"id": 10080446380
},
{
"lat": 43.3201357,
"lon": -2.8626823,
"id": 10095004238
},
{
"lat": 44.6726205,
"lon": 26.2035561,
"id": 10115182351
},
{
"lat": 46.8188776,
"lon": -95.8531688,
"id": 10118113557
},
{
"lat": 14.4585228,
"lon": -87.63972486,
"id": 10164408209
},
{
"lat": -17.39562645,
"lon": -66.15368415,
"id": 10213726234
},
{
"lat": 43.544113625,
"lon": 76.98817112500001,
"id": 10217000724
},
{
"lat": 37.3619472,
"lon": 126.7378353,
"id": 10289044027
},
{
"lat": 54.45324969500001,
"lon": -5.9791662049999985,
"id": 10291636996
},
{
"lat": 47.09578485,
"lon": 13.6134917,
"id": 10313224844
},
{
"lat": 38.98316559743589,
"lon": -94.97414200512819,
"id": 10416000563
},
{
"lat": 39.89354184625849,
"lon": -89.26944156122447,
"id": 10542792518
},
{
"lat": 47.009861,
"lon": -1.8843209666666667,
"id": 10693141819
},
{
"lat": 39.97560084264707,
"lon": -82.93688099999997,
"id": 10800510096
},
{
"lat": 42.67809416874999,
"lon": -79.06118295625,
"id": 10828130058
},
{
"lat": 36.04796596,
"lon": -95.997575665,
"id": 10911752786
},
{
"lat": 26.12919874,
"lon": -80.34144024,
"id": 10937129488
},
{
"lat": 41.505362009090916,
"lon": 1.0239366272727273,
"id": 10939031176
},
{
"lat": 42.964835883333336,
"lon": -77.01975345416666,
"id": 10945232508
},
{
"lat": 35.48426370970873,
"lon": -97.52778463689327,
"id": 11001547575
},
{
"lat": 35.2148465,
"lon": -100.74404185,
"id": 11019052023
},
{
"lat": 35.23040005,
"lon": -102.85522995,
"id": 11019052025
},
{
"lat": 31.542903014634152,
"lon": -97.3923150512195,
"id": 11019052027
},
{
"lat": 5.2272983,
"lon": 100.4254328,
"id": 11054240119
},
{
"lat": 39.95067505,
"lon": -86.1269044,
"id": 11128741981
},
{
"lat": 30.188785101886793,
"lon": -85.66608733207548,
"id": 11153415133
},
{
"lat": -36.0435284,
"lon": 140.3033339,
"id": 11164917239
},
{
"lat": 56.7973144,
"lon": 61.3192467,
"id": 11174880992
},
{
"lat": 24.997346200000003,
"lon": 51.548919049999995,
"id": 11193090974
},
{
"lat": 37.047959725,
"lon": 35.2987446,
"id": 11212439046
},
{
"lat": 11.33355195,
"lon": 125.6152166,
"id": 11226725209
},
{
"lat": -28.425582,
"lon": -65.72378605,
"id": 11276569104
},
{
"lat": 53.8964187,
"lon": 27.6426795,
"id": 11376019979
},
{
"lat": 29.686285447058815,
"lon": -95.49650955686272,
"id": 11390366547
},
{
"lat": 44.8383932,
"lon": -0.5691424,
"id": 11427748333
},
{
"lat": 42.855520049999996,
"lon": 13.82151575,
"id": 11573031141
},
{
"lat": 22.2698036,
"lon": 113.9346059,
"id": 11575963642
},
{
"lat": 36.91654560909091,
"lon": -2.229015381818182,
"id": 11578259953
},
{
"lat": 28.62320878235294,
"lon": 77.26864867058823,
"id": 11775354712
},
{
"lat": 32.3765604,
"lon": -104.2289746,
"id": 11805581107
},
{
"lat": 35.212911899999995,
"lon": -94.26012285,
"id": 11805581108
},
{
"lat": 37.396571172222224,
"lon": -79.19256750555556,
"id": 11873051110
},
{
"lat": 20.7819613,
"lon": 105.9021437,
"id": 11890773916
},
{
"lat": 35.32606256666667,
"lon": -84.3774859,
"id": 11914271298
},
{
"lat": -43.73740755,
"lon": 172.0457342,
"id": 11954825997
},
{
"lat": -7.4278682,
"lon": 146.6685026,
"id": 11956049019
},
{
"lat": 34.1822572,
"lon": -97.15479933333334,
"id": 12007388723
},
{
"lat": 45.7485163,
"lon": 4.8583428,
"id": 12017283553
},
{
"lat": 39.0766616,
"lon": 17.130523949999997,
"id": 12025285545
},
{
"lat": 67.9351106,
"lon": 13.0887991,
"id": 12037972445
},
{
"lat": -7.9050373,
"lon": 112.5693157,
"id": 12048260217
},
{
"lat": 50.223537,
"lon": 21.80047785,
"id": 12066868688
},
{
"lat": 41.3665805,
"lon": 60.5992706,
"id": 12079512103
},
{
"lat": 55.0995108,
"lon": 14.692537,
"id": 12083930964
},
{
"lat": 51.14395195,
"lon": 14.9802798,
"id": 12131061963
},
{
"lat": 42.44321505,
"lon": -123.3743058,
"id": 12177063007
},
{
"lat": 27.71026544,
"lon": -97.36848358,
"id": 12184882598
},
{
"lat": 34.68302499146343,
"lon": -86.6719257682927,
"id": 12187369976
},
{
"lat": -21.3832554,
"lon": -51.713266499999996,
"id": 12197869463
},
{
"lat": 52.44952085,
"lon": 16.702914075000002,
"id": 12207832066
},
{
"lat": 51.089264,
"lon": 16.9948541,
"id": 12213717696
},
{
"lat": 37.5721757882353,
"lon": -77.40523577647059,
"id": 12288735372
},
{
"lat": 25.9507272,
"lon": -97.5091525,
"id": 12291389477
},
{
"lat": 39.82849021935483,
"lon": -105.08539391290321,
"id": 12294183847
},
{
"lat": 42.82092225,
"lon": -85.907112175,
"id": 12294771482
},
{
"lat": 39.9061195,
"lon": 32.7868066,
"id": 12300082921
},
{
"lat": 38.0333561,
"lon": -84.5243437,
"id": 12331056577
},
{
"lat": 27.372223575,
"lon": -82.549264025,
"id": 12331080088
},
{
"lat": 35.126027820000004,
"lon": -89.94004336,
"id": 12331283276
},
{
"lat": 38.0016849,
"lon": -87.5823694,
"id": 12331572923
},
{
"lat": 41.2105516,
"lon": -111.96655874999999,
"id": 12332700153
},
{
"lat": 44.96608164375,
"lon": -92.72644428750002,
"id": 12333489524
},
{
"lat": 43.3229691,
"lon": -87.92466653333334,
"id": 12333501372
}
]
}

View File

@@ -1,62 +0,0 @@
import requests
import json
from sklearn.cluster import DBSCAN
from geopy.distance import great_circle
from geopy.point import Point
import numpy as np
# Set up the Overpass API query
# TODO: remove the bbox
query = """
[out:json];
node["man_made"="surveillance"]["surveillance:type"="ALPR"];
out body;
"""
# Request data from Overpass API
print("Requesting data from Overpass API...")
url = "http://overpass-api.de/api/interpreter"
response = requests.get(url, params={'data': query}, headers={'User-Agent': 'DeFlock/1.0'})
data = response.json()
print("Data received. Parsing nodes...")
# Parse nodes and extract lat/lon for clustering
coordinates = []
node_ids = []
for element in data['elements']:
if element['type'] == 'node':
coordinates.append([element['lat'], element['lon']])
node_ids.append(element['id'])
# Convert coordinates to NumPy array for DBSCAN
coordinates = np.array(coordinates)
# Define the clustering radius (10 miles in meters)
radius_miles = 50
radius_km = radius_miles * 1.60934 # 1 mile = 1.60934 km
radius_in_radians = radius_km / 6371.0 # Earth's radius in km
# Perform DBSCAN clustering
db = DBSCAN(eps=radius_in_radians, min_samples=1, algorithm='ball_tree', metric='haversine').fit(np.radians(coordinates))
labels = db.labels_
# Prepare clusters and calculate centroids
clusters = {}
for label in set(labels):
cluster_points = coordinates[labels == label]
centroid = np.mean(cluster_points, axis=0)
first_node_id = node_ids[labels.tolist().index(label)]
# Store in clusters dict with centroid and first node ID
clusters[label] = {
"lat": centroid[0],
"lon": centroid[1],
"id": first_node_id
}
# Save clusters to JSON
output = {"clusters": list(clusters.values())}
with open("alpr_clusters.json", "w") as outfile:
json.dump(output, outfile, indent=2)
print("Clustering complete. Results saved to alpr_clusters.json.")

View File

@@ -0,0 +1,31 @@
#!/bin/bash
ECR_REPO_URL=912821578123.dkr.ecr.us-east-1.amazonaws.com/alpr_clusters-lambda
set -e
# check if AWS role is assumed
if ! aws sts get-caller-identity &> /dev/null; then
echo "Error: AWS role is not assumed. Please assume the necessary role and try again."
exit 1
fi
cd src
# build Docker image
docker build -t alpr_clusters .
# tag docker image with ECR repo
docker tag alpr_clusters:latest $ECR_REPO_URL:latest
# login to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $ECR_REPO_URL
# push Docker image to ECR
docker push $ECR_REPO_URL:latest
# update lambda function
export AWS_PAGER=""
aws lambda update-function-code --function-name alpr_clusters --image-uri $ECR_REPO_URL:latest
echo "Deployed!"

View File

@@ -0,0 +1,78 @@
import boto3
import requests
import json
from sklearn.cluster import DBSCAN
import numpy as np
def get_clusters():
# Set up the Overpass API query
query = """
[out:json];
node["man_made"="surveillance"]["surveillance:type"="ALPR"];
out body;
"""
# Request data from Overpass API
print("Requesting data from Overpass API.")
url = "http://overpass-api.de/api/interpreter"
response = requests.get(url, params={'data': query}, headers={'User-Agent': 'DeFlock/1.0'})
data = response.json()
print("Data received. Parsing nodes...")
# Parse nodes and extract lat/lon for clustering
coordinates = []
node_ids = []
for element in data['elements']:
if element['type'] == 'node':
coordinates.append([element['lat'], element['lon']])
node_ids.append(element['id'])
# Convert coordinates to NumPy array for DBSCAN
coordinates = np.array(coordinates)
# Define the clustering radius (10 miles in meters)
radius_miles = 50
radius_km = radius_miles * 1.60934 # 1 mile = 1.60934 km
radius_in_radians = radius_km / 6371.0 # Earth's radius in km
# Perform DBSCAN clustering
db = DBSCAN(eps=radius_in_radians, min_samples=1, algorithm='ball_tree', metric='haversine').fit(np.radians(coordinates))
labels = db.labels_
# Prepare clusters and calculate centroids
clusters = {}
for label in set(labels):
cluster_points = coordinates[labels == label]
centroid = np.mean(cluster_points, axis=0)
first_node_id = node_ids[labels.tolist().index(label)]
# Store in clusters dict with centroid and first node ID
clusters[label] = {
"lat": centroid[0],
"lon": centroid[1],
"id": first_node_id
}
output = {"clusters": list(clusters.values())}
print("Clustering complete.")
return output
def lambda_handler(event, context):
alpr_clusters = get_clusters()
s3 = boto3.client('s3')
bucket = 'deflock-clusters'
key = 'alpr_clusters.json'
s3.put_object(
Bucket=bucket,
Key=key,
Body=json.dumps(alpr_clusters),
ContentType='application/json'
)
return {
'statusCode': 200,
'body': 'Successfully fetched ALPR counts.',
}

View File

@@ -0,0 +1,4 @@
boto3
requests
scikit-learn
numpy

17
terraform/main.tf Normal file
View File

@@ -0,0 +1,17 @@
provider "aws" {
region = "us-east-1"
}
module "alpr_counts" {
module_name = "alpr_counts"
source = "./modules/alpr_counts"
deflock_stats_bucket = var.deflock_stats_bucket
rate = "rate(60 minutes)"
}
module "alpr_clusters" {
module_name = "alpr_clusters"
source = "./modules/alpr_clusters"
deflock_stats_bucket = var.deflock_stats_bucket
rate = "rate(1 day)"
}

View File

@@ -0,0 +1,72 @@
resource "aws_iam_role" "lambda_role" {
name = "${var.module_name}_lambda_s3_write_role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
resource "aws_iam_policy" "lambda_s3_write_policy" {
name = "${var.module_name}_lambda_s3_write_policy"
description = "Policy for Lambda to write to S3 bucket ${var.deflock_stats_bucket}"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:PutObject",
"s3:PutObjectAcl"
]
Effect = "Allow"
Resource = "arn:aws:s3:::${var.deflock_stats_bucket}/${var.output_filename}"
}
]
})
}
resource "aws_iam_role_policy_attachment" "lambda_s3_write_attachment" {
role = aws_iam_role.lambda_role.name
policy_arn = aws_iam_policy.lambda_s3_write_policy.arn
}
resource "aws_lambda_function" "overpass_lambda" {
function_name = var.module_name
role = aws_iam_role.lambda_role.arn
package_type = "Image"
image_uri = "${aws_ecr_repository.lambda_repository.repository_url}:latest"
timeout = 90
}
resource "aws_cloudwatch_event_rule" "lambda_rule" {
name = "${var.module_name}_rule"
description = "Rule to trigger ${var.module_name} lambda"
schedule_expression = var.rate
}
resource "aws_cloudwatch_event_target" "lambda_target" {
target_id = "${var.module_name}_target"
rule = aws_cloudwatch_event_rule.lambda_rule.name
arn = aws_lambda_function.overpass_lambda.arn
}
resource "aws_lambda_permission" "allow_cloudwatch_to_call_lambda" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.overpass_lambda.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.lambda_rule.arn
}
resource "aws_ecr_repository" "lambda_repository" {
name = "${var.module_name}-lambda"
}

View File

@@ -0,0 +1,3 @@
output "ecr_repository_url" {
value = aws_ecr_repository.lambda_repository.repository_url
}

View File

@@ -0,0 +1,16 @@
variable "module_name" {
description = "Name of the module"
}
variable "output_filename" {
description = "Filename for the ALPR clusters JSON file"
default = "alpr_clusters.json"
}
variable "deflock_stats_bucket" {
description = "S3 bucket for the ALPR clusters JSON file"
}
variable "rate" {
description = "Rate at which to run the Lambda function"
}

View File

@@ -1,12 +1,3 @@
locals {
alpr_counts_filename = "alpr-counts.json"
}
provider "aws" {
region = "us-east-1"
}
resource "aws_iam_role" "lambda_role" {
name = "lambda_s3_write_role"
@@ -26,7 +17,7 @@ resource "aws_iam_role" "lambda_role" {
resource "aws_iam_policy" "lambda_s3_write_policy" {
name = "lambda_s3_write_policy"
description = "Policy for Lambda to write to S3 bucket deflock-clusters"
description = "Policy for Lambda to write to S3 bucket ${var.deflock_stats_bucket}"
policy = jsonencode({
Version = "2012-10-17"
@@ -37,7 +28,7 @@ resource "aws_iam_policy" "lambda_s3_write_policy" {
"s3:PutObjectAcl"
]
Effect = "Allow"
Resource = "arn:aws:s3:::deflock-clusters/${local.alpr_counts_filename}"
Resource = "arn:aws:s3:::${var.deflock_stats_bucket}/${var.output_filename}"
}
]
})
@@ -51,43 +42,43 @@ resource "aws_iam_role_policy_attachment" "lambda_s3_write_attachment" {
resource "null_resource" "pip_install" {
provisioner "local-exec" {
command = <<EOT
cd ${path.module}/../serverless/alpr-counts/src
cd ${path.root}/../serverless/${var.module_name}/src
pip3 install -r requirements.txt -t .
EOT
}
triggers = {
# Re-run the provisioner if the file changes
file_hash = "${filemd5("${path.module}/../serverless/alpr-counts/src/alpr_counts.py")}"
file_hash = "${filemd5("${path.root}/../serverless/${var.module_name}/src/${var.module_name}.py")}"
}
}
data "archive_file" "python_lambda_package" {
type = "zip"
source_dir = "${path.module}/../serverless/alpr-counts/src"
output_path = "${path.module}/../serverless/alpr-counts/lambda.zip"
source_dir = "${path.root}/../serverless/${var.module_name}/src"
output_path = "${path.root}/../serverless/${var.module_name}/lambda.zip"
depends_on = [ null_resource.pip_install ]
}
resource "aws_lambda_function" "overpass_lambda" {
filename = data.archive_file.python_lambda_package.output_path
function_name = "alpr_counts"
function_name = var.module_name
role = aws_iam_role.lambda_role.arn
handler = "alpr_counts.lambda_handler"
handler = "${var.module_name}.lambda_handler"
runtime = "python3.9"
source_code_hash = data.archive_file.python_lambda_package.output_base64sha256
timeout = 30
}
resource "aws_cloudwatch_event_rule" "lambda_rule" {
name = "alpr_counts_rule"
description = "Rule to trigger alpr_counts lambda"
schedule_expression = "rate(60 minutes)"
name = "${var.module_name}_rule"
description = "Rule to trigger ${var.module_name} lambda"
schedule_expression = var.rate
}
resource "aws_cloudwatch_event_target" "lambda_target" {
target_id = "alpr_counts_target"
target_id = "${var.module_name}_target"
rule = aws_cloudwatch_event_rule.lambda_rule.name
arn = aws_lambda_function.overpass_lambda.arn
}

View File

@@ -0,0 +1,16 @@
variable "module_name" {
description = "Name of the module"
}
variable "output_filename" {
description = "Filename for the ALPR counts JSON file"
default = "alpr-counts.json"
}
variable "deflock_stats_bucket" {
description = "S3 bucket for the ALPR counts JSON file"
}
variable "rate" {
description = "Rate at which to run the Lambda function"
}

View File

@@ -0,0 +1,39 @@
resource "aws_s3_bucket" "terraform_state" {
bucket = "deflock-terraform-state"
tags = {
Name = "Terraform State Bucket"
}
}
resource "aws_s3_bucket_versioning" "enable_versioning" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_dynamodb_table" "terraform_locks" {
name = "deflock-terraform-state-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = {
Name = "Terraform State Lock Table"
}
}
terraform {
backend "s3" {
bucket = "deflock-terraform-state"
key = "global/s3/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-state-locks"
encrypt = true
}
}

4
terraform/variables.tf Normal file
View File

@@ -0,0 +1,4 @@
variable "deflock_stats_bucket" {
description = "S3 bucket for the ALPR counts JSON file"
default = "deflock-clusters"
}