Add SeaweedFS Alpine package build

This commit is contained in:
Joachim Schlöffel
2026-06-07 14:30:21 +02:00
commit ca2db8e206
29 changed files with 1578 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
.cache/
packages/
packaging/alpine/*/*/pkg/
packaging/alpine/*/*/src/
packaging/alpine/*/*/*.apk
packaging/alpine/*/*/*.tar.*

96
README.md Normal file
View File

@@ -0,0 +1,96 @@
# SeaweedFS Alpine package base
This repository is set up to build a local SeaweedFS Alpine package with `mise` while keeping all Alpine tooling inside Docker.
## Tasks
```sh
mise run apk:build
mise run apk:build-all
mise run apk:build-x86_64
mise run apk:build-aarch64
mise run apk:checksum
mise run apk:lint
mise run apk:packages
mise run apk:smoke
mise run apk:publish-check
mise run apk:shell
mise run apk:clean
```
`mise` also declares the Docker CLI as a project tool dependency, so the package workflow has a single command surface.
## Tool stub
The SeaweedFS release binary is also available as a mise HTTP tool stub:
```sh
./bin/weed version
```
The stub is pinned to SeaweedFS `4.31` and includes Linux x64 and arm64 release URLs with SHA-256 checksums.
The default build targets `x86_64`. Multi-arch builds currently target `x86_64` and Alpine's `aarch64`; override this with `ALPINE_ARCHES`, for example:
```sh
ALPINE_ARCHES="x86_64 aarch64" mise run apk:build-all
```
Generated packages and repository indexes are written under `packages/`, typically:
```text
packages/local/x86_64/
packages/local/aarch64/
```
Because this package repackages upstream release binaries, all targets are built from the native Docker builder platform by default (`ALPINE_BUILD_PLATFORM=linux/amd64`). No QEMU/binfmt setup is required.
The signing key and distfiles cache are kept in `.cache/abuild/` and `.cache/apk-distfiles/` so repeated builds use the same local repository key.
## Local repository
To test the package in an Alpine container:
```sh
mise run apk:smoke
```
Before publishing or handing the local repository to another system, run:
```sh
mise run apk:publish-check
```
That runs APKBUILD linting, rebuilds all configured architectures, lists the generated package metadata, and performs the install smoke test.
To install OpenRC service scripts as well:
```sh
apk add seaweedfs seaweedfs-master-openrc
```
The OpenRC subpackages are:
```text
seaweedfs-master-openrc -> seaweedfs.master
seaweedfs-volume-openrc -> seaweedfs.volume
seaweedfs-filer-openrc -> seaweedfs.filer
seaweedfs-s3-openrc -> seaweedfs.s3
seaweedfs-webdav-openrc -> seaweedfs.webdav
seaweedfs-sftp-openrc -> seaweedfs.sftp
seaweedfs-admin-openrc -> seaweedfs.admin
seaweedfs-worker-openrc -> seaweedfs.worker
```
Each service has matching defaults in `/etc/conf.d/seaweedfs.*`. Remote targets use distinct variables such as `SEAWEEDFS_MASTER`, `SEAWEEDFS_FILER`, and `SEAWEEDFS_ADMIN`; `SEAWEEDFS_OPTS` remains available for additional flags. Enable only the roles needed for a node, for example:
```sh
rc-update add seaweedfs.master default
rc-service seaweedfs.master start
```
If the repository key is not copied into `packages/local/x86_64/`, copy the public key from `.cache/abuild/*.rsa.pub` into `/etc/apk/keys/` in the test system before running `apk update`.
## Package inputs
The package lives in `packaging/alpine/local/seaweedfs/APKBUILD`. It repackages SeaweedFS `4.31` release binaries from GitHub and installs the `weed` binary. Generated scaffold configs are installed under `/etc/seaweedfs/`.

15
bin/weed Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env -S mise tool-stub
# SeaweedFS 4.31 release binary.
version = "4.31"
bin = "weed"
[platforms.linux-x64]
url = "https://github.com/seaweedfs/seaweedfs/releases/download/4.31/linux_amd64.tar.gz"
checksum = "sha256:81103c53453fddfa75d9d611cd3e61aa57e7a3099dc7a1de5977ab4b19211958"
size = 43223476
[platforms.linux-arm64]
url = "https://github.com/seaweedfs/seaweedfs/releases/download/4.31/linux_arm64.tar.gz"
checksum = "sha256:76ac1ffce87a2a710a3913282a19de6d9a3785f4a3586556858ec7b81a87d958"
size = 38036794

48
mise.toml Normal file
View File

@@ -0,0 +1,48 @@
[tools]
codex = "latest"
docker-cli = "latest"
shellcheck = "latest"
[tasks."apk:build"]
description = "Build the Alpine package in Docker for the default architecture"
run = "scripts/apk/build.sh build"
[tasks."apk:build-all"]
description = "Build the Alpine package in Docker for all configured architectures"
run = "scripts/apk/build.sh build-all"
[tasks."apk:build-x86_64"]
description = "Build the Alpine package in Docker for x86_64"
run = "scripts/apk/build.sh build x86_64"
[tasks."apk:build-aarch64"]
description = "Build the Alpine package in Docker for aarch64"
run = "scripts/apk/build.sh build aarch64"
[tasks."apk:checksum"]
description = "Refresh APKBUILD checksums in Docker"
run = "scripts/apk/build.sh checksum"
[tasks."apk:lint"]
description = "Run Alpine APKBUILD lint in Docker"
run = "scripts/apk/build.sh lint"
[tasks."apk:smoke"]
description = "Install-test built packages from the local repository in Docker"
run = "scripts/apk/smoke.sh"
[tasks."apk:packages"]
description = "List built Alpine packages and dependencies"
run = "scripts/apk/list-packages.sh"
[tasks."apk:publish-check"]
description = "Run lint, multi-arch build, package listing, and smoke test"
run = "scripts/apk/publish-check.sh"
[tasks."apk:shell"]
description = "Open an Alpine package build shell in Docker"
run = "scripts/apk/build.sh shell"
[tasks."apk:clean"]
description = "Remove generated Alpine package build outputs"
run = "scripts/apk/clean.sh"

View File

