Merge pull request #42 from cisagov/sspj/proof-of-concept

Add Django
This commit is contained in:
Seamus Johnston 2022-08-19 13:24:28 +00:00 committed by GitHub
commit 5e4c44a444
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 951 additions and 15 deletions

View file

@ -0,0 +1,30 @@
# 7. Python Buildpack
Date: 2022-08-12
## Status
Accepted
## Context
We had previously drafted ADRs to use Docker to build images for containerized deployment. The motivation was to reduce dependency on Cloud.gov. These ADRs were rejected, mainly owing to the necessity of having an image repository to hold the built images. The team had concerns about an overreliance on GitHub with their GitHub Packages service and about the overall maintenance burden of any image repository.
Cloud.gov uses Cloud Foundry which provides several “buildpacks”. These are automated environments which will take a code repository of a certain language and do the usual setup steps to prepare a deployment of that code. In the case of Python, this means automated detection of Pipfile and installation of packages.
We do not anticipate needing a custom buildpack, because our current use case falls completely within the Python buildpack's purview.
## Decision
To use Cloud Foundrys Python buildpack.
## Consequences
There will be a small amount of future work if the code is migrated off of Cloud.gov, but only a small amount. Proportionate to the overall effort of migration, it is inconsequential.
Cloud.gov provides [documentation around the trade-offs](https://cloud.gov/docs/deployment/docker/):
| | Supported buildpack | Docker container |
|---|---|---|
|Pros|It “just works”. Automatic and constant security updates. All you need to do is write code.|Can build container images and run containers on local workstation. Fine-grained control over compilation and root filesystem.|
|Cons|Difficult to recreate the execution environment locally. Testing compilation and the result of staging is harder.|Added responsibility for all security updates and bug fixes. More compliance responsibility means more work.|

View file

@ -0,0 +1,25 @@
# 8. Server Side Rendering
Date: 2022-08-16
## Status
Accepted
## Context
In deciding how content would be delivered to the end user, we held a meeting with Engineering and Design ~and the mayor of Igorville~ [^1] and consulted the [18F Engineering Practices Guide](https://engineering.18f.gov/web-architecture/).
The two major options considered were 1) a single page application, developed with a JavaScript framework such as React, perhaps served from its own Node-based server or a static file host such as Federalist; or 2) traditional server rendered HTML pages, delivered by Django, accompanied by a small amount of JavaScript and CSS.
## Decision
To use server-side rendering performed by Django.
## Consequences
The positive aspects of this include: easier state management, fewer bugs caused by stale cache data, easier 508 compliance for disabled users, better maintainability for future developers due to fewer dependencies and a shallower learning curve, and better cross browser compatibility.
The risks to this approach are given by the frontend lead on a sister 18F project: a less well-developed API since this approach does not require an API upfront, users/stakeholders attempting to request ever increasing levels of interactivity, and familiarity with vanilla.js has waned over the years.
[^1]: Inside joke. To obtain access to the registrant flow, a member of our team signed up for .gov using the fictitious town of Igorville.

View file

@ -0,0 +1,41 @@
digraph sequenceDiagram {
# Install graphviz and run `fdp -Tpng request.dot -o request_diagram.png`
subgraph cluster_1 {
label="Request and Response";
browserHead [ label="{Browser|user makes request}" pos="0.1,4.75!" shape="record" ];
browserPoint0 [ pos="0.1,4!" shape="point" width="0" ]
browserPoint5 [ pos="0.1,0.75!" shape="point" width="0" ]
browserFoot [ label="Browser" pos="0,0!" shape="record" ];
viewHead [ label="{Django Views|business logic applied}" pos="2.5,4.75!" shape="record" ];
viewPoint0 [ pos="2.5,4!" shape="point" width="0" ]
viewPoint1 [ pos="2.5,3.75!" shape="point" width="0" ]
viewPoint2 [ pos="2.5,3.5!" shape="point" width="0" ]
viewPoint3 [ pos="2.5,1.25!" shape="point" width="0" ]
viewPoint4 [ pos="2.5,1!" shape="point" width="0" ]
viewPoint5 [ pos="2.5,0.75!" shape="point" width="0" ]
viewFoot [ label="Django view" pos="2.5,0!" shape="record" ];
databaseHead [ label="{Django ORM|database consulted}" pos="5,4.75!" shape="record" ];
databasePoint1 [ pos="5,3.75!" shape="point" width="0" ]
databasePoint2 [ pos="5,3.5!" shape="point" width="0" ]
databaseFoot [ label="Django ORM" pos="5,2.5!" shape="record" ];
templateHead [ label="{Django Templates|html response prepared}" pos="5,2.25!" shape="record" ];
templatePoint3 [ pos="5,1.25!" shape="point" width="0" ]
templatePoint4 [ pos="5,1!" shape="point" width="0" ]
templateFoot [ label="Django Templates" pos="5,0!" shape="record" ];
browserHead -> browserPoint0 -> browserFoot [ dir="none" style="dashed" ]
viewHead -> viewPoint0 -> viewFoot [ dir="none" style="dashed" ]
databaseHead -> databasePoint1 -> databaseFoot [ dir="none" style="dashed" ]
templateHead -> templatePoint3 -> templateFoot [ dir="none" style="dashed" ]
browserPoint0 -> viewPoint0 [ style="solid" ]
viewPoint1 -> databasePoint1 [ style="solid" ]
databasePoint2 -> viewPoint2 [ style="solid" ]
viewPoint3 -> templatePoint3 [ style="solid" ]
templatePoint4 -> viewPoint4 [ style="solid" ]
viewPoint5 -> browserPoint5 [ style="solid" ]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View file

@ -0,0 +1,22 @@
digraph systemDiagram {
# Install graphviz and run `fdp -Tpng system.dot -o system_diagram.png`
subgraph cluster_0 {
label="System Diagram";
node [shape=record];
registrant [label="Registrant"];
subgraph cluster_cloud {
label="cloud.gov";
node [shape=record];
postgres [label="Postgres Database"];
subgraph cluster_django {
label="Django MVC";
node [shape=record];
models [pos="0,1!" label="Database ORM"];
views [pos="1,.5!" label="Views"];
templates [pos="1,0!" label="Templates"];
}
}
registrant -> views [dir=both];
models -> postgres [dir=both];
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

59
docs/ops/README.md Normal file
View file

@ -0,0 +1,59 @@
# Operations
========================
## 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 were originally created with:
```sh
cf cups getgov-credentials -p credentials-<ENVIRONMENT>.json
```
Where `credentials-<ENVIRONMENT>.json` looks like:
```json
{
"DJANGO_SECRET_KEY": "EXAMPLE",
...
}
```
You can see the current environment with `cf env <APP>`, for example `cf env getgov-unstable`.
The command `cups` stands for [create user provided service](https://docs.cloudfoundry.org/devguide/services/user-provided.html). User provided services are the way currently recommended by Cloud.gov for deploying secrets. The user provided service is bound to the application in `manifest-<ENVIRONMENT>.json`.
To rotate secrets, create a new `credentials-<ENVIRONMENT>.json` file, upload it, then restage the app.
Example:
```bash
cf uups 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/).