Skip to content

Commit dfaba1f

Browse files
committed
Add docker support, start supporting views on relations
1 parent 4bdc7a4 commit dfaba1f

29 files changed

Lines changed: 839 additions & 102 deletions

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@
22
__pycache__/
33
*.pyc
44
/sample_queries
5-
requirements.txt
5+
/requirements.txt
6+
/pg_data
7+
/gf
8+
dist/

Dockerfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM python:3.7-slim
2+
3+
COPY requirements.txt /app/
4+
WORKDIR /app/
5+
RUN pip install -r requirements.txt
6+
EXPOSE 5000/tcp
7+
COPY goldfig.py /app/
8+
COPY goldfig /app/goldfig
9+
LABEL goldfig-cli=0.0.1
10+
11+
ENTRYPOINT ["/app/goldfig.py", "serve"]

Pipfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ tabulate = "*"
2525
jsonschema = "*"
2626
click = "*"
2727
flask = "*"
28+
requests = "*"
2829

2930
[requires]
3031
python_version = "3.7"

Pipfile.lock

Lines changed: 31 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
6+
7+
PACKAGE=`basename $DIR`
8+
9+
echo "Building package ${PACKAGE}"
10+
11+
pipenv lock -r > requirements.txt
12+
13+
GOLDFIG_DOCKER_REPO=${DOCKER_REPO:-goldfig}
14+
IMAGE="${GOLDFIG_DOCKER_REPO}/${PACKAGE}"
15+
docker build -t ${IMAGE} .
16+
17+
echo "Building launcher"
18+
launcher/build.sh
19+
20+
mkdir -p dist
21+
22+
ESCAPED_IMAGE=$(printf '%s\n' "$IMAGE" | sed -e 's/[\/&]/\\&/g')
23+
sed "s/build: ./image: ${ESCAPED_IMAGE}:latest/g" docker-compose.yml > dist/docker-compose.yml
24+
cp launcher/dist/* dist/
25+
26+
cd dist
27+
# Build linux package
28+
ln gf_linux gf
29+
zip goldfig_linux.zip gf docker-compose.yml
30+
unlink gf
31+
32+
# Build osx package
33+
ln gf_osx gf
34+
zip goldfig_osx.zip gf docker-compose.yml
35+
unlink gf
36+
37+
echo "To publish"
38+
echo "docker push ${IMAGE}:latest"

docker-compose.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
version: "3.8"
2+
services:
3+
goldfig:
4+
build: .
5+
init: true
6+
environment:
7+
- GOLDFIG_DB_HOST=db
8+
- GOLDFIG_DB_SU_USER=postgres
9+
- GOLDFIG_DB_SU_PASSWORD=postgres
10+
depends_on:
11+
- db
12+
db:
13+
image: postgres
14+
environment:
15+
- POSTGRES_DB=postgres
16+
- POSTGRES_USER=postgres
17+
- POSTGRES_PASSWORD=postgres
18+
volumes:
19+
- pg_data:/var/lib/postgresql/data
20+
ports:
21+
- "5432:5432"
22+
volumes:
23+
pg_data:
24+
driver: local

gf

Lines changed: 0 additions & 1 deletion
This file was deleted.

goldfig/aws/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import logging
44
import os
55
import textwrap
6-
from typing import Callable, Dict, Generator, List, Optional, Tuple
6+
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple
77

88
from botocore.loaders import create_loader
99
from botocore.regions import EndpointResolver
@@ -162,7 +162,7 @@ def build_graph(parent_id: str, path: List[str]):
162162
}
163163

164164

165-
def load_boto_session_from_config(config: Dict) -> boto.Session:
165+
def load_boto_session_from_config(config: Dict[str, Any]) -> boto.Session:
166166
if config.get('from_environment', False):
167167
session = boto.get_session()
168168
else:

goldfig/aws/transforms/Instance.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ resources:
7373
id:
7474
path: Ebs.VolumeId
7575
attributes:
76-
DeleteOnTermiation:
76+
DeleteOnTermination:
7777
path: Ebs.DeleteOnTermination
7878
AttachTime:
7979
path: Ebs.AttachTime

goldfig/bootstrap_db.py

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
from typing import List, Tuple
55

66
# import psycopg2.errors
7+
import psycopg2
8+
from psycopg2 import sql
79
from sqlalchemy import create_engine, text
10+
from sqlalchemy.exc import OperationalError
811
from sqlalchemy.orm import sessionmaker, Session
912

1013
from goldfig import models
1114

1215
_log = logging.getLogger(__name__)
1316

1417
DBNAME = 'goldfig'
15-
HOST = 'localhost'
18+
HOST = os.environ.get('GOLDFIG_DB_HOST', 'localhost')
19+
PORT = int(os.environ.get('GOLDFIG_DB_PORT', 5432))
1620

1721

1822
@dataclass
@@ -29,12 +33,12 @@ def connection_string(self) -> str:
2933
_ImportCredential = DbCredential(db_name=DBNAME,
3034
user='goldfig',
3135
password='goldfig',
32-
host=HOST)
36+
host=f'{HOST}:5432')
3337

3438
_ReadonlyCredential = DbCredential(db_name=DBNAME,
3539
user='goldfig_ro',
3640
password='goldfig_ro',
37-
host=HOST)
41+
host=f'{HOST}:5432')
3842

3943
_import_engine = None
4044
_readonly_engine = None
@@ -43,8 +47,7 @@ def connection_string(self) -> str:
4347

4448

4549
def _view_files() -> Tuple[str, List[str]]:
46-
path = os.path.realpath(
47-
os.path.join(os.path.dirname(__file__), 'views'))
50+
path = os.path.realpath(os.path.join(os.path.dirname(__file__), 'views'))
4851
files = [
4952
f for f in os.listdir(path)
5053
if os.path.isfile(os.path.join(path, f)) and f[-4:] == '.sql'
@@ -76,9 +79,7 @@ def _install_views(db: Session):
7679
raise
7780

7881

79-
# TODO: switch to something like alembic?
80-
def init_db():
81-
db = import_session()
82+
def _install_schema(db: Session) -> None:
8283
version = None
8384
try:
8485
version = db.query(models.SchemaVersion).one_or_none()
@@ -100,10 +101,79 @@ def init_db():
100101
db.commit()
101102

102103

104+
def _install_db_and_roles():
105+
print('creating goldfig database and installing roles')
106+
cred = DbCredential(db_name='postgres',
107+
host=HOST,
108+
user=os.environ.get('GOLDFIG_DB_SU_USER', 'postgres'),
109+
password=os.environ.get('GOLDFIG_DB_SU_PASSWORD',
110+
'postgres'))
111+
su_conn = psycopg2.connect(dbname=cred.db_name,
112+
user=cred.user,
113+
password=cred.password,
114+
host=cred.host)
115+
su_conn.autocommit = True
116+
cursor = su_conn.cursor()
117+
cursor.execute('CREATE DATABASE goldfig')
118+
cursor.close()
119+
su_conn.autocommit = False
120+
cursor = su_conn.cursor()
121+
for user in (_ImportCredential, _ReadonlyCredential):
122+
cursor.execute(
123+
sql.SQL('CREATE USER {} WITH ENCRYPTED PASSWORD %s').format(
124+
sql.Identifier(user.user)), (user.password, ))
125+
126+
su_conn.commit()
127+
su_conn.close()
128+
129+
cred = DbCredential(db_name='goldfig',
130+
host=cred.host,
131+
user=cred.user,
132+
password=cred.password)
133+
su_conn = psycopg2.connect(dbname=cred.db_name,
134+
user=cred.user,
135+
password=cred.password,
136+
host=cred.host)
137+
su_conn.autocommit = False
138+
cursor = su_conn.cursor()
139+
import_user = sql.Identifier(_ImportCredential.user)
140+
ro_user = sql.Identifier(_ReadonlyCredential.user)
141+
cursor.execute('revoke create on schema public from public')
142+
cursor.execute(
143+
sql.SQL('grant all privileges on schema public to {}').format(
144+
import_user))
145+
cursor.execute(
146+
sql.SQL('grant select on all tables in schema public to {}').format(
147+
ro_user))
148+
cursor.execute(
149+
sql.SQL(
150+
'alter default privileges for role {} in schema public grant select on tables to {}'
151+
).format(import_user, ro_user))
152+
cursor.close()
153+
su_conn.commit()
154+
155+
156+
# TODO: switch to something like alembic?
157+
def init_db():
158+
try:
159+
db = import_session()
160+
except OperationalError as e:
161+
if 'password authentication failed' in str(e):
162+
# need to set up db
163+
_install_db_and_roles()
164+
db = import_session()
165+
else:
166+
raise
167+
_install_schema(db)
168+
169+
103170
def import_session() -> Session:
104171
global _import_engine
105172
if _import_engine is None:
106-
_import_engine = create_engine(_ImportCredential.connection_string())
173+
_import_engine = create_engine(_ImportCredential.connection_string(),
174+
connect_args={'connect_timeout': 3})
175+
# Force connection errors early
176+
_import_engine.connect()
107177
return sessionmaker(bind=_import_engine)()
108178

109179

0 commit comments

Comments
 (0)