@@ -0,0 +1,103 @@
# Maintainer: Local Builder <local@example.invalid>
pkgname=seaweedfs
pkgver=4.31
pkgrel=0
pkgdesc="Distributed storage system for object storage, file systems, and Iceberg tables"
url="https://github.com/seaweedfs/seaweedfs"
arch="x86_64 aarch64"
license="Apache-2.0"
depends="ca-certificates"
install="$pkgname.pre-install"
subpackages="
$pkgname-admin-openrc:_openrc_admin:noarch
$pkgname-filer-openrc:_openrc_filer:noarch
$pkgname-master-openrc:_openrc_master:noarch
$pkgname-s3-openrc:_openrc_s3:noarch
$pkgname-sftp-openrc:_openrc_sftp:noarch
$pkgname-volume-openrc:_openrc_volume:noarch
$pkgname-webdav-openrc:_openrc_webdav:noarch
$pkgname-worker-openrc:_openrc_worker:noarch
"
options="!check !strip"
case "$CARCH" in
x86_64)
_archive_source="$pkgname-$pkgver-linux-amd64.tar.gz::https://github.com/seaweedfs/seaweedfs/releases/download/$pkgver/linux_amd64.tar.gz"
_archive_sha512="60c758d6d565d0cbc533e4e5677186a4700d48be12ebbd2bd1c6b4bcf38f687d0ab1f66e0953c8a27c663549cdbae73e5fd7eda74bb422c20e10e0d0850b5ead seaweedfs-4.31-linux-amd64.tar.gz"
;;
aarch64)
_archive_source="$pkgname-$pkgver-linux-arm64.tar.gz::https://github.com/seaweedfs/seaweedfs/releases/download/$pkgver/linux_arm64.tar.gz"
_archive_sha512="93c7539fe15a0aa6192d26ac2360003961f0643e302d910179d02341b10a7243df776294fb1b0134ec2a2a9596b1abc3ce9ab701f9bc3d78f2b965ea464a4b18 seaweedfs-4.31-linux-arm64.tar.gz"
;;
esac
source="
$_archive_source
config/credential.toml
config/filer.toml
config/master.toml
config/notification.toml
config/replication.toml
config/security.toml
config/shell.toml
openrc/seaweedfs.admin.confd
openrc/seaweedfs.filer.confd
openrc/seaweedfs.initd
openrc/seaweedfs.master.confd
openrc/seaweedfs.s3.confd
openrc/seaweedfs.sftp.confd
openrc/seaweedfs.volume.confd
openrc/seaweedfs.webdav.confd
openrc/seaweedfs.worker.confd
"
builddir="$srcdir"
package() {
install -Dm755 "$srcdir"/weed "$pkgdir"/usr/bin/weed
local _config
for _config in credential filer master notification replication security shell; do
install -Dm644 "$srcdir"/$_config.toml \
"$pkgdir"/etc/seaweedfs/$_config.toml
done
}
_openrc_service() {
local _service="$1"
pkgdesc="$pkgdesc (OpenRC $_service service)"
depends="$pkgname=$pkgver-r$pkgrel openrc"
install -Dm755 "$srcdir"/seaweedfs.initd \
"$subpkgdir"/etc/init.d/seaweedfs.$_service
install -Dm644 "$srcdir"/seaweedfs.$_service.confd \
"$subpkgdir"/etc/conf.d/seaweedfs.$_service
}
_openrc_admin() { _openrc_service admin; }
_openrc_filer() { _openrc_service filer; }
_openrc_master() { _openrc_service master; }
_openrc_s3() { _openrc_service s3; }
_openrc_sftp() { _openrc_service sftp; }
_openrc_volume() { _openrc_service volume; }
_openrc_webdav() { _openrc_service webdav; }
_openrc_worker() { _openrc_service worker; }
sha512sums="
$_archive_sha512
257ed55050782379ed5b70437f6316ca2d8862817e17f6af48e599f000277453a0dbbb5cfa16697ba3e82acc7597e7e3e0505a57a4d601c5d743a46df195832d credential.toml
73f980cdfd3b453f8b279ed5823bb9ccad6780520abe64a3b957f2780de2d31dd863f1abb22e1dbf5f261073040026f702df7ab8a1f6cd8f85774ab49d188e72 filer.toml
cfb31d44311169a23215b1ad5cabf577d085388f935b47890281c8160bd02c85ff8ff16f58ef1075e40f6085b69aedfc3c3d1ced9ca228e5d25d0b99fa5f3fc6 master.toml
4e3468a848c1593b291f4b08e1214c9ddc54363d32f73da3981ac7c132fc2dd642f3c9d3ea4a6c4dd6b84a81ec50d3ca67caf3367fa62759b9f54d081bfeb19a notification.toml
72fdc133ad640c56cf3eee2421c53ac908497192cb68122b80e0deff057de68caa83e11fee01be617c9fd0d7663611cca051ab91b043e3f549111dff77dede1c replication.toml
e5cc5d93d1e8eb95961a8150b70e2bef105994a659153eb183f6d70f78c017b4696c4882c6ac46301fcc34bcbafd74f25430484cadd8b48d38decd47dfaa1e56 security.toml
7a91ce9da79b92e5ef42d4915f56a010bceaaa6c96dcef1f7b1821ee208d381aaa88b9cf495248276132e66d3215927f589b7fabc4eae2c2cb195645f904fac7 shell.toml
82e2793bf483ffb5b0c8fbe38e6c9df75afe01f824f8414cafd91e4bed5c79c13dae1ca659070401d4968d16ed4b26455b0c6208f777705527bceb2e5f286988 seaweedfs.admin.confd
5ad952c37c62a770327bd70f7349fa677406606f0b9bf03fdc8f6c6804701f1e77710ab20d6153f9e0cfb549ec161d68a3316abab9c59837bbe3f91ee99e5ce0 seaweedfs.filer.confd
6c7ebdaf8f941868051a025fda36278bdf5e5438a2d478ca1cbe35064f933e7b07bf6d56bdd2268894f01c4e232e48f69099f793baeb2d6011063ac56a132fe4 seaweedfs.initd
57ba77519bb266f0117eaadae3af5ae6bc99bb653668ff526202f327df1c8d93973c0e19dae0c53a19e06389868043c04e53f16f01c851b77fe1387e92cc993b seaweedfs.master.confd
22a3ea6c83266a979ea7c848fc74b40fc0f2ade960b273fa11e63d4a72a1328bd688c9918be3e9234cfb19f889fd9c931388bb5c2863cea3500e534b8bd90282 seaweedfs.s3.confd
3e9c41bc61cdd9a4c53ece64d847eca26ab7d0ce0cc98e72462c3dbb225a40de10a02201362801015a9c629d77d5c0d54c7228e9d20111fb6f478b2267d2db98 seaweedfs.sftp.confd
262cc5132a70a43f9f154d24da16fe2f34f736c0bfdafd56cd341b7f34b48b82aa3a38a93d64206360c391e1167e2b665a3ade5c7eddfe4b322253521629f4e0 seaweedfs.volume.confd
84a9caa8f5203a31f2f96ef812dca8e4f7597f8f06ed3f55db38daff167448b0332564a421dcb356405d761c7a9e815656208d431e84b8106951554ab7ef2142 seaweedfs.webdav.confd
b586dfbdbcf17591366a7a62c4da6f2b13e1bd5dfb5b066beea114d01057eeb3d81172814655d812c48e02336277b3d70fa9f9550923cfe616e9dd8162661f51 seaweedfs.worker.confd
"

View File

@@ -0,0 +1,47 @@
# Put this file to one of the location, with descending priority
# ./credential.toml
# $HOME/.seaweedfs/credential.toml
# /etc/seaweedfs/credential.toml
# this file is read by S3 API and IAM API servers
# Choose one of the credential stores below
# Only one store can be enabled at a time
# Filer-based credential store (default, uses existing filer storage)
[credential.filer_etc]
enabled = true
# filer address and grpc_dial_option will be automatically configured by the server
# PostgreSQL credential store (recommended for multi-node deployments)
[credential.postgres]
enabled = false
hostname = "localhost"
port = 5432
username = "seaweedfs"
password = "your_password"
database = "seaweedfs"
schema = "public"
sslmode = "disable"
# Optional: table name prefix (default: "sw_")
table_prefix = "sw_"
# Connection pool settings
connection_max_idle = 10
connection_max_open = 100
connection_max_lifetime_seconds = 3600
# Memory credential store (for testing only, data is lost on restart)
[credential.memory]
enabled = false
# Environment variable overrides:
# Any configuration value can be overridden by environment variables
# Rules:
# * Prefix with "WEED_CREDENTIAL_"
# * Convert to uppercase
# * Replace '.' with '_'
#
# Examples:
# export WEED_CREDENTIAL_POSTGRES_PASSWORD=secret
# export WEED_CREDENTIAL_POSTGRES_HOSTNAME=db.example.com
# export WEED_CREDENTIAL_FILER_ETC_ENABLED=true

View File

