Skip to content

Commit

Permalink
feat(project-operator): add filebrowser custom image
Browse files Browse the repository at this point in the history
  • Loading branch information
ialejandro committed Dec 23, 2024
1 parent 3c2fd71 commit c9637eb
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 428 deletions.
392 changes: 0 additions & 392 deletions app/api/go.sum

This file was deleted.

31 changes: 31 additions & 0 deletions filebrowser/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
ARG BASE_S3FUSE_IMAGE=efrecon/s3fs:1.94
ARG BASE_FILEBROWSER_IMAGE=filebrowser/filebrowser:v2

FROM efrecon/s3fs:${BASE_S3FUSE_IMAGE} AS s3fs
FROM filebrowser/filebrowser:${BASE_FILEBROWSER_IMAGE}

COPY --from=s3fs /usr/bin/s3fs /usr/bin/s3fs
COPY --from=s3fs /usr/local/bin/*.sh /usr/local/bin/
COPY --from=s3fs /etc/fuse.conf /etc/fuse.conf

# s3fs dependencies
RUN apk add --no-cache \
fuse \
libxml2 \
libcurl \
libgcc \
libstdc++ \
tini && \
mkdir -p /srv && \
# Create non-root user
addgroup -g 1000 filebrowser && \
adduser -D -u 1000 -G filebrowser filebrowser && \
chown -R filebrowser:filebrowser /srv

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

VOLUME ["/srv"]
EXPOSE 9696

ENTRYPOINT ["tini", "-g", "--", "/entrypoint.sh"]
78 changes: 78 additions & 0 deletions filebrowser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Filebrowser with S3Fuse

Docker image combining `Filebrowser` web interface with `S3Fuse` mount capabilities for S3-compatible storage, providing a seamless way to browse and manage files both locally and in S3 buckets.

## Security Features

* Non-root user (`filebrowser`) execution with configurable UID/GID (default `1000:1000`)
* Proper permission handling
* Tini as init system for proper process management

## Configuration

### Build Arguments

| Variable | Description | Default |
|--------------------------|----------------------------|---------|
| `BASE_FILEBROWSER_IMAGE` | Filebrowser base image tag | `v2` |
| `BASE_S3FUSE_IMAGE` | S3Fuse base image tag | `1.94` |

### Environment Variables

| Variable | Description | Required |
|----------------------------|---------------------------------|----------|
| `AWS_S3_ACCESS_KEY_ID` | AWS/S3 access key | `Yes` |
| `AWS_S3_BUCKET` | S3 bucket name to mount | `Yes` |
| `AWS_S3_MOUNT` | Mount point inside container | `No` |
| `AWS_S3_SECRET_ACCESS_KEY` | AWS/S3 secret key | `Yes` |
| `AWS_S3_URL` | S3-compatible endpoint URL | `Yes` |
| `S3FS_ARGS` | Additional S3Fuse mount options | `No` |

## Storage

* Web interface files: `/srv`
* S3 bucket mount: configurable via `AWS_S3_MOUNT` (default: `/srv`)
* Filebrowser database: `/database.db`

## Local Deployment

Basic usage:

```bash
docker run -p 9696:9696 \
-e "AWS_S3_ACCESS_KEY_ID=user" \
-e "AWS_S3_BUCKET=my-bucket" \
-e "AWS_S3_SECRET_ACCESS_KEY=pass" \
-e "FB_ADDRESS="0.0.0.0" \
-e "FB_DATABASE=/database.db \
-e "FB_LOG=stdout \
-e "FB_ROOT=/srv \
--device /dev/fuse \
--cap-add SYS_ADMIN \
--security-opt apparmor:unconfined \
konstellation/kdl-filebrowser:latest
```

With custom configuration:

```bash
docker run -p 9696:9696 \
--user 1000:1000 \
-e "AWS_S3_ACCESS_KEY_ID=user" \
-e "AWS_S3_BUCKET=my-bucket" \
-e "AWS_S3_MOUNT=/srv/data" \
-e "AWS_S3_SECRET_ACCESS_KEY=pass" \
-e "AWS_S3_URL=http://minio:9000" \
-e "FB_ADDRESS="0.0.0.0" \
-e "FB_DATABASE=/database.db \
-e "FB_LOG=stdout \
-e "FB_ROOT=/srv \
-e "S3FS_ARGS=allow_other,use_path_request_style" \
-v /path/to/config:/srv \
--device /dev/fuse \
--cap-add SYS_ADMIN \
--security-opt apparmor:unconfined \
konstellation/kdl-filebrowser:latest
```

Access the web interface at <http://localhost:9696>
79 changes: 79 additions & 0 deletions filebrowser/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"name": "filebrowser",
"version": "1.0.0",
"private": true,
"release": {
"tagFormat": "filebrowser-v${version}",
"extends": "semantic-release-monorepo",
"branches": [
"main",
{
"channel": "default",
"name": "release/*",
"prerelease": "rc"
},
{
"channel": "default",
"name": "hotfix/+([0-9])?(.{+([0-9]),x}).x",
"prerelease": false,
"range": "${name.replace(/^hotfix\\//g, '')}"
}
],
"plugins": [
"@semantic-release/commit-analyzer",
[
"@semantic-release/git",
{
"assets": [
"**/package.json"
],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
],
[
"@semantic-release/github",
{
"failComment": false,
"published": true,
"successComment": false
}
],
[
"@semantic-release/release-notes-generator",
{
"preset": "conventionalcommits",
"presetConfig": {
"types": [
{
"type": "chore",
"section": "Miscellaneous Chores"
},
{
"type": "ci",
"section": "Continuous Integration",
"hidden": true
},
{
"type": "docs",
"section": "Documentation",
"hidden": true
},
{
"type": "feat",
"section": "Features"
},
{
"type": "fix",
"section": "Bug Fixes"
},
{
"type": "refactor",
"section": "Code Refactoring"
}
]
}
}
]
]
}
}
5 changes: 5 additions & 0 deletions hack/scripts/kdlctl/cmd_build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ build_docker_images() {
build_user_tools_operator
build_vscode
build_repo_cloner
build_filebrowser
build_mlflow
build_kg
}
Expand All @@ -43,6 +44,10 @@ build_repo_cloner() {
build_image kdl-repo-cloner repo-cloner
}

