From 5c07deffee5c0be8ba07e4b437ac94415a10c2a4 Mon Sep 17 00:00:00 2001 From: Dario A Lencina-Talarico Date: Mon, 19 Jun 2023 13:53:49 -0400 Subject: [PATCH] Helm chart (#71) * DATABASE_URL instead of PG_URL * helm charts for deploying rustlemania * Deployed to kubernetes cluster successfully * cargo fmt * Deployed ssl * Update audio encoder * Adding some docs for helm deploy * use wss * do not install clippy * whatever * add default * override * remove default config * trying with lower quality audio to try reduce latency --------- Co-authored-by: Griffin Obeid --- .dockerignore | 2 + .github/workflows/cargo-lint.yaml | 6 +- .github/workflows/cargo-test.yaml | 7 +- Dockerfile.actix | 24 ++++ Dockerfile.yew | 25 ++++ actix-api/src/db/mod.rs | 2 +- actix-api/src/main.rs | 56 +++++--- actix-api/src/models/mod.rs | 9 ++ actix-api/startup.sh | 3 + dbmate/startup.sh | 2 +- docker/StateFile.yml | 2 - docker/docker-compose.yaml | 6 +- helm/.gitignore | 3 + helm/README.md | 10 ++ helm/cert-manager/Chart.lock | 6 + helm/cert-manager/Chart.yaml | 8 ++ helm/cert-manager/values.yaml | 2 + helm/ingress-nginx/Chart.lock | 6 + helm/ingress-nginx/Chart.yaml | 8 ++ helm/nats/Chart.lock | 6 + helm/nats/Chart.yaml | 8 ++ helm/nats/values.yaml | 8 ++ helm/postgres/Chart.lock | 6 + helm/postgres/Chart.yaml | 8 ++ helm/postgres/values.yaml | 3 + helm/rustlemania/.helmignore | 23 ++++ helm/rustlemania/Chart.yaml | 5 + helm/rustlemania/templates/NOTES.txt | 22 ++++ helm/rustlemania/templates/_helpers.tpl | 62 +++++++++ .../rustlemania/templates/api-deployment.yaml | 62 +++++++++ helm/rustlemania/templates/api-hpa.yaml | 32 +++++ helm/rustlemania/templates/api-service.yaml | 15 +++ helm/rustlemania/templates/ingress.yaml | 60 +++++++++ helm/rustlemania/templates/ui-deployment.yaml | 62 +++++++++ helm/rustlemania/templates/ui-hpa.yaml | 32 +++++ helm/rustlemania/templates/ui-service.yaml | 15 +++ helm/rustlemania/values.yaml | 124 ++++++++++++++++++ nginx.conf | 44 +++++++ yew-ui/.cargo/config.toml | 1 + yew-ui/Cargo.lock | 7 + yew-ui/Cargo.toml | 1 + yew-ui/src/constants.rs | 4 +- 42 files changed, 760 insertions(+), 37 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile.actix create mode 100644 Dockerfile.yew create mode 100755 actix-api/startup.sh create mode 100644 helm/.gitignore create mode 100644 helm/README.md create mode 100644 helm/cert-manager/Chart.lock create mode 100644 helm/cert-manager/Chart.yaml create mode 100644 helm/cert-manager/values.yaml create mode 100644 helm/ingress-nginx/Chart.lock create mode 100644 helm/ingress-nginx/Chart.yaml create mode 100644 helm/nats/Chart.lock create mode 100644 helm/nats/Chart.yaml create mode 100644 helm/nats/values.yaml create mode 100644 helm/postgres/Chart.lock create mode 100644 helm/postgres/Chart.yaml create mode 100644 helm/postgres/values.yaml create mode 100644 helm/rustlemania/.helmignore create mode 100644 helm/rustlemania/Chart.yaml create mode 100644 helm/rustlemania/templates/NOTES.txt create mode 100644 helm/rustlemania/templates/_helpers.tpl create mode 100644 helm/rustlemania/templates/api-deployment.yaml create mode 100644 helm/rustlemania/templates/api-hpa.yaml create mode 100644 helm/rustlemania/templates/api-service.yaml create mode 100644 helm/rustlemania/templates/ingress.yaml create mode 100644 helm/rustlemania/templates/ui-deployment.yaml create mode 100644 helm/rustlemania/templates/ui-hpa.yaml create mode 100644 helm/rustlemania/templates/ui-service.yaml create mode 100644 helm/rustlemania/values.yaml create mode 100644 nginx.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..6daad88d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +**/target +**/dist diff --git a/.github/workflows/cargo-lint.yaml b/.github/workflows/cargo-lint.yaml index a5bab309..f6127e2b 100644 --- a/.github/workflows/cargo-lint.yaml +++ b/.github/workflows/cargo-lint.yaml @@ -12,7 +12,7 @@ env: OAUTH_CLIENT_SECRET: blah OAUTH_REDIRECT_URL: http://localhost:8080/login/callback RUST_LOG: info - PG_URL: postgres://postgres:docker@postgres:5432/actix-api-db?sslmode=disable + DATABASE_URL: postgres://postgres:docker@postgres:5432/actix-api-db?sslmode=disable jobs: fmt: @@ -26,7 +26,7 @@ jobs: toolchain: stable override: true components: rustfmt - - uses: actions-rs/cargo@v1 + - uses: actions-rs/cargo@v1 with: command: fmt - args: --manifest-path ./actix-api/Cargo.toml --all -- --check + args: --manifest-path ./actix-api/Cargo.toml --all -- --check diff --git a/.github/workflows/cargo-test.yaml b/.github/workflows/cargo-test.yaml index 78a63dd9..09d33de9 100644 --- a/.github/workflows/cargo-test.yaml +++ b/.github/workflows/cargo-test.yaml @@ -6,15 +6,14 @@ name: cargo test env: ACTIX_PORT: 8080 - TRUNK_SERVE_PORT: 80 - TRUNK_SERVE_HOST: localhost + UI_ENDPOINT: localhost:80 OAUTH_CLIENT_ID: blah OAUTH_AUTH_URL: blah OAUTH_TOKEN_URL: blah OAUTH_CLIENT_SECRET: blah OAUTH_REDIRECT_URL: http://localhost:8080/login/callback RUST_LOG: info - PG_URL: postgres://postgres:docker@postgres:5432/actix-api-db?sslmode=disable + DATABASE_URL: postgres://postgres:docker@postgres:5432/actix-api-db?sslmode=disable jobs: @@ -39,4 +38,4 @@ jobs: continue-on-error: false # WARNING: only for this example, remove it! with: command: test - args: --manifest-path ./actix-api/Cargo.toml \ No newline at end of file + args: --manifest-path ./actix-api/Cargo.toml diff --git a/Dockerfile.actix b/Dockerfile.actix new file mode 100644 index 00000000..1ce426c4 --- /dev/null +++ b/Dockerfile.actix @@ -0,0 +1,24 @@ +FROM rust:1.70-slim-bullseye as development + +RUN apt-get --yes update && apt-get --yes install curl git pkg-config libssl-dev +RUN curl https://github.com/amacneil/dbmate/releases/download/v2.4.0/dbmate-linux-amd64 -L -o /usr/bin/dbmate && chmod +x /usr/bin/dbmate +RUN cargo install cargo-watch +RUN rustup component add clippy-preview +RUN rustup component add rustfmt + +FROM development as build + +COPY . /app +WORKDIR /app/actix-api +RUN cargo build --release + +FROM debian:bullseye-slim as production + +COPY --from=build /usr/bin/dbmate /usr/bin/dbmate +COPY --from=build /app/actix-api/target/release/actix-api /usr/bin/actix-api +COPY --from=build /app/actix-api/startup.sh /usr/bin/startup.sh +COPY --from=build /app/dbmate /app/dbmate + +STOPSIGNAL SIGINT + +CMD [ "startup.sh" ] diff --git a/Dockerfile.yew b/Dockerfile.yew new file mode 100644 index 00000000..452d88d3 --- /dev/null +++ b/Dockerfile.yew @@ -0,0 +1,25 @@ +FROM --platform=linux/amd64 rust:1.65-slim-bullseye as development + +RUN rustup default nightly-2022-10-21 +RUN apt-get --yes update && apt-get --yes install git pkg-config libssl-dev +RUN cargo install wasm-bindgen-cli --version 0.2.78 +RUN cargo install trunk --version 0.16.0 +RUN rustup target add wasm32-unknown-unknown + +FROM --platform=linux/amd64 development as build + +# TODO - this is a hack to get around the fact that the yew-ui crate is not bundled with the backend +ENV ENABLE_OAUTH=false +ENV LOGIN_URL="" +ENV ACTIX_UI_BACKEND_URL="wss://api.rustlemania.com" +WORKDIR /app +COPY . . +WORKDIR /app/yew-ui + +RUN trunk build --release + +FROM --platform=linux/amd64 nginx:1.21.5-alpine as production + +COPY nginx.conf /etc/nginx/nginx.conf + +COPY --from=build /app/yew-ui/dist /usr/share/nginx/html diff --git a/actix-api/src/db/mod.rs b/actix-api/src/db/mod.rs index 2bb2daec..b1d4b5f8 100644 --- a/actix-api/src/db/mod.rs +++ b/actix-api/src/db/mod.rs @@ -8,7 +8,7 @@ pub type PostgresPool = Pool>; pub type PostgresConnection = PooledConnection>; pub fn get_database_url() -> String { - env::var("PG_URL").unwrap() + env::var("DATABASE_URL").unwrap() } pub fn get_pool() -> PostgresPool { diff --git a/actix-api/src/main.rs b/actix-api/src/main.rs index a2e6bdf8..f8745eaa 100644 --- a/actix-api/src/main.rs +++ b/actix-api/src/main.rs @@ -20,6 +20,7 @@ use actix_web::{ }; use actix_web_actors::ws::{handshake, WebsocketContext}; use log::{debug, info}; +use models::AppConfig; use crate::{ actors::{chat_server::ChatServer, chat_session::WsChatSession}, @@ -32,18 +33,10 @@ use crate::{ }; use reqwest::header::LOCATION; -const OAUTH_CLIENT_ID: &str = std::env!("OAUTH_CLIENT_ID"); -const OAUTH_AUTH_URL: &str = std::env!("OAUTH_AUTH_URL"); -const OAUTH_TOKEN_URL: &str = std::env!("OAUTH_TOKEN_URL"); -const OAUTH_SECRET: &str = std::env!("OAUTH_CLIENT_SECRET"); -const OAUTH_REDIRECT_URL: &str = std::env!("OAUTH_REDIRECT_URL"); -const SCOPE: &str = "email%20profile%20openid"; -const ACTIX_PORT: &str = std::env!("ACTIX_PORT"); -const AFTER_LOGIN_URL: &str = concat!("http://localhost:", std::env!("TRUNK_SERVE_PORT")); - pub mod auth; pub mod db; +const SCOPE: &str = "email%20profile%20openid"; /** * Function used by the Web Application to initiate OAuth. * @@ -52,7 +45,10 @@ pub mod db; * The server implements PKCE (Proof Key for Code Exchange) to protect itself and the users. */ #[get("/login")] -async fn login(pool: web::Data) -> Result { +async fn login( + pool: web::Data, + cfg: web::Data, +) -> Result { // TODO: verify if user exists in the db by looking at the session cookie, (if the client provides one.) let pool2 = pool.clone(); @@ -68,9 +64,9 @@ async fn login(pool: web::Data) -> Result { // 3. Craft OAuth Login URL let oauth_login_url = format!("{oauth_url}?client_id={client_id}&redirect_uri={redirect_url}&response_type=code&scope={scope}&prompt=select_account&pkce_challenge={pkce_challenge}&state={state}&access_type=offline", - oauth_url=OAUTH_AUTH_URL, - redirect_url=OAUTH_REDIRECT_URL, - client_id=OAUTH_CLIENT_ID, + oauth_url=cfg.oauth_auth_url, + redirect_url=cfg.oauth_redirect_url, + client_id=cfg.oauth_client_id, scope=SCOPE, pkce_challenge=pkce_challenge.as_str(), state=&csrf_token.secret() @@ -94,6 +90,7 @@ async fn login(pool: web::Data) -> Result { async fn handle_google_oauth_callback( pool: web::Data, info: web::Query, + cfg: web::Data, ) -> Result { let state = info.state.clone(); @@ -109,11 +106,11 @@ async fn handle_google_oauth_callback( // 2. Request token from OAuth provider. let (oauth_response, claims) = request_token( - OAUTH_REDIRECT_URL, - OAUTH_CLIENT_ID, - OAUTH_SECRET, + &cfg.oauth_auth_url, + &cfg.oauth_client_id, + &cfg.oauth_secret, &oauth_request.pkce_verifier, - OAUTH_TOKEN_URL, + &cfg.oauth_token_url, &info.code, ) .await @@ -142,7 +139,7 @@ async fn handle_google_oauth_callback( // 5. Send cookie and redirect browser to AFTER_LOGIN_URL let mut response = HttpResponse::Found(); - response.append_header((LOCATION, AFTER_LOGIN_URL)); + response.append_header((LOCATION, cfg.after_login_url.clone())); response.cookie(cookie); Ok(response.finish()) } @@ -181,6 +178,13 @@ async fn main() -> std::io::Result<()> { env_logger::init(); info!("start"); let chat = ChatServer::new().start(); + let oauth_client_id: String = std::env::var("OAUTH_CLIENT_ID").unwrap_or(String::from("")); + let oauth_auth_url: String = std::env::var("OAUTH_AUTH_URL").unwrap_or(String::from("")); + let oauth_token_url: String = std::env::var("OAUTH_TOKEN_URL").unwrap_or(String::from("")); + let oauth_secret: String = std::env::var("OAUTH_CLIENT_SECRET").unwrap_or(String::from("")); + let oauth_redirect_url: String = + std::env::var("OAUTH_REDIRECT_URL").unwrap_or(String::from("")); + let after_login_url: String = std::env::var("UI_ENDPOINT").unwrap_or(String::from("")); HttpServer::new(move || { let cors = Cors::permissive(); @@ -190,12 +194,26 @@ async fn main() -> std::io::Result<()> { App::new() .app_data(web::Data::new(pool)) .app_data(web::Data::new(AppState { chat: chat.clone() })) + .app_data(web::Data::new(AppConfig { + oauth_client_id: oauth_client_id.clone(), + oauth_auth_url: oauth_auth_url.clone(), + oauth_token_url: oauth_token_url.clone(), + oauth_secret: oauth_secret.clone(), + oauth_redirect_url: oauth_redirect_url.clone(), + after_login_url: after_login_url.clone(), + })) .wrap(cors) .service(handle_google_oauth_callback) .service(login) .service(ws_connect) }) - .bind(("0.0.0.0", ACTIX_PORT.parse::().unwrap()))? + .bind(( + "0.0.0.0", + std::env::var("ACTIX_PORT") + .unwrap_or(String::from("8080")) + .parse::() + .unwrap(), + ))? .run() .await } diff --git a/actix-api/src/models/mod.rs b/actix-api/src/models/mod.rs index d8c68853..20a59350 100644 --- a/actix-api/src/models/mod.rs +++ b/actix-api/src/models/mod.rs @@ -5,3 +5,12 @@ use crate::actors::chat_server::ChatServer; pub struct AppState { pub chat: Addr, } + +pub struct AppConfig { + pub oauth_client_id: String, + pub oauth_secret: String, + pub oauth_redirect_url: String, + pub oauth_auth_url: String, + pub oauth_token_url: String, + pub after_login_url: String, +} diff --git a/actix-api/startup.sh b/actix-api/startup.sh new file mode 100755 index 00000000..57c6c7ce --- /dev/null +++ b/actix-api/startup.sh @@ -0,0 +1,3 @@ +#!/bin/bash -e +/app/dbmate/startup.sh +actix-api diff --git a/dbmate/startup.sh b/dbmate/startup.sh index 96849725..6fccdf1a 100755 --- a/dbmate/startup.sh +++ b/dbmate/startup.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e pushd /app/dbmate dbmate wait dbmate up diff --git a/docker/StateFile.yml b/docker/StateFile.yml index a771de35..3836d417 100644 --- a/docker/StateFile.yml +++ b/docker/StateFile.yml @@ -35,7 +35,6 @@ Cargoes: - ${{ Envs.HOME }}/.cargo/registry:/usr/local/cargo/registry Env: - ACTIX_HOST=api.zoom.rs - - ACTIX_PORT=8080 - ACTIX_UI_BACKEND_URL=ws://api.zoom.rs - TRUNK_SERVE_PORT=8081 - ENABLE_OAUTH=false @@ -72,7 +71,6 @@ Cargoes: - OAUTH_CLIENT_SECRET=${{Envs.OAUTH_CLIENT_SECRET}} - OAUTH_REDIRECT_URL=http://api.zoom.rs/login/callback - RUST_LOG=debug - - PG_URL=postgres://postgres:docker@db.zoom.rs:5432/actix-api-db?sslmode=disable - NATS_URL=nats.zoom.rs - DATABASE_URL=postgres://postgres:docker@db.zoom.rs:5432/actix-api-db?sslmode=disable diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 6752a408..28398add 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -12,8 +12,6 @@ services: working_dir: /app/yew-ui command: bash -c "trunk serve --address 0.0.0.0 --port ${TRUNK_SERVE_PORT:-80}" environment: - - ACTIX_HOST=${ACTIX_HOST:-localhost} - - ACTIX_PORT=${ACTIX_PORT:-8080} - ACTIX_UI_BACKEND_URL=${ACTIX_UI_BACKEND_URL:-ws://localhost:8080} - TRUNK_SERVE_PORT=${TRUNK_SERVE_PORT:-80} - ENABLE_OAUTH=false @@ -30,8 +28,7 @@ services: command: bash -c "/app/dbmate/startup.sh && cargo watch -x \"run -r\"" environment: - ACTIX_PORT=${ACTIX_PORT:-8080} - - TRUNK_SERVE_PORT=${TRUNK_SERVE_PORT:-80} - - TRUNK_SERVE_HOST=localhost + - UI_ENDPOINT=${UI_ENDPOINT:-http://localhost:80} - OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID} - OAUTH_AUTH_URL=${OAUTH_AUTH_URL} - OAUTH_TOKEN_URL=${OAUTH_TOKEN_URL} @@ -39,7 +36,6 @@ services: - OAUTH_REDIRECT_URL=http://localhost:${ACTIX_PORT:-8080}/login/callback - RUST_LOG=info - DATABASE_URL=postgres://postgres:docker@postgres:5432/actix-api-db?sslmode=disable - - PG_URL=postgres://postgres:docker@postgres:5432/actix-api-db?sslmode=disable - NATS_URL=nats:4222 ports: - "${ACTIX_PORT:-8080}:${ACTIX_PORT:-8080}" diff --git a/helm/.gitignore b/helm/.gitignore new file mode 100644 index 00000000..33c22f01 --- /dev/null +++ b/helm/.gitignore @@ -0,0 +1,3 @@ +charts +issuer.yaml +secret.yaml diff --git a/helm/README.md b/helm/README.md new file mode 100644 index 00000000..f5b270a8 --- /dev/null +++ b/helm/README.md @@ -0,0 +1,10 @@ +# Deploying with helm to kubernetes + +1. Create a cluster +1. Deploy ingress-nginx +1. Setup DNS records with the ingress-nginx external IP +1. Deploy internal nats and postgres +1. Deploy rustlemania without SSL +1. Deploy cert-manager +1. Create a cert-manager issuer +1. Upgrade rustlemania to include SSL diff --git a/helm/cert-manager/Chart.lock b/helm/cert-manager/Chart.lock new file mode 100644 index 00000000..36043a0b --- /dev/null +++ b/helm/cert-manager/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: cert-manager + repository: https://charts.jetstack.io + version: v1.12.2 +digest: sha256:e6bf98d4441b0fd59de1af1d0699137396520bfe1f50f36d092a676ce6395c26 +generated: "2023-06-16T16:31:58.732255816-04:00" diff --git a/helm/cert-manager/Chart.yaml b/helm/cert-manager/Chart.yaml new file mode 100644 index 00000000..5e8a71fe --- /dev/null +++ b/helm/cert-manager/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: rustlemania-cert-manager +version: 1.0.0 + +dependencies: + - name: cert-manager + version: 1.12.2 + repository: https://charts.jetstack.io diff --git a/helm/cert-manager/values.yaml b/helm/cert-manager/values.yaml new file mode 100644 index 00000000..0b21fc93 --- /dev/null +++ b/helm/cert-manager/values.yaml @@ -0,0 +1,2 @@ +cert-manager: + installCRDs: true diff --git a/helm/ingress-nginx/Chart.lock b/helm/ingress-nginx/Chart.lock new file mode 100644 index 00000000..80b27fcd --- /dev/null +++ b/helm/ingress-nginx/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: ingress-nginx + repository: https://kubernetes.github.io/ingress-nginx + version: 4.7.0 +digest: sha256:6dfa18d4071adbb07799d25c0612b2406666d72df9b7006c498ad0c6737f20e1 +generated: "2023-06-16T16:28:45.72699788-04:00" diff --git a/helm/ingress-nginx/Chart.yaml b/helm/ingress-nginx/Chart.yaml new file mode 100644 index 00000000..9f74a501 --- /dev/null +++ b/helm/ingress-nginx/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: rustlemania-ingress-nginx +version: 1.0.0 + +dependencies: + - name: ingress-nginx + version: 4.7.0 + repository: https://kubernetes.github.io/ingress-nginx diff --git a/helm/nats/Chart.lock b/helm/nats/Chart.lock new file mode 100644 index 00000000..fdb9c755 --- /dev/null +++ b/helm/nats/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: nats + repository: https://nats-io.github.io/k8s/helm/charts/ + version: 0.19.15 +digest: sha256:068e4c9258b50decadb4bc245b16a7036613591d64f1138fa7715c2af09aec1b +generated: "2023-06-16T15:39:38.310776778-04:00" diff --git a/helm/nats/Chart.yaml b/helm/nats/Chart.yaml new file mode 100644 index 00000000..f0899aca --- /dev/null +++ b/helm/nats/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: rustlemania-nats +version: 0.1.0 + +dependencies: + - name: nats + version: 0.19.15 + repository: https://nats-io.github.io/k8s/helm/charts/ diff --git a/helm/nats/values.yaml b/helm/nats/values.yaml new file mode 100644 index 00000000..57f7dc83 --- /dev/null +++ b/helm/nats/values.yaml @@ -0,0 +1,8 @@ +nats: + nats: + natsbox: + enabled: true + cluster: + enabled: true + replicas: 5 + noAdvertise: true diff --git a/helm/postgres/Chart.lock b/helm/postgres/Chart.lock new file mode 100644 index 00000000..185a42ce --- /dev/null +++ b/helm/postgres/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: postgresql + repository: https://charts.bitnami.com/bitnami + version: 12.5.7 +digest: sha256:b81e4b8537abfe5f8a5fbbd93ee3eb9b7396eb145d52bf717e1c81f286260993 +generated: "2023-06-16T15:46:17.792158524-04:00" diff --git a/helm/postgres/Chart.yaml b/helm/postgres/Chart.yaml new file mode 100644 index 00000000..d2d50866 --- /dev/null +++ b/helm/postgres/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: rustlemania-postgres +version: 1.0.0 + +dependencies: + - name: postgresql + version: 12.5.7 + repository: https://charts.bitnami.com/bitnami diff --git a/helm/postgres/values.yaml b/helm/postgres/values.yaml new file mode 100644 index 00000000..92c1bd21 --- /dev/null +++ b/helm/postgres/values.yaml @@ -0,0 +1,3 @@ +postgresql: + auth: + existingSecret: rustlemania diff --git a/helm/rustlemania/.helmignore b/helm/rustlemania/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/helm/rustlemania/.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/rustlemania/Chart.yaml b/helm/rustlemania/Chart.yaml new file mode 100644 index 00000000..dee62980 --- /dev/null +++ b/helm/rustlemania/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: rustlemania +description: A Helm chart for rustlemania +type: application +version: 0.1.0 diff --git a/helm/rustlemania/templates/NOTES.txt b/helm/rustlemania/templates/NOTES.txt new file mode 100644 index 00000000..de7bbc6b --- /dev/null +++ b/helm/rustlemania/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.api.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rustlemania.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.api.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "rustlemania.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rustlemania.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.api.service.port }} +{{- else if contains "ClusterIP" .Values.api.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rustlemania.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/helm/rustlemania/templates/_helpers.tpl b/helm/rustlemania/templates/_helpers.tpl new file mode 100644 index 00000000..ed4b53c1 --- /dev/null +++ b/helm/rustlemania/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "rustlemania.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "rustlemania.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "rustlemania.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "rustlemania.labels" -}} +helm.sh/chart: {{ include "rustlemania.chart" . }} +{{ include "rustlemania.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "rustlemania.selectorLabels" -}} +app.kubernetes.io/name: {{ include "rustlemania.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "rustlemania.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "rustlemania.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm/rustlemania/templates/api-deployment.yaml b/helm/rustlemania/templates/api-deployment.yaml new file mode 100644 index 00000000..2eb0b6fc --- /dev/null +++ b/helm/rustlemania/templates/api-deployment.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "rustlemania.fullname" . }}-api + labels: + {{- include "rustlemania.labels" . | nindent 4 }} +spec: + {{- if not .Values.api.autoscaling.enabled }} + replicas: {{ .Values.api.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "rustlemania.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.api.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "rustlemania.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.api.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.api.podSecurityContext | nindent 8 }} + containers: + - name: {{ include "rustlemania.fullname" . }}-api + securityContext: + {{- toYaml .Values.api.securityContext | nindent 12 }} + image: "{{ .Values.api.image.repository }}:{{ .Values.api.image.tag }}" + imagePullPolicy: {{ .Values.api.image.pullPolicy }} + env: + {{- toYaml .Values.api.env | nindent 12 }} + ports: + - name: api + containerPort: {{ .Values.api.service.port }} + protocol: TCP + #livenessProbe: + #httpGet: + #path: / + #port: http + #readinessProbe: + #httpGet: + #path: / + #port: http + resources: + {{- toYaml .Values.api.resources | nindent 12 }} + {{- with .Values.api.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.api.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.api.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm/rustlemania/templates/api-hpa.yaml b/helm/rustlemania/templates/api-hpa.yaml new file mode 100644 index 00000000..00a1d538 --- /dev/null +++ b/helm/rustlemania/templates/api-hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.api.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "rustlemania.fullname" . }}-api + labels: + {{- include "rustlemania.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "rustlemania.fullname" . }} + minReplicas: {{ .Values.api.autoscaling.minReplicas }} + maxReplicas: {{ .Values.api.autoscaling.maxReplicas }} + metrics: + {{- if .Values.api.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.api.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.api.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.api.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm/rustlemania/templates/api-service.yaml b/helm/rustlemania/templates/api-service.yaml new file mode 100644 index 00000000..f74cb762 --- /dev/null +++ b/helm/rustlemania/templates/api-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "rustlemania.fullname" . }}-api + labels: + {{- include "rustlemania.labels" . | nindent 4 }} +spec: + type: {{ .Values.api.service.type }} + ports: + - port: {{ .Values.api.service.port }} + targetPort: 8080 + protocol: TCP + name: api + selector: + {{- include "rustlemania.selectorLabels" . | nindent 4 }} diff --git a/helm/rustlemania/templates/ingress.yaml b/helm/rustlemania/templates/ingress.yaml new file mode 100644 index 00000000..dbe4a1e6 --- /dev/null +++ b/helm/rustlemania/templates/ingress.yaml @@ -0,0 +1,60 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "rustlemania.fullname" . -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "rustlemania.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ .service.name }} + port: + number: {{ .service.port.number }} + {{- else }} + serviceName: {{ .service.name }} + servicePort: {{ .service.port.number }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/rustlemania/templates/ui-deployment.yaml b/helm/rustlemania/templates/ui-deployment.yaml new file mode 100644 index 00000000..9fddb425 --- /dev/null +++ b/helm/rustlemania/templates/ui-deployment.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "rustlemania.fullname" . }}-ui + labels: + {{- include "rustlemania.labels" . | nindent 4 }} +spec: + {{- if not .Values.ui.autoscaling.enabled }} + replicas: {{ .Values.ui.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "rustlemania.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.ui.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "rustlemania.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.ui.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.ui.podSecurityContext | nindent 8 }} + containers: + - name: {{ include "rustlemania.fullname" . }}-ui + securityContext: + {{- toYaml .Values.ui.securityContext | nindent 12 }} + image: "{{ .Values.ui.image.repository }}:{{ .Values.ui.image.tag }}" + imagePullPolicy: {{ .Values.ui.image.pullPolicy }} + env: + {{- toYaml .Values.ui.env | nindent 12 }} + ports: + - name: http + containerPort: {{ .Values.ui.service.port }} + protocol: TCP + #livenessProbe: + #httpGet: + #path: / + #port: http + #readinessProbe: + #httpGet: + #path: / + #port: http + resources: + {{- toYaml .Values.ui.resources | nindent 12 }} + {{- with .Values.ui.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.ui.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.ui.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm/rustlemania/templates/ui-hpa.yaml b/helm/rustlemania/templates/ui-hpa.yaml new file mode 100644 index 00000000..de29a5fe --- /dev/null +++ b/helm/rustlemania/templates/ui-hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.ui.autoscaling.enabled }} +uiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "rustlemania.fullname" . }}-ui + labels: + {{- include "rustlemania.labels" . | nindent 4 }} +spec: + scaleTargetRef: + uiVersion: apps/v1 + kind: Deployment + name: {{ include "rustlemania.fullname" . }} + minReplicas: {{ .Values.ui.autoscaling.minReplicas }} + maxReplicas: {{ .Values.ui.autoscaling.maxReplicas }} + metrics: + {{- if .Values.ui.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.ui.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.ui.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.ui.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm/rustlemania/templates/ui-service.yaml b/helm/rustlemania/templates/ui-service.yaml new file mode 100644 index 00000000..f117174b --- /dev/null +++ b/helm/rustlemania/templates/ui-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "rustlemania.fullname" . }}-ui + labels: + {{- include "rustlemania.labels" . | nindent 4 }} +spec: + type: {{ .Values.ui.service.type }} + ports: + - port: {{ .Values.ui.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "rustlemania.selectorLabels" . | nindent 4 }} diff --git a/helm/rustlemania/values.yaml b/helm/rustlemania/values.yaml new file mode 100644 index 00000000..53193bd4 --- /dev/null +++ b/helm/rustlemania/values.yaml @@ -0,0 +1,124 @@ +# Default values for rustlemania. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +nameOverride: "" +fullnameOverride: "" + +api: + replicaCount: 3 + image: + repository: securityunion/rustlemania-api + pullPolicy: Always + tag: latest + env: + - name: RUST_LOG + value: info + - name: ACTIX_PORT + value: "8080" + - name: UI_ENDPOINT + value: https://rustlemania.com + - name: NATS_URL + value: nats:4222 + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: rustlemania + key: database_url + - name: OAUTH_CLIENT_ID + valueFrom: + secretKeyRef: + name: rustlemania + key: oauth_client_id + - name: OAUTH_AUTH_URL + valueFrom: + secretKeyRef: + name: rustlemania + key: oauth_auth_url + - name: OAUTH_TOKEN_URL + valueFrom: + secretKeyRef: + name: rustlemania + key: oauth_token_url + - name: OAUTH_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: rustlemania + key: oauth_client_secret + - name: OAUTH_REDIRECT_URL + valueFrom: + secretKeyRef: + name: rustlemania + key: oauth_redirect_url + resources: {} + podAnnotations: {} + podSecurityContext: {} + securityContext: {} + service: + type: ClusterIP + port: 8080 + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + nodeSelector: {} + tolerations: [] + affinity: {} + +ui: + replicaCount: 1 + image: + repository: securityunion/rustlemania-ui + pullPolicy: Always + tag: latest + env: + - name: LOGIN_URL + value: https://api.rustlemania.com/login + resources: {} + podAnnotations: {} + podSecurityContext: {} + securityContext: {} + service: + type: ClusterIP + port: 80 + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + nodeSelector: {} + tolerations: [] + affinity: {} + + +ingress: + enabled: true + className: nginx + annotations: + cert-manager.io/issuer: letsencrypt-prod + # kubernetes.io/tls-acme: "true" + hosts: + - host: api.rustlemania.com + paths: + - path: / + pathType: Prefix + service: + name: rustlemania-api + port: + number: 8080 + - host: rustlemania.com + paths: + - path: / + pathType: Prefix + service: + name: rustlemania-ui + port: + number: 80 + tls: + - secretName: rustlemania-tls + hosts: + - rustlemania.com + - api.rustlemania.com + diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 00000000..40e03893 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,44 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/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; + + server { + listen 80; + listen [::]:80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + } +} diff --git a/yew-ui/.cargo/config.toml b/yew-ui/.cargo/config.toml index 9cff7707..2d245cca 100644 --- a/yew-ui/.cargo/config.toml +++ b/yew-ui/.cargo/config.toml @@ -1,4 +1,5 @@ [build] +target = "wasm32-unknown-unknown" rustflags = [ "--cfg=web_sys_unstable_apis" ] [net] diff --git a/yew-ui/Cargo.lock b/yew-ui/Cargo.lock index 0cbd1e1b..744ddd54 100644 --- a/yew-ui/Cargo.lock +++ b/yew-ui/Cargo.lock @@ -800,6 +800,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "urlencoding" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" + [[package]] name = "version_check" version = "0.9.4" @@ -1036,6 +1042,7 @@ dependencies = [ "serde", "serde_derive", "types", + "urlencoding", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", diff --git a/yew-ui/Cargo.toml b/yew-ui/Cargo.toml index 6bf52ae5..12f73df3 100644 --- a/yew-ui/Cargo.toml +++ b/yew-ui/Cargo.toml @@ -24,6 +24,7 @@ serde_derive = "1" protobuf = "3.2.0" gloo = "0.8.0" gloo-timers = "0.2.6" +urlencoding = "2.1.2" [dependencies.web-sys] version = "0.3.60" diff --git a/yew-ui/src/constants.rs b/yew-ui/src/constants.rs index 80653f7e..40b8404b 100644 --- a/yew-ui/src/constants.rs +++ b/yew-ui/src/constants.rs @@ -1,6 +1,6 @@ // This is read at compile time, please restart if you change this value. pub const LOGIN_URL: &str = std::env!("LOGIN_URL"); -pub static AUDIO_CODEC: &str = "mp4a.40.05"; // https://www.w3.org/TR/webcodecs-codec-registry/#audio-codec-registry +pub static AUDIO_CODEC: &str = "opus"; // https://www.w3.org/TR/webcodecs-codec-registry/#audio-codec-registry pub static VIDEO_CODEC: &str = "vp8"; // profile 0,level 1.0, bit depth 8, // Commented out because it is not as fast as vp9. @@ -14,7 +14,7 @@ pub static VIDEO_CODEC: &str = "vp8"; // profile 0,level 1.0, bit depth 8, pub const AUDIO_CHANNELS: u32 = 1u32; pub const AUDIO_SAMPLE_RATE: u32 = 48000u32; -pub const AUDIO_BITRATE: f64 = 96000f64; +pub const AUDIO_BITRATE: f64 = 50000f64; // vga resolution // pub const VIDEO_HEIGHT: i32 = 480i32;