@@ -0,0 +1,453 @@
# A sample TOML config file for SeaweedFS filer store
# Used with "weed filer" or "weed server -filer"
# Put this file to one of the location, with descending priority
# ./filer.toml
# $HOME/.seaweedfs/filer.toml
# /etc/seaweedfs/filer.toml
####################################################
# Customizable filer server options
####################################################
[filer.options]
# with http DELETE, by default the filer would check whether a folder is empty.
# recursive_delete will delete all sub folders and files, similar to "rm -Rf"
recursive_delete = false
#max_file_name_length = 255
# for S3: how long to wait before deleting an empty folder.
# increase this if using tools like Spark that create temporary directories.
#s3.empty_folder_cleanup_delay = "2m"
####################################################
# The following are filer store options
####################################################
[leveldb2]
# local on disk, mostly for simple single-machine setup, fairly scalable
# faster than previous leveldb, recommended.
enabled = true
dir = "./filerldb2" # directory to store level db files
[leveldb3]
# similar to leveldb2.
# each bucket has its own meta store.
enabled = false
dir = "./filerldb3" # directory to store level db files
[rocksdb]
# local on disk, similar to leveldb
# since it is using a C wrapper, you need to install rocksdb and build it by yourself
enabled = false
dir = "./filerrdb" # directory to store rocksdb files
[sqlite]
# local on disk, similar to leveldb
enabled = false
dbFile = "./filer.db" # sqlite db file
[mysql] # or memsql, tidb
# CREATE TABLE IF NOT EXISTS `filemeta` (
# `dirhash` BIGINT NOT NULL COMMENT 'first 64 bits of MD5 hash value of directory field',
# `name` VARCHAR(766) NOT NULL COMMENT 'directory or file name',
# `directory` TEXT NOT NULL COMMENT 'full path to parent directory',
# `meta` LONGBLOB,
# PRIMARY KEY (`dirhash`, `name`)
# ) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
enabled = false
# dsn will take priority over "hostname, port, username, password, database".
# [username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]
dsn = "root@tcp(localhost:3306)/seaweedfs?collation=utf8mb4_bin"
enable_tls = false
ca_crt = "" # path to CA cert (PEM) — optional; if empty, the system trust store is used
client_crt = "" # path to client cert (PEM) — only when server requires mTLS; must be set together with client_key
client_key = "" # path to client key (PEM) — only when server requires mTLS; must be set together with client_crt
tls_insecure_skip_verify = false # skip server cert verification (use only for testing or with self-signed certs)
tls_server_name = "" # override SNI / cert hostname; leave empty to use `hostname` above
hostname = "localhost"
port = 3306
username = "root"
password = ""
database = "" # create or use an existing database
connection_max_idle = 10
connection_max_open = 50
connection_max_lifetime_seconds = 300
interpolateParams = false
# if insert/upsert failing, you can disable upsert or update query syntax to match your RDBMS syntax:
enableUpsert = true
# Default uses the row-alias form (`AS new`) added in MySQL 8.0.19 and is the
# preferred syntax there. For MariaDB (any version) and MySQL 5.7, override
# with the form below — MariaDB does not support row aliases in
# INSERT ... ON DUPLICATE KEY UPDATE:
# upsertQuery = """INSERT INTO `%s` (`dirhash`,`name`,`directory`,`meta`) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE `meta` = VALUES(`meta`)"""
upsertQuery = """INSERT INTO `%s` (`dirhash`,`name`,`directory`,`meta`) VALUES (?,?,?,?) AS `new` ON DUPLICATE KEY UPDATE `meta` = `new`.`meta`"""
[mysql2] # or memsql, tidb
enabled = false
createTable = """
CREATE TABLE IF NOT EXISTS `%s` (
`dirhash` BIGINT NOT NULL,
`name` VARCHAR(766) NOT NULL,
`directory` TEXT NOT NULL,
`meta` LONGBLOB,
PRIMARY KEY (`dirhash`, `name`)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
"""
hostname = "localhost"
port = 3306
username = "root"
password = ""
database = "" # create or use an existing database
connection_max_idle = 10
connection_max_open = 50
connection_max_lifetime_seconds = 300
interpolateParams = false
# if insert/upsert failing, you can disable upsert or update query syntax to match your RDBMS syntax:
enableUpsert = true
upsertQuery = """INSERT INTO `%s` (`dirhash`,`name`,`directory`,`meta`) VALUES (?,?,?,?) AS `new` ON DUPLICATE KEY UPDATE `meta` = `new`.`meta`"""
[postgres] # or cockroachdb, YugabyteDB
# CREATE TABLE IF NOT EXISTS filemeta (
# dirhash BIGINT,
# name VARCHAR(65535),
# directory VARCHAR(65535),
# meta bytea,
# PRIMARY KEY (dirhash, name)
# );
enabled = false
hostname = "localhost"
port = 5432
username = "postgres"
password = ""
database = "postgres" # create or use an existing database
schema = ""
sslmode = "disable"
# SSL certificate options for secure connections
# For sslmode=verify-full, uncomment and configure the following:
# sslcert = "/path/to/client.crt" # client certificate file
# sslkey = "/path/to/client.key" # client private key file
# sslrootcert = "/path/to/ca.crt" # CA certificate file
# sslcrl = "/path/to/client.crl" # Certificate Revocation List (CRL) (optional)
connection_max_idle = 10
connection_max_open = 50
connection_max_lifetime_seconds = 300
# Set to true when using PgBouncer connection pooler
pgbouncer_compatible = false
# if insert/upsert failing, you can disable upsert or update query syntax to match your RDBMS syntax:
enableUpsert = true
upsertQuery = """
INSERT INTO "%[1]s" (dirhash, name, directory, meta)
VALUES($1, $2, $3, $4)
ON CONFLICT (dirhash, name) DO UPDATE SET
directory=EXCLUDED.directory,
meta=EXCLUDED.meta
"""
[postgres2]
enabled = false
createTable = """
CREATE TABLE IF NOT EXISTS "%s" (
dirhash BIGINT,
name VARCHAR(65535),
directory VARCHAR(65535),
meta bytea,
PRIMARY KEY (dirhash, name)
);
"""
hostname = "localhost"
port = 5432
username = "postgres"
password = ""
database = "postgres" # create or use an existing database
schema = ""
sslmode = "disable"
# SSL certificate options for secure connections
# For sslmode=verify-full, uncomment and configure the following:
# sslcert = "/path/to/client.crt" # client certificate file
# sslkey = "/path/to/client.key" # client private key file
# sslrootcert = "/path/to/ca.crt" # CA certificate file
# sslcrl = "/path/to/client.crl" # Certificate Revocation List (CRL) (optional)
connection_max_idle = 10
connection_max_open = 50
connection_max_lifetime_seconds = 300
# Set to true when using PgBouncer connection pooler
pgbouncer_compatible = false
# if insert/upsert failing, you can disable upsert or update query syntax to match your RDBMS syntax:
enableUpsert = true
upsertQuery = """
INSERT INTO "%[1]s" (dirhash, name, directory, meta)
VALUES($1, $2, $3, $4)
ON CONFLICT (dirhash, name) DO UPDATE SET
directory=EXCLUDED.directory,
meta=EXCLUDED.meta
"""
[cassandra2]
# CREATE TABLE filemeta (
# dirhash bigint,
# directory varchar,
# name varchar,
# meta blob,
# PRIMARY KEY ((dirhash, directory), name)
# ) WITH CLUSTERING ORDER BY (name ASC);
enabled = false
keyspace = "seaweedfs"
hosts = [
"localhost:9042",
]
username = ""
password = ""
# Set the CA certificate path
ssl_ca_path = ""
# Set the client certificate path
ssl_cert_path = ""
# Set the client private key path
ssl_key_path = ""
# Check host name in the certificate
ssl_enable_host_verification = true
# This changes the data layout. Only add new directories. Removing/Updating will cause data loss.
superLargeDirectories = []
# Name of the datacenter local to this filer, used as host selection fallback.
localDC = ""
# Gocql connection timeout, default: 600ms
connection_timeout_millisecond = 600
[hbase]
enabled = false
zkquorum = ""
table = "seaweedfs"
[redis2]
enabled = false
address = "localhost:6379"
username = ""
password = ""
database = 0
# prefix for filer redis keys
keyPrefix = ""
enable_tls = false
ca_cert_path = ""
client_cert_path = ""
client_key_path = ""
# This changes the data layout. Only add new directories. Removing/Updating will cause data loss.
superLargeDirectories = []
[redis2_sentinel]
enabled = false
addresses = ["172.22.12.7:26379","172.22.12.8:26379","172.22.12.9:26379"]
masterName = "master"
username = ""
password = ""
database = 0
# prefix for filer redis keys
keyPrefix = ""
enable_tls = false
ca_cert_path = ""
client_cert_path = ""
client_key_path = ""
[redis_cluster2]
enabled = false
addresses = [
"localhost:30001",
"localhost:30002",
"localhost:30003",
"localhost:30004",
"localhost:30005",
"localhost:30006",
]
username = ""
password = ""
# prefix for filer redis keys
keyPrefix = ""
enable_tls = false
ca_cert_path = ""
client_cert_path = ""
client_key_path = ""
# allows reads from slave servers or the master, but all writes still go to the master
readOnly = false
# automatically use the closest Redis server for reads
routeByLatency = false
# This changes the data layout. Only add new directories. Removing/Updating will cause data loss.
superLargeDirectories = []
# The following lua redis stores uses lua to ensure atomicity
[redis_lua]
enabled = false
address = "localhost:6379"
username = ""
password = ""
database = 0
# prefix for filer redis keys
keyPrefix = ""
enable_tls = false
ca_cert_path = ""
client_cert_path = ""
client_key_path = ""
# This changes the data layout. Only add new directories. Removing/Updating will cause data loss.
superLargeDirectories = []
[redis_lua_sentinel]
enabled = false
addresses = ["172.22.12.7:26379","172.22.12.8:26379","172.22.12.9:26379"]
masterName = "master"
username = ""
password = ""
database = 0
# prefix for filer redis keys
keyPrefix = ""
enable_tls = false
ca_cert_path = ""
client_cert_path = ""
client_key_path = ""
[redis_lua_cluster]
enabled = false
addresses = [
"localhost:30001",
"localhost:30002",
"localhost:30003",
"localhost:30004",
"localhost:30005",
"localhost:30006",
]
username = ""
password = ""
# prefix for filer redis keys
keyPrefix = ""
enable_tls = false
ca_cert_path = ""
client_cert_path = ""
client_key_path = ""
# allows reads from slave servers or the master, but all writes still go to the master
readOnly = false
# automatically use the closest Redis server for reads
routeByLatency = false
# This changes the data layout. Only add new directories. Removing/Updating will cause data loss.
superLargeDirectories = []
[etcd]
enabled = false
servers = "localhost:2379"
username = ""
password = ""
key_prefix = "seaweedfs."
timeout = "3s"
# Set the CA certificate path
tls_ca_file=""
# Set the client certificate path
tls_client_crt_file=""
# Set the client private key path
tls_client_key_file=""
[mongodb]
enabled = false
uri = "mongodb://localhost:27017"
username = ""
password = ""
ssl = false
ssl_ca_file = ""
ssl_cert_file = ""
ssl_key_file = ""
insecure_skip_verify = false
option_pool_size = 0
database = "seaweedfs"
[elastic7]
enabled = false
servers = [
"http://localhost1:9200",
"http://localhost2:9200",
"http://localhost3:9200",
]
username = ""
password = ""
sniff_enabled = false
healthcheck_enabled = false
# increase the value is recommend, be sure the value in Elastic is greater or equal here
index.max_result_window = 10000
[arangodb] # in development dont use it
enabled = false
db_name = "seaweedfs"
servers=["http://localhost:8529"] # list of servers to connect to
# only basic auth supported for now
username=""
password=""
# skip tls cert validation
insecure_skip_verify = true
[ydb] # https://ydb.tech/
enabled = false
dsn = "grpc://localhost:2136?database=/local"
prefix = "seaweedfs"
useBucketPrefix = true # Fast Bucket Deletion
poolSizeLimit = 50
dialTimeOut = 10
# Authenticate produced with one of next environment variables:
# YDB_SERVICE_ACCOUNT_KEY_FILE_CREDENTIALS=<path/to/sa_key_file> — used service account key file by path
# YDB_ANONYMOUS_CREDENTIALS="1" — used for authenticate with anonymous access. Anonymous access needs for connect to testing YDB installation
# YDB_METADATA_CREDENTIALS="1" — used metadata service for authenticate to YDB from yandex cloud virtual machine or from yandex function
# YDB_ACCESS_TOKEN_CREDENTIALS=<access_token> — used for authenticate to YDB with short-life access token. For example, access token may be IAM token
##########################
##########################
# To add path-specific filer store:
#
# 1. Add a name following the store type separated by a dot ".". E.g., cassandra2.tmp
# 2. Add a location configuration. E.g., location = "/tmp/"
# 3. Copy and customize all other configurations.
# Make sure they are not the same if using the same store type!
# 4. Set enabled to true
#
# The following is just using redis as an example
##########################
[redis2.tmp]
enabled = false
location = "/tmp/"
address = "localhost:6379"
username = ""
password = ""
database = 1
keyPrefix = ""
[tikv]
enabled = false
# If you have many pd address, use ',' split then:
# pdaddrs = "pdhost1:2379, pdhost2:2379, pdhost3:2379"
pdaddrs = "localhost:2379"
# prefix for filer TiKV keys, useful for sharing a TiKV cluster with multiple seaweedfs clusters
keyPrefix = ""
# Enable 1PC
enable_1pc = false
# batch delete count, default 10000 in code
#batchdelete_count = 20000
# Set the CA certificate path
ca_path=""
# Set the certificate path
cert_path=""
# Set the private key path
key_path=""
# The name list used to verify the cn name
verify_cn=""
[foundationdb]
# FoundationDB provides ACID transactions and horizontal scalability.
# Requires: go build -tags foundationdb
enabled = false
cluster_file = "/etc/foundationdb/fdb.cluster"
# api_version = 740
# timeout = "5s"
# directory_prefix = "seaweedfs"
# For bulk ingestion, enable batching: batch_enabled = true
[tarantool]
address = "localhost:3301"
user = "guest"
password = ""
timeout = "5s"
maxReconnects = 1000

