commit ca2db8e206af09c81cfdfa8539e49442ba0cbcb9 Author: Joachim Schlöffel Date: Sun Jun 7 14:30:21 2026 +0200 Add SeaweedFS Alpine package build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5fcc504 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.cache/ +packages/ +packaging/alpine/*/*/pkg/ +packaging/alpine/*/*/src/ +packaging/alpine/*/*/*.apk +packaging/alpine/*/*/*.tar.* diff --git a/README.md b/README.md new file mode 100644 index 0000000..6123f1b --- /dev/null +++ b/README.md @@ -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/`. diff --git a/bin/weed b/bin/weed new file mode 100755 index 0000000..f8324e6 --- /dev/null +++ b/bin/weed @@ -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 diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..b5c9acd --- /dev/null +++ b/mise.toml @@ -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" diff --git a/packaging/alpine/local/seaweedfs/APKBUILD b/packaging/alpine/local/seaweedfs/APKBUILD new file mode 100644 index 0000000..e524d5f --- /dev/null +++ b/packaging/alpine/local/seaweedfs/APKBUILD @@ -0,0 +1,103 @@ +# Maintainer: Local Builder +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 +" diff --git a/packaging/alpine/local/seaweedfs/config/credential.toml b/packaging/alpine/local/seaweedfs/config/credential.toml new file mode 100644 index 0000000..d217786 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/config/credential.toml @@ -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 diff --git a/packaging/alpine/local/seaweedfs/config/filer.toml b/packaging/alpine/local/seaweedfs/config/filer.toml new file mode 100644 index 0000000..073acba --- /dev/null +++ b/packaging/alpine/local/seaweedfs/config/filer.toml @@ -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&...¶mN=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= — 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= — 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 + + diff --git a/packaging/alpine/local/seaweedfs/config/master.toml b/packaging/alpine/local/seaweedfs/config/master.toml new file mode 100644 index 0000000..5e85b72 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/config/master.toml @@ -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 diff --git a/packaging/alpine/local/seaweedfs/config/notification.toml b/packaging/alpine/local/seaweedfs/config/notification.toml new file mode 100644 index 0000000..ca82f2c --- /dev/null +++ b/packaging/alpine/local/seaweedfs/config/notification.toml @@ -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) diff --git a/packaging/alpine/local/seaweedfs/config/replication.toml b/packaging/alpine/local/seaweedfs/config/replication.toml new file mode 100644 index 0000000..b23a1ef --- /dev/null +++ b/packaging/alpine/local/seaweedfs/config/replication.toml @@ -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 diff --git a/packaging/alpine/local/seaweedfs/config/security.toml b/packaging/alpine/local/seaweedfs/config/security.toml new file mode 100644 index 0000000..860412d --- /dev/null +++ b/packaging/alpine/local/seaweedfs/config/security.toml @@ -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 = "" diff --git a/packaging/alpine/local/seaweedfs/config/shell.toml b/packaging/alpine/local/seaweedfs/config/shell.toml new file mode 100644 index 0000000..0213708 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/config/shell.toml @@ -0,0 +1,8 @@ +[cluster] +default = "c1" + +[cluster.c1] +master = "localhost:9333" # comma-separated master servers + +[cluster.c2] +master = "" diff --git a/packaging/alpine/local/seaweedfs/openrc/seaweedfs.admin.confd b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.admin.confd new file mode 100644 index 0000000..3883103 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.admin.confd @@ -0,0 +1,4 @@ +# Defaults for "weed admin". +SEAWEEDFS_MASTER="localhost:9333" +SEAWEEDFS_DATA_DIR="/var/lib/seaweedfs/admin" +SEAWEEDFS_OPTS="" diff --git a/packaging/alpine/local/seaweedfs/openrc/seaweedfs.filer.confd b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.filer.confd new file mode 100644 index 0000000..76fe352 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.filer.confd @@ -0,0 +1,4 @@ +# Defaults for "weed filer". +SEAWEEDFS_MASTER="localhost:9333" +SEAWEEDFS_DATA_DIR="/var/lib/seaweedfs/filer" +SEAWEEDFS_OPTS="" diff --git a/packaging/alpine/local/seaweedfs/openrc/seaweedfs.initd b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.initd new file mode 100644 index 0000000..350aa5d --- /dev/null +++ b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.initd @@ -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 +} diff --git a/packaging/alpine/local/seaweedfs/openrc/seaweedfs.master.confd b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.master.confd new file mode 100644 index 0000000..a92eaa9 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.master.confd @@ -0,0 +1,4 @@ +# Defaults for "weed master". +SEAWEEDFS_DATA_DIR="/var/lib/seaweedfs/master" +SEAWEEDFS_PEERS="none" +SEAWEEDFS_OPTS="" diff --git a/packaging/alpine/local/seaweedfs/openrc/seaweedfs.s3.confd b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.s3.confd new file mode 100644 index 0000000..ac1a594 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.s3.confd @@ -0,0 +1,3 @@ +# Defaults for "weed s3". +SEAWEEDFS_FILER="localhost:8888" +SEAWEEDFS_OPTS="" diff --git a/packaging/alpine/local/seaweedfs/openrc/seaweedfs.sftp.confd b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.sftp.confd new file mode 100644 index 0000000..46cc232 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.sftp.confd @@ -0,0 +1,3 @@ +# Defaults for "weed sftp". +SEAWEEDFS_FILER="localhost:8888" +SEAWEEDFS_OPTS="" diff --git a/packaging/alpine/local/seaweedfs/openrc/seaweedfs.volume.confd b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.volume.confd new file mode 100644 index 0000000..bbd56df --- /dev/null +++ b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.volume.confd @@ -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="" diff --git a/packaging/alpine/local/seaweedfs/openrc/seaweedfs.webdav.confd b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.webdav.confd new file mode 100644 index 0000000..ae9ea85 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.webdav.confd @@ -0,0 +1,4 @@ +# Defaults for "weed webdav". +SEAWEEDFS_FILER="localhost:8888" +SEAWEEDFS_CACHE_DIR="/var/cache/seaweedfs/webdav" +SEAWEEDFS_OPTS="" diff --git a/packaging/alpine/local/seaweedfs/openrc/seaweedfs.worker.confd b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.worker.confd new file mode 100644 index 0000000..b0d0546 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/openrc/seaweedfs.worker.confd @@ -0,0 +1,4 @@ +# Defaults for "weed worker". +SEAWEEDFS_ADMIN="localhost:23646" +SEAWEEDFS_DATA_DIR="/var/lib/seaweedfs/worker" +SEAWEEDFS_OPTS="" diff --git a/packaging/alpine/local/seaweedfs/seaweedfs.pre-install b/packaging/alpine/local/seaweedfs/seaweedfs.pre-install new file mode 100755 index 0000000..314aa80 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/seaweedfs.pre-install @@ -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 diff --git a/scripts/apk/Dockerfile b/scripts/apk/Dockerfile new file mode 100644 index 0000000..27da1c5 --- /dev/null +++ b/scripts/apk/Dockerfile @@ -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"] diff --git a/scripts/apk/build.sh b/scripts/apk/build.sh new file mode 100755 index 0000000..3f3f6b8 --- /dev/null +++ b/scripts/apk/build.sh @@ -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 }" \ + -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 diff --git a/scripts/apk/clean.sh b/scripts/apk/clean.sh new file mode 100755 index 0000000..c979ece --- /dev/null +++ b/scripts/apk/clean.sh @@ -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" diff --git a/scripts/apk/container-entrypoint.sh b/scripts/apk/container-entrypoint.sh new file mode 100755 index 0000000..fd23434 --- /dev/null +++ b/scripts/apk/container-entrypoint.sh @@ -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 }" +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 diff --git a/scripts/apk/list-packages.sh b/scripts/apk/list-packages.sh new file mode 100755 index 0000000..671e969 --- /dev/null +++ b/scripts/apk/list-packages.sh @@ -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 diff --git a/scripts/apk/publish-check.sh b/scripts/apk/publish-check.sh new file mode 100755 index 0000000..f95f5c5 --- /dev/null +++ b/scripts/apk/publish-check.sh @@ -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 diff --git a/scripts/apk/smoke.sh b/scripts/apk/smoke.sh new file mode 100755 index 0000000..a8c31f3 --- /dev/null +++ b/scripts/apk/smoke.sh @@ -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 + "