Merge branch 'main' into ik/setup-uswds

This commit is contained in:
igorkorenfeld 2022-09-12 18:22:14 -04:00
commit 0561d1f0ae
No known key found for this signature in database
GPG key ID: 826947A4B867F659
23 changed files with 552 additions and 101 deletions

View file

@ -35,10 +35,10 @@ cf login -a api.fr.cloud.gov --sso
- [ ] Setup [commit signing in Github](#setting-up-commit-signing) and with git locally.
### Steps for the onboarder
- [ ] Add the onboardee to cloud.gov org and relevant spaces as a SpaceDeveloper
- [ ] Add the onboardee to cloud.gov org (cisa-getgov-prototyping) and relevant spaces (unstable) as a SpaceDeveloper
```bash
cf set-space-role <cloud.account@email.gov> sandbox-gsa dotgov-poc SpaceDeveloper
cf set-space-role <cloud.account@email.gov> cisa-getgov-prototyping unstable SpaceDeveloper
```
- [ ] Add the onboardee to our login.gov sandbox team (`.gov registrar poc`) via the [dashboard](https://dashboard.int.identitysandbox.gov/)

View file

@ -3,8 +3,7 @@ name: Build and deploy
# This workflow runs on pushes to main (typically,
# a merged pull request) and on pushes of tagged commits.
# Pushes to main will deploy to Unstable; tagged commits
# will deploy to Staging
# Pushes to main will deploy to Staging
on:
push:
@ -17,9 +16,9 @@ on:
workflow_dispatch:
jobs:
deploy-unstable:
deploy-staging:
# if this job runs on a branch, we deduce that code
# has been pushed to main and should be deployed to unstable
# has been pushed to main and should be deployed to staging
if: ${{ github.ref_type == 'branch' }}
runs-on: ubuntu-latest
steps:
@ -30,13 +29,8 @@ jobs:
env:
DEPLOY_NOW: thanks
with:
cf_username: ${{ secrets.CF_USERNAME }}
cf_password: ${{ secrets.CF_PASSWORD }}
cf_org: sandbox-gsa
cf_space: dotgov-poc
push_arguments: "-f ops/manifests/manifest-unstable.yaml"
# deploy-staging:
# # if this job runs on a tag, we deduce that code
# # has been tagged for release and should be deployed to staging
# if: ${{ github.ref_type == 'tag' }}
cf_username: ${{ secrets.CF_STAGING_USERNAME }}
cf_password: ${{ secrets.CF_STAGING_PASSWORD }}
cf_org: cisa-getgov-prototyping
cf_space: staging
push_arguments: "-f ops/manifests/manifest-staging.yaml"

View file

@ -3,7 +3,7 @@ name: Run Migrations
# This workflow can be run from the CLI
# gh workflow run migrate.yaml -f environment=sandbox
# OR
# cf run-task getgov-unstable --wait \
# cf run-task getgov-staging --wait \
# --command 'python manage.py migrate' --name migrate
on:
@ -13,22 +13,19 @@ on:
type: choice
description: Where should we run migrations
options:
- unstable
- staging
jobs:
migrate-unstable:
if: ${{ github.event.inputs.environment == 'unstable' }}
migrate-staging:
if: ${{ github.event.inputs.environment == 'staging' }}
runs-on: ubuntu-latest
steps:
- name: Run Django migrations for unstable
- name: Run Django migrations for staging
uses: 18f/cg-deploy-action@main
with:
cf_username: ${{ secrets.CF_USERNAME }}
cf_password: ${{ secrets.CF_PASSWORD }}
cf_org: sandbox-gsa
cf_space: dotgov-poc
full_command: "cf run-task getgov-unstable --wait --command 'python manage.py migrate' --name migrate"
cf_org: cisa-getgov-prototyping
cf_space: staging
full_command: "cf run-task getgov-staging --wait --command 'python manage.py migrate' --name migrate"
# migrate:
# if: ${{ github.event.inputs.environment == 'staging' }}

View file

@ -33,3 +33,40 @@ jobs:
- name: Run bandit security scanning
working-directory: ./src
run: bandit -r .
python-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres
env:
POSTGRES_DB: app
POSTGRES_USER: user
POSTGRES_PASSWORD: feedabee
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
- name: Install Django
working-directory: ./src
run: |
python -m pip install --upgrade pip
pip install pipenv
pipenv install --system --dev
- name: Test
working-directory: ./src
env:
PYTHONUNBUFFERED: yup
DATABASE_URL: postgres://user:feedabee@localhost/app
DJANGO_SETTINGS_MODULE: registrar.config.settings
DJANGO_SECRET_KEY: feedabee
DJANGO_DEBUG: True
run: ./manage.py test

63
docs/developer/README.md Normal file
View file

@ -0,0 +1,63 @@
# Development
========================
If you're new to Django, see [Getting Started with Django](https://www.djangoproject.com/start/) for an introduction to the framework.
## Local Setup
* Install Docker <https://docs.docker.com/get-docker/>
* Initialize the application:
```shell
cd src
docker-compose build
```
* Run the server: `docker-compose up`
Press Ctrl-c when you'd like to exit or pass `-d` to run in detached mode.
Visit the running application at [http://localhost:8080](http://localhost:8080).
## Setting Vars
Every environment variable for local development is set in [src/docker-compose.yml](../../src/docker-compose.yml).
Including variables which would be secrets and set via a different mechanism elsewhere.
## Viewing Logs
If you run via `docker-compose up`, you'll see the logs in your terminal.
If you run via `docker-compose up -d`, you can get logs with `docker-compose logs -f`.
You can change the logging verbosity, if needed. Do a web search for "django log level".
## Running tests
Crash course on Docker's `run` vs `exec`: in order to run the tests inside of a container, a container must be running. If you already have a container running, you can use `exec`. If you do not, you can use `run`, which will attempt to start one.
To get a container running:
```shell
cd src
docker-compose build
docker-compose up -d
```
Django's test suite:
```shell
docker-compose exec app ./manage.py test
```
OR
```shell
docker-compose exec app python -Wa ./manage.py test # view deprecation warnings
```
Linters:
```shell
docker-compose exec app ./manage.py lint
```

45
docs/operations/README.md Normal file
View file

@ -0,0 +1,45 @@
# Operations
========================
Some basic information and setup steps are included in this README.
Instructions for specific actions can be found in our [runbooks](./runbooks/).
## Continuous Delivery
We use a [cloud.gov service account](https://cloud.gov/docs/services/cloud-gov-service-account/) to deploy from this repository to cloud.gov with a SpaceDeveloper user.
## Authenticating to Cloud.gov via the command line
You'll need the [Cloud Foundry CLI](https://docs.cloud.gov/getting-started/setup/).
We use the V7 Cloud Foundry CLI.
```shell
cf login -a api.fr.cloud.gov --sso
```
After authenticating, make sure you are targeting the correct org and space!
```bash
cf spaces
cf target -o <ORG> -s <SPACE>
```
## Database
In sandbox, created with `cf create-service aws-rds micro-psql getgov-ENV-database`.
Binding the database in `manifest-<ENVIRONMENT>.json` automatically inserts the connection string into the environment as `DATABASE_URL`.
[Cloud.gov RDS documentation](https://cloud.gov/docs/services/relational-database/).
# Deploy
We have two environments: `unstable` and `staging`. Developers can deploy locally to unstable whenever they want. However, only our CD service can deploy to `staging`, and it does so on every commit to `main`. This is to ensure that we have a "golden" environment to point to, and can still test things out in an unstable space. To deploy locally to `unstable`:
```bash
cf target -o cisa-getgov-prototyping -s unstable
cf push getgov-unstable -f ops/manifests/manifest-unstable.yaml
cf run-task getgov-unstable --command 'python manage.py migrate' --name migrate
```

View file

@ -1,24 +1,7 @@
# Operations
# HOWTO Rotate the Application's Secrets
========================
## Authenticating
You'll need the [Cloud Foundry CLI](https://docs.cloud.gov/getting-started/setup/).
We use the V7 Cloud Foundry CLI.
```shell
cf login -a api.fr.cloud.gov --sso
```
After authenticating, make sure you are targeting the correct org and space!
```bash
cf spaces
cf target -o <ORG> -s <SPACE>
```
## Rotating Environment Secrets
Secrets are read from the running environment.
Secrets were originally created with:
@ -44,16 +27,8 @@ To rotate secrets, create a new `credentials-<ENVIRONMENT>.json` file, upload it
Example:
```bash
cf uups getgov-credentials -p credentials-unstable.json
cf cups getgov-credentials -p credentials-unstable.json
cf restage getgov-unstable --strategy rolling
```
Non-secret environment variables can be declared in `manifest-<ENVIRONMENT>.json` directly.
## Database
In sandbox, created with `cf create-service aws-rds micro-psql getgov-database`.
Binding the database in `manifest-<ENVIRONMENT>.json` automatically inserts the connection string into the environment as `DATABASE_URL`.
[Cloud.gov RDS documentation](https://cloud.gov/docs/services/relational-database/).

View file

@ -0,0 +1,12 @@
# HOWTO Rotate Cloud.gov Secrets
========================
These are the secrets GitHub uses to access Cloud.gov during continuous deployment.
Make sure that you have cf v7 and not cf v8 as it will not work with this script.
Secrets are set and rotated using the [cloud.gov secret rotation script](../../../ops/scripts/rotate_cloud_secrets.sh).
Prerequisites for running the script are installations of `jq`, `gh`, and the `cf` CLI tool.
NOTE: Secrets must be rotated every 90 days. This script can be used for that routine rotation or it can be used to revoke and re-create tokens if they are compromised.

View file

@ -0,0 +1,8 @@
# HOWTO Update Python Dependencies
========================
1. Check the [Pipfile](./src/Pipfile) for pinned dependencies and manually adjust the version numbers
1. Run `cd src`, `docker-compose up -d`, and `docker-compose exec app pipenv update` to perform the upgrade and generate a new [Pipfile.lock](./src/Pipfile.lock)
1. (optional) Run `docker-compose stop` and `docker-compose build` to build a new image for local development with the updated dependencies.
The reason for de-coupling the `build` and `update` steps is to increase consistency between builds and reduce "it works on my laptop!". Therefore, `build` uses the lock file as-is; dependencies are never updated except by explicit choice.

View file

@ -1,17 +0,0 @@
# Cloud.gov Continuous Delivery
We use a [cloud.gov service account](https://cloud.gov/docs/services/cloud-gov-service-account/) to deploy from this repository to cloud.gov with a SpaceDeveloper user.
## Rotating Cloud.gov Secrets
Make sure that you have cf v7 and not cf v8 as it will not work with this script.
Secrets are set and rotated using the [cloud.gov secret rotation script](./scripts/rotate_cloud_secrets.sh).
Prerequistes for running the script are installations of `jq`, `gh`, and the `cf` CLI tool.
NOTE: Secrets must be rotated every 90 days. This script can be used for that routine rotation or it can be used to revoke and re-create tokens if they are compromised.
## Github Action
TBD info about how we are using the github action to deploy.

View file

@ -0,0 +1,23 @@
---
applications:
- name: getgov-staging
buildpacks:
- python_buildpack
path: ../../src
instances: 1
memory: 512M
stack: cflinuxfs3
timeout: 180
command: gunicorn registrar.config.wsgi -t 60
health-check-type: http
health-check-http-endpoint: /health
env:
# Send stdout and stderr straight to the terminal without buffering
PYTHONUNBUFFERED: yup
# Tell Django where to find its configuration
DJANGO_SETTINGS_MODULE: registrar.config.settings
routes:
- route: getgov-staging.app.cloud.gov
services:
- getgov-credentials
- getgov-staging-database

View file

@ -4,8 +4,13 @@ if [ ! $(command -v gh) ] || [ ! $(command -v jq) ] || [ ! $(command -v cf) ]; t
exit 1
fi
cf spaces
read -p "Are you logged in to the dotgov-poc CF space above? (y/n) " -n 1 -r
if [ -z "$1" ]; then
echo 'Please specify a space to target (i.e. unstable, staging)' >&2
exit 1
fi
cf target -o cisa-getgov-prototyping -s $1
read -p "Are you logged in to the cisa-getgov-prototyping CF org above and targeting the correct space? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
@ -13,7 +18,7 @@ then
fi
gh auth status
read -p "Are you logged into a Github account with access to cisagov/dotgov? (y/n) " -n 1 -r
read -p "Are you logged into a Github account with access to cisagov/getgov? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
@ -21,6 +26,7 @@ then
fi
echo "Great, removing and replacing Github CD account..."
cf target -s $1
cf delete-service-key github-cd-account github-cd-key
cf create-service-key github-cd-account github-cd-key
cf service-key github-cd-account github-cd-key
@ -31,8 +37,9 @@ then
exit 1
fi
upcase_space=$(printf "%s" "$1" | tr '[:lower:]' '[:upper:]')
cf service-key github-cd-account github-cd-key | sed 1,2d | jq -r '[.username, .password]|@tsv' |
while read -r username password; do
gh secret --repo cisagov/dotgov set CF_USERNAME --body $username
gh secret --repo cisagov/dotgov set CF_PASSWORD --body $password
gh secret --repo cisagov/getgov set CF_${upcase_space}_USERNAME --body $username
gh secret --repo cisagov/getgov set CF_${upcase_space}_PASSWORD --body $password
done

View file

@ -16,3 +16,8 @@ whitenoise = "*"
[dev-packages]
django-debug-toolbar = "*"
nplusone = "*"
bandit = "*"
black = "*"
flake8 = "*"
mypy = "*"
types-requests = "*"

241
src/Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "6f9b2bb95a6e9e6d1ad667fb9cf5473b37a2a21dbc3821281548f9de29528872"
"sha256": "a699f5429c7a64d18840baefdfc9731d7c73d57ed55fd701bcc411efeb6e4035"
},
"pipfile-spec": 6,
"requires": {},
@ -244,6 +244,43 @@
"markers": "python_version >= '3.7'",
"version": "==3.5.2"
},
"bandit": {
"hashes": [
"sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2",
"sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"
],
"index": "pypi",
"version": "==1.7.4"
},
"black": {
"hashes": [
"sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411",
"sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c",
"sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497",
"sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e",
"sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342",
"sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27",
"sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41",
"sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab",
"sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5",
"sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16",
"sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e",
"sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c",
"sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe",
"sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3",
"sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec",
"sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3",
"sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd",
"sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c",
"sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4",
"sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90",
"sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869",
"sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747",
"sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"
],
"index": "pypi",
"version": "==22.8.0"
},
"blinker": {
"hashes": [
"sha256:1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36",
@ -252,6 +289,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.5"
},
"click": {
"hashes": [
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
],
"markers": "python_version >= '3.7'",
"version": "==8.1.3"
},
"django": {
"hashes": [
"sha256:a153ffd5143bf26a877bfae2f4ec736ebd8924a46600ca089ad96b54a1d4e28e",
@ -268,6 +313,74 @@
"index": "pypi",
"version": "==3.6.0"
},
"flake8": {
"hashes": [
"sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db",
"sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"
],
"index": "pypi",
"version": "==5.0.4"
},
"gitdb": {
"hashes": [
"sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd",
"sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"
],
"markers": "python_version >= '3.6'",
"version": "==4.0.9"
},
"gitpython": {
"hashes": [
"sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704",
"sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"
],
"markers": "python_version >= '3.7'",
"version": "==3.1.27"
},
"mccabe": {
"hashes": [
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
],
"markers": "python_version >= '3.6'",
"version": "==0.7.0"
},
"mypy": {
"hashes": [
"sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655",
"sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9",
"sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3",
"sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6",
"sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0",
"sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58",
"sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103",
"sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09",
"sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417",
"sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56",
"sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2",
"sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856",
"sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0",
"sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8",
"sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27",
"sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5",
"sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71",
"sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27",
"sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe",
"sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca",
"sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf",
"sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9",
"sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c"
],
"index": "pypi",
"version": "==0.971"
},
"mypy-extensions": {
"hashes": [
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
],
"version": "==0.4.3"
},
"nplusone": {
"hashes": [
"sha256:1726c0a10c0aa7eabb04e24db2882ff97b6b7ee29d729a8d97dcbd12ef5a5651",
@ -276,6 +389,85 @@
"index": "pypi",
"version": "==1.0.0"
},
"pathspec": {
"hashes": [
"sha256:01eecd304ba0e6eeed188ae5fa568e99ef10265af7fd9ab737d6412b4ee0ab85",
"sha256:aefa80ac32d5bf1f96139dca67cefb69a431beff4e6bf1168468f37d7ab87015"
],
"markers": "python_version >= '3.7'",
"version": "==0.10.0"
},
"pbr": {
"hashes": [
"sha256:cfcc4ff8e698256fc17ea3ff796478b050852585aa5bae79ecd05b2ab7b39b9a",
"sha256:da3e18aac0a3c003e9eea1a81bd23e5a3a75d745670dcf736317b7d966887fdf"
],
"markers": "python_version >= '2.6'",
"version": "==5.10.0"
},
"platformdirs": {
"hashes": [
"sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788",
"sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"
],
"markers": "python_version >= '3.7'",
"version": "==2.5.2"
},
"pycodestyle": {
"hashes": [
"sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785",
"sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"
],
"markers": "python_version >= '3.6'",
"version": "==2.9.1"
},
"pyflakes": {
"hashes": [
"sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2",
"sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"
],
"markers": "python_version >= '3.6'",
"version": "==2.5.0"
},
"pyyaml": {
"hashes": [
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
"sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
"sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
"sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
"sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
"sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
"sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
"sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
"sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
"sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
"sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
"sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
"sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
"sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
"sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
"sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
"sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
"sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
"sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
"sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
"sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
"sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
"sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
"sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
"sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
"sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
"sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
"sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
"sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
"sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
"sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
],
"markers": "python_version >= '3.6'",
"version": "==6.0"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
@ -284,6 +476,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"smmap": {
"hashes": [
"sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94",
"sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"
],
"markers": "python_version >= '3.6'",
"version": "==5.0.0"
},
"sqlparse": {
"hashes": [
"sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae",
@ -291,6 +491,45 @@
],
"markers": "python_version >= '3.5'",
"version": "==0.4.2"
},
"stevedore": {
"hashes": [
"sha256:87e4d27fe96d0d7e4fc24f0cbe3463baae4ec51e81d95fbe60d2474636e0c7d8",
"sha256:f82cc99a1ff552310d19c379827c2c64dd9f85a38bcd5559db2470161867b786"
],
"markers": "python_version >= '3.8'",
"version": "==4.0.0"
},
"tomli": {
"hashes": [
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
],
"markers": "python_full_version < '3.11.0a7'",
"version": "==2.0.1"
},
"types-requests": {
"hashes": [
"sha256:86cb66d3de2f53eac5c09adc42cf6547eefbd0c7e1210beca1ee751c35d96083",
"sha256:feaf581bd580497a47fe845d506fa3b91b484cf706ff27774e87659837de9962"
],
"index": "pypi",
"version": "==2.28.9"
},
"types-urllib3": {
"hashes": [
"sha256:333e675b188a1c1fd980b4b352f9e40572413a4c1ac689c23cd546e96310070a",
"sha256:b78e819f0e350221d0689a5666162e467ba3910737bafda14b5c2c85e9bb1e56"
],
"version": "==1.26.23"
},
"typing-extensions": {
"hashes": [
"sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
"sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
],
"markers": "python_version >= '3.7'",
"version": "==4.3.0"
}
}
}

View file

@ -1,22 +1,6 @@
# Registrar
# Registrar source code
========================
## Development
[Developer documentation](../docs/developer/README.md) is in [docs/developer](../docs/developer/).
If you're new to Django, see [Getting Started with Django](https://www.djangoproject.com/start/) for an introduction to the framework.
### Local Setup
* Install Docker <https://docs.docker.com/get-docker/>
* Initialize the application:
```shell
cd src
docker-compose build
```
* Run the server: `docker-compose up`
### Update Dependencies
1. Check the [Pipfile](./src/Pipfile) for pinned dependencies and manually adjust the version numbers
1. Run `cd src`, `docker-compose up`, and `docker-compose exec app pipenv update` to perform the upgrade and generate a new [Pipfile.lock](./src/Pipfile.lock)
[Operations documentation](../docs/operations/README.md) is in [docs/operations](../docs/operations/).

View file

@ -28,9 +28,11 @@ services:
stdin_open: true
tty: true
ports:
- "8080:8000"
- "8080:8080"
# command: "python"
command: "python manage.py runserver 0.0.0.0:8000"
command: >
bash -c " python manage.py migrate &&
python manage.py runserver 0.0.0.0:8080"
db:
image: postgres:latest

View file

@ -78,6 +78,8 @@ INSTALLED_APPS = [
# (and any other places you specify) into a single location
# that can easily be served in production
"django.contrib.staticfiles",
# let's be sure to install our own application!
"registrar",
]
# Middleware are routines for processing web requests.
@ -392,6 +394,7 @@ SECURE_SSL_REDIRECT = True
# web server configurations.
ALLOWED_HOSTS = [
"getgov-unstable.app.cloud.gov",
"getgov-staging.app.cloud.gov",
"get.gov",
]

View file

View file

@ -0,0 +1,63 @@
from subprocess import run, CalledProcessError # nosec
from django.core.management.base import BaseCommand, CommandError
class Command(BaseCommand):
"""
Helper command for running installed linters.
Run using `./manage.py lint`.
"""
help = "Runs linters: flake8 black mypy bandit"
# Add new linters here.
#
# To maintain security while using subprocess, avoid passing user input
# and, in all cases, make sure to use a list (not a string) for flags/args
# as this will quote the output.
linters = {
"flake8": {
"purpose": "Linting",
"args": ["flake8", ".", "--count", "--show-source", "--statistics"],
},
"black": {
"purpose": "Formatting",
"args": ["black", "--check", "."],
},
"mypy": {
"purpose": "Type checking",
"args": ["mypy", "."],
},
"bandit": {
"purpose": "Security scanning",
"args": ["bandit", "-r", "."],
},
}
def add_arguments(self, parser):
parser.add_argument("linters", nargs="*", type=str)
def handle(self, *args, **options):
try:
for linter in self.linters.values():
self.stdout.write(f"[manage.py lint] {linter['purpose']}. . .")
result = run(linter["args"])
if result.returncode:
self.stderr.write(
self.style.NOTICE(
"[manage.py lint] Re-try with: [docker-compose exec app] "
f"{' '.join(linter['args'])}"
)
)
break
else:
self.stdout.write(
f"[manage.py lint] {linter['purpose']} completed with success!"
)
except CalledProcessError as e:
raise CommandError(e)
self.stdout.write(
self.style.SUCCESS("[manage.py lint] All linters ran successfully.")
)

View file

View file

View file

@ -0,0 +1,11 @@
from django.test import Client, TestCase
class HealthTest(TestCase):
def setUp(self):
self.client = Client()
def test_health_check_endpoint(self):
response = self.client.get("/health/")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"OK")