View File

@@ -0,0 +1,63 @@
# Put this file to one of the location, with descending priority
# ./master.toml
# $HOME/.seaweedfs/master.toml
# /etc/seaweedfs/master.toml
# this file is read by master
[master.maintenance]
# periodically run these scripts are the same as running them from 'weed shell'
# Scripts are skipped while an admin server is connected.
scripts = """
lock
ec.encode -fullPercent=95 -quietFor=1h
ec.rebuild -apply
ec.balance -apply
fs.log.purge -daysAgo=7
volume.deleteEmpty -quietFor=24h -apply
volume.balance -apply
volume.fix.replication -apply
s3.clean.uploads -timeAgo=24h
unlock
"""
sleep_minutes = 17 # sleep minutes between each script execution
[master.sequencer]
type = "raft" # Choose [raft|snowflake] type for storing the file id sequence
# when sequencer.type = snowflake, the snowflake id must be different from other masters
sequencer_snowflake_id = 0 # any number between 1~1023
# configurations for tiered cloud storage
# old volumes are transparently moved to cloud for cost efficiency
[storage.backend]
[storage.backend.s3.default]
enabled = false
aws_access_key_id = "" # if empty, loads from the shared credentials file (~/.aws/credentials).
aws_secret_access_key = "" # if empty, loads from the shared credentials file (~/.aws/credentials).
region = "us-east-2"
bucket = "your_bucket_name" # an existing bucket
endpoint = ""
storage_class = "STANDARD_IA"
# create this number of logical volumes if no more writable volumes
# count_x means how many copies of data.
# e.g.:
# 000 has only one copy, copy_1
# 010 and 001 has two copies, copy_2
# 011 has only 3 copies, copy_3
[master.volume_growth]
copy_1 = 7 # create 1 x 7 = 7 actual volumes
copy_2 = 6 # create 2 x 6 = 12 actual volumes
copy_3 = 3 # create 3 x 3 = 9 actual volumes
copy_other = 1 # create n x 1 = n actual volumes
threshold = 0.9 # create threshold
disable = false # disables volume growth if true
# configuration flags for replication
[master.replication]
# any replication counts should be considered minimums. If you specify 010 and
# have 3 different racks, that's still considered writable. Writes will still
# try to replicate to all available volumes. You should only use this option
# if you are doing your own replication or periodic sync of volumes.
treat_replication_as_minimums = false

