From 08b098310759f6ee749c53a85620d028c18c94ae Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Thu, 30 Apr 2020 10:19:07 +0200 Subject: [PATCH] Add Azure voting app --- .github/workflows/deploy.yaml | 82 ++++++++++++++++++++++++++++ Dockerfile | 3 ++ azure-vote/config_file.cfg | 5 ++ azure-vote/main.py | 88 ++++++++++++++++++++++++++++++ azure-vote/static/default.css | 96 +++++++++++++++++++++++++++++++++ azure-vote/templates/index.html | 29 ++++++++++ 6 files changed, 303 insertions(+) create mode 100644 .github/workflows/deploy.yaml create mode 100644 Dockerfile create mode 100644 azure-vote/config_file.cfg create mode 100644 azure-vote/main.py create mode 100644 azure-vote/static/default.css create mode 100644 azure-vote/templates/index.html diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..a5638bd --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,82 @@ +# This workflow will build a docker container, publish it to Azure Container Registry, and deploy it to Azure Kubernetes Service using a helm chart. +# +# To configure this workflow: +# +# 1. Set up the following secrets in your workspace: +# a. REGISTRY_USERNAME with ACR username +# b. REGISTRY_PASSWORD with ACR Password +# c. AZURE_CREDENTIALS with the output of `az ad sp create-for-rbac --sdk-auth` +# +# 2. Change the values for the REGISTRY_NAME, CLUSTER_NAME, CLUSTER_RESOURCE_GROUP and NAMESPACE environment variables (below). + +on: [push] + +# Environment variables available to all jobs and steps in this workflow +env: + REGISTRY_NAME: evryflaatten + CLUSTER_NAME: aks-demo + CLUSTER_RESOURCE_GROUP: aks-demo + NAMESPACE: default + IMAGE_NAME: myimage + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + + # Connect to Azure Container registry (ACR) + - uses: azure/docker-login@v1 + with: + login-server: ${{ env.REGISTRY_NAME }}.azurecr.io + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + # Container build and push to a Azure Container registry (ACR) + - run: | + docker build . -t ${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.IMAGE_NAME }}:${{ github.sha }} + docker push ${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.IMAGE_NAME }}:${{ github.sha }} + + # Set the target Azure Kubernetes Service (AKS) cluster. + - uses: azure/aks-set-context@v1 + with: + creds: '${{ secrets.AZURE_CREDENTIALS }}' + cluster-name: ${{ env.CLUSTER_NAME }} + resource-group: ${{ env.CLUSTER_RESOURCE_GROUP }} + + # Create namespace if doesn't exist + - run: | + kubectl create namespace ${{ env.NAMESPACE }} --dry-run -o json | kubectl apply -f - + + # Create imagepullsecret for Azure Container registry (ACR) + - uses: azure/k8s-create-secret@v1 + with: + container-registry-url: ${{ env.REGISTRY_NAME }}.azurecr.io + container-registry-username: ${{ secrets.REGISTRY_USERNAME }} + container-registry-password: ${{ secrets.REGISTRY_PASSWORD }} + secret-name: ${{ env.REGISTRY_NAME }}-registry-connection + namespace: ${{ env.NAMESPACE }} + + # Baking the helm chart to generate the manifests to deploy + - uses: azure/k8s-bake@v1 + with: + renderEngine: 'helm' + helmChart: './mychart/' + helm-version: 'latest' + releaseName: 'myapp' + overrideFiles: 'values.yaml' + overrides: | + image.repository:${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.IMAGE_NAME }} + image.tag:${{ github.sha }} + imagePullSecrets[0].name:${{ env.REGISTRY_NAME }}-registry-connection + id: bake + + # Deploy app to AKS + - uses: azure/k8s-deploy@v1 + with: + manifests: ${{ steps.bake.outputs.manifestsBundle }} + #images: | + # ${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.IMAGE_NAME }}:${{ github.sha }} + #imagepullsecrets: | + # ${{ env.REGISTRY_NAME }}-registry-connection + namespace: ${{ env.NAMESPACE }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ac10382 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM tiangolo/uwsgi-nginx-flask:python3.6 +RUN pip install redis +ADD /azure-vote /app diff --git a/azure-vote/config_file.cfg b/azure-vote/config_file.cfg new file mode 100644 index 0000000..e32731f --- /dev/null +++ b/azure-vote/config_file.cfg @@ -0,0 +1,5 @@ +# UI Configurations +TITLE = 'Azure Voting App' +VOTE1VALUE = 'Cats' +VOTE2VALUE = 'Dogs' +SHOWHOST = 'false' \ No newline at end of file diff --git a/azure-vote/main.py b/azure-vote/main.py new file mode 100644 index 0000000..e6198ce --- /dev/null +++ b/azure-vote/main.py @@ -0,0 +1,88 @@ +from flask import Flask, request, render_template +import os +import random +import redis +import socket +import sys + +app = Flask(__name__) + +# Load configurations from environment or config file +app.config.from_pyfile('config_file.cfg') + +if ("VOTE1VALUE" in os.environ and os.environ['VOTE1VALUE']): + button1 = os.environ['VOTE1VALUE'] +else: + button1 = app.config['VOTE1VALUE'] + +if ("VOTE2VALUE" in os.environ and os.environ['VOTE2VALUE']): + button2 = os.environ['VOTE2VALUE'] +else: + button2 = app.config['VOTE2VALUE'] + +if ("TITLE" in os.environ and os.environ['TITLE']): + title = os.environ['TITLE'] +else: + title = app.config['TITLE'] + +# Redis configurations +redis_server = os.environ['REDIS'] + +# Redis Connection +try: + if "REDIS_PWD" in os.environ: + r = redis.StrictRedis(host=redis_server, + port=6379, + password=os.environ['REDIS_PWD']) + else: + r = redis.Redis(redis_server) + r.ping() +except redis.ConnectionError: + exit('Failed to connect to Redis, terminating.') + +# Change title to host name to demo NLB +if app.config['SHOWHOST'] == "true": + title = socket.gethostname() + +# Init Redis +if not r.get(button1): r.set(button1,0) +if not r.get(button2): r.set(button2,0) + +@app.route('/', methods=['GET', 'POST']) +def index(): + + if request.method == 'GET': + + # Get current values + vote1 = r.get(button1).decode('utf-8') + vote2 = r.get(button2).decode('utf-8') + + # Return index with values + return render_template("index.html", value1=int(vote1), value2=int(vote2), button1=button1, button2=button2, title=title) + + elif request.method == 'POST': + + if request.form['vote'] == 'reset': + + # Empty table and return results + r.set(button1,0) + r.set(button2,0) + vote1 = r.get(button1).decode('utf-8') + vote2 = r.get(button2).decode('utf-8') + return render_template("index.html", value1=int(vote1), value2=int(vote2), button1=button1, button2=button2, title=title) + + else: + + # Insert vote result into DB + vote = request.form['vote'] + r.incr(vote,1) + + # Get current values + vote1 = r.get(button1).decode('utf-8') + vote2 = r.get(button2).decode('utf-8') + + # Return results + return render_template("index.html", value1=int(vote1), value2=int(vote2), button1=button1, button2=button2, title=title) + +if __name__ == "__main__": + app.run() diff --git a/azure-vote/static/default.css b/azure-vote/static/default.css new file mode 100644 index 0000000..02f1cc5 --- /dev/null +++ b/azure-vote/static/default.css @@ -0,0 +1,96 @@ +body { + background-color:#F8F8F8; +} + +div#container { + margin-top:5%; +} + +div#space { + display:block; + margin: 0 auto; + width: 500px; + height: 10px; + +} + +div#logo { + display:block; + margin: 0 auto; + width: 500px; + text-align: center; + font-size:30px; + font-family:Helvetica; + /*border-bottom: 1px solid black;*/ +} + +div#form { + padding: 20px; + padding-right: 20px; + padding-top: 20px; + display:block; + margin: 0 auto; + width: 500px; + text-align: center; + font-size:30px; + font-family:Helvetica; + border-bottom: 1px solid black; + border-top: 1px solid black; +} + +div#results { + display:block; + margin: 0 auto; + width: 500px; + text-align: center; + font-size:30px; + font-family:Helvetica; +} + +.button { + background-color: #4CAF50; /* Green */ + border: none; + color: white; + padding: 16px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + -webkit-transition-duration: 0.4s; /* Safari */ + transition-duration: 0.4s; + cursor: pointer; + width: 250px; +} + +.button1 { + background-color: white; + color: black; + border: 2px solid #008CBA; +} + +.button1:hover { + background-color: #008CBA; + color: white; +} +.button2 { + background-color: white; + color: black; + border: 2px solid #555555; +} + +.button2:hover { + background-color: #555555; + color: white; +} + +.button3 { + background-color: white; + color: black; + border: 2px solid #f44336; +} + +.button3:hover { + background-color: #f44336; + color: white; +} \ No newline at end of file diff --git a/azure-vote/templates/index.html b/azure-vote/templates/index.html new file mode 100644 index 0000000..603e110 --- /dev/null +++ b/azure-vote/templates/index.html @@ -0,0 +1,29 @@ + + + + + {{title}} + + + + + +
+
+ +
+
+ + + +
+
+
{{button1}} - {{ value1 }} | {{button2}} - {{ value2 }}
+ +
+
+ + \ No newline at end of file