Compare commits

...

9 Commits

Author SHA1 Message Date
ducoterra
4e66f3e2d2 Greatly simplify docker-compose.yaml
Remove pgadmin and specific version for compose
2021-03-24 09:16:36 -06:00
ducoterra
b43927c384 Perfect the entrypoint
Previously unexplored entrypoint technology has been explored. Now we
can run commands after the entrypoint completes with the magical "$@"
variable.
2021-03-24 09:15:29 -06:00
ducoterra
11dc31660d fix https issue with pipeline tag 2020-10-13 12:27:03 -04:00
ducoterra
438ae0fa93 don't overwrite deploy name 2020-10-12 17:23:07 -04:00
ducoterra
f9017ad302 sleep for pod to spin up 2020-10-12 16:55:24 -04:00
ducoterra
7bd7bde188 add secret true to values 2020-10-12 16:46:51 -04:00
ducoterra
5f9f4762a9 fix kubectl command 2020-10-12 16:37:17 -04:00
ducoterra
cead9d5aea fix helm upgrade command 2020-10-12 16:29:57 -04:00
ducoterra
03de5aacfb fix GET issue breaking test 2020-10-12 16:24:31 -04:00
19 changed files with 74 additions and 245 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
venv/ venv/
__pycache__/ __pycache__/
db/ db/
staticfiles/ staticfiles/
.vscode/settings.json

View File

@@ -36,8 +36,6 @@ test:
- python manage.py test - python manage.py test
deploy: deploy:
variables:
DEPLOY: test
stage: deploy stage: deploy
only: only:
variables: variables:
@@ -50,6 +48,7 @@ deploy:
- curl -o /usr/bin/kubectl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl - curl -o /usr/bin/kubectl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
- chmod +x /usr/bin/kubectl - chmod +x /usr/bin/kubectl
- curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
- helm install --upgrade $DEPLOY ./helm --set image=$CI_REGISTRY_IMAGE --set tag=$CI_COMMIT_TAG - helm upgrade --install $DEPLOY ./helm --set image=$CI_REGISTRY_IMAGE --set tag=$CI_COMMIT_TAG
- POD=$(./kubectl get pods --selector=app=$DEPLOY --output=jsonpath='{.items[*].metadata.name}') - sleep 10
- ./kubectl exec $POD -- python manage.py migrate - POD=$(kubectl get pods --selector=app=$DEPLOY --output=jsonpath='{.items[*].metadata.name}')
- kubectl exec $POD -- python manage.py migrate

11
.vscode/launch.json vendored
View File

@@ -12,8 +12,11 @@
"args": [ "args": [
"test", "test",
], ],
"env": {
"DB_HOST": "localhost"
},
"django": true, "django": true,
"preLaunchTask": "Migrate" "preLaunchTask": "docker-compose up"
}, },
{ {
"name": "Run Server", "name": "Run Server",
@@ -22,10 +25,12 @@
"program": "${workspaceFolder}/manage.py", "program": "${workspaceFolder}/manage.py",
"args": [ "args": [
"runserver", "runserver",
"--noreload"
], ],
"env": {
"DB_HOST": "localhost"
},
"django": true, "django": true,
"preLaunchTask": "Migrate" "preLaunchTask": "docker-compose up"
} }
] ]
} }

20
.vscode/tasks.json vendored
View File

@@ -2,23 +2,9 @@
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"label": "Collect Static", "label": "docker-compose up",
"command": "venv/bin/python manage.py collectstatic --no-input", "command": "docker-compose up -d",
"type": "shell", "type": "shell"
"presentation": {
"reveal": "always"
},
"group": "build"
},
{
"dependsOn": "Collect Static",
"label": "Migrate",
"command": "venv/bin/python manage.py migrate",
"type": "shell",
"presentation": {
"reveal": "always"
},
"group": "build"
} }
] ]
} }

View File