View File

@@ -0,0 +1,82 @@
# A sample TOML config file for SeaweedFS filer store
# Used by both "weed filer" or "weed server -filer" and "weed filer.replicate"
# Put this file to one of the location, with descending priority
# ./notification.toml
# $HOME/.seaweedfs/notification.toml
# /etc/seaweedfs/notification.toml
####################################################
# notification
# send and receive filer updates for each file to an external message queue
####################################################
[notification.log]
# this is only for debugging purpose and does not work with "weed filer.replicate"
enabled = false
[notification.kafka]
enabled = false
hosts = [
"localhost:9092"
]
topic = "seaweedfs_filer"
offsetFile = "./last.offset"
offsetSaveIntervalSeconds = 10
# SASL Authentication
sasl_enabled = false
sasl_mechanism = "PLAIN" # PLAIN, SCRAM-SHA-256, SCRAM-SHA-512
sasl_username = ""
sasl_password = ""
# TLS/SSL
tls_enabled = false
tls_ca_cert = "" # path to CA certificate PEM file
tls_client_cert = "" # path to client certificate PEM file (for mTLS)
tls_client_key = "" # path to client private key PEM file (for mTLS)
tls_insecure_skip_verify = false
[notification.aws_sqs]
# experimental, let me know if it works
enabled = false
aws_access_key_id = "" # if empty, loads from the shared credentials file (~/.aws/credentials).
aws_secret_access_key = "" # if empty, loads from the shared credentials file (~/.aws/credentials).
region = "us-east-2"
sqs_queue_name = "my_filer_queue" # an existing queue name
[notification.google_pub_sub]
# read credentials doc at https://cloud.google.com/docs/authentication/getting-started
enabled = false
google_application_credentials = "/path/to/x.json" # path to json credential file
project_id = "" # an existing project id
topic = "seaweedfs_filer_topic" # a topic, auto created if does not exists
[notification.gocdk_pub_sub]
# The Go Cloud Development Kit (https://gocloud.dev).
# PubSub API (https://godoc.org/gocloud.dev/pubsub).
# Supports AWS SNS/SQS, Azure Service Bus, Google PubSub, NATS and RabbitMQ.
enabled = false
# This URL will Dial the RabbitMQ server at the URL in the environment
# variable RABBIT_SERVER_URL and open the exchange "myexchange".
# The exchange must have already been created by some other means, like
# the RabbitMQ management plugin. Сreate myexchange of type fanout and myqueue then
# create binding myexchange => myqueue
topic_url = "rabbit://myexchange"
sub_url = "rabbit://myqueue"
[notification.webhook]
# Send file system events to HTTP webhook endpoints (push model)
# BEST FOR: Low to moderate traffic (< 100 events/second sustained)
# FOR HIGH TRAFFIC: Consider using Kafka, SQS, or pull-based event logs instead
# Documentation: https://github.com/seaweedfs/seaweedfs/wiki/Filer-Notification-Webhook
enabled = false
endpoint = "https://your-server.com/webhook" # required: HTTP endpoint URL
bearer_token = "" # optional: bearer token for authentication
timeout_seconds = 10 # optional: HTTP timeout (default: 10, range: 1-300)
max_retries = 3 # optional: retry attempts (default: 3, range: 0-10)
backoff_seconds = 3 # optional: initial backoff delay (default: 3, range: 1-60)
max_backoff_seconds = 30 # optional: max backoff delay (default: 30, range: backoff_seconds-300)
workers = 5 # optional: concurrent workers (default: 5, range: 1-100)
buffer_size = 10000 # optional: event buffer size (default: 10000, range: 100-1000000)
# event_types = ["create", "update", "delete", "rename"] # optional: filter by event types (default: all)
# path_prefixes = ["/important", "/data"] # optional: filter by path prefixes (default: all)

View File

@@ -0,0 +1,74 @@
# A sample TOML config file for replicating SeaweedFS filer
# Used with "weed filer.backup"
# Using with "weed filer.replicate" is deprecated.
# Put this file to one of the location, with descending priority
# ./replication.toml
# $HOME/.seaweedfs/replication.toml
# /etc/seaweedfs/replication.toml
[source.filer] # deprecated. Only useful with "weed filer.replicate"
enabled = true
grpcAddress = "localhost:18888"
# all files under this directory tree are replicated.
# this is not a directory on your hard drive, but on your filer.
# i.e., all files with this "prefix" are sent to notification message queue.
directory = "/buckets"
# files from the directory separated by space are excluded from sending notifications
excludeDirectories = "/buckets/tmp"
[sink.local]
enabled = false
directory = "/data"
# all replicated files are under modified time as yyyy-mm-dd directories
# so each date directory contains all new and updated files.
is_incremental = false
[sink.filer]
enabled = false
grpcAddress = "localhost:18888"
# all replicated files are under this directory tree
# this is not a directory on your hard drive, but on your filer.
# i.e., all received files will be "prefixed" to this directory.
directory = "/backup"
replication = ""
collection = ""
ttlSec = 0
is_incremental = false
[sink.s3]
# read credentials doc at https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/sessions.html
# default loads credentials from the shared credentials file (~/.aws/credentials).
enabled = false
aws_access_key_id = "" # if empty, loads from the shared credentials file (~/.aws/credentials).
aws_secret_access_key = "" # if empty, loads from the shared credentials file (~/.aws/credentials).
region = "us-east-2"
bucket = "your_bucket_name" # an existing bucket
directory = "/" # destination directory
endpoint = ""
is_incremental = false
[sink.google_cloud_storage]
# read credentials doc at https://cloud.google.com/docs/authentication/getting-started
enabled = false
google_application_credentials = "/path/to/x.json" # path to json credential file
bucket = "your_bucket_seaweedfs" # an existing bucket
directory = "/" # destination directory
is_incremental = false
[sink.azure]
# experimental, let me know if it works
enabled = false
account_name = ""
account_key = ""
container = "mycontainer" # an existing container
directory = "/" # destination directory
is_incremental = false
[sink.backblaze]
enabled = false
b2_account_id = ""
b2_master_application_key = ""
b2_region = ""
bucket = "mybucket" # an existing bucket
directory = "/" # destination directory
is_incremental = false

View File

