mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-22 15:40:07 -04:00
Merge pull request #14 from oscartbeaumont-ext/main
@sd/server in Docker and deploy to Kubernetes
This commit is contained in:
55
.github/actions/build-and-publish-server/action.yml
vendored
Normal file
55
.github/actions/build-and-publish-server/action.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Build and Publish Server
|
||||
description: Builds and publishes the docker image for the Spacedrive server
|
||||
inputs:
|
||||
gh_token:
|
||||
description: 'A Github PAT'
|
||||
required: true
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ inputs.gh_token }}
|
||||
|
||||
- name: Build Server
|
||||
shell: bash
|
||||
run: |
|
||||
cargo build --release -p server
|
||||
cp ./target/release/server ./apps/server/server
|
||||
|
||||
- name: Determine image name & tag
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$GITHUB_EVENT_NAME" == "release" ]; then
|
||||
export IMAGE_TAG=${GITHUB_REF##*/}
|
||||
else
|
||||
export IMAGE_TAG=$(git rev-parse --short "$GITHUB_SHA")
|
||||
fi
|
||||
export GITHUB_REPOSITORY_LOWER=$(echo $GITHUB_REPOSITORY | awk '{print tolower($0)}')
|
||||
export IMAGE_NAME="ghcr.io/$GITHUB_REPOSITORY_LOWER/server"
|
||||
echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV
|
||||
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
|
||||
echo "Building $IMAGE_NAME:$IMAGE_TAG"
|
||||
|
||||
- name: Build & push Docker image
|
||||
shell: bash
|
||||
run: |
|
||||
docker build ./apps/server --tag $IMAGE_NAME:$IMAGE_TAG
|
||||
docker push $IMAGE_NAME:$IMAGE_TAG
|
||||
|
||||
- name: Tag & push image as latest staging image
|
||||
if: github.event_name != 'release'
|
||||
shell: bash
|
||||
run: |
|
||||
docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:staging
|
||||
docker push $IMAGE_NAME:staging
|
||||
|
||||
- name: Tag & push image as latest production image
|
||||
if: github.event_name == 'release'
|
||||
shell: bash
|
||||
run: |
|
||||
docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:production
|
||||
docker push $IMAGE_NAME:production
|
||||
17
.github/workflows/ci.yml
vendored
17
.github/workflows/ci.yml
vendored
@@ -114,4 +114,19 @@ jobs:
|
||||
with:
|
||||
projectPath: apps/desktop
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and publish server
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
uses: ./.github/actions/build-and-publish-server
|
||||
with:
|
||||
gh_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Deploy Spacedrive Server to Kubernetes
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
env:
|
||||
K8S_KUBECONFIG: ${{ secrets.K8S_KUBECONFIG }}
|
||||
run: |
|
||||
mkdir -p ~/.kube
|
||||
echo "$K8S_KUBECONFIG" > ~/.kube/config 2>&1
|
||||
kubectl rollout restart deployment/sdserver-deployment
|
||||
|
||||
2
.github/workflows/org-readme.yml
vendored
2
.github/workflows/org-readme.yml
vendored
@@ -4,6 +4,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- README.md
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- <script src="http://localhost:8097"></script> -->
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
@@ -93,7 +93,7 @@ function App() {
|
||||
onError={(e) => {
|
||||
setShowApp(false);
|
||||
}}
|
||||
src="http://localhost:8002?library_id=9068c6ec-cf90-451b-bb30-4174781e7bc6"
|
||||
src={`${import.meta.env.VITE_SDWEB_BASE_URL || "http://localhost:8002"}?library_id=9068c6ec-cf90-451b-bb30-4174781e7bc6`}
|
||||
/>
|
||||
)}
|
||||
<Section
|
||||
|
||||
9
apps/landing/src/env.d.ts
vendored
Normal file
9
apps/landing/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_SDWEB_BASE_URL: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
@@ -19,10 +19,11 @@ RUN mkdir /data
|
||||
ENV DATA_DIR /data
|
||||
|
||||
# Drop privledges to non-root user
|
||||
RUN adduser --system --no-create-home --shell /usr/sbin/nologin $USER && \
|
||||
RUN groupadd -g 1001 $USER && \
|
||||
adduser --system --no-create-home --shell /usr/sbin/nologin --uid 1001 --gid 1001 $USER && \
|
||||
chown -R $USER /data && \
|
||||
chmod -R 770 /data
|
||||
USER $USER
|
||||
|
||||
# Run the CLI when the container is started
|
||||
ENTRYPOINT [ "/sdserver" ]
|
||||
ENTRYPOINT [ "/sdserver" ]
|
||||
42
apps/server/k8s/infrastructure.yaml
Normal file
42
apps/server/k8s/infrastructure.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
# Infrastructure setups up the Kubernetes cluster for Spacedrive!
|
||||
#
|
||||
# To get the service account token use the following:
|
||||
# ```bash
|
||||
# TOKENNAME=`kubectl -n spacedrive get sa/spacedrive-ci -o jsonpath='{.secrets[0].name}'`
|
||||
# kubectl -n spacedrive get secret $TOKENNAME -o jsonpath='{.data.token}' | base64 -d
|
||||
# ```
|
||||
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: spacedrive
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: spacedrive-ci
|
||||
namespace: spacedrive
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: spacedrive-ns-full
|
||||
namespace: spacedrive
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
verbs: ["get", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: spacedrive-ci-rb
|
||||
namespace: spacedrive
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: spacedrive-ci
|
||||
namespace: spacedrive
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: spacedrive-ns-full
|
||||
118
apps/server/k8s/sdserver.yaml
Normal file
118
apps/server/k8s/sdserver.yaml
Normal file
@@ -0,0 +1,118 @@
|
||||
# This will deploy the Spacedrive Server container to the `spacedrive`` namespace on Kubernetes.
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: sdserver-ingress
|
||||
namespace: spacedrive
|
||||
labels:
|
||||
app.kubernetes.io/name: sdserver
|
||||
app.kubernetes.io/component: webserver
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.tls.certresolver: le
|
||||
traefik.ingress.kubernetes.io/router.middlewares: kube-system-antiseo@kubernetescrd
|
||||
spec:
|
||||
rules:
|
||||
- host: spacedrive.otbeaumont.me
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: sdserver-service
|
||||
port:
|
||||
number: 8080
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: sdserver-service
|
||||
namespace: spacedrive
|
||||
labels:
|
||||
app.kubernetes.io/name: sdserver
|
||||
app.kubernetes.io/component: webserver
|
||||
spec:
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
protocol: TCP
|
||||
selector:
|
||||
app.kubernetes.io/name: sdserver
|
||||
app.kubernetes.io/component: webserver
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: sdserver-pvc
|
||||
namespace: spacedrive
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: local-path
|
||||
resources:
|
||||
requests:
|
||||
storage: 512M
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: sdserver-deployment
|
||||
namespace: spacedrive
|
||||
labels:
|
||||
app.kubernetes.io/name: sdserver
|
||||
app.kubernetes.io/component: webserver
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: sdserver
|
||||
app.kubernetes.io/component: webserver
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: sdserver
|
||||
app.kubernetes.io/component: webserver
|
||||
spec:
|
||||
restartPolicy: Always
|
||||
# refer to Dockerfile to find securityContext values
|
||||
securityContext:
|
||||
runAsUser: 101
|
||||
runAsGroup: 101
|
||||
fsGroup: 101
|
||||
containers:
|
||||
- name: sdserver
|
||||
image: ghcr.io/oscartbeaumont/spacedrive/server:staging
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
volumeMounts:
|
||||
- name: data-volume
|
||||
mountPath: /data
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
resources:
|
||||
limits:
|
||||
memory: 100Mi
|
||||
cpu: 100m
|
||||
requests:
|
||||
memory: 5Mi
|
||||
cpu: 10m
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
failureThreshold: 4
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 20
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
volumes:
|
||||
- name: data-volume
|
||||
persistentVolumeClaim:
|
||||
claimName: sdserver-pvc
|
||||
@@ -5,7 +5,10 @@ use actix::{
|
||||
Actor, AsyncContext, ContextFutureSpawner, Handler, Message, StreamHandler,
|
||||
WrapFuture,
|
||||
};
|
||||
use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer};
|
||||
use actix_web::{
|
||||
get, http::StatusCode, web, App, Error, HttpRequest, HttpResponse, HttpServer,
|
||||
Responder,
|
||||
};
|
||||
use actix_web_actors::ws;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -123,7 +126,18 @@ impl Handler<SocketMessage> for Socket {
|
||||
}
|
||||
}
|
||||
|
||||
async fn index(
|
||||
#[get("/")]
|
||||
async fn index() -> impl Responder {
|
||||
format!("Spacedrive Server!")
|
||||
}
|
||||
|
||||
#[get("/health")]
|
||||
async fn healthcheck() -> impl Responder {
|
||||
format!("OK")
|
||||
}
|
||||
|
||||
#[get("/ws")]
|
||||
async fn ws_handler(
|
||||
req: HttpRequest,
|
||||
stream: web::Payload,
|
||||
event_receiver: web::Data<mpsc::Receiver<CoreEvent>>,
|
||||
@@ -141,6 +155,10 @@ async fn index(
|
||||
resp
|
||||
}
|
||||
|
||||
async fn not_found() -> impl Responder {
|
||||
HttpResponse::build(StatusCode::OK).body("We're past the event horizon...")
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let (event_receiver, controller) = setup().await;
|
||||
@@ -150,7 +168,10 @@ async fn main() -> std::io::Result<()> {
|
||||
App::new()
|
||||
.app_data(event_receiver.clone())
|
||||
.app_data(controller.clone())
|
||||
.route("/ws", web::get().to(index))
|
||||
.service(index)
|
||||
.service(healthcheck)
|
||||
.service(ws_handler)
|
||||
.default_service(web::route().to(not_found))
|
||||
})
|
||||
.bind(("0.0.0.0", 8080))?
|
||||
.run()
|
||||
|
||||
@@ -4,7 +4,9 @@ import SpacedriveInterface from '@sd/interface';
|
||||
import { ClientCommand, ClientQuery, CoreEvent } from '@sd/core';
|
||||
import { BaseTransport } from '@sd/client';
|
||||
|
||||
const websocket = new WebSocket('ws://localhost:8080/ws');
|
||||
const websocket = new WebSocket(
|
||||
import.meta.env.VITE_SDSERVER_BASE_URL || 'wss://spacedrive.otbeaumont.me/ws'
|
||||
);
|
||||
|
||||
const randomId = () => Math.random().toString(36).slice(2);
|
||||
|
||||
|
||||
9
apps/web/src/env.d.ts
vendored
Normal file
9
apps/web/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_SDSERVER_BASE_URL: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
Reference in New Issue
Block a user