@@ -1,4 +1,6 @@
FROM python:3.8.2 FROM python:3
USER root
WORKDIR /app WORKDIR /app
COPY config config COPY config config
@@ -8,10 +10,13 @@ COPY manage.py manage.py
COPY requirements.txt requirements.txt COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt RUN pip install -r requirements.txt
COPY scripts scripts
RUN chmod +x scripts/*
ENTRYPOINT ["scripts/entrypoint.sh"]
RUN useradd -ms /bin/bash django RUN useradd -ms /bin/bash django
RUN chown -R django . RUN chown -R django .
USER django USER django
RUN python manage.py collectstatic RUN python manage.py collectstatic --no-input
CMD ["gunicorn","-b",":8000", "-w", "4", "config.wsgi"] CMD ["scripts/cmd.sh"]

View File

@@ -2,4 +2,4 @@
My CI testing pipeline for a django project. My CI testing pipeline for a django project.
[![pipeline status](http://gitlab.ducoterra.net/ducoterra/ci_builder/badges/master/pipeline.svg)](http://gitlab.ducoterra.net/ducoterra/ci_builder/-/commits/master) [![pipeline status](https://gitlab.ducoterra.net/ducoterra/ci_builder/badges/master/pipeline.svg)](https://gitlab.ducoterra.net/ducoterra/ci_builder/-/commits/master)

View File

@@ -1,8 +1,9 @@
version: '3.5' version: '3'
services: services:
button: button:
build: . build: .
image: site:local
labels: labels:
- "traefik.http.routers.button.rule=Host(`button.localhost`)" - "traefik.http.routers.button.rule=Host(`button.localhost`)"
- "traefik.http.services.button-service.loadbalancer.server.port=8000" - "traefik.http.services.button-service.loadbalancer.server.port=8000"
@@ -18,37 +19,27 @@ services:
- SECRET_KEY=secret - SECRET_KEY=secret
- ALLOWED_HOSTS=button.localhost - ALLOWED_HOSTS=button.localhost
- DJANGO_SUPERUSER_PASSWORD=django - DJANGO_SUPERUSER_PASSWORD=django
postgres: postgres:
image: postgres:12 image: postgres:13
volumes: volumes:
- data:/var/lib/postgresql/data - data:/var/lib/postgresql/data
environment: environment:
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
ports:
pgadmin: - 5432:5432
image: dpage/pgadmin4:4
labels:
- "traefik.http.routers.pgadmin.rule=Host(`pgadmin.localhost`)"
- "traefik.http.services.pgadmin-service.loadbalancer.server.port=80"
volumes:
- pgadmin:/var/lib/pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: postgres
PGADMIN_DEFAULT_PASSWORD: postgres
traefik: traefik:
image: traefik:v2.2 image: traefik:v2.4
labels: labels:
- "traefik.http.routers.traefik.rule=Host(`traefik.localhost`)" - "traefik.http.routers.traefik.rule=Host(`traefik.localhost`)"
- "traefik.http.services.traefik-service.loadbalancer.server.port=8080" - "traefik.http.services.traefik-service.loadbalancer.server.port=8080"
command: --api.insecure=true --providers.docker --log.level=ERROR --accesslog=true command: --api.insecure=true --providers.docker --log.level=ERROR --accesslog=true
ports: ports:
- "80:80" - 80:80
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
volumes: volumes:
data: data:
pgadmin:

View File

@@ -13,8 +13,8 @@ else:
burst = False burst = False
sleep = 1 / rate sleep = 1 / rate
print(f"Beginning load test at {rate} calls/second") print(f"Beginning load test at {rate} calls/second")
# url = 'https://button.ducoterra.net/button/' url = 'https://button.ducoterra.net/'
url = 'http://button.localhost/button/' # url = 'http://button.localhost/'
def timer(func, *args, **kwargs): def timer(func, *args, **kwargs):
then = time.time() then = time.time()

View File

@@ -0,0 +1,3 @@
image: hub.ducoterra.net/ducoterra/button
tag: 1.0.2
secret: true

View File

@@ -1,6 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: $DEPLOY
data:
ALLOWED_HOSTS: localhost,$DEPLOY.ducoterra.net

View File

@@ -1,37 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: $DEPLOY
spec:
selector:
matchLabels:
app: $DEPLOY
template:
metadata:
labels:
app: $DEPLOY
spec:
containers:
- name: $DEPLOY
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
envFrom:
- configMapRef:
name: $DEPLOY
- secretRef:
name: django-secrets
volumeMounts:
- mountPath: /app/db
name: $DEPLOY
resources:
limits:
memory: "256Mi"
cpu: "250m"
requests:
memory: "1Mi"
cpu: "1m"
ports:
- containerPort: 8000
volumes:
- name: $DEPLOY
persistentVolumeClaim:
claimName: $DEPLOY

View File

@@ -1,79 +0,0 @@
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: $DEPLOY-internal-tls
annotations:
kubernetes.io/ingress.class: traefik-internal
spec:
entryPoints:
- websecure
tls:
certResolver: myresolver
domains:
- main: "*.ducoterra.net"
routes:
- match: Host(`$DEPLOY.ducoterra.net`)
kind: Rule
services:
- name: $DEPLOY
port: 8000
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: $DEPLOY-internal-web
annotations:
kubernetes.io/ingress.class: traefik-internal
spec:
entryPoints:
- web
routes:
- match: Host(`$DEPLOY.ducoterra.net`)
kind: Rule
services:
- name: $DEPLOY
port: 8000
middlewares:
- name: httpsredirect
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: $DEPLOY-external-tls
annotations:
kubernetes.io/ingress.class: traefik-external
spec:
entryPoints:
- websecure
tls:
certResolver: myresolver
routes:
- match: Host(`$DEPLOY.ducoterra.net`)
kind: Rule
services:
- name: $DEPLOY
port: 8000
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: $DEPLOY-external-web
annotations:
kubernetes.io/ingress.class: traefik-external
spec:
entryPoints:
- web
routes:
- match: Host(`$DEPLOY.ducoterra.net`)
kind: Rule
services:
- name: $DEPLOY
port: 8000
middlewares:
- name: httpsredirect

View File

@@ -1,11 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: $DEPLOY
spec:
storageClassName: nfs-encrypted
accessModes:
- ReadWriteMany
resources:
requests:
storage: 8Gi

View File

@@ -1,10 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: $DEPLOY
spec:
selector:
app: $DEPLOY
ports:
- port: 8000
targetPort: 8000

View File

@@ -1,39 +0,0 @@
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: $DEPLOY-internal-tls
annotations:
kubernetes.io/ingress.class: traefik-internal
spec:
entryPoints:
- websecure
tls:
certResolver: myresolver
domains:
- main: "*.ducoterra.net"
routes:
- match: Host(`$DEPLOY.ducoterra.net`)
kind: Rule
services:
- name: $DEPLOY
port: 8000
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: $DEPLOY-internal-web
annotations:
kubernetes.io/ingress.class: traefik-internal
spec:
entryPoints:
- web
routes:
- match: Host(`$DEPLOY.ducoterra.net`)
kind: Rule
services:
- name: $DEPLOY
port: 8000
middlewares:
- name: httpsredirect

3
scripts/cmd.sh Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
gunicorn -b :8000 -w 4 config.wsgi

22
scripts/entrypoint.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
if ! $SKIP_ENTRYPOINT || [ -z $SKIP_ENTRYPOINT ]; then
MIGRATED=false
while ! $MIGRATED; do
echo "Migrating..."
python manage.py migrate 2> /dev/null
if [ $? -eq 0 ]; then
MIGRATED=true
else
echo "ERROR - $(date) - Migrate failed."
sleep 1
fi
done
fi
$@

View File

@@ -13,28 +13,26 @@ class SimpleTest(TestCase):
def test_button(self): def test_button(self):
# Test initial load # Test initial load
c = Client() c = Client()
response = c.get('/button') response = c.get('/')
self.assertEqual(response.status_code, 301)
response = c.get('/button/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.context.get("achievement"), {}) self.assertEqual(response.context.get("achievement"), {})
# Test first achievement # Test first achievement
response = c.post('/button/', {}) response = c.post('/', {})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.json().get("pressed"), 1) self.assertEqual(response.json().get("pressed"), 1)
self.assertEqual(response.json().get("achievement"), "Clicked!") self.assertEqual(response.json().get("achievement"), "Clicked!")
self.assertEqual(c.session.get('pressed'), 1) self.assertEqual(c.session.get('pressed'), 1)
# Test second achievement # Test second achievement
response = c.post('/button/', {}) response = c.post('/', {})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.json().get("pressed"), 2) self.assertEqual(response.json().get("pressed"), 2)
self.assertEqual(response.json().get("achievement"), "Clicked Twice!") self.assertEqual(response.json().get("achievement"), "Clicked Twice!")
self.assertEqual(c.session.get('pressed'), 2) self.assertEqual(c.session.get('pressed'), 2)
# Test no achievement # Test no achievement
response = c.post('/button/', {}) response = c.post('/', {})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.json().get("pressed"), 3) self.assertEqual(response.json().get("pressed"), 3)
self.assertEqual(response.json().get("achievement"), None) self.assertEqual(response.json().get("achievement"), None)

View File

@@ -10,7 +10,6 @@ achievements = {
24: "I'm that old", 24: "I'm that old",
32: "2^5", 32: "2^5",
64: "2^6", 64: "2^6",
69: "Nice",
100: "one hundred", 100: "one hundred",
128: "2^7", 128: "2^7",
200: "two hundred", 200: "two hundred",
@@ -18,7 +17,6 @@ achievements = {
256: "2^8", 256: "2^8",
300: "three hundred", 300: "three hundred",
400: "four hundred", 400: "four hundred",
420: "Blaze it",
500: "half thousand", 500: "half thousand",
512: "2^9", 512: "2^9",
600: "six hundred", 600: "six hundred",
@@ -55,17 +53,17 @@ achievements = {
def button(request): def button(request):
PRESSED = 'pressed' PRESSED = 'pressed'
ACHIEVE = 'achievement' ACHIEVE = 'achievement'
current = request.session.get(PRESSED, 0) pressed = request.session.get(PRESSED, 0)
request.session[PRESSED] = current + 1
if request.method == "POST": if request.method == "POST":
pressed = pressed + 1
request.session[PRESSED] = pressed
response = { response = {
PRESSED: current, PRESSED: pressed,
ACHIEVE: achievements.get(current) ACHIEVE: achievements.get(pressed)
} }
return JsonResponse(response) return JsonResponse(response)
pressed = current
response = {PRESSED: pressed} response = {PRESSED: pressed}
achieved = {k:v for k,v in achievements.items() if k <= pressed} achieved = {k:v for k,v in achievements.items() if k <= pressed}
response.update({ACHIEVE: achieved}) response.update({ACHIEVE: achieved})