@@ -0,0 +1,204 @@
# Put this file to one of the location, with descending priority
# ./security.toml
# $HOME/.seaweedfs/security.toml
# /etc/seaweedfs/security.toml
# this file is read by master, volume server, filer, and worker
# comma separated origins allowed to make requests to the filer and s3 gateway.
# enter in this format: https://domain.com, or http://localhost:port
[cors.allowed_origins]
values = "*"
# this jwt signing key is read by master and volume server, and it is used for write operations:
# - the Master server generates the JWT, which can be used to write a certain file on a volume server
# - the Volume server validates the JWT on writing
# the jwt defaults to expire after 10 seconds.
[jwt.signing]
key = ""
expires_after_seconds = 10 # seconds
# by default, if the signing key above is set, the Volume UI over HTTP is disabled.
# by setting ui.access to true, you can re-enable the Volume UI. Despite
# some information leakage (as the UI is not authenticated), this should not
# pose a security risk.
[access]
ui = false
# by default the filer UI is enabled. This can be a security risk if the filer is exposed to the public
# and the JWT for reads is not set. If you don't want the public to have access to the objects in your
# storage, and you haven't set the JWT for reads it is wise to disable access to directory metadata.
# This disables access to the Filer UI, and will no longer return directory metadata in GET requests.
[filer.expose_directory_metadata]
enabled = true
# this jwt signing key is read by master and volume server, and it is used for read operations:
# - the Master server generates the JWT, which can be used to read a certain file on a volume server
# - the Volume server validates the JWT on reading
# NOTE: jwt for read is only supported with master+volume setup. Filer does not support this mode.
[jwt.signing.read]
key = ""
expires_after_seconds = 10 # seconds
# If this JWT key is configured, Filer only accepts writes over HTTP if they are signed with this JWT:
# - f.e. the S3 API Shim generates the JWT
# - the Filer server validates the JWT on writing
# NOTE: This key is ALSO used as a fallback signing key for S3 STS if s3.iam.config does not specify a signingKey.
# NOTE: This key also gates the filer IAM gRPC service (CreateUser, PutPolicy,
# CreateAccessKey, ...). When set, every IAM RPC must carry a Bearer
# token signed with this key in its "authorization" gRPC metadata; mint
# such a token with security.GenJwtForFilerAdmin. When empty, the IAM
# gRPC service runs unauthenticated, like the rest of the filer's gRPC
# surface — set the key on both filer and admin if the gRPC port is
# reachable beyond a trusted network.
# the jwt defaults to expire after 10 seconds.
[jwt.filer_signing]
key = ""
expires_after_seconds = 10 # seconds
# If this JWT key is configured, Filer only accepts reads over HTTP if they are signed with this JWT:
# - f.e. the S3 API Shim generates the JWT
# - the Filer server validates the JWT on reading
# the jwt defaults to expire after 10 seconds.
[jwt.filer_signing.read]
key = ""
expires_after_seconds = 10 # seconds
# gRPC mTLS configuration
# All gRPC TLS authentications are mutual (mTLS)
# The values for ca, cert, and key are paths to the certificate/key files
# The host name is not checked, so the certificate files can be shared
[grpc]
ca = ""
# Set wildcard domain for enable TLS authentication by common names
allowed_wildcard_domain = "" # .mycompany.com
# Volume server gRPC options (server-side)
# Enables mTLS for incoming gRPC connections to volume server
[grpc.volume]
cert = ""
key = ""
allowed_commonNames = "" # comma-separated SSL certificate common names
# Master server gRPC options (server-side)
# Enables mTLS for incoming gRPC connections to master server
[grpc.master]
cert = ""
key = ""
allowed_commonNames = "" # comma-separated SSL certificate common names
# Filer server gRPC options (server-side)
# Enables mTLS for incoming gRPC connections to filer server
[grpc.filer]
cert = ""
key = ""
allowed_commonNames = "" # comma-separated SSL certificate common names
# S3 server gRPC options (server-side)
# Enables mTLS for incoming gRPC connections to S3 server
[grpc.s3]
cert = ""
key = ""
allowed_commonNames = "" # comma-separated SSL certificate common names
[grpc.msg_broker]
cert = ""
key = ""
allowed_commonNames = "" # comma-separated SSL certificate common names
[grpc.msg_agent]
cert = ""
key = ""
allowed_commonNames = "" # comma-separated SSL certificate common names
[grpc.admin]
cert = ""
key = ""
allowed_commonNames = "" # comma-separated SSL certificate common names
[grpc.worker]
cert = ""
key = ""
allowed_commonNames = "" # comma-separated SSL certificate common names
[grpc.mq]
cert = ""
key = ""
allowed_commonNames = "" # comma-separated SSL certificate common names
# gRPC client configuration for outgoing gRPC connections
# Used by clients (S3, mount, backup, benchmark, filer.copy, filer.replicate, upload, etc.)
# when connecting to any gRPC server (master, volume, filer)
[grpc.client]
cert = ""
key = ""
# HTTPS client configuration for outgoing HTTP connections
# Used by S3, mount, filer.copy, backup, and other clients when communicating with master/volume/filer
# Set enabled=true to use HTTPS instead of HTTP for data operations (separate from gRPC)
# If [https.filer] or [https.volume] are enabled on servers, clients must have [https.client] enabled=true
[https.client]
enabled = false # Set to true to enable HTTPS for all outgoing HTTP client connections
cert = "" # Client certificate for mTLS (optional if server doesn't require client cert)
key = "" # Client key for mTLS (optional if server doesn't require client cert)
ca = "" # CA certificate to verify server certificates (required when enabled=true)
insecure_skip_verify = false # Skip TLS certificate verification (NOT recommended for production)
# Volume server HTTPS options (server-side)
# Enables HTTPS for incoming HTTP connections to volume server
[https.volume]
cert = ""
key = ""
ca = ""
# Master server HTTPS options (server-side)
# Enables HTTPS for incoming HTTP connections to master server (web UI, HTTP API)
[https.master]
cert = ""
key = ""
ca = ""
# Filer server HTTPS options (server-side)
# Enables HTTPS for incoming HTTP connections to filer server (web UI, HTTP API)
[https.filer]
cert = ""
key = ""
ca = ""
# disable_tls_verify_client_cert = true|false (default: false)
# Admin server HTTPS options (server-side)
# Enables HTTPS for incoming HTTP connections to admin server
[https.admin]
cert = ""
key = ""
ca = ""
# Admin server authentication
# If password is set, users must login to access the admin interface
# These can be overridden by environment variables with WEED_ prefix:
# WEED_ADMIN_USER, WEED_ADMIN_PASSWORD
# WEED_ADMIN_READONLY_USER, WEED_ADMIN_READONLY_PASSWORD
[admin]
user = ""
password = ""
[admin.readonly]
user = ""
password = ""
# SSE-S3 server-side encryption key management
# These settings configure the Key Encryption Key (KEK) for S3 SSE-S3 encryption.
# Set exactly one of kek or key. If neither is set, SSE-S3 is disabled.
# Can also be set via env vars: WEED_S3_SSE_KEK, WEED_S3_SSE_KEY
[s3.sse]
# hex-encoded 256-bit key, same format as the legacy /etc/s3/sse_kek filer file.
# Use this to migrate from a filer-stored KEK: copy the value from /etc/s3/sse_kek.
# Generate a new one with: openssl rand -hex 32
kek = ""
# any secret string; a 256-bit key is derived automatically via HKDF-SHA256.
# Cannot be used while /etc/s3/sse_kek exists on the filer — delete it first.
key = ""
# white list. It's checking request ip address.
[guard]
white_list = ""

View File

@@ -0,0 +1,8 @@
[cluster]
default = "c1"
[cluster.c1]
master = "localhost:9333" # comma-separated master servers
[cluster.c2]
master = ""

