Compare commits
50
Commits
b02a7a6bc2
..
1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e8ebce721 | ||
|
|
756a781524 | ||
|
|
0ffe89a881 | ||
|
|
5287ca66a1 | ||
|
|
cb63016421 | ||
|
|
43802259ed | ||
|
|
c8046eb98f | ||
|
|
57d1335fba | ||
|
|
ea7ee88ce1 | ||
|
|
a20c7e2c59 | ||
|
|
6945db40e1 | ||
|
|
1d8cfbfc07 | ||
|
|
74da180a07 | ||
|
|
0aa9488627 | ||
|
|
2782d6d43d | ||
|
|
82cf6bf7e6 | ||
|
|
7662463c7d | ||
|
|
9ee2fd2837 | ||
|
|
2fef740a5f | ||
|
|
7abb8db1e4 | ||
|
|
189a3c4e0c | ||
|
|
a5b006f728 | ||
|
|
e7b8102733 | ||
|
|
216b77c2ea | ||
|
|
bae53cc0a4 | ||
|
|
714db6f03d | ||
|
|
dcf9877fd0 | ||
|
|
3d3655a95e | ||
|
|
70c70ce756 | ||
|
|
ee2f211774 | ||
|
|
4b3bb98bd5 | ||
|
|
7e82888149 | ||
|
|
0951d32975 | ||
|
|
b4e22f6cee | ||
|
|
6d89419c37 | ||
|
|
df6b9e64df | ||
|
|
d523cfc8ed | ||
|
|
7514d810b9 | ||
|
|
012e69917c | ||
|
|
5050aca22b | ||
|
|
e4922fc3be | ||
|
|
403b4b564e | ||
|
|
9396e07120 | ||
|
|
365ae90db8 | ||
|
|
c0c97cccf4 | ||
|
|
f7683be745 | ||
|
|
86eb2002f0 | ||
|
|
d204047979 | ||
|
|
7acdc5e970 | ||
|
|
54ca0e3062 |
@@ -0,0 +1,78 @@
|
|||||||
|
name: Build and Push Docker Images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*.*.*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: registry.bouvais.lu
|
||||||
|
BASE_IMAGE_NAME: tenant/base
|
||||||
|
JUPYTER_IMAGE_NAME: tenant/jupyter
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
run: |
|
||||||
|
docker login ${{ env.REGISTRY }} \
|
||||||
|
--username adrien \
|
||||||
|
--password "${{ secrets.DOCKER_PASSWORD }}"
|
||||||
|
|
||||||
|
# --- 1. BUILD & TEST BASE IMAGE ---
|
||||||
|
- name: Build Base image
|
||||||
|
run: |
|
||||||
|
docker build \
|
||||||
|
-f ./images/base/dockerfile \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ github.ref_name }} \
|
||||||
|
./images/base
|
||||||
|
|
||||||
|
- name: Test Base image
|
||||||
|
run: |
|
||||||
|
cid=$(docker create -u root -w /tests ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ github.ref_name }} sleep 60)
|
||||||
|
docker start "$cid"
|
||||||
|
docker cp ${{ github.workspace }}/tests/. "$cid":/tests
|
||||||
|
docker exec "$cid" python3 /tests/base.py
|
||||||
|
docker rm -f "$cid"
|
||||||
|
|
||||||
|
- name: Tag Base image as latest
|
||||||
|
run: |
|
||||||
|
docker tag ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ github.ref_name }} \
|
||||||
|
${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest
|
||||||
|
|
||||||
|
- name: Push Base image
|
||||||
|
run: |
|
||||||
|
docker push ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ github.ref_name }}
|
||||||
|
docker push ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest
|
||||||
|
|
||||||
|
# --- 2. BUILD & TEST JUPYTER IMAGE ---
|
||||||
|
- name: Build Jupyter image
|
||||||
|
run: |
|
||||||
|
docker build \
|
||||||
|
-f ./images/jupyter/dockerfile \
|
||||||
|
--build-arg BASE_IMAGE=${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ github.ref_name }} \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.JUPYTER_IMAGE_NAME }}:${{ github.ref_name }} \
|
||||||
|
./images/jupyter
|
||||||
|
|
||||||
|
- name: Test Jupyter dependencies
|
||||||
|
run: |
|
||||||
|
cid=$(docker create -w /tests ${{ env.REGISTRY }}/${{ env.JUPYTER_IMAGE_NAME }}:${{ github.ref_name }} sleep 60)
|
||||||
|
docker start "$cid"
|
||||||
|
docker cp ${{ github.workspace }}/tests/. "$cid":/tests
|
||||||
|
docker exec "$cid" python3 /tests/dependencies.py
|
||||||
|
docker rm -f "$cid"
|
||||||
|
|
||||||
|
- name: Tag Jupyter image as latest
|
||||||
|
run: |
|
||||||
|
docker tag ${{ env.REGISTRY }}/${{ env.JUPYTER_IMAGE_NAME }}:${{ github.ref_name }} \
|
||||||
|
${{ env.REGISTRY }}/${{ env.JUPYTER_IMAGE_NAME }}:latest
|
||||||
|
|
||||||
|
- name: Push Jupyter image
|
||||||
|
run: |
|
||||||
|
docker push ${{ env.REGISTRY }}/${{ env.JUPYTER_IMAGE_NAME }}:${{ github.ref_name }}
|
||||||
|
docker push ${{ env.REGISTRY }}/${{ env.JUPYTER_IMAGE_NAME }}:latest
|
||||||
@@ -13,12 +13,17 @@ brew install docker
|
|||||||
|
|
||||||
# 2. Images
|
# 2. Images
|
||||||
|
|
||||||
```
|
To created the needed dependency, I did a simple 2 images base + jupyter.
|
||||||
|
The first image is a minimal python slim. I then add some
|
||||||
|
|
||||||
|
### 2.1. Base
|
||||||
|
|
||||||
|
```images/base/dockerfile
|
||||||
FROM python:3.12-slim-bookworm AS base
|
FROM python:3.12-slim-bookworm AS base
|
||||||
|
|
||||||
LABEL org.opencontainers.image.title="tenant-base" \
|
LABEL org.opencontainers.image.title="tenant-base" \
|
||||||
org.opencontainers.image.description="Hardened base image for tenant workspaces" \
|
org.opencontainers.image.description="Hardened base image for tenant workspaces" \
|
||||||
org.opencontainers.image.source="https://github.com/yourorg/yourrepo"
|
org.opencontainers.image.source="https://git.bouvais.lu/adrien/"
|
||||||
|
|
||||||
# System deps only — keep this layer stable so it's rarely rebuilt
|
# System deps only — keep this layer stable so it's rarely rebuilt
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
@@ -36,3 +41,41 @@ USER appuser
|
|||||||
|
|
||||||
ENTRYPOINT ["tini", "--"]
|
ENTRYPOINT ["tini", "--"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Then build and push it to the registry
|
||||||
|
|
||||||
|
```
|
||||||
|
docker build -t registry.bouvais.lu/tenant-base:1.0.0 images/base
|
||||||
|
docker push registry.bouvais.lu/tenant-base:1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2. Jupyter
|
||||||
|
|
||||||
|
Now I dp the same for a simple jupyter image.
|
||||||
|
|
||||||
|
```
|
||||||
|
ARG BASE_IMAGE=registry.bouvais.lu/tenant-base:1.0.0
|
||||||
|
FROM ${BASE_IMAGE}
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.title="tenant-jupyter" \
|
||||||
|
org.opencontainers.image.description="JupyterLab image built on tenant-base" \
|
||||||
|
org.opencontainers.image.base.name="${BASE_IMAGE}"
|
||||||
|
|
||||||
|
USER root
|
||||||
|
COPY --chown=appuser:appuser requirements.txt /tmp/requirements.txt
|
||||||
|
RUN pip install --no-cache-dir -r /tmp/requirements.txt \
|
||||||
|
&& rm /tmp/requirements.txt
|
||||||
|
|
||||||
|
# No secrets baked in — S3 creds come from a mounted K8s Secret / env at runtime
|
||||||
|
USER appuser
|
||||||
|
WORKDIR /home/appuser/work
|
||||||
|
|
||||||
|
EXPOSE 8888
|
||||||
|
|
||||||
|
CMD ["jupyter", "lab", \
|
||||||
|
"--ip=0.0.0.0", \
|
||||||
|
"--port=8888", \
|
||||||
|
"--no-browser", \
|
||||||
|
"--ServerApp.token=", \
|
||||||
|
"--ServerApp.allow_remote_access=True"]
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: tenant
|
||||||
|
description: Helm chart for workspace tenant
|
||||||
|
type: application
|
||||||
|
version: 0.1.0
|
||||||
|
appVersion: "1.0.0"
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: jupyter
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
app: jupyter
|
||||||
|
tenant: 'tenant-{{.Values.name}}'
|
||||||
|
annotations:
|
||||||
|
argocd.argoproj.io/sync-wave: "1"
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: jupyter
|
||||||
|
tenant: 'tenant-{{.Values.name}}'
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: jupyter
|
||||||
|
tenant: 'tenant-{{.Values.name}}'
|
||||||
|
spec:
|
||||||
|
serviceAccountName: 'tenant-{{ .Values.name }}-sa'
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: registry-credentials
|
||||||
|
containers:
|
||||||
|
- name: jupyter
|
||||||
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: s3-credentials
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: {{ .Values.resources.requests.cpu }}
|
||||||
|
memory: {{ .Values.resources.requests.memory }}
|
||||||
|
limits:
|
||||||
|
cpu: {{ .Values.resources.limits.cpu }}
|
||||||
|
memory: {{ .Values.resources.limits.memory }}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: 'tenant-{{ .Values.name }}-minio-setup'
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
annotations:
|
||||||
|
argocd.argoproj.io/hook: Sync
|
||||||
|
argocd.argoproj.io/hook-delete-policy: HookSucceeded
|
||||||
|
spec:
|
||||||
|
ttlSecondsAfterFinished: 100
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
containers:
|
||||||
|
- name: mc
|
||||||
|
image: minio/mc:RELEASE.2024-03-03T00-13-08Z
|
||||||
|
env:
|
||||||
|
# 1. MinIO Admin Credentials (Replicated from argocd namespace)
|
||||||
|
- name: MINIO_ADMIN_USER
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-admin-credentials
|
||||||
|
key: MINIO_ROOT_USER
|
||||||
|
- name: MINIO_ADMIN_PASS
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-admin-credentials
|
||||||
|
key: MINIO_ROOT_PASSWORD
|
||||||
|
|
||||||
|
# 2. Randomly Generated Tenant Credentials
|
||||||
|
- name: TENANT_USER
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: s3-credentials
|
||||||
|
key: MINIO_ACCESS_KEY_ID
|
||||||
|
- name: TENANT_PASS
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: s3-credentials
|
||||||
|
key: MINIO_SECRET_ACCESS_KEY
|
||||||
|
- name: BUCKET_REF
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: s3-credentials
|
||||||
|
key: BUCKET_REF
|
||||||
|
- name: BUCKET_WORK
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: s3-credentials
|
||||||
|
key: BUCKET_WORK
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
# Fail immediately if any command fails
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Connect to MinIO using quotes to protect special characters in passwords
|
||||||
|
mc alias set minioadmin http://minio.minio.svc.cluster.local:9000 "$MINIO_ADMIN_USER" "$MINIO_ADMIN_PASS"
|
||||||
|
|
||||||
|
# Create buckets
|
||||||
|
mc mb --ignore-existing minioadmin/$BUCKET_REF
|
||||||
|
mc mb --ignore-existing minioadmin/$BUCKET_WORK
|
||||||
|
|
||||||
|
echo "hello tenant-{{ .Values.name }}" | mc pipe minioadmin/$BUCKET_REF/hello.txt
|
||||||
|
|
||||||
|
# Create tenant user (REMOVED || true)
|
||||||
|
mc admin user add minioadmin "$TENANT_USER" "$TENANT_PASS"
|
||||||
|
|
||||||
|
# Apply IAM isolation policy
|
||||||
|
cat <<EOF > /tmp/policy.json
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": ["s3:GetBucketLocation", "s3:ListBucket"],
|
||||||
|
"Resource": [
|
||||||
|
"arn:aws:s3:::$BUCKET_REF",
|
||||||
|
"arn:aws:s3:::$BUCKET_WORK"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": ["s3:GetObject"],
|
||||||
|
"Resource": ["arn:aws:s3:::$BUCKET_REF/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": ["s3:*"],
|
||||||
|
"Resource": ["arn:aws:s3:::$BUCKET_WORK/*"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
mc admin policy create minioadmin tenant-{{ .Values.name }}-policy /tmp/policy.json || mc admin policy update minioadmin tenant-{{ .Values.name }}-policy /tmp/policy.json
|
||||||
|
# Good enough for now, would need better error handeling
|
||||||
|
mc admin policy attach minioadmin tenant-{{ .Values.name }}-policy --user="$TENANT_USER" || echo "Policy already attached, skipping."
|
||||||
|
echo "MinIO setup completed for tenant-{{ .Values.name }}"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace "s3-credentials" -}}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: s3-credentials
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/tenant: 'tenant-{{ .Values.name }}'
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
MINIO_ACCESS_KEY_ID: 'tenant-{{ .Values.name }}-user'
|
||||||
|
# If the secret exist, keep. Otherwise create a new random one. (To prevent a new password at each sync).
|
||||||
|
MINIO_SECRET_ACCESS_KEY: {{ if and $existingSecret $existingSecret.data (hasKey $existingSecret.data "AWS_SECRET_ACCESS_KEY") }}{{ index $existingSecret.data "AWS_SECRET_ACCESS_KEY" | b64dec }}{{ else }}{{ randAlphaNum 24 }}{{ end }}
|
||||||
|
S3_ENDPOINT: "http://minio.minio.svc.cluster.local:9000"
|
||||||
|
BUCKET_REF: "ref-tenant-{{ .Values.name }}"
|
||||||
|
BUCKET_WORK: "work-tenant-{{ .Values.name }}"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: 'tenant-{{ .Values.name }}-sa'
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: jupyter
|
||||||
|
app.kubernetes.io/tenant: 'tenant-{{ .Values.name }}'
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: root
|
||||||
|
namespace: argocd
|
||||||
|
spec:
|
||||||
|
project: default
|
||||||
|
source:
|
||||||
|
repoURL: https://git.bouvais.lu/adrien/ctie-exercice
|
||||||
|
targetRevision: main
|
||||||
|
path: gitops/tenants
|
||||||
|
directory:
|
||||||
|
recurse: true
|
||||||
|
destination:
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
namespace: argocd
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: true
|
||||||
|
selfHeal: true
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: ApplicationSet
|
||||||
|
metadata:
|
||||||
|
name: tenants
|
||||||
|
namespace: argocd
|
||||||
|
spec:
|
||||||
|
goTemplate: true
|
||||||
|
goTemplateOptions: ["missingkey=error"]
|
||||||
|
generators:
|
||||||
|
- git:
|
||||||
|
repoURL: https://git.bouvais.lu/adrien/ctie-exercice
|
||||||
|
revision: main
|
||||||
|
files:
|
||||||
|
- path: "tenants/*/config.yaml"
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: 'tenant-{{ trimPrefix "tenant-" .path.basename }}'
|
||||||
|
spec:
|
||||||
|
project: tenants
|
||||||
|
sources:
|
||||||
|
- repoURL: https://git.bouvais.lu/adrien/ctie-exercice
|
||||||
|
targetRevision: main
|
||||||
|
path: charts/tenant
|
||||||
|
helm:
|
||||||
|
valueFiles:
|
||||||
|
- '$values/{{.path.path}}/config.yaml'
|
||||||
|
parameters:
|
||||||
|
- name: name
|
||||||
|
value: '{{ trimPrefix "tenant-" .path.basename }}'
|
||||||
|
- repoURL: https://git.bouvais.lu/adrien/ctie-exercice
|
||||||
|
targetRevision: main
|
||||||
|
ref: values
|
||||||
|
destination:
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
namespace: 'tenant-{{ trimPrefix "tenant-" .path.basename }}'
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: true
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: AppProject
|
||||||
|
metadata:
|
||||||
|
name: tenants
|
||||||
|
namespace: argocd
|
||||||
|
spec:
|
||||||
|
sourceRepos:
|
||||||
|
- https://git.bouvais.lu/adrien/ctie-exercice
|
||||||
|
destinations:
|
||||||
|
- server: https://kubernetes.default.svc
|
||||||
|
namespace: 'tenant-*'
|
||||||
|
|
||||||
|
clusterResourceWhitelist:
|
||||||
|
- group: ''
|
||||||
|
kind: Namespace
|
||||||
|
|
||||||
|
namespaceResourceWhitelist:
|
||||||
|
- group: '*'
|
||||||
|
kind: '*'
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
FROM python:3.12-slim-bookworm AS base
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
tini \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN groupadd --gid 1000 appuser \
|
||||||
|
&& useradd --uid 1000 --gid appuser --shell /bin/bash --create-home appuser
|
||||||
|
|
||||||
|
WORKDIR /home/appuser
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
ENTRYPOINT ["tini", "--"]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
ARG BASE_IMAGE
|
||||||
|
FROM ${BASE_IMAGE}
|
||||||
|
|
||||||
|
USER root
|
||||||
|
COPY --chown=appuser:appuser test_buckets.py /home/appuser/work/test_buckets.py
|
||||||
|
COPY --chown=appuser:appuser requirements.txt /tmp/requirements.txt
|
||||||
|
RUN pip install --no-cache-dir -r /tmp/requirements.txt \
|
||||||
|
&& rm /tmp/requirements.txt
|
||||||
|
|
||||||
|
USER appuser
|
||||||
|
WORKDIR /home/appuser/work
|
||||||
|
|
||||||
|
EXPOSE 8888
|
||||||
|
|
||||||
|
CMD ["jupyter", "lab", \
|
||||||
|
"--ip=0.0.0.0", \
|
||||||
|
"--port=8888", \
|
||||||
|
"--no-browser", \
|
||||||
|
"--ServerApp.token=", \
|
||||||
|
"--ServerApp.allow_remote_access=True"]
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
jupyterlab==4.2.5
|
||||||
|
boto3==1.35.0
|
||||||
|
s3fs==2024.9.0
|
||||||
|
pandas==2.2.2
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import os
|
||||||
|
import boto3
|
||||||
|
|
||||||
|
s3 = boto3.client(
|
||||||
|
's3',
|
||||||
|
endpoint_url=os.environ['S3_ENDPOINT'],
|
||||||
|
aws_access_key_id=os.environ['MINIO_ACCESS_KEY_ID'],
|
||||||
|
aws_secret_access_key=os.environ['MINIO_SECRET_ACCESS_KEY']
|
||||||
|
)
|
||||||
|
|
||||||
|
ref_bucket = os.environ['BUCKET_REF']
|
||||||
|
work_bucket = os.environ['BUCKET_WORK']
|
||||||
|
|
||||||
|
# 1. READ FROM REFERENCE BUCKET (Should PASS)
|
||||||
|
response = s3.list_objects_v2(Bucket=ref_bucket)
|
||||||
|
data = s3.get_object(Bucket=ref_bucket, Key="hello.txt")['Body'].read()
|
||||||
|
print("SUCCESS: List items in ref bucket:", response['KeyCount'], data.decode())
|
||||||
|
|
||||||
|
# 2. WRITE TO REFERENCE BUCKET (Should FAIL with 403 Forbidden)
|
||||||
|
try:
|
||||||
|
s3.put_object(Bucket=ref_bucket, Key="test.txt", Body=b"forbidden write")
|
||||||
|
print("ERROR: Write succeeded when it should be forbidden!")
|
||||||
|
except Exception as e:
|
||||||
|
print("SUCCESS: Write to reference bucket blocked:", e)
|
||||||
|
|
||||||
|
# 3. WRITE & READ WORK BUCKET (Should PASS)
|
||||||
|
s3.put_object(Bucket=work_bucket, Key="data.txt", Body=b"hello tenant")
|
||||||
|
data = s3.get_object(Bucket=work_bucket, Key="data.txt")['Body'].read()
|
||||||
|
print("SUCCESS: Read back from work bucket:", data.decode())
|
||||||
|
|
||||||
|
# 4. ACCESS OTHER TENANT BUCKET (Should FAIL with Access Denied)
|
||||||
|
try:
|
||||||
|
s3.list_objects_v2(Bucket="work-tenant-b")
|
||||||
|
print("ERROR: Accessed tenant-b bucket!")
|
||||||
|
except Exception as e:
|
||||||
|
print("SUCCESS: Access to tenant-b blocked:", e)
|
||||||
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: minio
|
||||||
|
namespace: minio
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels: {app: minio}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels: {app: minio}
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: minio
|
||||||
|
image: minio/minio:latest
|
||||||
|
args: ["server", "/data", "--console-address", ":9001"]
|
||||||
|
env:
|
||||||
|
- name: MINIO_ROOT_USER
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-admin-credentials
|
||||||
|
key: MINIO_ROOT_USER
|
||||||
|
- name: MINIO_ROOT_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-admin-credentials
|
||||||
|
key: MINIO_ROOT_PASSWORD
|
||||||
|
ports:
|
||||||
|
- containerPort: 9000
|
||||||
|
- containerPort: 9001
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
emptyDir: {} # TODO: Make it persistant
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: minio
|
||||||
|
namespace: minio
|
||||||
|
spec:
|
||||||
|
selector: {app: minio}
|
||||||
|
ports:
|
||||||
|
- name: api
|
||||||
|
port: 9000
|
||||||
|
targetPort: 9000
|
||||||
|
- name: console
|
||||||
|
port: 9001
|
||||||
|
targetPort: 9001
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
image:
|
||||||
|
repository: registry.bouvais.lu/tenant/jupyter
|
||||||
|
tag: "1.0.1"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: "1"
|
||||||
|
memory: 2Gi
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
image:
|
||||||
|
repository: registry.bouvais.lu/tenant/jupyter
|
||||||
|
tag: "1.0.1"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: "1"
|
||||||
|
memory: 2Gi
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
print("SUCCESS: Python works.")
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import os
|
||||||
|
import boto3
|
||||||
|
print("SUCCESS: Import works.")
|
||||||
Reference in New Issue
Block a user