Skip to content

Commit 8f6096a

Browse files
authored
Merge pull request #87 from caktus/develop
Production Deploy 12/10/2021
2 parents 75d066f + 946addf commit 8f6096a

40 files changed

+586
-254
lines changed

.circleci/config.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ commands:
88
steps:
99
- restore_cache:
1010
keys:
11-
- v7-pip-dependencies-{{ checksum "requirements/test/test.txt" }}-{{ checksum "requirements/base/base.txt" }}
11+
- v10-pip-dependencies-{{ checksum "requirements/test/test.txt" }}-{{ checksum "requirements/base/base.txt" }}
1212
# fallback to using the latest cache if no exact match is found
13-
- v7-pip-dependencies-
13+
- v10-pip-dependencies-
1414
save_cache_cmd:
1515
steps:
1616
- save_cache:
1717
paths:
1818
- "env"
19-
key: v7-pip-dependencies-{{ checksum "requirements/test/test.txt" }}-{{ checksum "requirements/base/base.txt" }}
19+
key: v10-pip-dependencies-{{ checksum "requirements/test/test.txt" }}-{{ checksum "requirements/base/base.txt" }}
2020
orbs:
2121
slack: circleci/slack@3.4.2
2222

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ npm-debug.log
4343
.python-version
4444
env-local.sh
4545
*.dump
46+
*.pgdump
4647
.envrc
4748
.direnv
4849
.vscode

Dockerfile

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ RUN npm install --silent
77
COPY frontend/ /code/
88
RUN npm run build
99

10-
FROM python:3.8-slim as base
10+
FROM python:3.8-slim-bullseye as base
1111

1212
# Create a group and user to run our app
1313
ARG APP_USER=appuser
@@ -43,6 +43,7 @@ RUN set -ex \
4343
build-essential \
4444
libpcre3-dev \
4545
libpq-dev \
46+
git-core \
4647
" \
4748
&& apt-get update && apt-get install -y --no-install-recommends $BUILD_DEPS \
4849
&& pip install -U -q pip-tools \

README.rst

+4-14
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,15 @@
1-
NC Traffic Stops
1+
NC CopWatch
22
================
33

4-
.. image:: https://badge.waffle.io/OpenDataPolicingNC/Traffic-Stops.svg?label=ready&title=Ready
5-
:target: https://waffle.io/OpenDataPolicingNC/Traffic-Stops
6-
:alt: 'Stories in Ready'
4+
.. image:: https://circleci.com/gh/caktus/Traffic-Stops.svg?style=svg
5+
:target: https://circleci.com/gh/caktus/Traffic-Stops
76

8-
.. image:: https://readthedocs.org/projects/nc-traffic-stops/badge/?version=latest
9-
:target: http://nc-traffic-stops.readthedocs.org/en/latest/
10-
:alt: Documentation Status
11-
12-
.. image:: https://travis-ci.org/OpenDataPolicingNC/Traffic-Stops.svg?branch=master
13-
:target: https://travis-ci.org/OpenDataPolicingNC/Traffic-Stops
14-
15-
NC Traffic Stops is a website to monitor and identify racial profiling
7+
NC CopWatch is a website to monitor and identify racial profiling
168
practices by North Carolina law enforcement agencies. This project is lead by
179
`Forward Justice`_, a nonpartisan law, policy, and strategy center dedicated to advancing racial,
1810
social, and economic justice in the U.S. South.
1911

2012
Please see the `production documentation`_ and `development documentation`_
2113
for more information.
2214

23-
.. _production documentation: http://nc-traffic-stops.readthedocs.org/en/latest/
24-
.. _development documentation: http://nc-traffic-stops.readthedocs.org/en/dev/
2515
.. _Forward Justice: https://forwardjustice.org/

deploy/deploy-hosting-services.yml

+4
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@
66
gather_facts: false
77
roles:
88
- role: caktus.k8s-hosting-services
9+
tasks:
10+
- import_role:
11+
name: caktus.k8s-hosting-services
12+
tasks_from: monitoring

deploy/group_vars/all.yml

