From 8c9389ba1f60130823e0345689b1f5ce79c752c3 Mon Sep 17 00:00:00 2001 From: ducoterra Date: Mon, 8 Jun 2020 08:42:42 -0400 Subject: [PATCH] just go for it --- .gitlab-ci.yml | 39 ++++-------------- README.md | 21 +--------- config/settings.py | 8 +++- docker-compose.yml | 54 ++++++++++++++++++++++++ helm/.helmignore | 23 +++++++++++ helm/Chart.yaml | 23 +++++++++++ helm/templates/configmap.yaml | 8 ++++ helm/templates/deploy.yaml | 33 +++++++++++++++ helm/templates/hpa.yaml | 18 ++++++++ helm/templates/ingress.yaml | 78 +++++++++++++++++++++++++++++++++++ helm/templates/secret.yaml | 10 +++++ helm/templates/service.yaml | 15 +++++++ helm/values.yaml | 0 requirements.txt | 3 +- test.py | 38 +++++++++++++++++ ui/static/ui/button.js | 2 +- ui/static/ui/smooth.css | 23 ----------- ui/static/ui/smooth.js | 39 ------------------ ui/templates/ui/button.html | 2 +- ui/templates/ui/smooth.html | 31 -------------- ui/urls.py | 3 +- ui/views.py | 19 +++------ 22 files changed, 325 insertions(+), 165 deletions(-) create mode 100755 docker-compose.yml create mode 100644 helm/.helmignore create mode 100644 helm/Chart.yaml create mode 100644 helm/templates/configmap.yaml create mode 100644 helm/templates/deploy.yaml create mode 100644 helm/templates/hpa.yaml create mode 100755 helm/templates/ingress.yaml create mode 100644 helm/templates/secret.yaml create mode 100644 helm/templates/service.yaml create mode 100644 helm/values.yaml create mode 100644 test.py delete mode 100644 ui/static/ui/smooth.css delete mode 100644 ui/static/ui/smooth.js delete mode 100644 ui/templates/ui/smooth.html diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2b2d7f0..ede3da2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ variables: CI_PROJECT_DIR: "." - CI_REGISTRY_IMAGE: hub.ducoterra.net/ducoterra/mysite + CI_REGISTRY_IMAGE: hub.ducoterra.net/ducoterra/button + DEPLOY: button stages: - build @@ -30,7 +31,7 @@ test: script: - python manage.py test -deploy_to_test: +deploy: variables: DEPLOY: test stage: deploy @@ -42,35 +43,9 @@ deploy_to_test: entrypoint: [""] script: - apt -qq update >> /dev/null && apt -qq install -y curl gettext >> /dev/null - - curl -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 ./kubectl - - mkdir /deploy - - for f in $(find k8s -regex '.*\.ya*ml'); do envsubst < $f > "/deploy/$(basename $f)"; done - - for f in $(find k8s/test -regex '.*\.ya*ml'); do envsubst < $f > "/deploy/$(basename $f)"; done - - ./kubectl apply -f /deploy - - ./kubectl rollout status deploy $DEPLOY - - POD=$(./kubectl get pods --selector=app=$DEPLOY --output=jsonpath='{.items[*].metadata.name}') - - ./kubectl exec $POD -- python manage.py migrate - -deploy_to_prod: - variables: - DEPLOY: prod - stage: deploy - only: - variables: - - $CI_COMMIT_TAG - when: manual - image: - name: debian:10 - entrypoint: [""] - script: - - apt -qq update >> /dev/null && apt -qq install -y curl gettext >> /dev/null - - curl -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 ./kubectl - - mkdir /deploy - - for f in $(find k8s -regex '.*\.ya*ml'); do envsubst < $f > "/deploy/$(basename $f)"; done - - for f in $(find k8s/prod -regex '.*\.ya*ml'); do envsubst < $f > "/deploy/$(basename $f)"; done - - ./kubectl apply -f /deploy - - ./kubectl rollout status deploy $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 + - chmod +x /usr/bin/kubectl + - 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 - POD=$(./kubectl get pods --selector=app=$DEPLOY --output=jsonpath='{.items[*].metadata.name}') - ./kubectl exec $POD -- python manage.py migrate \ No newline at end of file diff --git a/README.md b/README.md index dbf05b1..1576364 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,4 @@ 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) - -## Django Environment Variables - -### Django Secret - -```bash -kubectl create secret generic django-secrets --from-literal=SECRET_KEY=$(python -c "import secrets ; print(secrets.token_urlsafe(32))") -``` - -### Django Allowed Hosts - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: test -data: - ALLOWED_HOSTS: localhost,test.ducoterra.net -``` +[![pipeline status](http://gitlab.ducoterra.net/ducoterra/ci_builder/badges/master/pipeline.svg)](http://gitlab.ducoterra.net/ducoterra/ci_builder/-/commits/master) \ No newline at end of file diff --git a/config/settings.py b/config/settings.py index 56e8a24..36637df 100644 --- a/config/settings.py +++ b/config/settings.py @@ -79,8 +79,12 @@ WSGI_APPLICATION = 'config.wsgi.application' DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db/db.sqlite3'), + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.getenv('DB_NAME', 'postgres'), + 'USER': os.getenv('POSTGRES_USER', 'postgres'), + 'PASSWORD': os.getenv('POSTGRES_PASSWORD', 'postgres'), + 'HOST': os.getenv('DB_HOST', 'postgres'), + 'PORT': os.getenv('DB_PORT', '5432'), } } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100755 index 0000000..5ebb74c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,54 @@ +version: '3.5' + +services: + button: + build: . + labels: + - "traefik.http.routers.button.rule=Host(`button.localhost`)" + - "traefik.http.services.button-service.loadbalancer.server.port=8000" + - "traefik.http.middlewares.btn-ratelimit.ratelimit.average=50" + - "traefik.http.middlewares.btn-ratelimit.ratelimit.burst=10" + - "traefik.http.routers.button.middlewares=btn-ratelimit@docker" + volumes: + - ./manage.py:/app/manage.py + - ./config:/app/config + - ./api:/app/api + environment: + - DEBUG=True + - SECRET_KEY=secret + - ALLOWED_HOSTS=button.localhost + - DJANGO_SUPERUSER_PASSWORD=django + + postgres: + image: postgres:12 + volumes: + - data:/var/lib/postgresql/data + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + + pgadmin: + 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: + image: traefik:v2.2 + labels: + - "traefik.http.routers.traefik.rule=Host(`traefik.localhost`)" + - "traefik.http.services.traefik-service.loadbalancer.server.port=8080" + command: --api.insecure=true --providers.docker --log.level=ERROR --accesslog=true + ports: + - "80:80" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + +volumes: + data: + pgadmin: \ No newline at end of file diff --git a/helm/.helmignore b/helm/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm/Chart.yaml b/helm/Chart.yaml new file mode 100644 index 0000000..cf7bc40 --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,23 @@ +apiVersion: v2 +name: helm +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +appVersion: 1.16.0 diff --git a/helm/templates/configmap.yaml b/helm/templates/configmap.yaml new file mode 100644 index 0000000..3fb1193 --- /dev/null +++ b/helm/templates/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }} + labels: + app: {{ .Release.Name }} +data: + ALLOWED_HOSTS: {{ .Release.Name }}.ducoterra.net diff --git a/helm/templates/deploy.yaml b/helm/templates/deploy.yaml new file mode 100644 index 0000000..4eed102 --- /dev/null +++ b/helm/templates/deploy.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }} + spec: + containers: + - image: {{ required "A valid .Values.image entry required!" .Values.image }}:{{ required "A valid .Values.tag entry required!" .Values.tag }} + name: {{ .Release.Name }} + ports: + - containerPort: 8080 + envFrom: + - configMapRef: + name: {{ .Release.Name }} + - secretRef: + name: {{ .Release.Name }} + - secretRef: + name: postgres + resources: + limits: + memory: "500Mi" + cpu: "250m" + requests: + memory: "1Mi" + cpu: "100m" \ No newline at end of file diff --git a/helm/templates/hpa.yaml b/helm/templates/hpa.yaml new file mode 100644 index 0000000..dcdd97c --- /dev/null +++ b/helm/templates/hpa.yaml @@ -0,0 +1,18 @@ +apiVersion: autoscaling/v2beta2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ .Release.Name }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ .Release.Name }} + minReplicas: 1 + maxReplicas: 4 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 50 \ No newline at end of file diff --git a/helm/templates/ingress.yaml b/helm/templates/ingress.yaml new file mode 100755 index 0000000..05f573e --- /dev/null +++ b/helm/templates/ingress.yaml @@ -0,0 +1,78 @@ +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: {{ .Release.Name }}-internal-tls + annotations: + kubernetes.io/ingress.class: traefik-internal +spec: + entryPoints: + - websecure + tls: + certResolver: myresolver + domains: + - main: "*.ducoterra.net" + routes: + - match: Host(`{{ .Release.Name }}.ducoterra.net`) + kind: Rule + services: + - name: {{ .Release.Name }} + port: 8000 + +--- + +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: {{ .Release.Name }}-internal-web + annotations: + kubernetes.io/ingress.class: traefik-internal +spec: + entryPoints: + - web + routes: + - match: Host(`{{ .Release.Name }}.ducoterra.net`) + kind: Rule + services: + - name: {{ .Release.Name }} + port: 8000 + middlewares: + - name: httpsredirect + +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: {{ .Release.Name }}-external-tls + annotations: + kubernetes.io/ingress.class: traefik-external +spec: + entryPoints: + - websecure + tls: + certResolver: myresolver + routes: + - match: Host(`{{ .Release.Name }}.ducoterra.net`) + kind: Rule + services: + - name: {{ .Release.Name }} + port: 8000 + +--- + +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: {{ .Release.Name }}-external-web + annotations: + kubernetes.io/ingress.class: traefik-external +spec: + entryPoints: + - web + routes: + - match: Host(`{{ .Release.Name }}.ducoterra.net`) + kind: Rule + services: + - name: {{ .Release.Name }} + port: 8000 + middlewares: + - name: httpsredirect \ No newline at end of file diff --git a/helm/templates/secret.yaml b/helm/templates/secret.yaml new file mode 100644 index 0000000..434418d --- /dev/null +++ b/helm/templates/secret.yaml @@ -0,0 +1,10 @@ +{{ if and .Values.secret .Release.IsInstall }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }} +type: generic +data: + SECRET_KEY: {{ randAlphaNum 64 | b64enc | quote }} + DJANGO_SUPERUSER_PASSWORD: {{ randAlphaNum 64 | b64enc | quote }} +{{ end }} \ No newline at end of file diff --git a/helm/templates/service.yaml b/helm/templates/service.yaml new file mode 100644 index 0000000..48cd3d3 --- /dev/null +++ b/helm/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: {{ .Release.Name }} + name: {{ .Release.Name }} +spec: + ports: + - port: 8000 + protocol: TCP + name: {{ .Release.Name }}-web + targetPort: 8000 + selector: + app: {{ .Release.Name }} # This selects the pod(s) that match the selector + type: ClusterIP \ No newline at end of file diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt index fb7a08e..a3d32eb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ django djangorestframework pygments gunicorn -whitenoise \ No newline at end of file +whitenoise +psycopg2-binary \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..a011862 --- /dev/null +++ b/test.py @@ -0,0 +1,38 @@ +import getpass +import threading +import requests +import time +import sys +from requests.auth import HTTPBasicAuth + +rate = int(sys.argv[1]) # /second +if rate == 0: + burst = True + print("Burst mode activated") +else: + burst = False + sleep = 1 / rate + print(f"Beginning load test at {rate} calls/second") +# url = 'https://button.ducoterra.net/button/' +url = 'http://button.localhost/button/' + +def timer(func, *args, **kwargs): + then = time.time() + func(*args, **kwargs) + print(time.time() - then) + +# init = requests.get('https://button.ducoterra.net/button/') +init = requests.get(url) +csrf = init.cookies.get('csrftoken') +session = init.cookies.get('sessionid') +me = lambda num: print(num) or print(requests.post(url, headers = {'Content-Type': 'application/json', 'X-CSRFToken': csrf, 'Cookie': f'csrftoken={csrf}; sessionid={session}'}).text) +threadme = lambda num: threading.Thread(target=timer, args=(me, num)).start() + +if burst: + [threadme(num) for num in range(0,100)] +else: + num = 1 + while True: + threadme(num) + num += 1 + time.sleep(sleep) \ No newline at end of file diff --git a/ui/static/ui/button.js b/ui/static/ui/button.js index ad5d9fc..3960b12 100644 --- a/ui/static/ui/button.js +++ b/ui/static/ui/button.js @@ -25,7 +25,7 @@ function add_achievement(text) { button.addEventListener("click", event => { button.disabled = true; button.classList.add("is-loading"); - fetch('/button/', { + fetch(button.dataset.action, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/ui/static/ui/smooth.css b/ui/static/ui/smooth.css deleted file mode 100644 index 72b1c52..0000000 --- a/ui/static/ui/smooth.css +++ /dev/null @@ -1,23 +0,0 @@ -html, body, .section, .container { - height: 100%; -} - -.container { - overflow-y: hidden; -} - -.element::-webkit-scrollbar { width: 0 !important } -.element { overflow: -moz-scrollbars-none; } -.element { -ms-overflow-style: none; } - -.hide-overflow { - overflow: hidden; -} - -.h-100 { - height: 100%; -} - -.w-100 { - width: 100%; -} \ No newline at end of file diff --git a/ui/static/ui/smooth.js b/ui/static/ui/smooth.js deleted file mode 100644 index a4d126b..0000000 --- a/ui/static/ui/smooth.js +++ /dev/null @@ -1,39 +0,0 @@ -// temp0.scrollIntoView({behavior: "smooth"}); -const scroll0 = document.getElementById("scroll-0"); -const scroll1 = document.getElementById("scroll-1"); -const scroll2 = document.getElementById("scroll-2"); -const scroll3 = document.getElementById("scroll-3"); - -window.addEventListener("scroll", function(event) { - window.addEventListener("mouseup", event => { - console.log("hello"); - }); - event.preventDefault(); - document.querySelector("html").classList.add("hide-overflow"); - if (scroll0.dataset.isScrolling != "true") { - scroll0.dataset.isScrolling = 'true'; - - if (scroll1.dataset.seen != "true") { - scroll1.dataset.seen = "true"; - scroll1.scrollIntoView({behavior: "smooth"}); - } - else if (scroll2.dataset.seen != "true") { - scroll2.dataset.seen = "true"; - scroll2.scrollIntoView({behavior: "smooth"}); - } - else if (scroll3.dataset.seen != "true") { - scroll3.dataset.seen = "true"; - scroll3.scrollIntoView({behavior: "smooth"}); - } - else { - scroll1.dataset.seen = "false"; - scroll2.dataset.seen = "false"; - scroll3.dataset.seen = "false"; - scroll0.scrollIntoView({behavior: "smooth"}); - } - setTimeout(() => { - scroll0.dataset.isScrolling = false; - document.querySelector("html").classList.remove("hide-overflow"); - }, 1000) - } -}); \ No newline at end of file diff --git a/ui/templates/ui/button.html b/ui/templates/ui/button.html index b5b404f..08d1e64 100644 --- a/ui/templates/ui/button.html +++ b/ui/templates/ui/button.html @@ -22,7 +22,7 @@

