mirror of
https://github.com/cozystack/cozystack.git
synced 2026-03-06 23:18:53 +00:00
Compare commits
28 Commits
fix/backup
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3736dd4285 | ||
|
|
3c971d0e1b | ||
|
|
8166df23de | ||
|
|
c30a3cff13 | ||
|
|
d8c96ecf50 | ||
|
|
2731972129 | ||
|
|
133a071d6b | ||
|
|
c838358c26 | ||
|
|
6408988115 | ||
|
|
e1b169d6a7 | ||
|
|
4e8733091d | ||
|
|
15ed534c25 | ||
|
|
72a91c7780 | ||
|
|
fdb015458c | ||
|
|
84da47a2ce | ||
|
|
175b3badd2 | ||
|
|
f777f659d5 | ||
|
|
69e0320e3a | ||
|
|
9e6c240d6e | ||
|
|
79b2546e67 | ||
|
|
1d41e270b9 | ||
|
|
b4f1e96b98 | ||
|
|
757f8b5699 | ||
|
|
70c6dfd704 | ||
|
|
d4043bd71c | ||
|
|
2dbc27075a | ||
|
|
96e27cbcab | ||
|
|
e822768fcf |
1640
dashboards/mongodb/mongodb-inmemory.json
Normal file
1640
dashboards/mongodb/mongodb-inmemory.json
Normal file
File diff suppressed because it is too large
Load Diff
1460
dashboards/mongodb/mongodb-overview.json
Normal file
1460
dashboards/mongodb/mongodb-overview.json
Normal file
File diff suppressed because it is too large
Load Diff
17
docs/changelogs/v1.0.3.md
Normal file
17
docs/changelogs/v1.0.3.md
Normal file
@@ -0,0 +1,17 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v1.0.3
|
||||
-->
|
||||
|
||||
## Fixes
|
||||
|
||||
* **[platform] Fix package name conversion in migration script**: Fixed the `migrate-to-version-1.0.sh` script to correctly prepend the `cozystack.` prefix when converting `BUNDLE_DISABLE` and `BUNDLE_ENABLE` package name lists, ensuring packages are properly identified during the v0.41→v1.0 upgrade ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2144, #2148).
|
||||
|
||||
## Documentation
|
||||
|
||||
* **[website] Add white labeling guide**: Added a comprehensive guide for configuring white labeling (branding) in Cozystack v1, covering Dashboard fields (`titleText`, `footerText`, `tenantText`, `logoText`, `logoSvg`, `iconSvg`) and Keycloak fields (`brandName`, `brandHtmlName`). Includes SVG preparation workflow with theme-aware template variables, portable base64 encoding, and migration notes from the v0 ConfigMap approach ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#441).
|
||||
|
||||
* **[website] Actualize backup and recovery documentation**: Reworked the backup and recovery docs to be user-focused, separating operator and tenant workflows. Added tenant-facing documentation for `BackupJob` and `Plan` resources and status inspection commands, and added a new Velero administration guide for operators covering storage credentials and backup storage configuration ([**@androndo**](https://github.com/androndo) in cozystack/website#434).
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.2...v1.0.3
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
@test "Create and Verify Seeweedfs Bucket" {
|
||||
# Create the bucket resource
|
||||
# Create the bucket resource with readwrite and readonly users
|
||||
name='test'
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: apps.cozystack.io/v1alpha1
|
||||
@@ -9,21 +9,29 @@ kind: Bucket
|
||||
metadata:
|
||||
name: ${name}
|
||||
namespace: tenant-test
|
||||
spec: {}
|
||||
spec:
|
||||
users:
|
||||
admin: {}
|
||||
viewer:
|
||||
readonly: true
|
||||
EOF
|
||||
|
||||
# Wait for the bucket to be ready
|
||||
kubectl -n tenant-test wait hr bucket-${name} --timeout=100s --for=condition=ready
|
||||
kubectl -n tenant-test wait bucketclaims.objectstorage.k8s.io bucket-${name} --timeout=300s --for=jsonpath='{.status.bucketReady}'
|
||||
kubectl -n tenant-test wait bucketaccesses.objectstorage.k8s.io bucket-${name} --timeout=300s --for=jsonpath='{.status.accessGranted}'
|
||||
kubectl -n tenant-test wait bucketaccesses.objectstorage.k8s.io bucket-${name}-admin --timeout=300s --for=jsonpath='{.status.accessGranted}'
|
||||
kubectl -n tenant-test wait bucketaccesses.objectstorage.k8s.io bucket-${name}-viewer --timeout=300s --for=jsonpath='{.status.accessGranted}'
|
||||
|
||||
# Get and decode credentials
|
||||
kubectl -n tenant-test get secret bucket-${name} -ojsonpath='{.data.BucketInfo}' | base64 -d > bucket-test-credentials.json
|
||||
# Get admin (readwrite) credentials
|
||||
kubectl -n tenant-test get secret bucket-${name}-admin -ojsonpath='{.data.BucketInfo}' | base64 -d > bucket-admin-credentials.json
|
||||
ADMIN_ACCESS_KEY=$(jq -r '.spec.secretS3.accessKeyID' bucket-admin-credentials.json)
|
||||
ADMIN_SECRET_KEY=$(jq -r '.spec.secretS3.accessSecretKey' bucket-admin-credentials.json)
|
||||
BUCKET_NAME=$(jq -r '.spec.bucketName' bucket-admin-credentials.json)
|
||||
|
||||
# Get credentials from the secret
|
||||
ACCESS_KEY=$(jq -r '.spec.secretS3.accessKeyID' bucket-test-credentials.json)
|
||||
SECRET_KEY=$(jq -r '.spec.secretS3.accessSecretKey' bucket-test-credentials.json)
|
||||
BUCKET_NAME=$(jq -r '.spec.bucketName' bucket-test-credentials.json)
|
||||
# Get viewer (readonly) credentials
|
||||
kubectl -n tenant-test get secret bucket-${name}-viewer -ojsonpath='{.data.BucketInfo}' | base64 -d > bucket-viewer-credentials.json
|
||||
VIEWER_ACCESS_KEY=$(jq -r '.spec.secretS3.accessKeyID' bucket-viewer-credentials.json)
|
||||
VIEWER_SECRET_KEY=$(jq -r '.spec.secretS3.accessSecretKey' bucket-viewer-credentials.json)
|
||||
|
||||
# Start port-forwarding
|
||||
bash -c 'timeout 100s kubectl port-forward service/seaweedfs-s3 -n tenant-root 8333:8333 > /dev/null 2>&1 &'
|
||||
@@ -31,17 +39,33 @@ EOF
|
||||
# Wait for port-forward to be ready
|
||||
timeout 30 sh -ec 'until nc -z localhost 8333; do sleep 1; done'
|
||||
|
||||
# Set up MinIO alias with error handling
|
||||
mc alias set local https://localhost:8333 $ACCESS_KEY $SECRET_KEY --insecure
|
||||
# --- Test readwrite user (admin) ---
|
||||
mc alias set rw-user https://localhost:8333 $ADMIN_ACCESS_KEY $ADMIN_SECRET_KEY --insecure
|
||||
|
||||
# Upload file to bucket
|
||||
mc cp bucket-test-credentials.json $BUCKET_NAME/bucket-test-credentials.json
|
||||
# Admin can upload
|
||||
echo "readwrite test" > /tmp/rw-test.txt
|
||||
mc cp --insecure /tmp/rw-test.txt rw-user/$BUCKET_NAME/rw-test.txt
|
||||
|
||||
# Verify file was uploaded
|
||||
mc ls $BUCKET_NAME/bucket-test-credentials.json
|
||||
# Admin can list
|
||||
mc ls --insecure rw-user/$BUCKET_NAME/rw-test.txt
|
||||
|
||||
# Clean up uploaded file
|
||||
mc rm $BUCKET_NAME/bucket-test-credentials.json
|
||||
# Admin can download
|
||||
mc cp --insecure rw-user/$BUCKET_NAME/rw-test.txt /tmp/rw-test-download.txt
|
||||
|
||||
# --- Test readonly user (viewer) ---
|
||||
mc alias set ro-user https://localhost:8333 $VIEWER_ACCESS_KEY $VIEWER_SECRET_KEY --insecure
|
||||
|
||||
# Viewer can list
|
||||
mc ls --insecure ro-user/$BUCKET_NAME/rw-test.txt
|
||||
|
||||
# Viewer can download
|
||||
mc cp --insecure ro-user/$BUCKET_NAME/rw-test.txt /tmp/ro-test-download.txt
|
||||
|
||||
# Viewer cannot upload (must fail with Access Denied)
|
||||
echo "readonly test" > /tmp/ro-test.txt
|
||||
! mc cp --insecure /tmp/ro-test.txt ro-user/$BUCKET_NAME/ro-test.txt
|
||||
|
||||
# --- Cleanup ---
|
||||
mc rm --insecure rw-user/$BUCKET_NAME/rw-test.txt
|
||||
kubectl -n tenant-test delete bucket.apps.cozystack.io ${name}
|
||||
}
|
||||
|
||||
@@ -2,5 +2,4 @@ include ../../../hack/package.mk
|
||||
|
||||
generate:
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
yq -o json -i '.properties = {}' values.schema.json
|
||||
../../../hack/update-crd.sh
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# S3 bucket
|
||||
|
||||
## Parameters
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ---------------------- | -------------------------------------------------------------------------- | ------------------- | ------- |
|
||||
| `locking` | Provisions bucket from the `-lock` BucketClass (with object lock enabled). | `bool` | `false` |
|
||||
| `storagePool` | Selects a specific BucketClass by storage pool name. | `string` | `""` |
|
||||
| `users` | Users configuration map. | `map[string]object` | `{}` |
|
||||
| `users[name].readonly` | Whether the user has read-only access. | `bool` | `false` |
|
||||
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
{{- $seaweedfs := .Values._namespace.seaweedfs }}
|
||||
{{- $pool := .Values.storagePool }}
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
kind: BucketClaim
|
||||
metadata:
|
||||
name: {{ .Release.Name }}
|
||||
spec:
|
||||
bucketClassName: {{ $seaweedfs }}
|
||||
bucketClassName: {{ $seaweedfs }}{{- if $pool }}-{{ $pool }}{{- end }}{{- if .Values.locking }}-lock{{- end }}
|
||||
protocols:
|
||||
- s3
|
||||
{{- range $name, $user := .Values.users }}
|
||||
---
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
kind: BucketAccess
|
||||
metadata:
|
||||
name: {{ .Release.Name }}
|
||||
name: {{ $.Release.Name }}-{{ $name }}
|
||||
spec:
|
||||
bucketAccessClassName: {{ $seaweedfs }}
|
||||
bucketClaimName: {{ .Release.Name }}
|
||||
credentialsSecretName: {{ .Release.Name }}
|
||||
protocol: s3
|
||||
---
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
kind: BucketAccess
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-readonly
|
||||
spec:
|
||||
bucketAccessClassName: {{ $seaweedfs }}-readonly
|
||||
bucketClaimName: {{ .Release.Name }}
|
||||
credentialsSecretName: {{ .Release.Name }}-readonly
|
||||
bucketAccessClassName: {{ $seaweedfs }}{{- if $pool }}-{{ $pool }}{{- end }}{{- if $user.readonly }}-readonly{{- end }}
|
||||
bucketClaimName: {{ $.Release.Name }}
|
||||
credentialsSecretName: {{ $.Release.Name }}-{{ $name }}
|
||||
protocol: s3
|
||||
{{- end }}
|
||||
|
||||
@@ -8,9 +8,9 @@ rules:
|
||||
resources:
|
||||
- secrets
|
||||
resourceNames:
|
||||
- {{ .Release.Name }}
|
||||
- {{ .Release.Name }}-credentials
|
||||
- {{ .Release.Name }}-readonly
|
||||
{{- range $name, $user := .Values.users }}
|
||||
- {{ $.Release.Name }}-{{ $name }}-credentials
|
||||
{{- end }}
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
|
||||
@@ -23,3 +23,4 @@ spec:
|
||||
name: cozystack-values
|
||||
values:
|
||||
bucketName: {{ .Release.Name }}
|
||||
users: {{ .Values.users | toJson }}
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
{
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
"properties": {
|
||||
"locking": {
|
||||
"description": "Provisions bucket from the `-lock` BucketClass (with object lock enabled).",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"storagePool": {
|
||||
"description": "Selects a specific BucketClass by storage pool name.",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"users": {
|
||||
"description": "Users configuration map.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"readonly": {
|
||||
"description": "Whether the user has read-only access.",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,11 @@
|
||||
{}
|
||||
## @param {bool} locking=false - Provisions bucket from the `-lock` BucketClass (with object lock enabled).
|
||||
locking: false
|
||||
|
||||
## @param {string} [storagePool] - Selects a specific BucketClass by storage pool name.
|
||||
storagePool: ""
|
||||
|
||||
## @typedef {struct} User - Bucket user configuration.
|
||||
## @field {bool} [readonly] - Whether the user has read-only access.
|
||||
|
||||
## @param {map[string]User} users - Users configuration map.
|
||||
users: {}
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:434aa3b8e2a3cbf6681426b174e1c4fde23bafd12a6cccd046b5cb1749092ec4
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:faaa6bcdb68196edb4baafe643679bd7d2ef35f910c639b71e06a4ecc034f232
|
||||
|
||||
@@ -3,6 +3,7 @@ cilium:
|
||||
k8sServiceHost: {{ .Release.Name }}.{{ .Release.Namespace }}.svc
|
||||
k8sServicePort: 6443
|
||||
routingMode: tunnel
|
||||
MTU: 1350
|
||||
enableIPv4Masquerade: true
|
||||
ipv4NativeRoutingCIDR: ""
|
||||
{{- if $.Values.addons.gatewayAPI.enabled }}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
cozystackOperator:
|
||||
# Deployment variant: talos, generic, hosted
|
||||
variant: talos
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.0.0@sha256:9e5229764b6077809a1c16566881a524c33e8986e36597e6833f8857a7e6a335
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.1.0@sha256:9367001a8d1d2dcf08ae74a42ac234eaa6af18f1af64ac28ce8a5946af9c5d3f
|
||||
platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages'
|
||||
platformSourceRef: 'digest=sha256:ef3e4ba7d21572a61794d8be594805f063aa04f4a8c3753351fc89c7804d337e'
|
||||
platformSourceRef: 'digest=sha256:7c6da38e7b99ec80d35ba2cef721ea1579f8a0824989454544fa85318bb7bf15'
|
||||
# Generic variant configuration (only used when cozystackOperator.variant=generic)
|
||||
cozystack:
|
||||
# Kubernetes API server host (IP only, no protocol/port)
|
||||
|
||||
@@ -18,5 +18,5 @@ spec:
|
||||
path: system/backupstrategy-controller
|
||||
install:
|
||||
privileged: true
|
||||
namespace: cozy-backup-controller
|
||||
namespace: cozy-backupstrategy-controller
|
||||
releaseName: backupstrategy-controller
|
||||
|
||||
@@ -5,7 +5,7 @@ sourceRef:
|
||||
path: /
|
||||
migrations:
|
||||
enabled: false
|
||||
image: ghcr.io/cozystack/cozystack/platform-migrations:v1.0.0@sha256:68dabdebc38ac439228ae07031cc70e0fa184a24bd4e5b3b22c17466b2a55201
|
||||
image: ghcr.io/cozystack/cozystack/platform-migrations:v1.1.0@sha256:d7e8955c1ad8c8fbd4ce42b014c0f849d73d0c3faf0cedaac8e15d647fb2f663
|
||||
targetVersion: 35
|
||||
# Bundle deployment configuration
|
||||
bundles:
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
e2e:
|
||||
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.0.0@sha256:0eae9f519669667d60b160ebb93c127843c470ad9ca3447fceaa54604503a7ba
|
||||
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.1.0@sha256:0eae9f519669667d60b160ebb93c127843c470ad9ca3447fceaa54604503a7ba
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/matchbox:v1.0.0@sha256:c48eb7b23f01a8ff58d409fdb51c88e771f819cb914eee03da89471e62302f33
|
||||
ghcr.io/cozystack/cozystack/matchbox:v1.1.0@sha256:e4c872f6dadc2bbcb9200d04a1d9878f62502f74e979b4eae6c7203abc6d8fa6
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0@sha256:2a3595cd88b30af55b2000d3ca204899beecef0012b0e0402754c3914aad1f7f
|
||||
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.1.0@sha256:2a3595cd88b30af55b2000d3ca204899beecef0012b0e0402754c3914aad1f7f
|
||||
|
||||
@@ -7,10 +7,32 @@ metadata:
|
||||
driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io
|
||||
deletionPolicy: Delete
|
||||
---
|
||||
kind: BucketClass
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
metadata:
|
||||
name: {{ .Release.Namespace }}-lock
|
||||
driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io
|
||||
deletionPolicy: Retain
|
||||
parameters:
|
||||
objectLockEnabled: "true"
|
||||
objectLockRetentionMode: "COMPLIANCE"
|
||||
objectLockRetentionDays: "365"
|
||||
---
|
||||
kind: BucketAccessClass
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
metadata:
|
||||
name: {{ .Release.Namespace }}
|
||||
driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io
|
||||
authenticationType: KEY
|
||||
parameters:
|
||||
accessPolicy: readwrite
|
||||
---
|
||||
kind: BucketAccessClass
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
metadata:
|
||||
name: {{ .Release.Namespace }}-readonly
|
||||
driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io
|
||||
authenticationType: KEY
|
||||
parameters:
|
||||
accessPolicy: readonly
|
||||
{{- end }}
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
{{- if not (regexMatch "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$" $poolName) }}
|
||||
{{- fail (printf "volume.pools key '%s' must be a valid DNS label (lowercase alphanumeric and hyphens, no dots)." $poolName) }}
|
||||
{{- end }}
|
||||
{{- if or (hasSuffix "-worm" $poolName) (hasSuffix "-readonly" $poolName) }}
|
||||
{{- fail (printf "volume.pools key '%s' must not end with '-worm' or '-readonly' (reserved suffixes for COSI resources)." $poolName) }}
|
||||
{{- if or (hasSuffix "-lock" $poolName) (hasSuffix "-readonly" $poolName) }}
|
||||
{{- fail (printf "volume.pools key '%s' must not end with '-lock' or '-readonly' (reserved suffixes for COSI resources)." $poolName) }}
|
||||
{{- end }}
|
||||
{{- if not $pool.diskType }}
|
||||
{{- fail (printf "volume.pools.%s.diskType is required." $poolName) }}
|
||||
@@ -52,8 +52,8 @@
|
||||
{{- if not (regexMatch "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$" $poolName) }}
|
||||
{{- fail (printf "volume.zones.%s.pools key '%s' must be a valid DNS label." $zoneName $poolName) }}
|
||||
{{- end }}
|
||||
{{- if or (hasSuffix "-worm" $poolName) (hasSuffix "-readonly" $poolName) }}
|
||||
{{- fail (printf "volume.zones.%s.pools key '%s' must not end with '-worm' or '-readonly' (reserved suffixes for COSI resources)." $zoneName $poolName) }}
|
||||
{{- if or (hasSuffix "-lock" $poolName) (hasSuffix "-readonly" $poolName) }}
|
||||
{{- fail (printf "volume.zones.%s.pools key '%s' must not end with '-lock' or '-readonly' (reserved suffixes for COSI resources)." $zoneName $poolName) }}
|
||||
{{- end }}
|
||||
{{- if not $pool.diskType }}
|
||||
{{- fail (printf "volume.zones.%s.pools.%s.diskType is required." $zoneName $poolName) }}
|
||||
|
||||
@@ -25,14 +25,14 @@ parameters:
|
||||
kind: BucketClass
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
metadata:
|
||||
name: {{ $.Release.Namespace }}-{{ $poolName }}-worm
|
||||
name: {{ $.Release.Namespace }}-{{ $poolName }}-lock
|
||||
driverName: {{ $.Release.Namespace }}.seaweedfs.objectstorage.k8s.io
|
||||
deletionPolicy: Retain
|
||||
parameters:
|
||||
disk: {{ $diskType }}
|
||||
objectLockEnabled: "true"
|
||||
objectLockRetentionMode: COMPLIANCE
|
||||
objectLockRetentionDays: "36500"
|
||||
objectLockRetentionMode: "COMPLIANCE"
|
||||
objectLockRetentionDays: "365"
|
||||
---
|
||||
kind: BucketAccessClass
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
|
||||
@@ -14,7 +14,3 @@ rules:
|
||||
- apiGroups: ["backups.cozystack.io"]
|
||||
resources: ["backupjobs"]
|
||||
verbs: ["create", "get", "list", "watch"]
|
||||
# Leader election (--leader-elect)
|
||||
- apiGroups: ["coordination.k8s.io"]
|
||||
resources: ["leases"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch"]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
backupController:
|
||||
image: "ghcr.io/cozystack/cozystack/backup-controller:v1.0.0@sha256:e1a6c8ac7ba64442812464b59c53e782e373a339c18b379c2692921b44c6edb5"
|
||||
image: "ghcr.io/cozystack/cozystack/backup-controller:v1.1.0@sha256:8e42e29f5d30ecbef1f05cb0601c32703c5f9572b89d2c9032c1dff186e9a526"
|
||||
replicas: 2
|
||||
debug: false
|
||||
metrics:
|
||||
|
||||
@@ -30,10 +30,6 @@ rules:
|
||||
- apiGroups: ["velero.io"]
|
||||
resources: ["backups", "restores"]
|
||||
verbs: ["create", "get", "list", "watch", "update", "patch"]
|
||||
# Events from Recorder.Event() calls
|
||||
- apiGroups: [""]
|
||||
resources: ["events"]
|
||||
verbs: ["create", "patch"]
|
||||
# Leader election (--leader-elect)
|
||||
- apiGroups: ["coordination.k8s.io"]
|
||||
resources: ["leases"]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
backupStrategyController:
|
||||
image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.0.0@sha256:29735d945c69c6bbaab21068bf4ea30f6b63f4c71a7a8d95590f370abcb4b328"
|
||||
image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.1.0@sha256:508e3bd5a83a316732cfb84fe598064e3092482d941cfc53738ca21237642e6f"
|
||||
replicas: 2
|
||||
debug: false
|
||||
metrics:
|
||||
|
||||
@@ -8,7 +8,7 @@ spec:
|
||||
plural: buckets
|
||||
singular: bucket
|
||||
openAPISchema: |-
|
||||
{"title":"Chart Values","type":"object","properties":{}}
|
||||
{"title":"Chart Values","type":"object","properties":{"locking":{"description":"Provisions bucket from the `-lock` BucketClass (with object lock enabled).","type":"boolean","default":false},"storagePool":{"description":"Selects a specific BucketClass by storage pool name.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"readonly":{"description":"Whether the user has read-only access.","type":"boolean"}}}}}}
|
||||
release:
|
||||
prefix: bucket-
|
||||
labels:
|
||||
@@ -26,14 +26,14 @@ spec:
|
||||
tags:
|
||||
- storage
|
||||
icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODNfMzA5MSkiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03MiAzMC4xNjQxTDExNy45ODMgMzYuNzc4OVY0MC42NzM5QzExNy45ODMgNDYuNDY1MyA5Ny4zODYyIDUxLjEzMzIgNzEuOTgyNyA1MS4xMzMyQzQ2LjU3OTIgNTEuMTMzMiAyNiA0Ni40NjUzIDI2IDQwLjY3MzlWMzYuNDQzMUw3MiAzMC4xNjQxWk03MiA1OC4yNjc4QzkxLjIwODQgNTguMjY3OCAxMDcuNjU4IDU1LjU5ODYgMTE0LjU0NyA1MS44MDQ4TDExNi44MDMgNDguMTExTDExNy43MjMgNDQuNzUzVjQ4LjkxNzFMMTAyLjY3OSAxMTEuMDMzQzEwMi42NzkgMTE0Ljg5NSA4OC45NTMzIDExOCA3Mi4wMTcyIDExOEM1NS4wODEyIDExOCA0MS4zNzQzIDExNC44OTUgNDEuMzc0MyAxMTEuMDMzTDI2LjMzIDQ4LjkxNzFWNDQuODM2OUwyOS44MDA3IDUxLjkzODJDMzYuNzA2NSA1NS42NjUzIDUyLjk5OTcgNTguMjY3OCA3MiA1OC4yNjc4WiIgZmlsbD0iIzhDMzEyMyIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcyLjAwMDMgMjZDOTcuNDAzOCAyNiAxMTggMzAuNjgzOSAxMTggMzYuNDQyQzExOCA0Mi4yIDk3LjM4NjYgNDYuODUwNyA3Mi4wMDAzIDQ2Ljg1MDdDNDYuNjE0MSA0Ni44NTA3IDI2LjAxNzYgNDIuMjM0NSAyNi4wMTc2IDM2LjQ0MkMyNi4wMTc2IDMwLjY0OTQgNDYuNTk2OCAyNiA3Mi4wMDAzIDI2Wk03Mi4wMDAzIDU0LjEwMzdDOTUuNjg1NyA1NC4xMDM3IDExNS4xNzIgNTAuMDU4IDExNy43MDYgNDQuODE5N0wxMDIuNjYyIDEwNi45MzdDMTAyLjY2MiAxMTAuNzk5IDg4LjkzNjQgMTEzLjkwNSA3Mi4wMDAzIDExMy45MDVDNTUuMDY0MyAxMTMuOTA1IDQxLjMzOSAxMTAuODE2IDQxLjMzOSAxMDYuOTU0TDI2LjI5NTkgNDQuODM3QzI4Ljg0NjYgNTAuMDU4IDQ4LjMzMzMgNTQuMTAzNyA3Mi4wMDAzIDU0LjEwMzdaIiBmaWxsPSIjRTA1MjQzIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjEuMTcyNSA2MC4wMjkzSDgxLjA5MjhWNzkuMTY3Nkg2MS4xNzI1VjYwLjAyOTNaTTQ1LjMzMDEgOTUuMzY4OEM0NS4zMzAxIDkwLjE0MiA0OS43MTA0IDg1LjkzNDIgNTUuMTUxMSA4NS45MzQyQzYwLjU5MTcgODUuOTM0MiA2NC45NzIxIDkwLjE0MiA2NC45NzIxIDk1LjM2ODhDNjQuOTcyMSAxMDAuNTk2IDYwLjU5MTcgMTA0LjgwMyA1NS4xNTExIDEwNC44MDNDNDkuNzEwNCAxMDQuODAzIDQ1LjMzMDEgMTAwLjU5NiA0NS4zMzAxIDk1LjM2ODhaTTk2LjQ0ODcgMTA0LjM2OEg3Ni43NzIyTDg2LjYxMDUgODYuNzczN0w5Ni40NDg3IDEwNC4zNjhaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4M18zMDkxIiB4MT0iMCIgeTE9IjAiIHgyPSIxNTEiIHkyPSIxODAiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRjBFRSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNFQzg4N0QiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K
|
||||
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"]]
|
||||
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "locking"], ["spec", "storagePool"], ["spec", "users"]]
|
||||
secrets:
|
||||
exclude: []
|
||||
include:
|
||||
- resourceNames:
|
||||
- bucket-{{ .name }}
|
||||
- bucket-{{ .name }}-credentials
|
||||
- bucket-{{ .name }}-readonly
|
||||
- matchLabels:
|
||||
apps.cozystack.io/user-secret: "true"
|
||||
ingresses:
|
||||
exclude: []
|
||||
include:
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:279008f87460d709e99ed25ee8a1e4568a290bb9afa0e3dd3a06d524163a132b
|
||||
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:5a7cae722ff6b424bdfbc4aba9d072c11b6930e2ee0f5fa97c3a565bd1c8dc88
|
||||
|
||||
@@ -9,6 +9,7 @@ WORKDIR /usr/src/app
|
||||
RUN wget -O- https://github.com/cloudlena/s3manager/archive/9a7c8e446b422f8973b8c461990f39fdafee9c27.tar.gz | tar -xzf- --strip 1
|
||||
ADD cozystack.patch /
|
||||
RUN git apply /cozystack.patch
|
||||
RUN go mod tidy
|
||||
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -ldflags="-s -w" -a -installsuffix cgo -o bin/s3manager
|
||||
|
||||
FROM docker.io/library/alpine:latest
|
||||
|
||||
@@ -1,3 +1,235 @@
|
||||
diff --git a/go.mod b/go.mod
|
||||
index b5d8540..6ede8e8 100644
|
||||
--- a/go.mod
|
||||
+++ b/go.mod
|
||||
@@ -1,10 +1,11 @@
|
||||
module github.com/cloudlena/s3manager
|
||||
|
||||
-go 1.22.5
|
||||
+go 1.23
|
||||
|
||||
require (
|
||||
github.com/cloudlena/adapters v0.0.0-20240708203353-a39be02cc801
|
||||
github.com/gorilla/mux v1.8.1
|
||||
+ github.com/gorilla/sessions v1.4.0
|
||||
github.com/matryer/is v1.4.1
|
||||
github.com/minio/minio-go/v7 v7.0.74
|
||||
github.com/spf13/viper v1.19.0
|
||||
@@ -16,6 +17,7 @@ require (
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
+ github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
diff --git a/go.sum b/go.sum
|
||||
index 1ea1b16..d7866ce 100644
|
||||
--- a/go.sum
|
||||
+++ b/go.sum
|
||||
@@ -16,10 +16,16 @@ github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
+github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
+github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
+github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
|
||||
+github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
diff --git a/main.go b/main.go
|
||||
index 2ffe8ab..723a1b8 100644
|
||||
--- a/main.go
|
||||
+++ b/main.go
|
||||
@@ -41,10 +41,12 @@ type configuration struct {
|
||||
Timeout int32
|
||||
SseType string
|
||||
SseKey string
|
||||
+ LoginMode bool
|
||||
}
|
||||
|
||||
func parseConfiguration() configuration {
|
||||
var accessKeyID, secretAccessKey, iamEndpoint string
|
||||
+ var loginMode bool
|
||||
|
||||
viper.AutomaticEnv()
|
||||
|
||||
@@ -57,13 +59,10 @@ func parseConfiguration() configuration {
|
||||
iamEndpoint = viper.GetString("IAM_ENDPOINT")
|
||||
} else {
|
||||
accessKeyID = viper.GetString("ACCESS_KEY_ID")
|
||||
- if len(accessKeyID) == 0 {
|
||||
- log.Fatal("please provide ACCESS_KEY_ID")
|
||||
- }
|
||||
-
|
||||
secretAccessKey = viper.GetString("SECRET_ACCESS_KEY")
|
||||
- if len(secretAccessKey) == 0 {
|
||||
- log.Fatal("please provide SECRET_ACCESS_KEY")
|
||||
+ if len(accessKeyID) == 0 || len(secretAccessKey) == 0 {
|
||||
+ log.Println("ACCESS_KEY_ID or SECRET_ACCESS_KEY not set, starting in login mode")
|
||||
+ loginMode = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +114,7 @@ func parseConfiguration() configuration {
|
||||
Timeout: timeout,
|
||||
SseType: sseType,
|
||||
SseKey: sseKey,
|
||||
+ LoginMode: loginMode,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,57 +135,96 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
- // Set up S3 client
|
||||
- opts := &minio.Options{
|
||||
- Secure: configuration.UseSSL,
|
||||
- }
|
||||
- if configuration.UseIam {
|
||||
- opts.Creds = credentials.NewIAM(configuration.IamEndpoint)
|
||||
- } else {
|
||||
- var signatureType credentials.SignatureType
|
||||
-
|
||||
- switch configuration.SignatureType {
|
||||
- case "V2":
|
||||
- signatureType = credentials.SignatureV2
|
||||
- case "V4":
|
||||
- signatureType = credentials.SignatureV4
|
||||
- case "V4Streaming":
|
||||
- signatureType = credentials.SignatureV4Streaming
|
||||
- case "Anonymous":
|
||||
- signatureType = credentials.SignatureAnonymous
|
||||
- default:
|
||||
- log.Fatalf("Invalid SIGNATURE_TYPE: %s", configuration.SignatureType)
|
||||
+ // Set up router
|
||||
+ r := mux.NewRouter()
|
||||
+ r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(statics)))).Methods(http.MethodGet)
|
||||
+
|
||||
+ if configuration.LoginMode {
|
||||
+ // Login mode: no pre-configured S3 client, per-session credentials
|
||||
+ sessionCfg := &s3manager.SessionConfig{
|
||||
+ Store: s3manager.NewSessionStore(),
|
||||
+ Endpoint: configuration.Endpoint,
|
||||
+ UseSSL: configuration.UseSSL,
|
||||
+ SkipSSLVerify: configuration.SkipSSLVerification,
|
||||
+ AllowDelete: configuration.AllowDelete,
|
||||
+ ForceDownload: configuration.ForceDownload,
|
||||
+ ListRecursive: configuration.ListRecursive,
|
||||
+ SseInfo: sseType,
|
||||
+ Templates: templates,
|
||||
}
|
||||
|
||||
- opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", signatureType)
|
||||
- }
|
||||
+ // Public routes (no auth required)
|
||||
+ r.Handle("/login", s3manager.HandleLoginView(templates)).Methods(http.MethodGet)
|
||||
+ r.Handle("/login", s3manager.HandleLogin(sessionCfg)).Methods(http.MethodPost)
|
||||
+ r.Handle("/logout", s3manager.HandleLogout(sessionCfg)).Methods(http.MethodPost)
|
||||
+
|
||||
+ // Protected routes (auth required via middleware)
|
||||
+ protected := mux.NewRouter()
|
||||
+ protected.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet)
|
||||
+ protected.Handle("/buckets", s3manager.HandleBucketsViewDynamic(templates, configuration.AllowDelete)).Methods(http.MethodGet)
|
||||
+ protected.PathPrefix("/buckets/").Handler(s3manager.HandleBucketViewDynamic(templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet)
|
||||
+ protected.Handle("/api/buckets", s3manager.HandleCreateBucketDynamic()).Methods(http.MethodPost)
|
||||
+ if configuration.AllowDelete {
|
||||
+ protected.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucketDynamic()).Methods(http.MethodDelete)
|
||||
+ }
|
||||
+ protected.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObjectDynamic(sseType)).Methods(http.MethodPost)
|
||||
+ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrlDynamic()).Methods(http.MethodGet)
|
||||
+ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObjectDynamic(configuration.ForceDownload)).Methods(http.MethodGet)
|
||||
+ if configuration.AllowDelete {
|
||||
+ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObjectDynamic()).Methods(http.MethodDelete)
|
||||
+ }
|
||||
|
||||
- if configuration.Region != "" {
|
||||
- opts.Region = configuration.Region
|
||||
- }
|
||||
- if configuration.UseSSL && configuration.SkipSSLVerification {
|
||||
- opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec
|
||||
- }
|
||||
- s3, err := minio.New(configuration.Endpoint, opts)
|
||||
- if err != nil {
|
||||
- log.Fatalln(fmt.Errorf("error creating s3 client: %w", err))
|
||||
- }
|
||||
+ r.PathPrefix("/").Handler(s3manager.RequireAuth(sessionCfg, protected))
|
||||
+ } else {
|
||||
+ // Pre-configured mode: existing behavior with static S3 client
|
||||
+ opts := &minio.Options{
|
||||
+ Secure: configuration.UseSSL,
|
||||
+ }
|
||||
+ if configuration.UseIam {
|
||||
+ opts.Creds = credentials.NewIAM(configuration.IamEndpoint)
|
||||
+ } else {
|
||||
+ var signatureType credentials.SignatureType
|
||||
+
|
||||
+ switch configuration.SignatureType {
|
||||
+ case "V2":
|
||||
+ signatureType = credentials.SignatureV2
|
||||
+ case "V4":
|
||||
+ signatureType = credentials.SignatureV4
|
||||
+ case "V4Streaming":
|
||||
+ signatureType = credentials.SignatureV4Streaming
|
||||
+ case "Anonymous":
|
||||
+ signatureType = credentials.SignatureAnonymous
|
||||
+ default:
|
||||
+ log.Fatalf("Invalid SIGNATURE_TYPE: %s", configuration.SignatureType)
|
||||
+ }
|
||||
+
|
||||
+ opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", signatureType)
|
||||
+ }
|
||||
|
||||
- // Set up router
|
||||
- r := mux.NewRouter()
|
||||
- r.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet)
|
||||
- r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(statics)))).Methods(http.MethodGet)
|
||||
- r.Handle("/buckets", s3manager.HandleBucketsView(s3, templates, configuration.AllowDelete)).Methods(http.MethodGet)
|
||||
- r.PathPrefix("/buckets/").Handler(s3manager.HandleBucketView(s3, templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet)
|
||||
- r.Handle("/api/buckets", s3manager.HandleCreateBucket(s3)).Methods(http.MethodPost)
|
||||
- if configuration.AllowDelete {
|
||||
- r.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucket(s3)).Methods(http.MethodDelete)
|
||||
- }
|
||||
- r.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObject(s3, sseType)).Methods(http.MethodPost)
|
||||
- r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrl(s3)).Methods(http.MethodGet)
|
||||
- r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObject(s3, configuration.ForceDownload)).Methods(http.MethodGet)
|
||||
- if configuration.AllowDelete {
|
||||
- r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObject(s3)).Methods(http.MethodDelete)
|
||||
+ if configuration.Region != "" {
|
||||
+ opts.Region = configuration.Region
|
||||
+ }
|
||||
+ if configuration.UseSSL && configuration.SkipSSLVerification {
|
||||
+ opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec
|
||||
+ }
|
||||
+ s3, err := minio.New(configuration.Endpoint, opts)
|
||||
+ if err != nil {
|
||||
+ log.Fatalln(fmt.Errorf("error creating s3 client: %w", err))
|
||||
+ }
|
||||
+
|
||||
+ r.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet)
|
||||
+ r.Handle("/buckets", s3manager.HandleBucketsView(s3, templates, configuration.AllowDelete)).Methods(http.MethodGet)
|
||||
+ r.PathPrefix("/buckets/").Handler(s3manager.HandleBucketView(s3, templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet)
|
||||
+ r.Handle("/api/buckets", s3manager.HandleCreateBucket(s3)).Methods(http.MethodPost)
|
||||
+ if configuration.AllowDelete {
|
||||
+ r.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucket(s3)).Methods(http.MethodDelete)
|
||||
+ }
|
||||
+ r.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObject(s3, sseType)).Methods(http.MethodPost)
|
||||
+ r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrl(s3)).Methods(http.MethodGet)
|
||||
+ r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObject(s3, configuration.ForceDownload)).Methods(http.MethodGet)
|
||||
+ if configuration.AllowDelete {
|
||||
+ r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObject(s3)).Methods(http.MethodDelete)
|
||||
+ }
|
||||
}
|
||||
|
||||
lr := logging.Handler(os.Stdout)(r)
|
||||
diff --git a/web/template/bucket.html.tmpl b/web/template/bucket.html.tmpl
|
||||
index e2f8d28..87add13 100644
|
||||
--- a/web/template/bucket.html.tmpl
|
||||
@@ -24,3 +256,298 @@ index c7ea184..fb1dce7 100644
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
diff --git a/internal/app/s3manager/auth.go b/internal/app/s3manager/auth.go
|
||||
new file mode 100644
|
||||
index 0000000..58589e2
|
||||
--- /dev/null
|
||||
+++ b/internal/app/s3manager/auth.go
|
||||
@@ -0,0 +1,237 @@
|
||||
+package s3manager
|
||||
+
|
||||
+import (
|
||||
+ "context"
|
||||
+ "crypto/rand"
|
||||
+ "crypto/tls"
|
||||
+ "fmt"
|
||||
+ "html/template"
|
||||
+ "io/fs"
|
||||
+ "log"
|
||||
+ "net/http"
|
||||
+
|
||||
+ "github.com/gorilla/sessions"
|
||||
+ "github.com/minio/minio-go/v7"
|
||||
+ "github.com/minio/minio-go/v7/pkg/credentials"
|
||||
+)
|
||||
+
|
||||
+type contextKey string
|
||||
+
|
||||
+const s3ContextKey contextKey = "s3client"
|
||||
+
|
||||
+// SessionConfig holds session store and S3 connection settings for login mode.
|
||||
+type SessionConfig struct {
|
||||
+ Store *sessions.CookieStore
|
||||
+ Endpoint string
|
||||
+ UseSSL bool
|
||||
+ SkipSSLVerify bool
|
||||
+ AllowDelete bool
|
||||
+ ForceDownload bool
|
||||
+ ListRecursive bool
|
||||
+ SseInfo SSEType
|
||||
+ Templates fs.FS
|
||||
+}
|
||||
+
|
||||
+// NewSessionStore creates a CookieStore with a random encryption key.
|
||||
+func NewSessionStore() *sessions.CookieStore {
|
||||
+ key := make([]byte, 32)
|
||||
+ if _, err := rand.Read(key); err != nil {
|
||||
+ log.Fatal("failed to generate session key:", err)
|
||||
+ }
|
||||
+ store := sessions.NewCookieStore(key)
|
||||
+ store.Options = &sessions.Options{
|
||||
+ Path: "/",
|
||||
+ MaxAge: 86400,
|
||||
+ HttpOnly: true,
|
||||
+ Secure: true,
|
||||
+ SameSite: http.SameSiteLaxMode,
|
||||
+ }
|
||||
+ return store
|
||||
+}
|
||||
+
|
||||
+// NewS3Client creates a minio client from user-provided credentials.
|
||||
+func NewS3Client(endpoint, accessKey, secretKey string, useSSL, skipSSLVerify bool) (*minio.Client, error) {
|
||||
+ opts := &minio.Options{
|
||||
+ Creds: credentials.NewStaticV4(accessKey, secretKey, ""),
|
||||
+ Secure: useSSL,
|
||||
+ }
|
||||
+ if useSSL && skipSSLVerify {
|
||||
+ opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec
|
||||
+ }
|
||||
+ return minio.New(endpoint, opts)
|
||||
+}
|
||||
+
|
||||
+// S3FromContext retrieves the S3 client stored in request context.
|
||||
+func S3FromContext(ctx context.Context) S3 {
|
||||
+ if s3, ok := ctx.Value(s3ContextKey).(S3); ok {
|
||||
+ return s3
|
||||
+ }
|
||||
+ return nil
|
||||
+}
|
||||
+
|
||||
+func contextWithS3(ctx context.Context, s3 S3) context.Context {
|
||||
+ return context.WithValue(ctx, s3ContextKey, s3)
|
||||
+}
|
||||
+
|
||||
+// RequireAuth is middleware that validates session credentials and injects
|
||||
+// an S3 client into the request context. Redirects to /login if no session.
|
||||
+func RequireAuth(cfg *SessionConfig, next http.Handler) http.Handler {
|
||||
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
+ session, _ := cfg.Store.Get(r, "s3session")
|
||||
+ accessKey, ok1 := session.Values["accessKey"].(string)
|
||||
+ secretKey, ok2 := session.Values["secretKey"].(string)
|
||||
+ if !ok1 || !ok2 || accessKey == "" || secretKey == "" {
|
||||
+ http.Redirect(w, r, "/login", http.StatusFound)
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
+ s3, err := NewS3Client(cfg.Endpoint, accessKey, secretKey, cfg.UseSSL, cfg.SkipSSLVerify)
|
||||
+ if err != nil {
|
||||
+ // Session has bad credentials — clear and redirect to login
|
||||
+ session.Options.MaxAge = -1
|
||||
+ _ = session.Save(r, w)
|
||||
+ http.Redirect(w, r, "/login", http.StatusFound)
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
+ ctx := contextWithS3(r.Context(), s3)
|
||||
+ next.ServeHTTP(w, r.WithContext(ctx))
|
||||
+ })
|
||||
+}
|
||||
+
|
||||
+// HandleLoginView renders the login page.
|
||||
+func HandleLoginView(templates fs.FS) http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ errorMsg := r.URL.Query().Get("error")
|
||||
+
|
||||
+ data := struct {
|
||||
+ Error string
|
||||
+ }{
|
||||
+ Error: errorMsg,
|
||||
+ }
|
||||
+
|
||||
+ t, err := template.ParseFS(templates, "layout.html.tmpl", "login.html.tmpl")
|
||||
+ if err != nil {
|
||||
+ handleHTTPError(w, fmt.Errorf("error parsing login template: %w", err))
|
||||
+ return
|
||||
+ }
|
||||
+ err = t.ExecuteTemplate(w, "layout", data)
|
||||
+ if err != nil {
|
||||
+ handleHTTPError(w, fmt.Errorf("error executing login template: %w", err))
|
||||
+ return
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// HandleLogin processes the login form POST.
|
||||
+func HandleLogin(cfg *SessionConfig) http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ accessKey := r.FormValue("accessKey")
|
||||
+ secretKey := r.FormValue("secretKey")
|
||||
+
|
||||
+ if accessKey == "" || secretKey == "" {
|
||||
+ http.Redirect(w, r, "/login?error=credentials+required", http.StatusFound)
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
+ // Validate credentials by attempting ListBuckets
|
||||
+ s3, err := NewS3Client(cfg.Endpoint, accessKey, secretKey, cfg.UseSSL, cfg.SkipSSLVerify)
|
||||
+ if err != nil {
|
||||
+ http.Redirect(w, r, "/login?error=connection+failed", http.StatusFound)
|
||||
+ return
|
||||
+ }
|
||||
+ _, err = s3.ListBuckets(r.Context())
|
||||
+ if err != nil {
|
||||
+ http.Redirect(w, r, "/login?error=invalid+credentials", http.StatusFound)
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
+ // Save credentials to session
|
||||
+ session, _ := cfg.Store.Get(r, "s3session")
|
||||
+ session.Values["accessKey"] = accessKey
|
||||
+ session.Values["secretKey"] = secretKey
|
||||
+ err = session.Save(r, w)
|
||||
+ if err != nil {
|
||||
+ handleHTTPError(w, fmt.Errorf("error saving session: %w", err))
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
+ http.Redirect(w, r, "/buckets", http.StatusFound)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// HandleLogout destroys the session and redirects to login.
|
||||
+func HandleLogout(cfg *SessionConfig) http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ session, _ := cfg.Store.Get(r, "s3session")
|
||||
+ session.Options.MaxAge = -1
|
||||
+ _ = session.Save(r, w)
|
||||
+ http.Redirect(w, r, "/login", http.StatusFound)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// Dynamic handler wrappers — extract S3 from context, delegate to original handlers.
|
||||
+
|
||||
+// HandleBucketsViewDynamic wraps HandleBucketsView for login mode.
|
||||
+func HandleBucketsViewDynamic(templates fs.FS, allowDelete bool) http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ s3 := S3FromContext(r.Context())
|
||||
+ HandleBucketsView(s3, templates, allowDelete).ServeHTTP(w, r)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// HandleBucketViewDynamic wraps HandleBucketView for login mode.
|
||||
+func HandleBucketViewDynamic(templates fs.FS, allowDelete bool, listRecursive bool) http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ s3 := S3FromContext(r.Context())
|
||||
+ HandleBucketView(s3, templates, allowDelete, listRecursive).ServeHTTP(w, r)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// HandleCreateBucketDynamic wraps HandleCreateBucket for login mode.
|
||||
+func HandleCreateBucketDynamic() http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ s3 := S3FromContext(r.Context())
|
||||
+ HandleCreateBucket(s3).ServeHTTP(w, r)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// HandleDeleteBucketDynamic wraps HandleDeleteBucket for login mode.
|
||||
+func HandleDeleteBucketDynamic() http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ s3 := S3FromContext(r.Context())
|
||||
+ HandleDeleteBucket(s3).ServeHTTP(w, r)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// HandleCreateObjectDynamic wraps HandleCreateObject for login mode.
|
||||
+func HandleCreateObjectDynamic(sseInfo SSEType) http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ s3 := S3FromContext(r.Context())
|
||||
+ HandleCreateObject(s3, sseInfo).ServeHTTP(w, r)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// HandleGenerateUrlDynamic wraps HandleGenerateUrl for login mode.
|
||||
+func HandleGenerateUrlDynamic() http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ s3 := S3FromContext(r.Context())
|
||||
+ HandleGenerateUrl(s3).ServeHTTP(w, r)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// HandleGetObjectDynamic wraps HandleGetObject for login mode.
|
||||
+func HandleGetObjectDynamic(forceDownload bool) http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ s3 := S3FromContext(r.Context())
|
||||
+ HandleGetObject(s3, forceDownload).ServeHTTP(w, r)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// HandleDeleteObjectDynamic wraps HandleDeleteObject for login mode.
|
||||
+func HandleDeleteObjectDynamic() http.HandlerFunc {
|
||||
+ return func(w http.ResponseWriter, r *http.Request) {
|
||||
+ s3 := S3FromContext(r.Context())
|
||||
+ HandleDeleteObject(s3).ServeHTTP(w, r)
|
||||
+ }
|
||||
+}
|
||||
diff --git a/web/template/login.html.tmpl b/web/template/login.html.tmpl
|
||||
new file mode 100644
|
||||
index 0000000..f153018
|
||||
--- /dev/null
|
||||
+++ b/web/template/login.html.tmpl
|
||||
@@ -0,0 +1,46 @@
|
||||
+{{ define "content" }}
|
||||
+<nav>
|
||||
+ <div class="nav-wrapper container">
|
||||
+ <a href="/" class="brand-logo">Cozystack S3 Manager</a>
|
||||
+ </div>
|
||||
+</nav>
|
||||
+
|
||||
+<div class="container">
|
||||
+ <div class="section">
|
||||
+ <div class="row">
|
||||
+ <div class="col l6 offset-l3 m8 offset-m2 s12">
|
||||
+ <div class="card">
|
||||
+ <div class="card-content">
|
||||
+ <span class="card-title">Sign In</span>
|
||||
+ <p>Enter your S3 credentials to access the bucket manager.</p>
|
||||
+ <br>
|
||||
+
|
||||
+ {{ if .Error }}
|
||||
+ <div class="card-panel red lighten-4 red-text text-darken-4">
|
||||
+ <i class="material-icons tiny">error</i> {{ .Error }}
|
||||
+ </div>
|
||||
+ {{ end }}
|
||||
+
|
||||
+ <form method="POST" action="/login">
|
||||
+ <div class="input-field">
|
||||
+ <i class="material-icons prefix">vpn_key</i>
|
||||
+ <input id="accessKey" name="accessKey" type="text" required>
|
||||
+ <label for="accessKey">Access Key ID</label>
|
||||
+ </div>
|
||||
+ <div class="input-field">
|
||||
+ <i class="material-icons prefix">lock</i>
|
||||
+ <input id="secretKey" name="secretKey" type="password" required>
|
||||
+ <label for="secretKey">Secret Access Key</label>
|
||||
+ </div>
|
||||
+ <br>
|
||||
+ <button type="submit" class="btn waves-effect waves-light" style="width:100%;">
|
||||
+ Sign In <i class="material-icons right">send</i>
|
||||
+ </button>
|
||||
+ </form>
|
||||
+ </div>
|
||||
+ </div>
|
||||
+ </div>
|
||||
+ </div>
|
||||
+ </div>
|
||||
+</div>
|
||||
+{{ end }}
|
||||
|
||||
@@ -17,19 +17,6 @@ spec:
|
||||
image: "{{ $.Files.Get "images/s3manager.tag" | trim }}"
|
||||
env:
|
||||
- name: ENDPOINT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.bucketName }}-credentials
|
||||
key: endpoint
|
||||
value: "s3.{{ .Values._namespace.host }}"
|
||||
- name: SKIP_SSL_VERIFICATION
|
||||
value: "true"
|
||||
- name: ACCESS_KEY_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.bucketName }}-credentials
|
||||
key: accessKey
|
||||
- name: SECRET_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.bucketName }}-credentials
|
||||
key: secretKey
|
||||
|
||||
@@ -8,9 +8,6 @@ kind: Ingress
|
||||
metadata:
|
||||
name: {{ .Values.bucketName }}-ui
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/auth-type: "basic"
|
||||
nginx.ingress.kubernetes.io/auth-secret: "{{ .Values.bucketName }}-ui-auth"
|
||||
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "99999"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "99999"
|
||||
|
||||
@@ -1,24 +1,2 @@
|
||||
{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace .Values.bucketName }}
|
||||
{{- $bucketInfo := fromJson (b64dec (index $existingSecret.data "BucketInfo")) }}
|
||||
{{- $accessKeyID := index $bucketInfo.spec.secretS3 "accessKeyID" }}
|
||||
{{- $accessSecretKey := index $bucketInfo.spec.secretS3 "accessSecretKey" }}
|
||||
{{- $endpoint := index $bucketInfo.spec.secretS3 "endpoint" }}
|
||||
{{- $bucketName := index $bucketInfo.spec "bucketName" }}
|
||||
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ .Values.bucketName }}-credentials
|
||||
type: Opaque
|
||||
stringData:
|
||||
accessKey: {{ $accessKeyID | quote }}
|
||||
secretKey: {{ $accessSecretKey | quote }}
|
||||
endpoint: {{ trimPrefix "https://" $endpoint }}
|
||||
bucketName: {{ $bucketName | quote }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ .Values.bucketName }}-ui-auth
|
||||
data:
|
||||
auth: {{ htpasswd $accessKeyID $accessSecretKey | b64enc | quote }}
|
||||
{{/* Secrets previously used for s3manager credential injection and nginx basic auth */}}
|
||||
{{/* are no longer needed — s3manager now handles authentication via its own login page */}}
|
||||
|
||||
20
packages/system/bucket/templates/user-credentials.yaml
Normal file
20
packages/system/bucket/templates/user-credentials.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
{{- range $name, $user := .Values.users }}
|
||||
{{- $secretName := printf "%s-%s" $.Values.bucketName $name }}
|
||||
{{- $existingSecret := lookup "v1" "Secret" $.Release.Namespace $secretName }}
|
||||
{{- if $existingSecret }}
|
||||
{{- $bucketInfo := fromJson (b64dec (index $existingSecret.data "BucketInfo")) }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ $secretName }}-credentials
|
||||
labels:
|
||||
apps.cozystack.io/user-secret: "true"
|
||||
type: Opaque
|
||||
stringData:
|
||||
accessKey: {{ index $bucketInfo.spec.secretS3 "accessKeyID" | quote }}
|
||||
secretKey: {{ index $bucketInfo.spec.secretS3 "accessSecretKey" | quote }}
|
||||
endpoint: {{ trimPrefix "https://" (index $bucketInfo.spec.secretS3 "endpoint") }}
|
||||
bucketName: {{ index $bucketInfo.spec "bucketName" | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -1 +1,2 @@
|
||||
bucketName: "cozystack"
|
||||
users: {}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
cozystackAPI:
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-api:v1.0.0@sha256:bd70ecb944bde9a0d6b88114aea89bdbbe2d07e33f03175cfd885de013e88294
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-api:v1.1.0@sha256:3a8e559b1a71cffb445bab14178d9abeba1b90509f9fec31df5ff5a9a38333d1
|
||||
replicas: 2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cozystackController:
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.0.0@sha256:da01085026a4a01514ae435c7bfb48cca2cf00eb17feb2ed7ae88711f82693e0
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.1.0@sha256:f04fa839924a761571e1035d83f380f39f62d1708ea8d22f7a323f17bb59ff96
|
||||
debug: false
|
||||
disableTelemetry: false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{{- $brandingConfig := .Values._cluster.branding | default dict }}
|
||||
|
||||
{{- $tenantText := "v1.0.0" }}
|
||||
{{- $tenantText := "v1.1.0" }}
|
||||
{{- $footerText := "Cozystack" }}
|
||||
{{- $titleText := "Cozystack Dashboard" }}
|
||||
{{- $logoText := "" }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
openapiUI:
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui:v1.0.0@sha256:73a8bd4283a46a99d22536eece9c2059fa2fb1c17b43ddefe6716e8960e4731e
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui:v1.1.0@sha256:bc530ae2e428727eed284d7f80b2eea4fdd98b7618d20cab262eef7199af5fa5
|
||||
openapiUIK8sBff:
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.0.0@sha256:c938fee904acd948800d4dc5e121c4c5cd64cb4a3160fb8d2f9dbff0e5168740
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.1.0@sha256:c938fee904acd948800d4dc5e121c4c5cd64cb4a3160fb8d2f9dbff0e5168740
|
||||
tokenProxy:
|
||||
image: ghcr.io/cozystack/cozystack/token-proxy:v1.0.0@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc
|
||||
image: ghcr.io/cozystack/cozystack/token-proxy:v1.1.0@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/grafana-dashboards:v1.0.0@sha256:7a3c9af59f8d74d5a23750bbc845c7de64610dbd4d4f84011e10be037b3ce2a0
|
||||
ghcr.io/cozystack/cozystack/grafana-dashboards:v1.1.0@sha256:2c9aa0b48e2bf6167db198f4d15882bfe51700108edf2e9f6d0942940a2c1204
|
||||
|
||||
@@ -3,7 +3,7 @@ kamaji:
|
||||
deploy: false
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
tag: v1.0.0@sha256:50db517ebe7698083dd32223a96c987b6ed0c88d3a093969beb571e4a96d18e4
|
||||
tag: v1.1.0@sha256:914d04f7442f0faecf18f8282c192dee9fe244a711494a8c892e2f9e2ad415f7
|
||||
repository: ghcr.io/cozystack/cozystack/kamaji
|
||||
resources:
|
||||
limits:
|
||||
@@ -13,4 +13,4 @@ kamaji:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
extraArgs:
|
||||
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.0.0@sha256:50db517ebe7698083dd32223a96c987b6ed0c88d3a093969beb571e4a96d18e4
|
||||
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.1.0@sha256:914d04f7442f0faecf18f8282c192dee9fe244a711494a8c892e2f9e2ad415f7
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
portSecurity: true
|
||||
routes: ""
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.0.0@sha256:b6045fdb4f324b9b1cb44a218c40422aafbbc600b085c819ff58809bb6e97220
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.1.0@sha256:b91bf0964a3204e50f703092f190b7d96c078a6ccee430215042ae1275ed5127
|
||||
ovnCentralName: ovn-central
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
portSecurity: true
|
||||
routes: ""
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.0.0@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.1.0@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
storageClass: replicated
|
||||
csiDriver:
|
||||
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:434aa3b8e2a3cbf6681426b174e1c4fde23bafd12a6cccd046b5cb1749092ec4
|
||||
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:faaa6bcdb68196edb4baafe643679bd7d2ef35f910c639b71e06a4ecc034f232
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
lineageControllerWebhook:
|
||||
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.0.0@sha256:af765c2829db4f513084522a384710acc321bd4a332eaf7fe814fecacea1022f
|
||||
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.1.0@sha256:4d6a2bb76cae84e24cd48c7377b03ed6bdfefe611221d2c0a7f77a5457db8849
|
||||
debug: false
|
||||
localK8sAPIEndpoint:
|
||||
enabled: true
|
||||
|
||||
@@ -13,4 +13,4 @@ linstor:
|
||||
linstorCSI:
|
||||
image:
|
||||
repository: ghcr.io/cozystack/cozystack/linstor-csi
|
||||
tag: v1.10.5@sha256:c87b6f6dadaa6e3a3643d3279e81742830147f6c38f99e9232d9780abbcac897
|
||||
tag: v1.10.5@sha256:50ab1ab0210d4e7ebfca311f445bb764516db5ddb63fc6d28536b28622eee753
|
||||
|
||||
@@ -45,3 +45,5 @@ hubble/l7-http-metrics
|
||||
hubble/network-overview
|
||||
nats/nats-jetstream
|
||||
nats/nats-server
|
||||
mongodb/mongodb-overview
|
||||
mongodb/mongodb-inmemory
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
objectstorage:
|
||||
controller:
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.0.0@sha256:e40e94f3014cfd04cce4230597315a1acfcca2daa8051b987614d0c05da6d928"
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.1.0@sha256:e40e94f3014cfd04cce4230597315a1acfcca2daa8051b987614d0c05da6d928"
|
||||
|
||||
@@ -7,12 +7,25 @@ metadata:
|
||||
driverName: {{ .Values.cosi.driverName }}
|
||||
deletionPolicy: Delete
|
||||
---
|
||||
kind: BucketClass
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
metadata:
|
||||
name: {{ .Values.cosi.bucketClassName }}-lock
|
||||
driverName: {{ .Values.cosi.driverName }}
|
||||
deletionPolicy: Retain
|
||||
parameters:
|
||||
objectLockEnabled: "true"
|
||||
objectLockRetentionMode: "COMPLIANCE"
|
||||
objectLockRetentionDays: "365"
|
||||
---
|
||||
kind: BucketAccessClass
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
metadata:
|
||||
name: {{ .Values.cosi.bucketClassName }}
|
||||
driverName: {{ .Values.cosi.driverName }}
|
||||
authenticationType: KEY
|
||||
parameters:
|
||||
accessPolicy: readwrite
|
||||
---
|
||||
kind: BucketAccessClass
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
@@ -21,5 +34,5 @@ metadata:
|
||||
driverName: {{ .Values.cosi.driverName }}
|
||||
authenticationType: KEY
|
||||
parameters:
|
||||
accessPolicy: "readonly"
|
||||
accessPolicy: readonly
|
||||
{{- end }}
|
||||
|
||||
@@ -1546,7 +1546,7 @@ allInOne:
|
||||
# For more information, visit: https://container-object-storage-interface.github.io/docs/deployment-guide
|
||||
cosi:
|
||||
enabled: false
|
||||
image: "ghcr.io/seaweedfs/seaweedfs-cosi-driver:v0.1.2"
|
||||
image: "ghcr.io/seaweedfs/seaweedfs-cosi-driver:v0.3.0"
|
||||
driverName: "seaweedfs.objectstorage.k8s.io"
|
||||
bucketClassName: "seaweedfs"
|
||||
endpoint: ""
|
||||
|
||||
@@ -177,7 +177,7 @@ seaweedfs:
|
||||
bucketClassName: "seaweedfs"
|
||||
region: ""
|
||||
sidecar:
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0@sha256:2a3595cd88b30af55b2000d3ca204899beecef0012b0e0402754c3914aad1f7f"
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.1.0@sha256:2a3595cd88b30af55b2000d3ca204899beecef0012b0e0402754c3914aad1f7f"
|
||||
certificates:
|
||||
commonName: "SeaweedFS CA"
|
||||
ipAddresses: []
|
||||
|
||||
Reference in New Issue
Block a user