diff --git a/.gitignore b/.gitignore
index 3d8b36a..3a97242 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,8 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+
+# binaries
+kompose
+skaffold
+minikube*
diff --git a/_history.md b/_history.md
new file mode 100644
index 0000000..97435e9
--- /dev/null
+++ b/_history.md
@@ -0,0 +1,15 @@
+2022-06-21
+clone repo and add deploy key
+make run added version to docker file
+ errors:
+
+gcloud auth configure-docker
+
+
+
+STEPS:
+1. Local Development:
+npm install
+npm run start
+
+docker run -d -p 3030:3000 --name reactle-dev reactle:dev
\ No newline at end of file
diff --git a/docker-compose-k8s.yml b/docker-compose-k8s.yml
new file mode 100644
index 0000000..2de1a43
--- /dev/null
+++ b/docker-compose-k8s.yml
@@ -0,0 +1,18 @@
+# To build the entire stack run 'make run'
+version: '3.4'
+
+services:
+ reactle:
+ container_name: reactle
+ restart: unless-stopped
+ build:
+ context: .
+ dockerfile: ./src/Dockerfile
+ ports:
+ - 8999:3000
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost/8999"]
+ interval: 30s
+ timeout: 5s
+ retries: 3
+ start_period: 15s
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index b6aaa0a..ce86bed 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,5 @@
# To build the entire stack run 'make run'
+version: '3.4'
services:
reactle:
@@ -6,12 +7,12 @@ services:
restart: unless-stopped
build:
context: .
- dockerfile: ./docker/app/Dockerfile
+ dockerfile: ./src/Dockerfile
ports:
- - 3000:3000
+ - 8081:8081
healthcheck:
- test: ["CMD", "curl", "-f", "http://localhost:3000"]
+ test: ["CMD", "curl", "-f", "http://localhost:8081"]
interval: 30s
timeout: 5s
retries: 3
- start_period: 15s
+ start_period: 15s
\ No newline at end of file
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..9d789ce
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,24 @@
+FROM node:16.14.0-alpine3.14 AS node_modules
+WORKDIR /app
+COPY package-lock.json package.json ./
+RUN npm install
+COPY . .
+
+FROM node_modules AS prod_builder
+RUN npm run build
+
+## Production image
+FROM nginx:1.20.2-alpine AS prod
+COPY docker/etc/nginx/nginx.conf /etc/nginx/nginx.conf
+COPY docker/etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
+COPY --from=prod_builder /app/build /usr/share/nginx/html
+COPY docker/build_system.sh .
+RUN ./build_system.sh && rm ./build_system.sh
+# port use by Nginx within docker network.
+EXPOSE 8080
+USER reactle
+
+## Development image
+FROM node_modules AS dev
+EXPOSE 3000
+CMD npm run start
diff --git a/docker/build_system.sh b/docker/build_system.sh
new file mode 100755
index 0000000..8cf1f8f
--- /dev/null
+++ b/docker/build_system.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# Strict shell settings.
+set -e pipefall;
+set -u pipefall;
+
+UID=1001
+GID=1001
+USER=reactle
+GROUP=reactle
+
+# Enable print for all executed commands
+trace_on() { set -x;}
+# Disable print for all executed commands
+trace_off() { set +x;}
+
+add_custom_user_group() {
+ addgroup -g $GID -S $GROUP
+ adduser -S -D -H \
+ -u $UID \
+ -G $GROUP \
+ -s /sbin/nologin \
+ -h /var/cache/nginx \
+ $USER
+}
+
+add_user_permission() {
+ chown -R $USER:$GROUP \
+ /usr/share/nginx \
+ /var/cache/nginx \
+ /var/log/nginx \
+ /etc/nginx
+}
+
+trace_on
+ add_custom_user_group
+ add_user_permission
+trace_off
diff --git a/docker/etc/nginx/conf.d/default.conf b/docker/etc/nginx/conf.d/default.conf
new file mode 100644
index 0000000..fe3f915
--- /dev/null
+++ b/docker/etc/nginx/conf.d/default.conf
@@ -0,0 +1,19 @@
+server {
+ # Since non-root user cannot bind to port below 1024
+ # We will use the docker port 8080 instead of port 80.
+ listen 8080;
+ listen [::]:8080;
+ server_name localhost;
+
+ # Location of build files.
+ location / {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+ }
+
+ # Redirect server error pages to the static page /50x.html
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+}
diff --git a/docker/etc/nginx/nginx.conf b/docker/etc/nginx/nginx.conf
new file mode 100644
index 0000000..b4c3a44
--- /dev/null
+++ b/docker/etc/nginx/nginx.conf
@@ -0,0 +1,36 @@
+# Don't use default nginx user.
+# Intead let the non-root user run the nginx.
+# user nginx;
+worker_processes auto;
+
+error_log /var/log/nginx/error.log notice;
+
+# The nginx.pid file will be created when the nginx process starts.
+# This file can be deleted and created at any number of times during production.
+# So, we will store it in /tmp where non-root user have permission to access it.
+pid /tmp/nginx.pid;
+
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+
+ sendfile on;
+ #tcp_nopush on;
+
+ keepalive_timeout 65;
+
+ #gzip on;
+
+ include /etc/nginx/conf.d/*.conf;
+}
diff --git a/k8s-manifests/reactle-deployment.yaml b/k8s-manifests/reactle-deployment.yaml
new file mode 100644
index 0000000..12b84e4
--- /dev/null
+++ b/k8s-manifests/reactle-deployment.yaml
@@ -0,0 +1,43 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ annotations:
+ kompose.cmd: kompose convert -f docker-compose.yml
+ kompose.version: 1.26.1 (a9d05d509)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: reactle
+ name: reactle
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ io.kompose.service: reactle
+ strategy: {}
+ template:
+ metadata:
+ annotations:
+ kompose.cmd: kompose convert -f docker-compose.yml
+ kompose.version: 1.26.1 (a9d05d509)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: reactle
+ spec:
+ containers:
+ - image: reactle
+ livenessProbe:
+ exec:
+ command:
+ - curl
+ - -f
+ - http://localhost:8080
+ failureThreshold: 3
+ initialDelaySeconds: 15
+ periodSeconds: 30
+ timeoutSeconds: 5
+ name: reactle
+ ports:
+ - containerPort: 3000
+ resources: {}
+ restartPolicy: Always
+status: {}
diff --git a/k8s-manifests/reactle-service.yaml b/k8s-manifests/reactle-service.yaml
new file mode 100644
index 0000000..24c539f
--- /dev/null
+++ b/k8s-manifests/reactle-service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+ annotations:
+ kompose.cmd: kompose convert -f docker-compose.yml
+ kompose.version: 1.26.1 (a9d05d509)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: reactle
+ name: reactle
+spec:
+ ports:
+ - name: "8001"
+ port: 8001
+ targetPort: 3000
+ selector:
+ io.kompose.service: reactle
+status:
+ loadBalancer: {}
diff --git a/k8s/reactle-deployment.yaml b/k8s/reactle-deployment.yaml
new file mode 100644
index 0000000..4398855
--- /dev/null
+++ b/k8s/reactle-deployment.yaml
@@ -0,0 +1,43 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ annotations:
+ kompose.cmd: kompose convert -f docker-compose.yml
+ kompose.version: 1.26.1 (a9d05d509)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: reactle
+ name: reactle
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ io.kompose.service: reactle
+ strategy: {}
+ template:
+ metadata:
+ annotations:
+ kompose.cmd: kompose convert -f docker-compose.yml
+ kompose.version: 1.26.1 (a9d05d509)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: reactle
+ spec:
+ containers:
+ - image: reactle
+ livenessProbe:
+ exec:
+ command:
+ - curl
+ - -f
+ - http://localhost:8081
+ failureThreshold: 3
+ initialDelaySeconds: 15
+ periodSeconds: 30
+ timeoutSeconds: 5
+ name: reactle
+ ports:
+ - containerPort: 8081
+ resources: {}
+ restartPolicy: Always
+status: {}
diff --git a/k8s/reactle-service.yaml b/k8s/reactle-service.yaml
new file mode 100644
index 0000000..04dca87
--- /dev/null
+++ b/k8s/reactle-service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+ annotations:
+ kompose.cmd: kompose convert -f docker-compose.yml
+ kompose.version: 1.26.1 (a9d05d509)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: reactle
+ name: reactle
+spec:
+ ports:
+ - name: "8081"
+ port: 8081
+ targetPort: 8081
+ selector:
+ io.kompose.service: reactle
+status:
+ loadBalancer: {}
diff --git a/skaffold.yaml b/skaffold.yaml
new file mode 100644
index 0000000..9212826
--- /dev/null
+++ b/skaffold.yaml
@@ -0,0 +1,36 @@
+apiVersion: skaffold/v2beta28
+kind: Config
+metadata:
+ name: devops
+
+build:
+ artifacts:
+ - image: reactle
+ context: .
+
+profiles:
+ - name: dev
+ activation:
+ - command: dev
+ build:
+ artifacts:
+ - image: reactle
+ context: .
+ docker:
+ dockerfile: docker/Dockerfile
+ buildArgs:
+ ENV: development
+ sync:
+ manual:
+ - src: 'src/**/**'
+ dest: .
+deploy:
+ kubectl:
+ manifests:
+ - k8s/reactle-deployment.yaml
+ - k8s/reactle-service.yaml
+portForward:
+ - resourceType: deployment
+ resourceName: reactle
+ port: 3000
+ localPort: 9000 # *Optional*
\ No newline at end of file
diff --git a/slides/assets/codespaces-connect.png b/slides/assets/codespaces-connect.png
new file mode 100644
index 0000000..d9b1ef7
Binary files /dev/null and b/slides/assets/codespaces-connect.png differ
diff --git a/slides/assets/codespaces-launch.png b/slides/assets/codespaces-launch.png
new file mode 100644
index 0000000..5623ddd
Binary files /dev/null and b/slides/assets/codespaces-launch.png differ
diff --git a/slides/assets/containers.png b/slides/assets/containers.png
new file mode 100644
index 0000000..60fe0e2
Binary files /dev/null and b/slides/assets/containers.png differ
diff --git a/slides/assets/devops-loop.svg b/slides/assets/devops-loop.svg
new file mode 100644
index 0000000..630ffe1
--- /dev/null
+++ b/slides/assets/devops-loop.svg
@@ -0,0 +1 @@
+
diff --git a/slides/assets/devops.png b/slides/assets/devops.png
new file mode 100644
index 0000000..af668b6
Binary files /dev/null and b/slides/assets/devops.png differ
diff --git a/slides/assets/discord-bot-server-members-intent.png b/slides/assets/discord-bot-server-members-intent.png
new file mode 100644
index 0000000..35858bd
Binary files /dev/null and b/slides/assets/discord-bot-server-members-intent.png differ
diff --git a/slides/assets/discord-bot-token-copy.png b/slides/assets/discord-bot-token-copy.png
new file mode 100644
index 0000000..4e873d5
Binary files /dev/null and b/slides/assets/discord-bot-token-copy.png differ
diff --git a/slides/assets/discord-profile-id.png b/slides/assets/discord-profile-id.png
new file mode 100644
index 0000000..4cda605
Binary files /dev/null and b/slides/assets/discord-profile-id.png differ
diff --git a/slides/assets/grafana-with-skaffold.png b/slides/assets/grafana-with-skaffold.png
new file mode 100644
index 0000000..630a1d7
Binary files /dev/null and b/slides/assets/grafana-with-skaffold.png differ
diff --git a/slides/assets/k8s-diagram.png b/slides/assets/k8s-diagram.png
new file mode 100644
index 0000000..5a19e8e
Binary files /dev/null and b/slides/assets/k8s-diagram.png differ
diff --git a/slides/assets/pages-workflow.png b/slides/assets/pages-workflow.png
new file mode 100644
index 0000000..4ad2ba9
Binary files /dev/null and b/slides/assets/pages-workflow.png differ
diff --git a/slides/assets/pages.png b/slides/assets/pages.png
new file mode 100644
index 0000000..5f31875
Binary files /dev/null and b/slides/assets/pages.png differ
diff --git a/slides/assets/pipeline-example.png b/slides/assets/pipeline-example.png
new file mode 100644
index 0000000..58cc137
Binary files /dev/null and b/slides/assets/pipeline-example.png differ
diff --git a/slides/assets/project-directories.png b/slides/assets/project-directories.png
new file mode 100644
index 0000000..0783efe
Binary files /dev/null and b/slides/assets/project-directories.png differ
diff --git a/slides/assets/tests-passing.png b/slides/assets/tests-passing.png
new file mode 100644
index 0000000..f68e897
Binary files /dev/null and b/slides/assets/tests-passing.png differ
diff --git a/slides/slides.md b/slides/slides.md
new file mode 100644
index 0000000..b145c8f
--- /dev/null
+++ b/slides/slides.md
@@ -0,0 +1,1009 @@
+---
+# try also 'default' to start simple
+theme: geist
+# random image from a curated Unsplash collection by Anthony
+# like them? see https://unsplash.com/collections/94734566/slidev
+background: https://source.unsplash.com/collection/94734566/1920x1080
+# apply any windi css classes to the current slide
+class: 'text-center'
+# https://sli.dev/custom/highlighters.html
+highlighter: shiki
+# show line numbers in code blocks
+lineNumbers: false
+# some information about the slides, markdown enabled
+info: |
+ ## Intro to DevOps with Errbot
+ An introduction to the world of DevOps
+
+# persist drawings in exports and build
+drawings:
+ persist: false
+---
+
+# DevOps for Fun and Profit
+## Intro to the software development lifecycle
+
+
+
+ Press Space to begin the journey
+
+
+
+
+
+
+
+
+
+---
+
+# What is DevOps?
+
+DevOps is a set of combined practices that merge development (Dev) and operations (Ops) together
+
+- π‘ Continuous planning
+- β‘ Rapid application development
+- π― Sharable development environments
+- π€ Automated and repeatable builds & tests
+- π CI/CD pipelines
+- π Obervability
+- π Security
+
+
+
+
+Read more about [what is DevOps](https://en.wikipedia.org/wiki/DevOps)
+
+
+
+
+
+---
+
+# DevOps Visualized
+
+
+
+---
+
+# Plan π‘
+
+The planning phase of DevOps is often [agile software development](https://en.wikipedia.org/wiki/Agile_software_development)
+
+- β’ Iterative
+- β’ Continuous
+- β’ Can (and will) change frequently
+- β’ No "big bang" launch, an iterative approach
+
+> For this demo we will be planning the launch of a Wordle clone.
+
+
+
+---
+
+# Code π»
+
+The coding phase of DevOps is where ideas come to life... and where nightmares are born
+
+```python
+if production == "down":
+ print("This does not bring joy")
+```
+
+- π― Develop in sharable and automated environments
+- ποΈ Adopt a common code style (use a linter)
+- βοΈ Use a version control system
+- πΎ Commit and push often
+- π Work in the open, get feedback, request reviews
+
+> For the DevOpsDaysLA demo, we will be writting code in [GitHub codespaces](https://github.com/features/codespaces)
+
+
+
+---
+
+# Build π¦
+
+The build phase of DevOps is where the code is compiled and often saved as an artifact for later deployment
+
+- π οΈ Build your application / binaries in a repeatable environment (CI/CD)
+- π³ Containerize applications and services where you can
+- π Secure your software supply chain when building applications
+- π Seperate packages, containers, and binaries from configurations and secrets
+
+> For the DevOpsDaysLA demo, we will be using Docker to containerize and build our application
+
+
+
+---
+
+# Test π§ͺ
+
+The test phase of DevOps is where the application is... tested!
+
+- π― Repeatable test environment (CI/CD + Docker)
+- π¨βπ¬ Unit tests
+- ποΈ Staging Environment
+- π Integration tests
+- π¬ Container scanning
+- π SAST & DAST - Code scanning
+
+> For the DevOpsDaysLA demo, you will get exposure to unit tests, container scanning, and static analysis
+
+
+
+---
+
+# Release π·οΈ
+
+The release stage of DevOps is where the application is released to users.
+
+Examples include:
+
+- π¦ Publishing a binary to an opensource repository
+- π·οΈ Publishing a release label and Git tag for an application
+- π’ Releasing a new version with an installer for a desktop application
+
+---
+
+# Deploy π
+
+The deploy stage of DevOps is my personal favorite. This is where code is deployed into systems in production
+
+- π§± Infrastructure as Code
+- β© CI/CD Pipelines
+- π Deployment
+
+---
+
+# Operate π§°
+
+The operate stage of DevOps is where the application is used by real users and we react to that usage
+
+- ποΈ Scaling
+- π Bug Reports
+- π¬ Continuous feedback
+- π Rolling updates for security
+
+---
+
+# Monitor π
+
+The monitor stage of DevOps is where the application is "monitored" via its metrics, events, and logs
+
+- π Gather Metrics
+- π Visualize and Dashboard all the things
+- π Collect logs and events
+- π Search and audit for anomalies
+- π Trigger alerts and notifications
+
+---
+
+# About our Application? π€
+
+[Reactle](https://github.com/cwackerfuss/react-wordle) is a based off of an open source repository from [cwackerfuss](https://github.com/cwackerfuss).
+
+Reactle is built using
+
+- β’ React
+- β’ Typescript
+- β’ Tailwind
+
+> Reactle can be configured and customized for different languages-from Japanese to Latin, Themes, including birdle and Trekle, and even Morse Code!
+
+
+
+---
+
+# Hands-on Workshop Time π
+
+In this workshop, we will be doing the following:
+
+- π Setup and explore our IDE
+- π» Get hands-on with different open source development tools
+- π¦ Build the app using [npm](link here), [docker](link here), then finally [skaffold](https://skaffold.dev/)
+- βΈοΈ Deploy the app to Kubernetes
+- π§ͺ Ensure our test suite is passing
+- π Run SAST on our code and container scanning on our container image
+- π€Ή Interact with our new "production" application
+- π Observe reactle's usage with Grafana & Loki
+- π Run a real world CI/CD pipeline and deployment
+
+
+Plan > Code > Build > Test > Release > Deploy > Operate > Monitor
+
+---
+# Tech and Tools
+
+We will be using or learning the following tools during our workshop:
+- github + codespaces + actions
+- react
+- npm
+- Docker
+- minikube + kubernetes
+- skaffold
+
+
+---
+# Getting Started π‘
+
+- 1: Go to [github.com/DevOpsDaysLA/devops](https://github.com/DevOpsDaysLA/devops) and click "Fork" in the upper right corner
+- 2: Ensure you fork the repo into the [DevOpsDaysLA](https://github.com/DevOpsDaysLA) organization. Note: If you are not part of the DevOpsDaysLA workshop, you can fork the repo to your personal GitHub account instead
+- 3: Upon the successful fork, you will notice a GitHub action workflow has started for your documentation page
+- 4: Go to your repo settings and ensure your GitHub pages site is accessible to view your documentation
+
+
+
+---
+
+# GitHub Pages π
+
+[GitHub Pages](https://pages.github.com/) is a free service for hosting static website content on GitHub.com
+
+Commits / Pushes to the `main` branch automatically update our documentation site
+
+```yaml
+name: pages
+on:
+ push:
+ branches:
+ - main
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # pin@v2
+ - uses: actions/setup-python@f38219332975fe8f9c04cca981d674bf22aea1d3 # pin@v2
+ with:
+ python-version: 3.x
+ - run: pip install mkdocs-material
+ - run: mkdocs gh-deploy --force
+```
+
+---
+
+# Pages GitHub Actions Workflow β©
+
+
+
+Hooray! We not have all the nitty gritty documentation for our bot publically hosted on GitHub Pages!
+
+---
+
+# Setup
+
+To begin implementing our `Reactle` clone, we need to first start our dev environment:
+
+**DevOpsDaysLA Workshop:**
+
+Simply create a new GitHub Codespace
+
+
+
+**Non-DevOpsDaysLA Workshop:**
+
+[Public setup documentation](https://errbot.birki.io/)
+
+Plan > Code > Build > Test > Release > Deploy > Operate > Monitor
+
+---
+
+# Codespaces π»
+
+If you are apart of the DevOpsDaysLA workshop, you will be able to create a new GitHub Codespace for development
+
+**What is GitHub Codespaces?**
+
+- βοΈ Cloud hosted development environment
+- π― Consistent, repeatable, and shareable
+- π§ Removes the "it works on my machine" barrier
+- π₯ Saves us from dependency hell
+- β Allows developers to deploy a dev environement in one-click and begin working on a project
+
+You can read more about GitHub Codespaces [here](https://github.com/features/codespaces)
+
+> Note: If you are not apart of the DevOpsDaysLA workshop, you will **not** have free access to GitHub codespaces and will need to setup errbot [locally for development](https://errbot.birki.io/setup/)
+
+
+
+---
+
+# Connect to Codespace
+
+
+
+You will now connect to Codespaces through your browser. If you have [Visual Studio Code](https://code.visualstudio.com/) installed, you can optionally attach there as well.
+
+Our Codespace environment comes pre-installed with all the dependencies we need to run errbot locally and develop new features.
+
+> Note: If you are **not** apart of the DevOpsDaysLA workshop, you will be doing all the following steps from here on locally and not in GitHub Codespaces
+
+
+
+---
+
+# Build and run the App with npm π¨βπ¬
+
+Simply run the following commands to start the app with npm:
+
+```bash
+npm install
+npm run start
+```
+
+> Checkout the application running in your browser at [localhost:3000](http://localhost:3000).
+> Port 3000 is the default port for a react development server.
+
+
+
+---
+# Build and run the App with Docker π¨βπ¬
+
+## Development
+```
+$ docker build -t reactle:dev -f docker/app/Dockerfile .
+$ docker run -d -p 3000:3000 --name reactle-dev reactle:dev
+```
+Open http://localhost:3000 in browser.
+
+## Production
+```
+$ docker build --target=prod -t reactle:prod -f docker/app/Dockerfile .
+$ docker run -d -p 80:8080 --name reactle-prod reactle:prod
+```
+---
+# Run the App with Docker Compose βΈοΈ
+
+Open the Dockerfile
+
+```
+$ make run
+```
+
+---
+# BREAK
+---
+# Install Tools
+Install Skaffold, Minikube and Kompose.
+- [Skaffold](https://skaffold.dev/docs/install/)
+```
+curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 &&
+sudo install skaffold /usr/local/bin/
+```
+- [Minikube](https://minikube.sigs.k8s.io/docs/start/)
+```
+curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
+sudo install minikube-linux-amd64 /usr/local/bin/minikube
+```
+- [Kompose](https://kompose.io/)
+Kompose is used by Skaffold to convert the docker-compose.yml into Kubernetes manifests.
+```
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.1/kompose-linux-amd64 -o kompose
+sudo install kompose /usr/local/bin
+```
+---
+# Initialize Skaffold
+`skaffold init --compose-file docker-compose.yml`
+
+> ? Choose the builder to build image reactle [Use arrows to move, type to filter]
+ Buildpacks (package.json)
+ Docker (docker/app/Dockerfile)
+ None (image not built from these sources)
+
+
+? Which builders would you like to create kubernetes resources for? [Use arrows to move, space to select, to all, to none, type to filter]
+ [ ] Buildpacks (package.json)
+> [ ] Docker (src/Dockerfile)
+
+Select Docker
+
+---
+# Start minikube
+```
+minikube start
+minikube status
+```
+
+---
+# Open Skaffold.yaml
+
+---
+---
+# Run the App with Skaffold βΈοΈ
+
+We will be using [Skaffold](https://skaffold.dev/) to run our bot for development (either in Codespaces or locally)
+
+Start our [minikube](https://minikube.sigs.k8s.io/docs/start/), cluster:
+
+> Note: You may need to run `sudo chown -R $USER $HOME/.minikube; chmod -R u+wrx $HOME/.minikube` to start your minikube cluster in Codespaces
+
+```text
+minikube start --profile custom
+
+skaffold config set --global local-cluster true
+
+eval $(minikube -p custom docker-env)
+```
+
+Run the bot with Skaffold (and tail the logs):
+
+```text
+skaffold dev --tail=true
+```
+
+
+
+---
+
+# Development Environment 1,000 Foot Overview π¦
+
+While the Skaffold command is running, let's look at our dev env ([source](https://errbot.birki.io/deployment/)):
+
+
+
+TODO: UPDATE IMAGE
+Intermission!
+---
+
+# What are Containers? π³
+
+
+Intermission!
+---
+
+# What is Kubernetes? βΈοΈ
+
+Kubernetes, or k8s, is an open source platform that automates container orchestration and application deployment.
+
+- β’ Declarative language for defining containers and workloads (k8s manifests)
+- β’ Scalable, highly available, and resilient
+- β’ Open source
+- β’ Creating by Google
+Intermission!
+---
+
+# Kubernetes Diagram πΊοΈ
+
+
+
+---
+
+# Kubernetes Files π
+
+The `script/k8s/` directory contains all the files we need to deploy our bot to Kubernetes locally (using Skaffold)
+
+**Benefits of using Skaffold:**
+
+- β’ Live reload (changes to source files or k8s manifests)
+- β’ Handles all `kubectl` commands for us
+- β’ Faster dev cycles
+- β’ Very close to our production environment (if not identical)
+
+**Alternative dev building options:**
+
+- π³ Docker-compose - The `make run` command from the root of this repo is a wrapper command for building locally with Docker-compose
+- βΈοΈ Minikube - The `make kube` command from the root of this repo is a wrapper command for building locally with minikube using `kubectl`
+Intermission!
+---
+
+# Back to the Terminal π»
+
+Going back to our terminal, we should see live output from our bot running in k8s via skaffold.
+
+Additionally, the following has happened:
+
+- β’ π’ The configured bot owner got a ping that the bot is now online
+- β’ π¬ The bot can now been as online on our Discord server
+- β’ β¨οΈ We can now type a command and our bot will respond -> `!uptime`
+
+Let's go ahead and press `ctrl+c` in our terminal running skaffold to stop the bot
+
+> Note: You can also test out other commands that are available. Try `!help`
+
+
+
+---
+
+# Creating our very own chat command!
+
+> π‘ Plan Stage of DevOps
+
+We want a new command that returns a link to the DevOps life-cycle
+
+Discord will conveniently render this link as an image for us.
+
+This will be a brand new chat command so we will need to implement it and add the associated tests for our new code
+
+
+
+---
+
+# Code for the Chat Command π
+
+> π» Code Stage of DevOps
+
+Another engineer has already written most of the code for our new command.
+
+Let's copy that code into our `src/errbot/plugins/` directory:
+
+```text
+mkdir src/errbot/plugins/devops
+
+cp demo/code/devops/* src/errbot/plugins/devops/
+```
+
+
+
+---
+
+# Build with Skaffold βΈοΈ
+
+> π¦ Build Stage of DevOps
+
+Now that we have our new command, we need to build our bot with Skaffold:
+
+```text
+skaffold dev --tail=true
+```
+
+Once the container starts up, run the new command: `!devops`
+
+
+
+---
+
+# Fix our Command π¨
+
+> π» Code Stage of DevOps
+
+Oh no! Our command is borked! Back to our editor to fix the bad code:
+
+```python
+# Delete these lines
+if self.chaos():
+ return "CHAOS"
+...
+# Delete this function
+def chaos(self):
+ """
+ This does not bring joy
+ """
+ if random() > 0.5:
+ return True
+ else:
+ return False
+```
+
+Redeploy and test with `skaffold dev --tail=true` -> `!devops`
+
+
+
+---
+
+# Lint π§Ή
+
+> π» Code Stage of DevOps
+
+A best pratice for all projects is to use a linter or some form of code standard.
+
+For this project, we are using [Black](https://black.readthedocs.io/en/stable/).
+
+**Run the linter:**
+
+```text
+script/lint
+```
+
+
+
+---
+
+# Run our Test Suite π¬
+
+> π§ͺ Test Stage of DevOps
+
+The other engineer also wrote some tests for us, let's copy those over as well:
+
+```text
+cp demo/code/tests/devops_test_example.py tests/plugins/test_devops.py
+```
+
+Run the test suite using [pytest](https://docs.pytest.org/en/latest/):
+
+```text
+script/test
+```
+
+> Note: Tests like to live in CI/CD pipelines
+
+
+
+---
+
+# Fix our Test π¨
+
+> π§ͺ Test Stage of DevOps
+
+Checking our test output, we expect that a a url with a `.jpg` extension is returned. However, the actual image we are returning is a `.png`. Let's fix that:
+
+```python
+# tests/plugins/test_devops.py
+assert "demo/assets/devops.png" in testbot.pop_message()
+```
+
+π
+
+
+
+
+
+---
+
+# SAST π
+
+> π Security Stage of DevOps - Shift Left!
+
+[SAST](https://en.wikipedia.org/wiki/Static_application_security_testing) is a testing methodology for detecting security vulnerabilities in software.
+
+It generally takes place by scanning files and looking for misconfigurations, bad practices, and other security issues.
+
+> Note: SAST likes to live in CI/CD pipelines
+
+
+
+---
+
+# KUBESEC π
+
+> π Security Stage of DevOps - Shift Left!
+
+[KUBESEC](https://github.com/controlplaneio/kubesec) is another SAST tool for scanning kubernetes manifests.
+
+**Example Usage:**
+
+```text
+kubesec scan script/k8s/errbot/deployment.yaml | jq
+```
+
+**Make a check fail and then fix it again:**
+
+1. 1: Edit: `script\k8s\errbot\deployment.yaml` -> comment out: `runAsUser: 10001`
+2. 2: Run: `kubesec scan script/k8s/errbot/deployment.yaml | jq`
+3. 3: Observe the output warning that the `runAsUser` check is now failing
+4. 4: Fix it back by uncommenting the `runAsUser` field in the k8s manifest
+
+> Note: KUBESEC likes to live in CI/CD pipelines
+
+
+
+---
+
+# Trivy π
+
+> π Security Stage of DevOps - Shift Left!
+
+[Trivy](https://github.com/aquasecurity/trivy) is a container / misconfiguration / IaC vulnerability scanner.
+
+**Let's hunt for some vulnerabilities in our container image with Trivy:**
+
+```text
+docker build -t scan-errbot:latest src/errbot/
+
+trivy image scan-errbot:latest
+```
+
+**If no vulnerabilities are found, let's create one π:**
+
+Edit: `src/errbot/requirements.txt` -> add: `markdown2==2.2.2` anywhere to that file
+
+Run the build + scan again (as seen above)
+
+> Note: Trivy likes to live in CI/CD pipelines
+
+
+
+---
+
+# Pull Request π·οΈ
+
+> π·οΈ Release Stage of DevOps
+
+For the **Release** and **Deploy** steps, we will have to do a bit of *pretending* since we do not have a production environement.
+
+Our new `!devops` command is all ready to be released to production and used by the world!
+
+**So far we have done the following:**
+
+- β
Created our new chat command
+- β
Ran our linter
+- β
Ran our test suite
+- β
Built the image and tested locally with Skaffold
+- β
Ran security checks with TFSEC, Kubesec, and Trivy
+- π‘ Opened a pull request and got proper approvals / peer reviews (pretend)
+- π·οΈ Tagged our release with a new version number and updated the change log with our new feature (pretend)
+
+
+
+---
+
+# Deploy π
+
+> π Deploy Stage of DevOps
+
+We now press the **Merge** button on our pull request and this will automatically trigger our GitHub Actions pipeline to deploy our changes to production!
+
+Again, you will not have a production environement with a cloud provider as this will incur costs. In the next slides I will give an overview of our GitHub Actions pipeline and do a live demo of a deployment to Azure.
+
+
+
+---
+
+# GitHub Actions β©
+
+> π Deploy Stage of DevOps
+
+[GitHub Actions](https://github.com/features/actions) runs two types of pipelines for this project:
+
+- π‘ - **Plan:** This is the pipeline that runs before the pull request is merged to plan changes
+- π - **Deploy:** This is the pipeline that runs after the pull request is merged to `main` to deploy changes
+
+All GitHub Actions workflow definitions can be found in the `.github/workflows/` directory.
+
+
+
+
+
+---
+# Live Deploy Demo π₯
+
+> π Deploy Stage of DevOps
+
+TODO: Notes for this slide will be added after the first live demo. Public Pull Requests will be used
+
+
+
+---
+
+# Operate the Bot π€
+
+> π§° Operate Stage of DevOps
+
+Usually, this is customers, clients, or the public would interact with your service. For this demo, we will be those users.
+
+Go ahead and interact with your running bot and have some fun!
+
+**Commands to try:**
+
+- β’ `!remember is ` - Stores a value in DynamoDB
+- β’ `!insult @user` - Use at your own risk, its crowd sourced
+- β’ `!lmf ` - Get the last League of Legends match for a summoner
+- β’ `!play ` - Join a voice channel and run this command to get some tunes
+- β’ `!crypto ` - Get the current price of a crypto currency
+- β’ `!sparkle @user for ` - Show your appreciation to someone
+
+> `!help` for a full list of available commands
+
+
+
+---
+
+# Operations and Scaling
+
+> π§° Operate Stage of DevOps
+
+Another part of the DevOps journey is to scale your service to meet your needs.
+
+For this demo we won't be scaling but it is important to know how to do so and what your options are:
+
+- β’ **Autoscaling** - Scale your service based on the number of requests (e.g. `kubectl autoscale deployment errbot --cpu-percent=50`)
+- β’ **Horizontal Scaling** - Scale your service based on the number of replicas (e.g. `kubectl scale deployment errbot --replicas=3`)
+- β’ **Vertical Scaling** - Scale your service by making each container use more resources (e.g. `kubectl scale deployment errbot --cpu=2 --memory=2Gi`)
+
+> Note: Horizontal scaling doesn't work well for traditional chat bots since they will repond multiple times to the same message.
+
+
+
+---
+
+# Monitoring and Logging with Grafana π
+
+> π Monitor Stage of DevOps
+
+For this demo, we are using Promtail + Loki + Grafana to collect logs and metrics for our chatbot
+
+1. **1:** Open a new terminal window while `skaffold dev` is still running
+
+2. **2:** Get your Grafana password (username will be `admin`):
+
+ ```text
+ kubectl get secret --namespace observability grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
+ ```
+
+3. **3:** Port forward to your Grafana instance
+
+ ```text
+ kubectl port-forward --namespace observability service/grafana 3000:80
+ ```
+
+ > If your IDE does not automatically direct you, simply go to http://127.0.0.1:3000/login after port forwarding with kubectl
+
+
+
+---
+
+# Logs in Grafana
+
+
+
+---
+
+# You made it!
+
+If you have made it this far, you should have accomplished the following:
+
+- β
Self-hosted your own documenation
+- β
Added a new feature to your very own chatbot
+- β
Built, tested, and deployed your container with Skaffold in k8s
+- β
Checked your application for security vulnerabilities
+- β
Learned a bit about Kubernetes, Terraform, and tying them together with GitHub Actions through CI/CD
+- β
Monitored your application with Promtail and Grafana
+- β
Learned about a new tool or two
+- β
Gained some insight into the DevOps life-cycle
+- β
Had fun!
+
+---
+
+# Learn More π
+
+Curious to learn more about DevOps?
+
+- β’ [awesome-devops](https://github.com/wmariuss/awesome-devops) - A curated list of DevOps resources
+- β’ [90 Days of DevOps](https://github.com/MichaelCade/90DaysOfDevOps) - A journey through DevOps in 90 days
+- β’ [DevOps Exercises](https://github.com/bregman-arie/devops-exercises) - Questions and interview challenges about DevOps
+- β’ [DevOps Resources](https://github.com/bregman-arie/devops-resources) - A curated repo packed full of DevOps resources
+- β’ [8 Phases of DevOps](https://medium.com/taptuit/the-eight-phases-of-a-devops-pipeline-fda53ec9bba) - The 8 phases of DevOps defined
diff --git a/docker/app/Dockerfile b/src/Dockerfile
similarity index 96%
rename from docker/app/Dockerfile
rename to src/Dockerfile
index 2692832..93ba8ab 100644
--- a/docker/app/Dockerfile
+++ b/src/Dockerfile
@@ -10,6 +10,6 @@ FROM base AS prod
RUN npm run build
RUN chown -R nonroot:nonroot /app/node_modules/.cache
-EXPOSE 3000
+EXPOSE 8080
USER nonroot
CMD npm run start
diff --git a/workshop.md b/workshop.md
new file mode 100644
index 0000000..65ad77f
--- /dev/null
+++ b/workshop.md
@@ -0,0 +1,72 @@
+# DRAFT
+# DevOps Days LA Workshop
+DevOps for Fun and Profit
+
+ Plan > Code > Build > Test > Release > Deploy > Operate > Monitor
+## Overview
+## Plan
+
+ Sprint Planning Meeting
+ Feature Design
+ LETS (BUY) MAKE WORDLE (Again)
+ but this time for fun and not profit
+ should we do it for profit? --> Lets Decide Later. [Deploy to Production]
+
+## Code
+ Intro:
+ Coding starts with your IDE
+ Popular Frameworks and Tools
+ Front End and back end
+
+ IDE:
+ What is this?
+ What are some popular IDEs?
+ POLL
+ what will we use today?
+
+## HANDS ON #1: Setup your IDE adn get ready to code!
+
+1. Install Visual Studio Code
+- Go to https://code.visualstudio.com/
+Download and install locally
+
+
+APPENDIX: If you want to
+
+
+2.
+ VSCode
+ git
+ Skaffold
+ Docker
+ Minikube
+ Codespaces
+
+## Some Theory
+ App Patterns:
+ Three Tier
+ FE BE etc
+ Monolith & Microservices
+ Code vs Containers
+ Containerization and K8S
+
+## Build
+ Manual (?)
+ Docker Build
+ Kubernetes
+ -- Skaffold
+ -- Reload with a different version
+
+## Test
+
+## Release <> Deploy
+
+## Operate
+ heroku options
+
+## Monitor
+
+---
+
+## To-Do
+- Quiz or Polls (Forms? Kahoot?)