The Button

- +

diff --git a/ui/templates/ui/smooth.html b/ui/templates/ui/smooth.html deleted file mode 100644 index 70179ed..0000000 --- a/ui/templates/ui/smooth.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends 'ui/base.html' %} - -{% load static %} - -{% block css %} - -{% endblock %} - -{% block js %} - -{% endblock %} - -{% block body %} -
-
-

Hello There

-
- -
-

I'm an Apple Ad

-
- -
-

See me scroll

-
- -
-

You owe me $3,000 dollars for this

-
-
-{% endblock %} \ No newline at end of file diff --git a/ui/urls.py b/ui/urls.py index 5f8f493..e36302a 100644 --- a/ui/urls.py +++ b/ui/urls.py @@ -2,6 +2,5 @@ from django.urls import path from . import views urlpatterns = [ - path('button/', views.button, name = 'button'), - path('smooth/', views.smooth, name = 'smooth'), + path('', views.button, name = 'button'), ] \ No newline at end of file diff --git a/ui/views.py b/ui/views.py index 6e60782..57e778d 100644 --- a/ui/views.py +++ b/ui/views.py @@ -55,25 +55,18 @@ achievements = { def button(request): PRESSED = 'pressed' ACHIEVE = 'achievement' - - try: - request.session[PRESSED] - except KeyError: - request.session[PRESSED] = 0 + current = request.session.get(PRESSED, 0) + request.session[PRESSED] = current + 1 if request.method == "POST": - request.session[PRESSED] += 1 response = { - PRESSED: request.session[PRESSED], - ACHIEVE: achievements.get(request.session[PRESSED]) + PRESSED: current, + ACHIEVE: achievements.get(current) } return JsonResponse(response) - pressed = request.session[PRESSED] + pressed = current response = {PRESSED: pressed} achieved = {k:v for k,v in achievements.items() if k <= pressed} response.update({ACHIEVE: achieved}) - return render(request, "ui/button.html", response) - -def smooth(request): - return render(request, "ui/smooth.html") \ No newline at end of file + return render(request, "ui/button.html", response) \ No newline at end of file