+27-10
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ cloudformation_stack:
4242
UseAES256Encryption: "true"
4343
CustomerManagedCmkArn: ""
4444
ContainerInstanceType: t3a.medium
45+
ContainerVolumeSize: 40
4546
DatabaseAllocatedStorage: 100
4647
DatabaseClass: db.t3.large
4748
DatabaseEngineVersion: "12"
@@ -71,17 +72,33 @@ k8s_ci_repository_arn: arn:aws:ecr:us-east-2:606178775542:repository/traff-appli
7172
k8s_ci_vault_password_arn: arn:aws:secretsmanager:us-east-2:606178775542:secret:trafficstops-ansible-vault-password-XKpR8f
7273

7374
k8s_letsencrypt_email: admin@caktusgroup.com
74-
# New Relic Infrastructure: Caktus Paid Account
75-
k8s_newrelic_license_key: !vault |
76-
$ANSIBLE_VAULT;1.1;AES256
77-
31623963653434303137323231656263643235616539316537346331646133313732316465623865
78-
3438623336353035323437653033313434646366383236390a656531636336663530373462323331
79-
32643434333833363433663932316534373565663035383334336231313366373763303263393836
80-
3035363662323335630a306331303761303434633235616564386362353766336462656535663033
81-
31333537343865616436623063386539303339653165636664633736666365623337326363646437
82-
6565393035313438666364363231353562613334376135663031
75+
k8s_iam_users: [copelco]
76+
77+
# Pin ingress-nginx and cert-manager to current versions so future upgrades of this
78+
# role will not upgrade these charts without your intervention:
79+
# https://github.com/kubernetes/ingress-nginx/releases
80+
k8s_ingress_nginx_chart_version: "3.39.0"
81+
# https://github.com/jetstack/cert-manager/releases
82+
k8s_cert_manager_chart_version: "v1.6.1"
83+
# AWS only:
84+
# Use the newer load balancer type (NLB). DO NOT edit k8s_aws_load_balancer_type after
85+
# creating your Service.
86+
k8s_aws_load_balancer_type: nlb
87+
88+
# ----------------------------------------------------------------------------
89+
# caktus.k8s-hosting-services: Logging and monitoring configuration
90+
# ----------------------------------------------------------------------------
8391

8492
k8s_papertrail_logspout_destination: "syslog+tls://logs2.papertrailapp.com:20851"
8593
k8s_papertrail_logspout_memory_limit: 128Mi
8694

87-
k8s_iam_users: [copelco]
95+
# New Relic Infrastructure: admin+newrelic@caktusgroup.com
96+
k8s_newrelic_chart_version: "3.2.4"
97+
k8s_newrelic_license_key: !vault |
98+
$ANSIBLE_VAULT;1.1;AES256
99+
37656631623333346263383231386165666531333961373931383661366338343634333362356430
100+
3963613833663637313632373465613730383365626461630a383432346335386632303935356532
101+
61643737636132336339336332396262623362333663333130393031376338363266363430326136
102+
6131396135646236360a323766623330313365306539316263393533623063346166653433316631
103+
39356263623363653934333064376364363562303236646238666234356136663539343064383463
104+
3161356339656137373935623562366134393765346466643365

deploy/requirements.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
- src: https://github.com/caktus/ansible-role-django-k8s
44
name: caktus.django-k8s
5-
version: v0.0.11
5+
version: v1.3.0
66

77
- src: https://github.com/caktus/ansible-role-aws-web-stacks
88
name: caktus.aws-web-stacks
99
version: ''
1010

1111
- src: https://github.com/caktus/ansible-role-k8s-web-cluster
1212
name: caktus.k8s-web-cluster
13-
version: v0.0.7
13+
version: v1.1.0
1414

1515
- src: https://github.com/caktus/ansible-role-k8s-hosting-services
1616
name: caktus.k8s-hosting-services
17-
version: v0.0.1
17+
version: v0.3.0

docs/deploy.rst

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Caktus AWS Access
1414
.. code-block::
1515
1616
[trafficstops]
17-
role_arn = arn:aws:iam::000000000000:role/CaktusAccessRole
17+
role_arn = arn:aws:iam::000000000000:role/CaktusAccountAccessRole-Admins
1818
source_profile = caktus
1919
2020
See LastPass entry *Traffic Stops AWS Profile role_arn* for the AWS account
@@ -111,4 +111,3 @@ Deploy application
111111
4. Deploy::
112112

113113
inv staging deploy --tag=...
114-
=======