build_filebrowser() {
build_image kdl-filebrowser filebrowser
}

build_mlflow() {
build_image kdl-mlflow mlflow
}
Expand Down
4 changes: 2 additions & 2 deletions project-operator/helm-charts/kdl-project/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ helm template test . -f ci/ci-values.yaml
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| domain | string | `"kdl.local"` | String to set domain to deploy |
| filebrowser | object | `{"affinity":{},"args":["-c","/entrypoint.sh"],"autoscaling":{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80},"command":["/bin/sh"],"enabled":true,"env":{"FB_ADDRESS":"0.0.0.0","FB_DATABASE":"/database.db","FB_LOG":"stdout","FB_ROOT":"/srv"},"envFromConfigMap":{},"envFromFiles":[],"envFromSecrets":{},"extraContainers":[],"image":{"pullPolicy":"IfNotPresent","repository":"filebrowser/filebrowser","tag":"v2"},"imagePullSecrets":[],"initContainers":[],"lifecycle":{},"livenessProbe":{"enabled":false,"failureThreshold":3,"initialDelaySeconds":30,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":5},"livenessProbeCustom":{},"networkPolicy":{"egress":[],"enabled":false,"ingress":[],"policyTypes":[]},"nodeSelector":{},"podAnnotations":{},"podDisruptionBudget":{"enabled":false,"maxUnavailable":1,"minAvailable":null},"podLabels":{},"podSecurityContext":{},"readinessProbe":{"enabled":false,"failureThreshold":3,"initialDelaySeconds":10,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":1},"readinessProbeCustom":{},"replicaCount":1,"resources":{},"securityContext":{},"service":{"port":9696,"type":"ClusterIP"},"serviceAccount":{"annotations":{},"automount":true,"create":true,"name":""},"startupProbe":{"enabled":false,"failureThreshold":30,"initialDelaySeconds":30,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":5},"startupProbeCustom":{},"terminationGracePeriodSeconds":30,"tolerations":[],"topologySpreadConstraints":[],"volumeMounts":[],"volumes":[]}` | Filebrowser sevice </br> Ref: https://filebrowser.org |
| filebrowser | object | `{"affinity":{},"args":["-c","/entrypoint.sh"],"autoscaling":{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80},"command":["/bin/sh"],"enabled":true,"env":{"FB_ADDRESS":"0.0.0.0","FB_DATABASE":"/database.db","FB_LOG":"stdout","FB_ROOT":"/srv"},"envFromConfigMap":{},"envFromFiles":[],"envFromSecrets":{},"extraContainers":[],"image":{"pullPolicy":"IfNotPresent","repository":"konstellation/kdl-filebrowser","tag":"latest"},"imagePullSecrets":[],"initContainers":[],"lifecycle":{},"livenessProbe":{"enabled":false,"failureThreshold":3,"initialDelaySeconds":30,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":5},"livenessProbeCustom":{},"networkPolicy":{"egress":[],"enabled":false,"ingress":[],"policyTypes":[]},"nodeSelector":{},"podAnnotations":{},"podDisruptionBudget":{"enabled":false,"maxUnavailable":1,"minAvailable":null},"podLabels":{},"podSecurityContext":{},"readinessProbe":{"enabled":false,"failureThreshold":3,"initialDelaySeconds":10,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":1},"readinessProbeCustom":{},"replicaCount":1,"resources":{},"securityContext":{},"service":{"port":9696,"type":"ClusterIP"},"serviceAccount":{"annotations":{},"automount":true,"create":true,"name":""},"startupProbe":{"enabled":false,"failureThreshold":30,"initialDelaySeconds":30,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":5},"startupProbeCustom":{},"terminationGracePeriodSeconds":30,"tolerations":[],"topologySpreadConstraints":[],"volumeMounts":[],"volumes":[]}` | Filebrowser sevice </br> Ref: https://filebrowser.org |
| filebrowser.affinity | object | `{}` | Affinity for pod assignment </br> Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity |
| filebrowser.args | list | `["-c","/entrypoint.sh"]` | Configure args </br> Ref: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/ |
| filebrowser.autoscaling | object | `{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80}` | Autoscaling with CPU or memory utilization percentage </br> Ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ |
Expand All @@ -101,7 +101,7 @@ helm template test . -f ci/ci-values.yaml
| filebrowser.envFromFiles | list | `[]` | Load all variables from files </br> Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variables |
| filebrowser.envFromSecrets | object | `{}` | Variables from secrets |
| filebrowser.extraContainers | list | `[]` | Configure extra containers |
| filebrowser.image | object | `{"pullPolicy":"IfNotPresent","repository":"filebrowser/filebrowser","tag":"v2"}` | Image registry The image configuration for the base service |
| filebrowser.image | object | `{"pullPolicy":"IfNotPresent","repository":"konstellation/kdl-filebrowser","tag":"latest"}` | Image registry The image configuration for the base service |
| filebrowser.imagePullSecrets | list | `[]` | Specifies the secrets to use for pulling images from private registries Leave empty if no secrets are required E.g. imagePullSecrets: - name: myRegistryKeySecretName |
| filebrowser.initContainers | list | `[]` | Configure additional containers </br> Ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ |
| filebrowser.lifecycle | object | `{}` | Configure lifecycle hooks </br> Ref: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ </br> Ref: https://learnk8s.io/graceful-shutdown |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,57 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "kdl-project.filebrowser.name" . }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "0"
name: {{ .Values.projectId }}-filebrowser-config
labels:
{{- include "kdl-project.filebrowserLabels" . | nindent 4 }}
data:
entrypoint.sh: |
#!/bin/sh
set -e
if [ -n "$AWS_S3_BUCKET" ]; then
echo "Mounting S3 bucket..."
# minio authentication
if [ -n "$AWS_S3_ACCESS_KEY_ID" ] && [ -n "$AWS_S3_SECRET_ACCESS_KEY" ]; then
echo "$AWS_S3_ACCESS_KEY_ID:$AWS_S3_SECRET_ACCESS_KEY" > /etc/passwd-s3fs
chmod 600 /etc/passwd-s3fs
fi
# bucket with basic options
s3fs "$AWS_S3_BUCKET" "$AWS_S3_MOUNT" \
-o passwd_file=/etc/passwd-s3fs \
-o url="$AWS_S3_URL" \
-o allow_other \
$S3FS_ARGS
# wait until mount to be ready
while ! mountpoint -q "$AWS_S3_MOUNT"; do
echo "Waiting for S3 mount to be ready..."
sleep 1
done
echo "S3 mount completed"
fi
# filebrowser configuration
echo "Initializing Filebrowser..."
/filebrowser config init
/filebrowser config set --auth.method=noauth
# KDL admin user
echo "Configuring KDL admin user..."
/filebrowser users add kdladmin not_used_pass \
--perm.admin=false \
--perm.download=true \
--perm.create=true \
--perm.delete=true \
--perm.execute=false \
--perm.modify=true \
--perm.rename=true \
--perm.share=false \
--lockPassword=true
/filebrowser
--perm.admin=false \
--perm.download=true \
--perm.create=false \
--perm.delete=false \
--perm.execute=false \
--perm.modify=false \
--perm.rename=false \
--perm.share=false \
--lockPassword=true
# filebrowser
echo "Starting Filebrowser..."
exec /filebrowser
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ spec:
value: /filebrowser/{{ .Values.projectId }}
- name: FB_PORT
value: {{ .Values.filebrowser.env.FB_PORT | default (int .Values.filebrowser.service.targetPort | default .Values.filebrowser.service.port) | quote }}
- name: AWS_S3_BUCKET
value: {{ .Values.projectId | quote }}
# Variables from secrets have precedence
{{- $envList := dict }}
{{- if .Values.filebrowser.envFromSecrets }}
Expand Down
Loading

0 comments on commit c9637eb

Please sign in to comment.