diff --git a/.gitignore b/.gitignore index 1a01630..aa89564 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ venv *.tar.gz config.py zappa_settings.json +__pycache__ +*.pyc diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 055284f..0000000 --- a/Pipfile +++ /dev/null @@ -1,15 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -pyramid = "*" -zappa = "*" -boto3 = "*" -pytz = "*" - -[dev-packages] - -[requires] -python_version = "3.6" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index d058752..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,343 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "59f231989196004c8eecb3b0e59316baf14fb7ec42ba638d4d8a7c34cccc49d9" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.6" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "argcomplete": { - "hashes": [ - "sha256:c079ceb0b72d4d4e03531ed77e6071babb9d42c3f790d7def2c41295b4990b44", - "sha256:d97b7f3cfaa4e494ad59ed6d04c938fc5ed69b590bd8f53274e258fb1119bd1b" - ], - "version": "==1.9.3" - }, - "base58": { - "hashes": [ - "sha256:93fa54b615a7c406701a56e3d11c3a5defdbcd371f36c0452f1ac77623e42d16", - "sha256:c5fe8b00fab798b4a3393da6235bdecb143db505833e3f979890f7c6fc99f651" - ], - "version": "==1.0.0" - }, - "boto3": { - "hashes": [ - "sha256:2f3592f19847c6da02662e7a688a0d26731a36b06357c87da96257712a533dcf", - "sha256:f2be145131cdc0837fb759f04156895f86eb21e26318158c611fcb8e9b6734c5" - ], - "index": "pypi", - "version": "==1.8.8" - }, - "botocore": { - "hashes": [ - "sha256:c05733dd4fed3c79283fb77f4e9c8d5a65571c45e7eec7a6158bfed54446c3ac", - "sha256:e3c9ba538e3e60ac414a84e0bcc7ec3fb64716b6004cbf99a68aad39b7d67e08" - ], - "version": "==1.11.8" - }, - "certifi": { - "hashes": [ - "sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638", - "sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a" - ], - "version": "==2018.8.24" - }, - "cfn-flip": { - "hashes": [ - "sha256:9c61039c71995ab204c005ec46d47d0f7a109e9f1b6d63569397f8bc648a8151" - ], - "version": "==1.0.3" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "click": { - "hashes": [ - "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", - "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" - ], - "version": "==6.7" - }, - "docutils": { - "hashes": [ - "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", - "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", - "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6" - ], - "version": "==0.14" - }, - "durationpy": { - "hashes": [ - "sha256:5ef9416b527b50d722f34655becfb75e49228eb82f87b855ed1911b3314b5408" - ], - "version": "==0.5" - }, - "future": { - "hashes": [ - "sha256:e39ced1ab767b5936646cedba8bcce582398233d6a627067d4c6a454c90cfedb" - ], - "version": "==0.16.0" - }, - "futures": { - "hashes": [], - "version": "==3.2.0" - }, - "hjson": { - "hashes": [ - "sha256:1d1727faa6aaef2973921877125a3ab7c5f6d34b93233179d01770f41fab51f9" - ], - "version": "==3.0.1" - }, - "hupper": { - "hashes": [ - "sha256:20387760e4d32bd4813c2cabc8e51d92b2c22c546102a0af182c33c152cd7ede", - "sha256:6b8133e9c5cc0a8ec422a29ef3b38aea2c49a809a0af73f419a78a7015b32615" - ], - "version": "==1.3" - }, - "idna": { - "hashes": [ - "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", - "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" - ], - "version": "==2.7" - }, - "jmespath": { - "hashes": [ - "sha256:6a81d4c9aa62caf061cb517b4d9ad1dd300374cd4706997aff9cd6aedd61fc64", - "sha256:f11b4461f425740a1d908e9a3f7365c3d2e569f6ca68a2ff8bc5bcd9676edd63" - ], - "version": "==0.9.3" - }, - "kappa": { - "hashes": [ - "sha256:4b5b372872f25d619e427e04282551048dc975a107385b076b3ffc6406a15833", - "sha256:4d6b7b3accce4a0aaaac92b36237a6304f0f2fffbbe3caea3f7c9f52d12c9989" - ], - "version": "==0.6.0" - }, - "lambda-packages": { - "hashes": [ - "sha256:b5e3b81ecef5f7c1b0903b5c40813536ba2343a33868a567e4e4ff1e26243406" - ], - "version": "==0.20.0" - }, - "pastedeploy": { - "hashes": [ - "sha256:39973e73f391335fac8bc8a8a95f7d34a9f42e2775600ce2dc518d93b37ef943", - "sha256:d5858f89a255e6294e63ed46b73613c56e3b9a2d82a42f1df4d06c8421a9e3cb" - ], - "version": "==1.5.2" - }, - "placebo": { - "hashes": [ - "sha256:40269b5eeaf1ee9a28491ef982c722d1aebff577a0815528906bf392a10265a5" - ], - "version": "==0.8.2" - }, - "plaster": { - "hashes": [ - "sha256:215c921a438b5349931fd7df9a5a11a3572947f20f4bc6dd622ac08f1c3ba249", - "sha256:8351c7c7efdf33084c1de88dd0f422cbe7342534537b553c49b857b12d98c8c3" - ], - "version": "==1.0" - }, - "plaster-pastedeploy": { - "hashes": [ - "sha256:71e29b0ab90df8343bca5f0debe4706f0f8147308a78922c8c26e8252809bce4", - "sha256:c231130cb86ae414084008fe1d1797db7e61dc5eaafb5e755de21387c27c6fae" - ], - "version": "==0.6" - }, - "pyramid": { - "hashes": [ - "sha256:600f12e0d11211a55c2da970120af33214f77607ed45caba6af6c891afeaa771", - "sha256:cf89a48cb899291639686bf3d4a883b39e496151fa4871fb83cc1a3200d5b925" - ], - "index": "pypi", - "version": "==1.9.2" - }, - "python-dateutil": { - "hashes": [ - "sha256:891c38b2a02f5bb1be3e4793866c8df49c7d19baabf9c1bad62547e0b4866aca", - "sha256:95511bae634d69bc7329ba55e646499a842bc4ec342ad54a8cdb65645a0aad3c" - ], - "markers": "python_version >= '2.7'", - "version": "==2.6.1" - }, - "python-slugify": { - "hashes": [ - "sha256:57a385df7a1c6dbd15f7666eaff0ff29d3f60363b228b1197c5308ed3ba5f824", - "sha256:c3733135d3b184196fdb8844f6a74bbfb9cf6720d1dcce3254bdc434353f938f" - ], - "version": "==1.2.4" - }, - "pytz": { - "hashes": [ - "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", - "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277" - ], - "index": "pypi", - "version": "==2018.5" - }, - "pyyaml": { - "hashes": [ - "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736", - "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f", - "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab", - "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7", - "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1", - "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8", - "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4", - "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269" - ], - "version": "==3.12" - }, - "repoze.lru": { - "hashes": [ - "sha256:0429a75e19380e4ed50c0694e26ac8819b4ea7851ee1fc7583c8572db80aff77", - "sha256:f77bf0e1096ea445beadd35f3479c5cff2aa1efe604a133e67150bc8630a62ea" - ], - "version": "==0.7" - }, - "requests": { - "hashes": [ - "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", - "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" - ], - "version": "==2.19.1" - }, - "s3transfer": { - "hashes": [ - "sha256:90dc18e028989c609146e241ea153250be451e05ecc0c2832565231dacdf59c1", - "sha256:c7a9ec356982d5e9ab2d4b46391a7d6a950e2b04c472419f5fdec70cc0ada72f" - ], - "version": "==0.1.13" - }, - "six": { - "hashes": [ - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" - ], - "version": "==1.11.0" - }, - "toml": { - "hashes": [ - "sha256:8e86bd6ce8cc11b9620cb637466453d94f5d57ad86f17e98a98d1f73e3baab2d" - ], - "version": "==0.9.4" - }, - "tqdm": { - "hashes": [ - "sha256:ba650e08b8b102923a05896bf9d7e1c9cdc20b484156df0511a4bbf1f6b6f89b", - "sha256:fa6d2ea6285f56e75d7efe9259805deadc450f16066a1f82ad0629ea9be2cd0f" - ], - "version": "==4.19.1" - }, - "translationstring": { - "hashes": [ - "sha256:4ee44cfa58c52ade8910ea0ebc3d2d84bdcad9fa0422405b1801ec9b9a65b72d", - "sha256:e26c7bf383413234ed442e0980a2ebe192b95e3745288a8fd2805156d27515b4" - ], - "version": "==1.3" - }, - "troposphere": { - "hashes": [ - "sha256:c34b2ecec9ceeaca7c22c53b5440a712c08a260332f23faef436b43946576681" - ], - "version": "==2.3.3" - }, - "unidecode": { - "hashes": [ - "sha256:72f49d3729f3d8f5799f710b97c1451c5163102e76d64d20e170aedbbd923582", - "sha256:8c33dd588e0c9bc22a76eaa0c715a5434851f726131bd44a6c26471746efabf5" - ], - "version": "==1.0.22" - }, - "urllib3": { - "hashes": [ - "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", - "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" - ], - "version": "==1.23" - }, - "venusian": { - "hashes": [ - "sha256:757162c5f907e18571b6ab41b7673e5bf18cc8715abf8164292eaef4f1610668", - "sha256:9902e492c71a89a241a18b2f9950bea7e41d025cc8f3af1ea8d8201346f8577d" - ], - "version": "==1.1.0" - }, - "webob": { - "hashes": [ - "sha256:1fe722f2ab857685fc96edec567dc40b1875b21219b3b348e58cd8c4d5ea7df3", - "sha256:263690003a3e092ca1ec4df787f93feb0004e39d7bac9cba2c19a552c765894b" - ], - "version": "==1.8.2" - }, - "werkzeug": { - "hashes": [ - "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", - "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" - ], - "version": "==0.14.1" - }, - "wheel": { - "hashes": [ - "sha256:0a2e54558a0628f2145d2fc822137e322412115173e8a2ddbe1c9024338ae83c", - "sha256:80044e51ec5bbf6c894ba0bc48d26a8c20a9ba629f4ca19ea26ecfcf87685f5f" - ], - "version": "==0.31.1" - }, - "wsgi-request-logger": { - "hashes": [ - "sha256:445d7ec52799562f812006394d0b4a7064b37084c6ea6bd74ea7a2136c97ed83" - ], - "version": "==0.4.6" - }, - "zappa": { - "hashes": [ - "sha256:6b303a82698035d465e3426008a355b17733204c205e6efab7b4168ac84eb918", - "sha256:b4d310821d19c773f9d1457fa00287c154097c72e502d71f0879cb765a2130b4" - ], - "index": "pypi", - "version": "==0.46.2" - }, - "zope.deprecation": { - "hashes": [ - "sha256:7d52e134bbaaa0d72e1e2bc90f0587f1adc116c4bdf15912afaf2f1e8856b224", - "sha256:c83cfef3085d10dcb07de5a59a2d95713865befa46e0e88784c5648610fba789" - ], - "version": "==4.3.0" - }, - "zope.interface": { - "hashes": [ - "sha256:21506674d30c009271fe68a242d330c83b1b9d76d62d03d87e1e9528c61beea6", - "sha256:3d184aff0756c44fff7de69eb4cd5b5311b6f452d4de28cb08343b3f21993763", - "sha256:467d364b24cb398f76ad5e90398d71b9325eb4232be9e8a50d6a3b3c7a1c8789", - "sha256:57c38470d9f57e37afb460c399eb254e7193ac7fb8042bd09bdc001981a9c74c", - "sha256:9ada83f4384bbb12dedc152bcdd46a3ac9f5f7720d43ac3ce3e8e8b91d733c10", - "sha256:a1daf9c5120f3cc6f2b5fef8e1d2a3fb7bbbb20ed4bfdc25bc8364bc62dcf54b", - "sha256:e6b77ae84f2b8502d99a7855fa33334a1eb6159de45626905cb3e454c023f339", - "sha256:e881ef610ff48aece2f4ee2af03d2db1a146dc7c705561bd6089b2356f61641f", - "sha256:f41037260deaacb875db250021fe883bf536bf6414a4fd25b25059b02e31b120" - ], - "version": "==4.5.0" - } - }, - "develop": {} -} diff --git a/README.md b/README.md index be75f6c..24c59d1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# owntracks-aws-slt +# owntracker Serverless location tracking HTTP endpoints for use with [Owntracks](https://owntracks.org). Uses several [AWS](https://aws.amazon.com) services: @@ -16,20 +16,79 @@ Before you can use this tool, you must have these prerequisites: * [Amazon Web Services account](https://aws.amazon.com/free/) * [Administrative IAM API key pair](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html) configured on your computer -* [Pipenv](https://pipenv.readthedocs.io/en/latest/) installed on your computer -* Python 3.6 installed on your computer (I use [pyenv](https://github.com/pyenv/pyenv)) +* Python >3.6 installed on your computer (I use [pyenv](https://github.com/pyenv/pyenv)) ## Setup Assuming you have all the requirements met, the following steps will create everything needed: ```bash -$ git clone https://github.com/lalanza808/owntracks-aws-slt.git -$ cd owntracks-aws-slt -$ pipenv install --python 3.6 -$ pipenv shell -$ zappa init -$ zappa deploy +# 1. Define project meta/naming +export PROJECT=owntracker +export CODENAME=lzahq +export ENV=live + +# 2. Clone project and install Python dependencies +git clone https://github.com/lalanza808/owntracker.git owntracker +cd owntracker +python3 -m venv .venv +source .venv/bin/activate +pip install . + +# 3. Setup backend for location data +aws cloudformation deploy \ + --stack-name "${PROJECT}-${CODENAME}-${ENV}" \ + --template-file ./static/owntracker_backend.yaml + +# 4. Setup local config files +init_zappa > zappa_settings.json +init_config > owntracker/config.py + +# 5. Use Zappa to deploy +zappa deploy ``` The Zappa output should provide you with an endpoint for API Gateway - the HTTP endpoints with Python Lambda scripts being triggered behind them. + +I didn't test the above super well. It was already up and running for me and I didn't feel like messing with the data so I whipped together these little functions to assemble configs for you. Hope it helps, but you'll likely have to tinker a bit. + +## Usage + +The application works by looping through the `devices` specified in `config.py`. The first one is generated on your behalf, but feel free to substitute it's values with your own. If you want multiple devices, append another device to the dictionary in the config. + +You'll have to configure each device that you want to report in. Find a way to share the deployed API Gateway endpoint, the device name, and device secret token with each of your devices. Dial the values into the app by accessing the settings. + +![](./static/ot_settings.png) + +Once you add your API Gateway endpoint, the app should begin communicating with it and data should hit S3. You can check logs via `zappa tail`. + +I also set my Owntracks app to mode `Move` to get data flowing in all the time. That's pretty much it! Now just leave it alone and go about your business. Check back in a few weeks later and check out all the data! + +### Setup Regions + +Within the app you can optionally setup Regions - places of interest to you. You can use them for many purposes, but I typically use them for Home, Work, Cabin, etc. + +You need to create a new region, provide it a proper label, and set a radius. The app will indicate when you are within the region and the payloads deliver will include it. Regions are a great way to trigger workflows and automations. + +![](./static/ot_regions.png) + +![](./static/ot_notices.png) + +You can use simple logic to determine if you're home and have devices set to check. A simple crontab with a shell script will work fine for a simple setup. + +```bash +curl -s https://xxxxxxxxxx.execute-api.us-west-2.amazonaws.com/live/status/DEVICE | grep Home + +if [[ $? == 0 ]] +then + echo -e "[+] Looks like you're home. Doing thing." + # dothing +else + echo -e "[+] Looks like you're not home. Doing other thing." + # dootherthing +fi +``` + +### Analyzing Your Data + +This subject is a whole other animal that we'll have to cover in another document. Go here: diff --git a/owntracker/__init__.py b/owntracker/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app.py b/owntracker/app.py similarity index 81% rename from app.py rename to owntracker/app.py index ff9df2a..a1b9a87 100644 --- a/app.py +++ b/owntracker/app.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -import functions as lambda_functions -import config as app_config +import owntracker.functions as lambda_functions +import owntracker.config as app_config from wsgiref.simple_server import make_server from pyramid.view import view_config, view_defaults @@ -13,6 +13,14 @@ from pathlib import Path # Generate routes and views and build the app def configure_app(): with Configurator() as config: + # Generate simple index route + config.add_route("index", "/") + config.add_view( + lambda_functions.index, + route_name="index", + renderer="json" + ) + # Generate routes for devices for device_name in app_config.devices: device_key = app_config.devices[device_name] @@ -34,6 +42,8 @@ def configure_app(): route_name="{}-status".format(device_name), renderer="json" ) + + # Return the app app = config.make_wsgi_app() return app diff --git a/config.example.py b/owntracker/config.example.py similarity index 100% rename from config.example.py rename to owntracker/config.example.py diff --git a/functions.py b/owntracker/functions.py similarity index 85% rename from functions.py rename to owntracker/functions.py index 2c2a640..0fe12d0 100644 --- a/functions.py +++ b/owntracker/functions.py @@ -1,12 +1,12 @@ #!/usr/bin/env python +import boto3 +from pytz import utc from datetime import datetime from json import dumps, loads -from pytz import utc -import config as app_config -import schema as app_schema -import boto3 +import owntracker.config as app_config +import owntracker.schema as app_schema bucket_name = app_config.backend['s3']['name'] @@ -21,6 +21,8 @@ def response(code, method, device): "device": device } +def index(request): + return response(200, request.method, None) def status(request): """View status of the device""" @@ -64,8 +66,10 @@ def ingest(request): ACL="private", Body=owntracks.to_csv(), Bucket=bucket_name, - Key="{}/{}.csv".format( - json_data["_type"], json_data["timestamp"] + Key="live/{}/{}/{}/{}/{}.csv".format( + json_data["_type"], + now.year, now.month, now.day, + json_data["timestamp"] ), ServerSideEncryption='AES256' ) @@ -76,7 +80,7 @@ def ingest(request): ACL="private", Body=owntracks.to_json(), Bucket=bucket_name, - Key="current/{}-status.json".format(device_name), + Key="live/{}-status.json".format(device_name), ServerSideEncryption='AES256' ) diff --git a/owntracker/init.py b/owntracker/init.py new file mode 100644 index 0000000..64fb2f3 --- /dev/null +++ b/owntracker/init.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + + +import json +from os import environ +from secrets import token_urlsafe + + +project_name = f"{environ['PROJECT']}-{environ['CODENAME']}-{environ['ENV']}" + +def init_zappa(): + zappa_settings = { + "live": { + "app_function": "owntracker.app.generate_wsgi_app", + "aws_region": "us-west-2", + "profile_name": "default", + "project_name": project_name, + "runtime": "python3.6", + "s3_bucket": project_name, + "keep_warm": False, + "log_level": "CRITICAL", + "extra_permissions": [{ + "Effect": "Allow", + "Action": [ + "s3:Get*", + "s3:Put*" + ], + "Resource": [f"arn:aws:s3:::{project_name}"] + }], + "tags": { + "Project": project_name + }, + "timeout_seconds": 3 + } + } + + print(json.dumps(zappa_settings)) + + +def init_config(): + devices = { + "device0": token_urlsafe(16) + } + + backend = { + "s3": { + "name": project_name, + "region": "us-west-2", + "retention": 365 + } + } + + config_body = f"devices = {devices}\n" + config_body += f"backend = {backend}\n" + + print(config_body) diff --git a/schema.py b/owntracker/schema.py similarity index 98% rename from schema.py rename to owntracker/schema.py index 82c98b1..f380f23 100644 --- a/schema.py +++ b/owntracker/schema.py @@ -1,5 +1,6 @@ -import config as app_config import json +import owntracker.config as app_config + bucket_name = app_config.backend['s3']['name'] diff --git a/prebuild.py b/prebuild.py deleted file mode 100644 index a11a4f7..0000000 --- a/prebuild.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python - -from botocore.client import ClientError -from datetime import datetime -from json import dumps -import config as app_config -import boto3 - - -bucket_name = app_config.backend["s3"]["name"] -bucket_region = app_config.backend["s3"]["region"] -bucket_retention = app_config.backend["s3"]["retention"] -# account_id = boto3.client("sts").get_caller_identity().get("Account") -# glue_role_name = "AWSGlue-{}".format(bucket_name) -# glue_policy_name = "AWSGlue-{}-ReadOnly".format(bucket_name) -# glue_policy_arn = "arn:aws:iam::{account_id}:policy/{policy_name}".format( -# account_id=account_id, -# policy_name=glue_policy_name -# ) -# glue_managed_policy = "arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole" -# glue_assume_policy = { -# "Version": "2012-10-17", -# "Statement": [ -# { -# "Effect": "Allow", -# "Principal": { -# "Service": "glue.amazonaws.com" -# }, -# "Action": "sts:AssumeRole" -# } -# ] -# } -# glue_custom_policy = { -# "Version": "2012-10-17", -# "Statement": [ -# { -# "Effect": "Allow", -# "Action": "s3:Get*", -# "Resource": "arn:aws:s3:::{}/*".format(bucket_name) -# } -# ] -# } - - -def create_bucket(): - """Create the s3 bucket used for capturing log data if it doesn"t exist already""" - s3 = boto3.resource("s3") - s3client = boto3.client("s3") - - try: - s3.meta.client.head_bucket(Bucket=bucket_name) - except ClientError: - s3.create_bucket( - ACL="private", - Bucket=bucket_name, - CreateBucketConfiguration={ - "LocationConstraint": bucket_region - } - ) - s3client.put_bucket_lifecycle_configuration( - Bucket=bucket_name, - LifecycleConfiguration={ - "Rules": [ - { - "Expiration": { - "Days": bucket_retention - }, - "Filter": { - "Prefix": "" - }, - "ID": "{}-day-retention".format(bucket_retention), - "Status": "Enabled" - } - ] - } - ) - - return - - -def create_glue_iam(): - """Create the required IAM roles for AWS Glue to assume""" - iam = boto3.client("iam") - res = boto3.resource("iam") - - # Ensure IAM role exists - try: - res.meta.client.get_role(RoleName=glue_role_name) - pass - except ClientError: - iam.create_role( - RoleName=glue_role_name, - AssumeRolePolicyDocument=dumps(glue_assume_policy) - ) - - # Ensure custom IAM policy exists - try: - res.meta.client.get_policy(PolicyArn=glue_policy_arn) - pass - except ClientError: - iam.create_policy( - PolicyName=glue_policy_name, - PolicyDocument=dumps(glue_custom_policy) - ) - - # Ensure custom Glue policy is attached to role - try: - res.meta.client.attach_role_policy( - RoleName=glue_role_name, - PolicyArn=glue_policy_arn - ) - pass - except ClientError: - iam.attach_role_policy( - RoleName=glue_role_name, - PolicyArn=glue_policy_arn - ) - - # Ensure managed Glue policy is attached to role - try: - res.meta.client.attach_role_policy( - RoleName=glue_role_name, - PolicyArn=glue_managed_policy - ) - pass - except ClientError: - iam.attach_role_policy( - RoleName=glue_role_name, - PolicyArn=glue_managed_policy - ) - - return - - -def create_glue(): - """Sets up the Glue database and crawler""" - glue = boto3.client("glue") - - # Ensure Glue database exists - try: - glue.get_database(Name=bucket_name) - pass - except ClientError: - glue.create_database( - DatabaseInput={ - "Name": bucket_name, - "Description": "Owntracks SLT database" - } - ) - - # Ensure Glue crawler exists and run it if you create it - try: - glue.get_crawler(Name=bucket_name) - pass - except ClientError: - glue.create_crawler( - Name=bucket_name, - Description="Owntracks SLT data crawler", - Role=glue_role_name, - DatabaseName=bucket_name, - Targets={ - "S3Targets": [{ - "Path": "s3://{}/".format(bucket_name) - }] - } - ) - glue.start_crawler( - Name=bucket_name - ) - - return - - -def setup(): - print("[+] Setting up S3 bucket resources") - create_bucket() - # print("[+] Setting up Glue IAM resources") - # create_glue_iam() - # print("[+] Setting up Glue resources") - # create_glue() diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..91e8543 --- /dev/null +++ b/setup.py @@ -0,0 +1,39 @@ +from setuptools import setup, find_packages + +NAME = "owntracker" +DESCRIPTION = "Personal location tracking system - own your location data" +URL = "https://github.com/lalanza808/owntracker" +EMAIL = "lance@lzahq.tech" +AUTHOR = "Lance Allen" +REQUIRES_PYTHON = ">=3.6.0" +VERSION = "0.0.1" +REQUIRED = [ + "pyramid", + "zappa", + "boto3", + "pytz" +] +EXTRAS = {} +TESTS = [] +SETUP = [] + +setup( + name=NAME, + version=VERSION, + description=DESCRIPTION, + author=AUTHOR, + author_email=EMAIL, + include_package_data=True, + extras_require=EXTRAS, + install_requires=REQUIRED, + setup_requires=SETUP, + tests_require=TESTS, + packages=find_packages(exclude=['ez_setup']), + zip_safe=False, + entry_points={ + 'console_scripts': [ + 'init_zappa=owntracker.init:init_zappa', + 'init_config=owntracker.init:init_config' + ] + } +) diff --git a/static/analyzing_data.md b/static/analyzing_data.md new file mode 100644 index 0000000..5fc7690 --- /dev/null +++ b/static/analyzing_data.md @@ -0,0 +1,3 @@ +# Analyzing Your Data + +WIP - watch the repo for updates diff --git a/static/ot_notices.png b/static/ot_notices.png new file mode 100644 index 0000000..939599e Binary files /dev/null and b/static/ot_notices.png differ diff --git a/static/ot_regions.png b/static/ot_regions.png new file mode 100644 index 0000000..dde0206 Binary files /dev/null and b/static/ot_regions.png differ diff --git a/static/ot_settings.png b/static/ot_settings.png new file mode 100644 index 0000000..ef9dc1b Binary files /dev/null and b/static/ot_settings.png differ diff --git a/static/owntracker_backend.yaml b/static/owntracker_backend.yaml new file mode 100644 index 0000000..f96ee4a --- /dev/null +++ b/static/owntracker_backend.yaml @@ -0,0 +1,14 @@ +AWSTemplateFormatVersion: 2010-09-09 +Description: LZAHQ Owntracker backend - versioned, encrypted state storage for location data +Resources: + OwntrackerStateBucket: + Type: AWS::S3::Bucket + Properties: + AccessControl: Private + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 + BucketName: !Ref AWS::StackName + VersioningConfiguration: + Status: Enabled diff --git a/zappa_settings.example.json b/zappa_settings.example.json deleted file mode 100644 index a85dc27..0000000 --- a/zappa_settings.example.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "live": { - "app_function": "app.generate_wsgi_app", - "aws_region": "us-west-2", - "profile_name": "default", - "project_name": "MYPROJECTNAME", - "prebuild_script": "prebuild.setup", - "runtime": "python3.6", - "s3_bucket": "MYZAPPABUCKETNAME", - "keep_warm": false, - "log_level": "CRITICAL", - "extra_permissions": [{ - "Effect": "Allow", - "Action": [ - "s3:Get*", - "s3:Put*" - ], - "Resource": ["arn:aws:s3:::MYLOGBUCKETNAME"] - }], - "tags": { - "Project": "lzahq-ha" - }, - "timeout_seconds": 10 - } -}