docs/hosting-services.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Hosting Services
2+
3+
The services configured for this project are:
4+
* PostgreSQL database backups to S3 (within Caktus AWS account)
5+
* Currently, this is only `traffic_stops`, which contains users, census data, etc.
6+
* `traffic_stops_nc` is not backed up since the entire dataset is re-imported daily.
7+
* Papertrail logging (to Caktus account)
8+
* New Relic Infrastructure monitoring (Account: `admin+newrelic@caktusgroup.com`)
9+
10+
11+
## Production database disaster recovery
12+
13+
In the event a restore from a historical backup is needed, access to the [Caktus
14+
AssumeRole is
15+
required](https://github.com/caktus/caktus-hosting-services/blob/main/docs/aws-assumerole.md#aws-accounts).
16+
Once you have that access, you can use invoke tools to pull historical backups.
17+
18+
To download the latest `daily` backup:
19+
20+
```sh
21+
inv utils.get-db-backup
22+
```
23+
24+
25+
## Production backup configuration
26+
27+
[caktus.k8s-hosting-services](https://github.com/caktus/ansible-role-k8s-hosting-services)
28+
manages database backups.
29+
30+
Run this command to set up database backups and monitoring services:
31+
32+
```sh
33+
inv deploy.playbook -n deploy-hosting-services.yml
34+
```

frontend/src/Components/Charts/ChartPrimitives/Bar.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import { VictoryAxis, VictoryBar, VictoryChart, VictoryContainer } from 'victory';
44
import { AXIS_STYLE } from './chartConstants';
5+
import ChartLoading from 'Components/Charts/ChartPrimitives/ChartLoading';
56
import BarSkeleton from 'Components/Elements/Skeletons/BarSkeleton';
67

78
function Bar({ data, chartProps, xAxisProps, yAxisProps, barProps }) {
8-
if (!data) return <BarSkeleton />;
9+
if (!data) return <ChartLoading skeleton={BarSkeleton} />;
910
return (
1011
<VictoryChart
1112
{...chartProps}

frontend/src/Components/Charts/ChartPrimitives/ChartBase.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import React, { useState, useEffect } from 'react';
22
import { ChartBaseStyled } from './ChartBase.styled';
33

44
// Children
5-
import PieSkeleton from 'Components/Elements/Skeletons/PieSkeleton';
65
import ResponsiveChartContainer from 'Components/Charts/ResponsiveChartContainer.styled';
76
import Legend from 'Components/Charts/ChartPrimitives/Legend/Legend';
7+
import DataLoading from 'Components/Charts/ChartPrimitives/DataLoading';
88

99
function ChartBase({
1010
children,
@@ -60,7 +60,7 @@ function ChartBase({
6060
{...props}
6161
>
6262
{hasError && <p>some chart error message</p>}
63-
{isLoading && <PieSkeleton />}
63+
{isLoading && <DataLoading />}
6464
<h2>{chartTitle}</h2>
6565
{!hideLegend && (
6666
<Legend
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import React from 'react';
2-
import { ChartLoadingStyled } from './ChartLoading.styled';
2+
import * as S from './ChartLoading.styled';
33

4-
function ChartLoading({ pastDelay }) {
5-
if (pastDelay) {
6-
return (
7-
<ChartLoadingStyled>
8-
<p>ChartLoading</p>
9-
</ChartLoadingStyled>
10-
);
11-
} else {
12-
return null;
13-
}
4+
// Hooks
5+
import useOfficerId from 'Hooks/useOfficerId';
6+
7+
function ChartLoading({ skeleton: Skeleton }) {
8+
const officerId = useOfficerId();
9+
10+
return (
11+
<S.ChartLoading>
12+
<h3>Loading {officerId ? "Officer" : "Agency"} data...</h3>
13+
<Skeleton scale={2} />
14+
</S.ChartLoading>
15+
);
1416
}
1517

1618
export default ChartLoading;
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
import styled from 'styled-components';
22

3-
export const ChartLoadingStyled = styled.div``;
3+
export const ChartLoading = styled.div`
4+
padding: 2rem 0;
5+
h3 {
6+
text-align: center;
7+
font-size: 28px;
8+
font-weight: 200;
9+
color: ${props => props.theme.colors.grey};
10+
}
11+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from 'react';
2+
import { DataLoadingStyled } from './DataLoading.styled';
3+
4+
// Hooks
5+
import useOfficerId from 'Hooks/useOfficerId';
6+
7+
// Children
8+
import PieSkeleton from 'Components/Elements/Skeletons/PieSkeleton';
9+
10+
function DataLoading() {
11+
const officerId = useOfficerId();
12+
13+
return (
14+
<DataLoadingStyled>
15+
<h3>Loading {officerId ? "Officer" : "Agency"} data...</h3>
16+
<PieSkeleton scale={2} />
17+
</DataLoadingStyled>
18+
);
19+
}
20+
21+
export default DataLoading;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import styled from 'styled-components';
2+
3+
export const DataLoadingStyled = styled.div`
4+
padding: 2rem 0;
5+
h3 {
6+
text-align: center;
7+
font-size: 28px;
8+
font-weight: 200;
9+
color: ${props => props.theme.colors.grey};
10+
}
11+
`;

frontend/src/Components/Charts/ChartPrimitives/GroupedBar.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
33

44
import { VictoryChart, VictoryGroup, VictoryBar, VictoryAxis, VictoryContainer } from 'victory';
55
import { AXIS_STYLE } from './chartConstants';
6+
import ChartLoading from 'Components/Charts/ChartPrimitives/ChartLoading';
67
import BarSkeleton from 'Components/Elements/Skeletons/BarSkeleton';
78

89
function GroupedBar({
@@ -16,7 +17,7 @@ function GroupedBar({
1617
iAxisProps,
1718
barProps,
1819
}) {
19-
if (loading) return <BarSkeleton />;
20+
if (loading) return <ChartLoading skeleton={BarSkeleton} />
2021

2122
return (
2223
<VictoryChart

frontend/src/Components/Charts/ChartPrimitives/Line.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { AXIS_STYLE } from './chartConstants';
55

66
// Deps
77
import { VictoryChart, VictoryLine, VictoryAxis, VictoryContainer } from 'victory';
8+
import ChartLoading from 'Components/Charts/ChartPrimitives/ChartLoading';
89
import BarSkeleton from 'Components/Elements/Skeletons/BarSkeleton';
910

1011
function Line({
@@ -17,7 +18,7 @@ function Line({
1718
dAxisProps = {},
1819
iAxisProps = {},
1920
}) {
20-
if (loading) return <BarSkeleton />;
21+
if (loading) return <ChartLoading skeleton={BarSkeleton} />
2122

2223
return (
2324
<VictoryChart containerComponent={<VictoryContainer style={{ touchAction: 'auto' }} />}>

frontend/src/Components/Charts/ChartPrimitives/Pie.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import styled from 'styled-components';
33
import { useTheme } from 'styled-components';
44

55
// Elements
6+
import ChartLoading from 'Components/Charts/ChartPrimitives/ChartLoading'
67
import PieSkeleton from 'Components/Elements/Skeletons/PieSkeleton';
78
import { VictoryPie, VictoryLabel, VictoryTooltip } from 'victory';
89
import { P, WEIGHTS } from 'styles/StyledComponents/Typography';
@@ -23,7 +24,7 @@ function Pie({ data, loading }) {
2324
return d.length === 0 || d.every((dt) => dt.y === 0);
2425
};
2526

26-
if (loading) return <PieSkeleton />;
27+
if (loading) return <ChartLoading skeleton={PieSkeleton} />;
2728

2829
if (_dataIsZeros(data)) {
2930
return (

frontend/src/Components/Charts/ChartPrimitives/StackedBar.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import PropTypes from 'prop-types';
44
import { VictoryChart, VictoryStack, VictoryBar, VictoryAxis, VictoryContainer } from 'victory';
55
import { AXIS_STYLE } from './chartConstants';
66

7-
// Childre
7+
// Children
8+
import ChartLoading from 'Components/Charts/ChartPrimitives/ChartLoading';
89
import BarSkeleton from 'Components/Elements/Skeletons/BarSkeleton';
910

1011
function StackedBar({ data, loading, tickValues }) {
11-
if (loading) return <BarSkeleton />;
12+
if (loading) return <ChartLoading skeleton={BarSkeleton} />
1213

1314
return (
1415
<VictoryChart

0 commit comments

Comments
 (0)