View File

@@ -0,0 +1,4 @@
# Defaults for "weed admin".
SEAWEEDFS_MASTER="localhost:9333"
SEAWEEDFS_DATA_DIR="/var/lib/seaweedfs/admin"
SEAWEEDFS_OPTS=""

View File

@@ -0,0 +1,4 @@
# Defaults for "weed filer".
SEAWEEDFS_MASTER="localhost:9333"
SEAWEEDFS_DATA_DIR="/var/lib/seaweedfs/filer"
SEAWEEDFS_OPTS=""

View File

@@ -0,0 +1,86 @@
#!/sbin/openrc-run
service_name="${RC_SVCNAME#seaweedfs.}"
description="SeaweedFS ${service_name} service"
command="/usr/bin/weed"
command_user="${SEAWEEDFS_USER:-seaweedfs}:${SEAWEEDFS_GROUP:-seaweedfs}"
command_background="yes"
pidfile="/run/seaweedfs/${RC_SVCNAME}.pid"
output_log="${SEAWEEDFS_LOG:-/var/log/seaweedfs/${RC_SVCNAME}.log}"
error_log="${SEAWEEDFS_ERRLOG:-/var/log/seaweedfs/${RC_SVCNAME}.log}"
build_command_args() {
command_args="$service_name"
data_dir=""
cache_dir=""
case "$service_name" in
admin)
data_dir="${SEAWEEDFS_DATA_DIR:-/var/lib/seaweedfs/admin}"
command_args="$command_args -master=${SEAWEEDFS_MASTER:-localhost:9333} -dataDir=${data_dir}"
;;
filer)
data_dir="${SEAWEEDFS_DATA_DIR:-/var/lib/seaweedfs/filer}"
command_args="$command_args -master=${SEAWEEDFS_MASTER:-localhost:9333} -defaultStoreDir=${data_dir}"
;;
master)
data_dir="${SEAWEEDFS_DATA_DIR:-/var/lib/seaweedfs/master}"
command_args="$command_args -mdir=${data_dir} -peers=${SEAWEEDFS_PEERS:-none}"
;;
s3)
command_args="$command_args -filer=${SEAWEEDFS_FILER:-localhost:8888}"
;;
sftp)
command_args="$command_args -filer=${SEAWEEDFS_FILER:-localhost:8888}"
;;
volume)
data_dir="${SEAWEEDFS_DATA_DIR:-/var/lib/seaweedfs/volume}"
command_args="$command_args -dir=${data_dir} -max=${SEAWEEDFS_VOLUME_MAX:-0} -master=${SEAWEEDFS_MASTER:-localhost:9333}"
;;
webdav)
cache_dir="${SEAWEEDFS_CACHE_DIR:-/var/cache/seaweedfs/webdav}"
command_args="$command_args -filer=${SEAWEEDFS_FILER:-localhost:8888} -cacheDir=${cache_dir}"
;;
worker)
data_dir="${SEAWEEDFS_DATA_DIR:-/var/lib/seaweedfs/worker}"
command_args="$command_args -admin=${SEAWEEDFS_ADMIN:-localhost:23646} -workingDir=${data_dir}"
;;
esac
command_args="$command_args ${SEAWEEDFS_OPTS:-}"
}
build_command_args
depend() {
need net
case "$service_name" in
volume|filer|admin)
after seaweedfs.master
;;
s3|webdav|sftp)
after seaweedfs.filer
;;
worker)
after seaweedfs.admin
;;
esac
}
start_pre() {
checkpath -d -m 0755 -o "${SEAWEEDFS_USER:-seaweedfs}:${SEAWEEDFS_GROUP:-seaweedfs}" \
/run/seaweedfs \
/var/log/seaweedfs \
/var/lib/seaweedfs
if [ -n "$data_dir" ]; then
checkpath -d -m 0750 -o "${SEAWEEDFS_USER:-seaweedfs}:${SEAWEEDFS_GROUP:-seaweedfs}" \
"$data_dir"
fi
if [ -n "$cache_dir" ]; then
checkpath -d -m 0750 -o "${SEAWEEDFS_USER:-seaweedfs}:${SEAWEEDFS_GROUP:-seaweedfs}" \
"$cache_dir"
fi
}

View File

@@ -0,0 +1,4 @@
# Defaults for "weed master".
SEAWEEDFS_DATA_DIR="/var/lib/seaweedfs/master"
SEAWEEDFS_PEERS="none"
SEAWEEDFS_OPTS=""

View File

@@ -0,0 +1,3 @@
# Defaults for "weed s3".
SEAWEEDFS_FILER="localhost:8888"
SEAWEEDFS_OPTS=""

View File

@@ -0,0 +1,3 @@
# Defaults for "weed sftp".
SEAWEEDFS_FILER="localhost:8888"
SEAWEEDFS_OPTS=""

View File

@@ -0,0 +1,5 @@
# Defaults for "weed volume".
SEAWEEDFS_MASTER="localhost:9333"
SEAWEEDFS_DATA_DIR="/var/lib/seaweedfs/volume"
SEAWEEDFS_VOLUME_MAX="0"
SEAWEEDFS_OPTS=""

View File

@@ -0,0 +1,4 @@
# Defaults for "weed webdav".
SEAWEEDFS_FILER="localhost:8888"
SEAWEEDFS_CACHE_DIR="/var/cache/seaweedfs/webdav"
SEAWEEDFS_OPTS=""

View File

@@ -0,0 +1,4 @@
# Defaults for "weed worker".
SEAWEEDFS_ADMIN="localhost:23646"
SEAWEEDFS_DATA_DIR="/var/lib/seaweedfs/worker"
SEAWEEDFS_OPTS=""

View File

@@ -0,0 +1,7 @@
#!/bin/sh
addgroup -S seaweedfs 2>/dev/null
adduser -S -D -H -h /var/lib/seaweedfs -s /sbin/nologin \
-G seaweedfs -g seaweedfs seaweedfs 2>/dev/null
exit 0

32
scripts/apk/Dockerfile Normal file
View File

@@ -0,0 +1,32 @@
ARG ALPINE_VERSION=3.22
FROM alpine:${ALPINE_VERSION}
ARG BUILDER_UID=1000
ARG BUILDER_GID=1000
RUN apk add --no-cache \
abuild-rootbld \
alpine-sdk \
atools-apkbuild-lint \
bash \
ca-certificates \
doas \
git \
sudo
RUN addgroup -g "${BUILDER_GID}" builder \
&& adduser -D -u "${BUILDER_UID}" -G builder builder \
&& addgroup builder abuild \
&& addgroup builder wheel \
&& mkdir -p /var/cache/distfiles /home/builder/packages \
&& chgrp abuild /var/cache/distfiles /home/builder/packages \
&& chmod g+w /var/cache/distfiles /home/builder/packages \
&& printf 'permit nopass :wheel\n' > /etc/doas.d/wheel.conf \
&& printf '%%wheel ALL=(ALL) NOPASSWD: ALL\n' > /etc/sudoers.d/wheel
COPY scripts/apk/container-entrypoint.sh /usr/local/bin/alpine-package-entrypoint
RUN chmod +x /usr/local/bin/alpine-package-entrypoint
USER builder
WORKDIR /work
ENTRYPOINT ["/usr/local/bin/alpine-package-entrypoint"]

72
scripts/apk/build.sh Executable file
View File

@@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -euo pipefail
command_name="${1:-build}"
requested_arch="${2:-${ALPINE_ARCH:-x86_64}}"
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
alpine_version="${ALPINE_VERSION:-3.22}"
repo_name="${ALPINE_REPO_NAME:-local}"
build_platform="${ALPINE_BUILD_PLATFORM:-linux/amd64}"
validate_arch() {
case "$1" in
x86_64|aarch64) ;;
*) printf 'unsupported Alpine architecture: %s\n' "$1" >&2; return 2 ;;
esac
}
run_for_arch() {
local arch="$1"
local subcommand="$2"
local image_name
validate_arch "${arch}"
image_name="${ALPINE_APK_BUILDER_IMAGE:-seaweedfs-apk-builder:${arch}}"
docker build \
--platform "${build_platform}" \
--build-arg "ALPINE_VERSION=${alpine_version}" \
--build-arg "BUILDER_UID=$(id -u)" \
--build-arg "BUILDER_GID=$(id -g)" \
-f "${repo_root}/scripts/apk/Dockerfile" \
-t "${image_name}" \
"${repo_root}"
docker_args=(--rm --platform "${build_platform}")
if [[ -t 0 && -t 1 ]]; then
docker_args+=(-it)
fi
docker run "${docker_args[@]}" \
-e "ALPINE_ARCH=${arch}" \
-e "CARCH=${arch}" \
-e "ALPINE_REPO_NAME=${repo_name}" \
-e "PACKAGER=${PACKAGER:-Local Builder <local@example.invalid>}" \
-v "${repo_root}:/work" \
-v "${repo_root}/.cache/abuild:/home/builder/.abuild" \
-v "${repo_root}/.cache/apk-distfiles:/var/cache/distfiles" \
-v "${repo_root}/packages:/home/builder/packages" \
"${image_name}" \
"${subcommand}"
}
mkdir -p \
"${repo_root}/.cache/abuild" \
"${repo_root}/.cache/apk-distfiles" \
"${repo_root}/packages"
case "${command_name}" in
build-all)
for arch in ${ALPINE_ARCHES:-x86_64 aarch64}; do
run_for_arch "${arch}" build
done
;;
build|checksum|lint|shell)
run_for_arch "${requested_arch}" "${command_name}"
;;
*)
printf 'unknown command: %s\n' "${command_name}" >&2
printf 'usage: %s [build|build-all|checksum|lint|shell] [x86_64|aarch64]\n' "$0" >&2
exit 2
;;
esac

9
scripts/apk/clean.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
rm -rf \
"${repo_root}/packages" \
"${repo_root}/packaging/alpine/local/seaweedfs/pkg" \
"${repo_root}/packaging/alpine/local/seaweedfs/src"

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env bash
set -euo pipefail
command_name="${1:-build}"
package_dir="${APKBUILD_DIR:-/work/packaging/alpine/local/seaweedfs}"
repo_name="${ALPINE_REPO_NAME:-local}"
arch="${ALPINE_ARCH:-x86_64}"
export CARCH="${CARCH:-$arch}"
export PACKAGER="${PACKAGER:-Local Builder <local@example.invalid>}"
if [[ -n "${PACKAGER_PRIVKEY:-}" ]]; then
export PACKAGER_PRIVKEY
fi
git config --global --add safe.directory /work
mkdir -p /home/builder/.abuild /home/builder/packages "${package_dir}"
if [[ -n "${PACKAGER_PRIVKEY:-}" && ! -f "${PACKAGER_PRIVKEY}" ]]; then
abuild-keygen -a -n
elif ! compgen -G "/home/builder/.abuild/*.rsa" > /dev/null; then
abuild-keygen -a -n
fi
if [[ -z "${PACKAGER_PRIVKEY:-}" && -f /home/builder/.abuild/abuild.conf ]]; then
# shellcheck source=/dev/null
. /home/builder/.abuild/abuild.conf
fi
if [[ -n "${PACKAGER_PRIVKEY:-}" ]]; then
export PACKAGER_PRIVKEY
fi
if [[ -n "${PACKAGER_PRIVKEY:-}" && -f "${PACKAGER_PRIVKEY}.pub" ]]; then
doas cp "${PACKAGER_PRIVKEY}.pub" /etc/apk/keys/
elif compgen -G "/home/builder/.abuild/*.rsa.pub" > /dev/null; then
doas cp /home/builder/.abuild/*.rsa.pub /etc/apk/keys/
fi
case "${command_name}" in
build)
cd "${package_dir}"
abuild -r
mkdir -p "/home/builder/packages/${repo_name}/${arch}"
if [[ -n "${PACKAGER_PRIVKEY:-}" && -f "${PACKAGER_PRIVKEY}.pub" ]]; then
cp "${PACKAGER_PRIVKEY}.pub" "/home/builder/packages/${repo_name}/${arch}/"
elif compgen -G "/home/builder/.abuild/*.rsa.pub" > /dev/null; then
cp /home/builder/.abuild/*.rsa.pub "/home/builder/packages/${repo_name}/${arch}/"
fi
;;
checksum)
cd "${package_dir}"
abuild checksum
;;
lint)
cd "${package_dir}"
apkbuild-lint APKBUILD
;;
shell)
cd "${package_dir}"
exec bash
;;
*)
printf 'unknown command: %s\n' "${command_name}" >&2
printf 'usage: %s [build|checksum|lint|shell]\n' "$0" >&2
exit 2
;;
esac

36
scripts/apk/list-packages.sh Executable file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
repo_dir="${repo_root}/packages/local"
if [[ -n "${ALPINE_ARCH:-}" ]]; then
package_dirs=("${repo_dir}/${ALPINE_ARCH}")
else
package_dirs=("${repo_dir}"/*)
fi
if [[ ! -d "${repo_dir}" || ! -d "${package_dirs[0]}" ]]; then
printf 'missing local repository: packages/local\n' >&2
printf 'run: mise run apk:build-all\n' >&2
exit 1
fi
for package_dir in "${package_dirs[@]}"; do
[[ -d "${package_dir}" ]] || continue
shopt -s nullglob
apks=("${package_dir}"/*.apk)
shopt -u nullglob
if [[ "${#apks[@]}" -eq 0 ]]; then
continue
fi
printf '## %s\n' "$(basename "${package_dir}")"
for apk in "${apks[@]}"; do
printf '### %s\n' "$(basename "${apk}")"
tar -xOf "${apk}" .PKGINFO 2>/dev/null \
| sed -n '/^pkgname =/p;/^pkgver =/p;/^arch =/p;/^depend =/p;/^provides =/p;/^install_if =/p'
done
done

10
scripts/apk/publish-check.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "${repo_root}"
mise run apk:lint
mise run apk:build-all
mise run apk:packages
mise run apk:smoke

30
scripts/apk/smoke.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
alpine_version="${ALPINE_VERSION:-3.22}"
platform="${ALPINE_BUILD_PLATFORM:-linux/amd64}"
arch="${ALPINE_ARCH:-x86_64}"
packages="${SMOKE_PACKAGES:-seaweedfs seaweedfs-master-openrc seaweedfs-filer-openrc seaweedfs-worker-openrc}"
if [[ ! -d "${repo_root}/packages/local/${arch}" ]]; then
printf 'missing local repository: packages/local/%s\n' "${arch}" >&2
printf 'run: mise run apk:build-all\n' >&2
exit 1
fi
docker run --rm --platform "${platform}" \
-v "${repo_root}/packages/local:/repo:ro" \
"alpine:${alpine_version}" \
sh -lc "
set -e
cp /repo/${arch}/*.rsa.pub /etc/apk/keys/
echo /repo >> /etc/apk/repositories
apk update >/dev/null
apk add ${packages} >/dev/null
weed version
ls -1 /etc/seaweedfs
if ls /etc/init.d/seaweedfs.* >/dev/null 2>&1; then
ls -1 /etc/init.d/seaweedfs.* | sort
fi
"