mirror of
https://github.com/pgsty/minio.git
synced 2026-08-10 08:13:28 +03:00
Compare commits
226 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3be10fcc1a | |||
| b14ea22aa8 | |||
| 86a7782900 | |||
| 4679314556 | |||
| 062a91beed | |||
| 16b78eb4e5 | |||
| 9462cce16e | |||
| b6d47b739c | |||
| 4c34d23099 | |||
| 2ff594f4bb | |||
| 219670d317 | |||
| 6bd9cf77ef | |||
| a6d6d9b028 | |||
| 05be686b84 | |||
| b57275be34 | |||
| 6740e6978f | |||
| 62717d7bf6 | |||
| f1c77d5a2b | |||
| c47733abc4 | |||
| c46b16ec62 | |||
| fd2ca1c6d2 | |||
| 6613c2a3cb | |||
| bd8df51665 | |||
| e071bb77e4 | |||
| 30749911bd | |||
| 15ab10833b | |||
| 77bdc4c0cd | |||
| 15def34dce | |||
| d88f46ccee | |||
| 021110b451 | |||
| aa51393694 | |||
| e064b5555f | |||
| 2ca4971d91 | |||
| 4c185d5a66 | |||
| ca674a6967 | |||
| 3b8a55deef | |||
| 11d79fddc3 | |||
| 475236c79c | |||
| 1814ae52f4 | |||
| 632ade111b | |||
| 32863c8523 | |||
| cf7df097b2 | |||
| 10c7670b80 | |||
| 9c799f42d5 | |||
| 8eae745ab2 | |||
| b42ee4e8ac | |||
| 5f4513fd40 | |||
| dfe6698627 | |||
| 2602177ef6 | |||
| 9dd1dc172d | |||
| 0c14d81510 | |||
| 162ded3438 | |||
| fe6dc47804 | |||
| 744a9dcd71 | |||
| 2f55347f78 | |||
| 97b7d28040 | |||
| 22c1e41fd2 | |||
| 38366f6543 | |||
| 1af351a702 | |||
| b6f70ab085 | |||
| 80e8eaa423 | |||
| ca7baa670d | |||
| a36fd8fffb | |||
| 8069a32ac8 | |||
| 89d346bf51 | |||
| 9247179269 | |||
| 3e14733f15 | |||
| c8590413fd | |||
| 3f192f3f0c | |||
| 15fcc3c8ac | |||
| c1aec0518a | |||
| 7babc0c390 | |||
| b7f52ca433 | |||
| 4dfc27ce32 | |||
| ce01ccbdc1 | |||
| d495d30d57 | |||
| 3e61b1d3a5 | |||
| df627ff896 | |||
| 73ac524724 | |||
| fd69c89d05 | |||
| 5e40665acd | |||
| 65795ee1f4 | |||
| f48dbe777d | |||
| f44110890b | |||
| 9e10f6d9a0 | |||
| 18b712d49a | |||
| db4c0fd5e3 | |||
| efb6e5b00b | |||
| f444b6f37e | |||
| 3252d5b7f3 | |||
| 56fa63bfd1 | |||
| 3b950f8fa8 | |||
| d24f449e08 | |||
| e4fa063942 | |||
| ff58df9499 | |||
| 1869bd30b8 | |||
| 68e0ba9971 | |||
| ce1c537eb1 | |||
| ee55e5391a | |||
| f2f9a40dce | |||
| 377fc616d9 | |||
| 5abd9a80f6 | |||
| 00f3cf74fc | |||
| 68521b37f2 | |||
| 8630937e7d | |||
| d4cd4b4337 | |||
| 27742d4694 | |||
| 58659f26f4 | |||
| 3a0cc6c86e | |||
| 10b0a234d2 | |||
| 18f97e70b1 | |||
| 52eee5a2f1 | |||
| c6d3aac5c4 | |||
| fa18589d1c | |||
| 05e569960a | |||
| 9e49d5e7a6 | |||
| c1a49490c7 | |||
| 334c313da4 | |||
| 1b8ac0af9f | |||
| ba3c0fd1c7 | |||
| d51a4a4ff6 | |||
| 62383dfbfe | |||
| bde0d5a291 | |||
| 534f4a9fb1 | |||
| b8631cf531 | |||
| 456d9462e5 | |||
| 756f3c8142 | |||
| 7a80ec1cce | |||
| ae71d76901 | |||
| 07c3a429bf | |||
| 0cde982902 | |||
| d0f50cdd9b | |||
| da532ab93d | |||
| 558fc1c09c | |||
| 9fdbf6fe83 | |||
| 5c87d4ae87 | |||
| f0b91e5504 | |||
| 3b7cb6512c | |||
| 4ea6f3b06b | |||
| 86d9d9b55e | |||
| 5a35585acd | |||
| 0848e69602 | |||
| 02ba581ecf | |||
| b44b2a090c | |||
| c7d6a9722d | |||
| a8abdc797e | |||
| 0638ccc5f3 | |||
| b1a34fd63f | |||
| ffcfa36b13 | |||
| 376fbd11a7 | |||
| c76f209ccc | |||
| 7a6a2256b1 | |||
| d002beaee3 | |||
| 71f293d9ab | |||
| e3d183b6a4 | |||
| 752abc2e2c | |||
| b9f0e8c712 | |||
| 7ced9663e6 | |||
| 50fcf9b670 | |||
| 64f5c6103f | |||
| e909be6380 | |||
| 83b2ad418b | |||
| 7a64bb9766 | |||
| 34679befef | |||
| 4021d8c8e2 | |||
| de234b888c | |||
| 2718d9a430 | |||
| a65292cab1 | |||
| e0c79be251 | |||
| a6c538c5a1 | |||
| e1fcaebc77 | |||
| 21409f112d | |||
| 417c8648f0 | |||
| e2245a0b12 | |||
| b4b3d208dd | |||
| 0a36d41dcd | |||
| ea77bcfc98 | |||
| 9f24ca5d66 | |||
| 816666a4c6 | |||
| 2c7fe094d1 | |||
| 9ebe168782 | |||
| ee2028cde6 | |||
| ecde75f911 | |||
| 12a6ea89cc | |||
| 63e102c049 | |||
| 160f8a901b | |||
| ef9b03fbf5 | |||
| 1d50cae43d | |||
| c0a33952c6 | |||
| 8cad40a483 | |||
| 6d18dba9a2 | |||
| 9ea14c88d8 | |||
| 30a1261c22 | |||
| 0e017ab071 | |||
| f14198e3dc | |||
| 93c389dbc9 | |||
| ddd9a84cd7 | |||
| b7540169a2 | |||
| f01374950f | |||
| 18aceae620 | |||
| 427826abc5 | |||
| 2780778c10 | |||
| 2d8ba15b9e | |||
| bd6dd55e7f | |||
| 0d7408fc99 | |||
| 864f80e226 | |||
| 0379d6a37f | |||
| 43aa8e4259 | |||
| e2ed696619 | |||
| fb3f67a597 | |||
| 7ee75368e0 | |||
| 1d6478b8ae | |||
| 0581001b6f | |||
| 479303e7e9 | |||
| 89aec6804b | |||
| eb33bc6bf5 | |||
| 3310f740f0 | |||
| 4595293ca0 | |||
| 02a67cbd2a | |||
| 2b34e5b9ae | |||
| a6258668a6 | |||
| d0cada583f | |||
| 0bd8f06b62 | |||
| 6640be3bed | |||
| eafeb27e90 | |||
| f2c9eb0f79 |
@@ -8,9 +8,12 @@ assignees: ''
|
||||
---
|
||||
|
||||
## NOTE
|
||||
All GitHub issues are addressed on a best-effort basis at MinIO's sole discretion. There are no Service Level Agreements (SLA) or Objectives (SLO). Remember our [Code of Conduct](https://github.com/minio/minio/blob/master/code_of_conduct.md) when engaging with MinIO Engineers and the larger community.
|
||||
|
||||
For urgent issues (e.g. production down, etc.), subscribe to [SUBNET](https://min.io/pricing?jmp=github) for direct to engineering support.
|
||||
Silo issues are handled by community maintainers on a best-effort basis. There
|
||||
is no SLA, SLO, or emergency production-support channel. Follow the local
|
||||
[Code of Conduct](../code_of_conduct.md) when participating. Report suspected
|
||||
vulnerabilities through the private process in [SECURITY.md](../SECURITY.md),
|
||||
not in a public issue.
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
@@ -29,7 +32,7 @@ For urgent issues (e.g. production down, etc.), subscribe to [SUBNET](https://mi
|
||||
## Steps to Reproduce (for bugs)
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
<!--- and make sure you have followed https://github.com/minio/minio/tree/release/docs/debugging to capture relevant logs -->
|
||||
<!--- and include relevant Silo logs with secrets and credentials removed -->
|
||||
|
||||
1.
|
||||
2.
|
||||
@@ -42,10 +45,10 @@ For urgent issues (e.g. production down, etc.), subscribe to [SUBNET](https://mi
|
||||
|
||||
## Regression
|
||||
<!-- Is this issue a regression? (Yes / No) -->
|
||||
<!-- If Yes, optionally please include minio version or commit id or PR# that caused this regression, if you have these details. -->
|
||||
<!-- If Yes, optionally include the Silo version, commit id, or PR that caused this regression. -->
|
||||
|
||||
## Your Environment
|
||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
||||
* Version used (`minio --version`):
|
||||
* Version used (`silo --version`):
|
||||
* Server setup and configuration:
|
||||
* Operating System and version (`uname -a`):
|
||||
|
||||
@@ -7,9 +7,6 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## NOTE
|
||||
If this case is urgent, please subscribe to [Subnet](https://min.io/pricing) so that our 24/7 support team may help you faster.
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behavior
|
||||
@@ -27,7 +24,7 @@ If this case is urgent, please subscribe to [Subnet](https://min.io/pricing) so
|
||||
## Steps to Reproduce (for bugs)
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
<!--- and make sure you have followed https://github.com/minio/minio/tree/release/docs/debugging to capture relevant logs -->
|
||||
<!--- and include the relevant Silo logs with secrets and credentials removed -->
|
||||
|
||||
1.
|
||||
2.
|
||||
@@ -40,10 +37,10 @@ If this case is urgent, please subscribe to [Subnet](https://min.io/pricing) so
|
||||
|
||||
## Regression
|
||||
<!-- Is this issue a regression? (Yes / No) -->
|
||||
<!-- If Yes, optionally please include minio version or commit id or PR# that caused this regression, if you have these details. -->
|
||||
<!-- If Yes, optionally include the Silo version, commit id, or PR that caused the regression. -->
|
||||
|
||||
## Your Environment
|
||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
||||
* Version used (`minio --version`):
|
||||
* Version used (`silo --version`):
|
||||
* Server setup and configuration:
|
||||
* Operating System and version (`uname -a`):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: MinIO Community Support
|
||||
url: https://slack.min.io
|
||||
about: Join here for Community Support
|
||||
- name: MinIO SUBNET Support
|
||||
url: https://min.io/pricing
|
||||
about: Join here for Enterprise Support
|
||||
- name: Silo Documentation
|
||||
url: https://silo.pgsty.com/docs/
|
||||
about: Installation, configuration, operations, and compatibility guidance
|
||||
- name: Private Security Report
|
||||
url: https://github.com/pgsty/silo/security/advisories/new
|
||||
about: Privately report a suspected vulnerability in Silo
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
## Community Contribution License
|
||||
All community contributions in this pull request are licensed to the project maintainers
|
||||
under the terms of the [Apache 2 license](https://www.apache.org/licenses/LICENSE-2.0).
|
||||
By creating this pull request I represent that I have the right to license the
|
||||
contributions to the project maintainers under the Apache 2 license.
|
||||
## Contribution Licensing (no CLA, inbound=outbound, DCO required)
|
||||
|
||||
This project does not use a CLA; contributions are accepted inbound=outbound.
|
||||
By submitting this pull request I represent that I have the right to contribute
|
||||
the changes, which are licensed under this repository's
|
||||
[GNU Affero General Public License v3.0 or later](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
and remain my copyright. Every commit must carry a DCO `Signed-off-by` trailer
|
||||
(`git commit -s`) certifying the
|
||||
[Developer Certificate of Origin](https://developercertificate.org/) — see
|
||||
[CONTRIBUTING.md](https://github.com/pgsty/silo/blob/main/CONTRIBUTING.md).
|
||||
|
||||
## Description
|
||||
|
||||
@@ -13,6 +18,12 @@ contributions to the project maintainers under the Apache 2 license.
|
||||
## How to test this PR?
|
||||
|
||||
|
||||
## Compatibility impact
|
||||
|
||||
<!-- Note effects on APIs, clients, MINIO_* configuration, metrics, headers,
|
||||
routes, storage metadata, module/import paths, upgrades, or rollback. -->
|
||||
|
||||
|
||||
## Types of changes
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
@@ -20,7 +31,11 @@ contributions to the project maintainers under the Apache 2 license.
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
|
||||
## Checklist:
|
||||
- [ ] All commits are signed off (`git commit -s`) per the [DCO](https://developercertificate.org/)
|
||||
- [ ] Fixes a regression (If yes, please add `commit-id` or `PR #` here)
|
||||
- [ ] Unit tests added/updated
|
||||
- [ ] `make verifiers` passes
|
||||
- [ ] Relevant package tests and `make build` pass
|
||||
- [ ] Compatibility and rollback impact documented
|
||||
- [ ] Internal documentation updated
|
||||
- [ ] Create a documentation update request [here](https://github.com/minio/docs/issues/new?label=doc-change,title=Doc+Updated+Needed+For+PR+github.com%2fminio%2fminio%2fpull%2fNNNNN)
|
||||
- [ ] Public documentation update opened in `pgsty/silo.pgsty.com`, if needed
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
version: 2
|
||||
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
|
||||
builds:
|
||||
- id: silo
|
||||
main: .
|
||||
binary: silo
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
goamd64:
|
||||
- v1
|
||||
flags:
|
||||
- -tags=kqueue
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- "{{ .Env.LDFLAGS }}"
|
||||
|
||||
archives:
|
||||
- id: silo
|
||||
ids:
|
||||
- silo
|
||||
name_template: "silo_{{ .Env.PKG_VERSION }}_{{ .Os }}_{{ .Arch }}"
|
||||
# Explicit so the license materials cannot silently drop out of the
|
||||
# binary archives: GoReleaser's default file globs would miss NOTICE.
|
||||
# CREDITS stays out deliberately -- at 1.8MB it would dominate the
|
||||
# archive; it remains available in the repository and the OCI image.
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
- NOTICE
|
||||
|
||||
checksum:
|
||||
name_template: "silo_{{ .Env.PKG_VERSION }}_checksums.txt"
|
||||
algorithm: sha256
|
||||
|
||||
# Generate one SPDX JSON document per platform archive. SBOMs are created
|
||||
# before the checksum stage, so the signed checksum manifest covers both the
|
||||
# archives and their corresponding software bills of materials.
|
||||
sboms:
|
||||
- id: silo-archives
|
||||
artifacts: archive
|
||||
# Avoid network-backed package enrichment: the release SBOM must be
|
||||
# reproducible from the artifact alone and the PR gate must work offline.
|
||||
args:
|
||||
- "$artifact"
|
||||
- "--output"
|
||||
- "spdx-json=$document"
|
||||
env:
|
||||
- SYFT_FILE_METADATA_CATALOGER_ENABLED=true
|
||||
- SYFT_CHECK_FOR_APP_UPDATE=false
|
||||
|
||||
# A keyless Sigstore bundle is the detached signature for the checksum
|
||||
# manifest. Consumers can verify the whole archive/SBOM set without trusting a
|
||||
# long-lived project key copied into the repository.
|
||||
signs:
|
||||
- id: silo-checksums
|
||||
cmd: cosign
|
||||
signature: "${artifact}.sigstore.json"
|
||||
args:
|
||||
- sign-blob
|
||||
- "--bundle=${signature}"
|
||||
- "${artifact}"
|
||||
- --yes
|
||||
artifacts: checksum
|
||||
output: true
|
||||
|
||||
release:
|
||||
github:
|
||||
owner: pgsty
|
||||
name: silo
|
||||
draft: true
|
||||
prerelease: false
|
||||
mode: append
|
||||
replace_existing_artifacts: false
|
||||
name_template: "{{ .Tag }}"
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- "^docs:"
|
||||
- "^test:"
|
||||
- "Merge pull request"
|
||||
- "Merge branch"
|
||||
|
||||
announce:
|
||||
skip: true
|
||||
@@ -1 +0,0 @@
|
||||
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 162.612 24.465"><path d="M52.751.414h9.108v23.63h-9.108zM41.711.74l-18.488 9.92a.919.919 0 0 1-.856 0L3.879.74A2.808 2.808 0 0 0 2.558.414h-.023A2.4 2.4 0 0 0 0 2.641v21.376h9.1V13.842a.918.918 0 0 1 1.385-.682l10.361 5.568a3.634 3.634 0 0 0 3.336.028l10.933-5.634a.917.917 0 0 1 1.371.69v10.205h9.1V2.641A2.4 2.4 0 0 0 43.055.414h-.023a2.808 2.808 0 0 0-1.321.326zm65.564-.326h-9.237v10.755a.913.913 0 0 1-1.338.706L72.762.675a2.824 2.824 0 0 0-1.191-.261h-.016a2.4 2.4 0 0 0-2.535 2.227v21.377h9.163V13.275a.914.914 0 0 1 1.337-.707l24.032 11.2a2.813 2.813 0 0 0 1.188.26 2.4 2.4 0 0 0 2.535-2.227zm7.161 23.63V.414h4.191v23.63zm28.856.421c-11.274 0-19.272-4.7-19.272-12.232C124.02 4.741 132.066 0 143.292 0s19.32 4.7 19.32 12.233-7.902 12.232-19.32 12.232zm0-21.333c-8.383 0-14.84 3.217-14.84 9.1 0 5.926 6.457 9.1 14.84 9.1s14.887-3.174 14.887-9.1c0-5.883-6.504-9.1-14.887-9.1z" fill="#c72c48"/></svg>
|
||||
|
Before Width: | Height: | Size: 978 B |
@@ -0,0 +1,111 @@
|
||||
name: silo
|
||||
arch: ${NFPM_ARCH}
|
||||
platform: linux
|
||||
version: ${PKG_VERSION}
|
||||
version_schema: none
|
||||
release: ${NFPM_RELEASE}
|
||||
section: utils
|
||||
priority: optional
|
||||
maintainer: "Ruohang Feng (@Vonng) <rh@vonng.com>"
|
||||
description: S3-Interface Libre Object Storage, a community-maintained S3-compatible server.
|
||||
vendor: PGSTY
|
||||
homepage: https://silo.pgsty.com
|
||||
license: AGPL-3.0-or-later
|
||||
|
||||
contents:
|
||||
- src: ${NFPM_SOURCE}
|
||||
dst: /usr/bin/silo
|
||||
expand: true
|
||||
file_info:
|
||||
mode: 0755
|
||||
owner: root
|
||||
group: root
|
||||
- src: ${NFPM_UNIT}
|
||||
dst: /usr/lib/systemd/system/silo.service
|
||||
expand: true
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
- src: ${NFPM_DEFAULTS}
|
||||
dst: /etc/default/silo
|
||||
type: config|noreplace
|
||||
expand: true
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
- src: ${NFPM_SYSUSERS}
|
||||
dst: /usr/lib/sysusers.d/silo.conf
|
||||
expand: true
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
# The license materials are declared once per packager: nfpm only honors
|
||||
# type: license on rpm and silently drops such entries from deb and apk, so
|
||||
# the rpm keeps its %license flag while deb and apk carry plain files at the
|
||||
# same path.
|
||||
- src: ${NFPM_LICENSE}
|
||||
dst: /usr/share/doc/silo/LICENSE
|
||||
type: license
|
||||
packager: rpm
|
||||
expand: true
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
- src: ${NFPM_NOTICE}
|
||||
dst: /usr/share/doc/silo/NOTICE
|
||||
type: license
|
||||
packager: rpm
|
||||
expand: true
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
- src: ${NFPM_LICENSE}
|
||||
dst: /usr/share/doc/silo/LICENSE
|
||||
packager: deb
|
||||
expand: true
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
- src: ${NFPM_NOTICE}
|
||||
dst: /usr/share/doc/silo/NOTICE
|
||||
packager: deb
|
||||
expand: true
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
- src: ${NFPM_LICENSE}
|
||||
dst: /usr/share/doc/silo/LICENSE
|
||||
packager: apk
|
||||
expand: true
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
- src: ${NFPM_NOTICE}
|
||||
dst: /usr/share/doc/silo/NOTICE
|
||||
packager: apk
|
||||
expand: true
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
scripts:
|
||||
postinstall: buildscripts/package/postinstall.sh
|
||||
preremove: buildscripts/package/preremove.sh
|
||||
|
||||
rpm:
|
||||
group: Applications/File
|
||||
compression: gzip:9
|
||||
|
||||
deb:
|
||||
compression: gzip
|
||||
fields:
|
||||
License: AGPL-3.0-or-later
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="1500" height="570" viewBox="0 0 1500 570"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
role="img" aria-labelledby="silo-logo-title silo-logo-desc"
|
||||
shape-rendering="geometricPrecision">
|
||||
<title id="silo-logo-title">SILO logo</title>
|
||||
<desc id="silo-logo-desc">The SILO horizontal lockup: the circular silo emblem on the left, the SILO wordmark on the right.</desc>
|
||||
|
||||
<defs>
|
||||
<!-- Emblem gradient, in the emblem's local coordinates; the group transform maps it. -->
|
||||
<linearGradient id="silo-logo-mark-color" x1="276" y1="720" x2="742" y2="286" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#064A83"/>
|
||||
<stop offset="0.5" stop-color="#007FA8"/>
|
||||
<stop offset="1" stop-color="#22C7C9"/>
|
||||
</linearGradient>
|
||||
<!-- Wordmark gradient, in the wordmark's local coordinates. -->
|
||||
<linearGradient id="silo-logo-word-color" x1="44.0" y1="-94.4" x2="1950.0" y2="794.4" gradientUnits="userSpaceOnUse">
|
||||
<stop class="silo-logo-wm-a" offset="0.06" stop-color="#1d588c"/>
|
||||
<stop class="silo-logo-wm-b" offset="0.94" stop-color="#b4762e"/>
|
||||
</linearGradient>
|
||||
<style>
|
||||
/* Light theme values of --pg-strong / --copper; dark theme swaps in its own pair. */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.silo-logo-wm-a { stop-color: #7fb8e8; }
|
||||
.silo-logo-wm-b { stop-color: #e0a35c; }
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
|
||||
<!-- Circular emblem, shifted from its native viewBox (230 213 570 570) to the 0..570 square. -->
|
||||
<g id="silo-logo-mark" transform="translate(-230 -213)" fill="url(#silo-logo-mark-color)">
|
||||
<!-- Outer circular band, intentionally opened at the lower-right plinth. -->
|
||||
<path d="
|
||||
M 734 676
|
||||
A 283.5 278.5 0 1 0 310 692
|
||||
L 359 692
|
||||
A 247 248.5 0 1 1 688 676
|
||||
Z"/>
|
||||
|
||||
<!-- Flowing left wall; its upper tangent matches the inner ellipse. -->
|
||||
<path d="
|
||||
M 300.57 375
|
||||
C 292 390 300 430 328 450
|
||||
C 343 461 357 472 374 481
|
||||
C 410 501 426 517 426 544
|
||||
L 426 676
|
||||
L 336 676
|
||||
C 308 647 286 608 274 565
|
||||
C 262 522 263 479 272 439
|
||||
C 278 413 286 389 300.57 375
|
||||
Z"/>
|
||||
|
||||
<!-- Right column with tangent-continuous upper shoulder. -->
|
||||
<path d="
|
||||
M 602 373
|
||||
Q 602 368 607 370
|
||||
C 619 374 634 381 634 389
|
||||
L 634 647
|
||||
Q 634 649 636 649
|
||||
L 708 649
|
||||
L 734 676
|
||||
L 602 676
|
||||
Z"/>
|
||||
|
||||
<!-- Lower circular cap. -->
|
||||
<path d="
|
||||
M 310 692
|
||||
L 714 692
|
||||
C 668 743 596 774 512 774
|
||||
C 428 774 355 743 310 692
|
||||
Z"/>
|
||||
|
||||
<!-- Main silo body, with a tangent-continuous right shoulder. -->
|
||||
<path d="
|
||||
M 389 389
|
||||
C 389 374 447 351 512 351
|
||||
C 540 351 565 354 580 359
|
||||
Q 583 360 583 364
|
||||
L 583 676
|
||||
L 443 676
|
||||
L 443 541
|
||||
C 443 509 426 490 389 470
|
||||
Z"/>
|
||||
|
||||
<!-- Peaked roof with softly tapered, burr-free tips. -->
|
||||
<path d="
|
||||
M 512 274
|
||||
L 647 365
|
||||
Q 649 370 647 376
|
||||
C 609 350 563 337 512 337
|
||||
C 460 337 414 350 377 376
|
||||
Q 375 370 377 365
|
||||
Z"/>
|
||||
</g>
|
||||
|
||||
<!-- Wordmark, scaled 3/7 to a 300-unit cap height and centered on the emblem's axis. -->
|
||||
<g id="silo-logo-word" transform="translate(645.429 135) scale(0.428571)"
|
||||
fill="url(#silo-logo-word-color)" fill-rule="nonzero">
|
||||
<path id="silo-logo-s" d="M0 592V492H134V551L167 584H374L408 550V434L375 401H110L2 293V108L110 0H426L534 108V209H400V149L367 116H169L136 149V252L169 285H434L542 393V590L432 700H108Z"/>
|
||||
<path id="silo-logo-i" d="M637 0H773V700H637Z"/>
|
||||
<path id="silo-logo-l" d="M888 0H1024V585H1374V700H888Z"/>
|
||||
<path id="silo-logo-o" d="M1404 585V115L1519 0H1879L1994 115V585L1879 700H1519ZM1807 584 1858 533V167L1807 116H1591L1540 167V533L1591 584Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="1624" height="570" viewBox="0 0 1994 700"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
role="img" aria-labelledby="title desc"
|
||||
shape-rendering="geometricPrecision">
|
||||
<title id="title">SILO wordmark</title>
|
||||
<desc id="desc">The SILO wordmark set in Chakra Petch Bold, outlined, with the blue-to-copper brand gradient.</desc>
|
||||
|
||||
<defs>
|
||||
<linearGradient id="silo-wordmark-color" x1="44.0" y1="-94.4" x2="1950.0" y2="794.4" gradientUnits="userSpaceOnUse">
|
||||
<stop class="wm-a" offset="0.06" stop-color="#1d588c"/>
|
||||
<stop class="wm-b" offset="0.94" stop-color="#b4762e"/>
|
||||
</linearGradient>
|
||||
<style>
|
||||
/* Light theme values of --pg-strong / --copper; dark theme swaps in its own pair. */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.wm-a { stop-color: #7fb8e8; }
|
||||
.wm-b { stop-color: #e0a35c; }
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
|
||||
<g fill="url(#silo-wordmark-color)" fill-rule="nonzero">
|
||||
<path id="silo-s" d="M0 592V492H134V551L167 584H374L408 550V434L375 401H110L2 293V108L110 0H426L534 108V209H400V149L367 116H169L136 149V252L169 285H434L542 393V590L432 700H108Z"/>
|
||||
<path id="silo-i" d="M637 0H773V700H637Z"/>
|
||||
<path id="silo-l" d="M888 0H1024V585H1374V700H888Z"/>
|
||||
<path id="silo-o" d="M1404 585V115L1519 0H1879L1994 115V585L1879 700H1519ZM1807 584 1858 533V167L1807 116H1591L1540 167V533L1591 584Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="570" height="570" viewBox="230 213 570 570"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
role="img" aria-labelledby="title desc"
|
||||
shape-rendering="geometricPrecision">
|
||||
<title id="title">SILO emblem</title>
|
||||
<desc id="desc">A smooth circular SILO mark with a peaked roof, flowing left wall, columns, and a curved base.</desc>
|
||||
|
||||
<defs>
|
||||
<linearGradient id="silo-color" x1="276" y1="720" x2="742" y2="286" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#064A83"/>
|
||||
<stop offset="0.5" stop-color="#007FA8"/>
|
||||
<stop offset="1" stop-color="#22C7C9"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<g fill="url(#silo-color)">
|
||||
<!-- Outer circular band, intentionally opened at the lower-right plinth. -->
|
||||
<path d="
|
||||
M 734 676
|
||||
A 283.5 278.5 0 1 0 310 692
|
||||
L 359 692
|
||||
A 247 248.5 0 1 1 688 676
|
||||
Z"/>
|
||||
|
||||
<!-- Flowing left wall; its upper tangent matches the inner ellipse. -->
|
||||
<path d="
|
||||
M 300.57 375
|
||||
C 292 390 300 430 328 450
|
||||
C 343 461 357 472 374 481
|
||||
C 410 501 426 517 426 544
|
||||
L 426 676
|
||||
L 336 676
|
||||
C 308 647 286 608 274 565
|
||||
C 262 522 263 479 272 439
|
||||
C 278 413 286 389 300.57 375
|
||||
Z"/>
|
||||
|
||||
<!-- Right column with tangent-continuous upper shoulder. -->
|
||||
<path d="
|
||||
M 602 373
|
||||
Q 602 368 607 370
|
||||
C 619 374 634 381 634 389
|
||||
L 634 647
|
||||
Q 634 649 636 649
|
||||
L 708 649
|
||||
L 734 676
|
||||
L 602 676
|
||||
Z"/>
|
||||
|
||||
<!-- Lower circular cap. -->
|
||||
<path d="
|
||||
M 310 692
|
||||
L 714 692
|
||||
C 668 743 596 774 512 774
|
||||
C 428 774 355 743 310 692
|
||||
Z"/>
|
||||
|
||||
<!-- Main silo body, with a tangent-continuous right shoulder. -->
|
||||
<path d="
|
||||
M 389 389
|
||||
C 389 374 447 351 512 351
|
||||
C 540 351 565 354 580 359
|
||||
Q 583 360 583 364
|
||||
L 583 676
|
||||
L 443 676
|
||||
L 443 541
|
||||
C 443 509 426 490 389 470
|
||||
Z"/>
|
||||
|
||||
<!-- Peaked roof with softly tapered, burr-free tips. -->
|
||||
<path d="
|
||||
M 512 274
|
||||
L 647 365
|
||||
Q 649 370 647 376
|
||||
C 609 350 563 337 512 337
|
||||
C 460 337 414 350 377 376
|
||||
Q 375 370 377 365
|
||||
Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,43 @@
|
||||
name: DCO
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Verify DCO sign-off
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Every non-merge commit in the pull request must carry a Signed-off-by
|
||||
# trailer matching the commit author's email, certifying the Developer
|
||||
# Certificate of Origin 1.1 (https://developercertificate.org/).
|
||||
# Only commits authored from a GitHub-issued bot address are exempt; a
|
||||
# display name is attacker-controlled and must never grant the exemption.
|
||||
- name: Check Signed-off-by trailers
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
fail=0
|
||||
while read -r sha; do
|
||||
author_name="$(git log -1 --format='%an' "${sha}")"
|
||||
author_email="$(git log -1 --format='%ae' "${sha}")"
|
||||
case "${author_email}" in
|
||||
*"[bot]@users.noreply.github.com") continue ;;
|
||||
esac
|
||||
if ! git log -1 --format='%(trailers:key=Signed-off-by,valueonly)' "${sha}" |
|
||||
grep -qiF "<${author_email}>"; then
|
||||
echo "::error::commit ${sha} by ${author_name} <${author_email}> lacks a matching Signed-off-by trailer; sign with 'git commit -s', repair with 'git rebase --signoff'"
|
||||
fail=1
|
||||
fi
|
||||
done < <(git rev-list --no-merges "${BASE_SHA}..${HEAD_SHA}")
|
||||
exit "${fail}"
|
||||
@@ -1,14 +0,0 @@
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v4
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v4
|
||||
@@ -0,0 +1,365 @@
|
||||
name: Publish Docker Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Published RELEASE.* tag to package as pgsty/silo"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
attestations: write
|
||||
artifact-metadata: write
|
||||
|
||||
concurrency:
|
||||
group: docker-release
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Images are built from a published release rather than from the build
|
||||
# that produced it, so an abandoned draft can never leave :latest
|
||||
# pointing at something nobody shipped.
|
||||
- name: Validate published release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
INPUT_TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="${INPUT_TAG}"
|
||||
VERSION_HYPHEN="${TAG#RELEASE.}"
|
||||
PKG_VERSION="$(echo "${VERSION_HYPHEN}" | sed -E 's/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2})-([0-9]{2})-([0-9]{2})Z$/\1\2\3\4\5\6.0.0/')"
|
||||
if [ "${PKG_VERSION}" = "${VERSION_HYPHEN}" ]; then
|
||||
echo "Invalid release tag: ${TAG}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IS_DRAFT="$(gh release view "${TAG}" --repo "${GITHUB_REPOSITORY}" --json isDraft --jq .isDraft)"
|
||||
IS_PRERELEASE="$(gh release view "${TAG}" --repo "${GITHUB_REPOSITORY}" --json isPrerelease --jq .isPrerelease)"
|
||||
PUBLISHED_AT="$(gh release view "${TAG}" --repo "${GITHUB_REPOSITORY}" --json publishedAt --jq .publishedAt)"
|
||||
LATEST_TAG="$(gh release view --repo "${GITHUB_REPOSITORY}" --json tagName --jq .tagName)"
|
||||
|
||||
if [ "${IS_DRAFT}" != false ] || [ "${IS_PRERELEASE}" != false ] || [ -z "${PUBLISHED_AT}" ]; then
|
||||
echo "${TAG} must be a published, non-prerelease GitHub Release"
|
||||
exit 1
|
||||
fi
|
||||
# This workflow moves :latest, so it must not run for an older tag.
|
||||
if [ "${TAG}" != "${LATEST_TAG}" ]; then
|
||||
echo "Refusing to replace Docker latest with non-latest release ${TAG} (latest is ${LATEST_TAG})"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
{
|
||||
echo "RELEASE_TAG=${TAG}"
|
||||
echo "PKG_VERSION=${PKG_VERSION}"
|
||||
echo "PUBLISHED_AT=${PUBLISHED_AT}"
|
||||
} >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Validate Docker Hub credentials
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then
|
||||
echo "Missing Docker Hub credentials. Set DOCKERHUB_USERNAME and DOCKERHUB_TOKEN repository secrets."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout release tag
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ inputs.tag }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Verify workflow identity matches release source
|
||||
run: |
|
||||
set -euo pipefail
|
||||
CHECKED_OUT_REVISION="$(git rev-parse HEAD)"
|
||||
if [ "${CHECKED_OUT_REVISION}" != "${GITHUB_SHA}" ]; then
|
||||
echo "Checked out ${CHECKED_OUT_REVISION}, but workflow identity is ${GITHUB_SHA}. Dispatch this workflow from ${RELEASE_TAG}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Prepare verified Docker contexts
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
assets_dir="docker-release/assets"
|
||||
mkdir -p "${assets_dir}"
|
||||
|
||||
amd64_archive="silo_${PKG_VERSION}_linux_amd64.tar.gz"
|
||||
arm64_archive="silo_${PKG_VERSION}_linux_arm64.tar.gz"
|
||||
checksums="silo_${PKG_VERSION}_checksums.txt"
|
||||
gh release download "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" \
|
||||
--dir "${assets_dir}" \
|
||||
--pattern "${amd64_archive}" \
|
||||
--pattern "${arm64_archive}" \
|
||||
--pattern "${checksums}"
|
||||
|
||||
# The binaries going into the images are the published ones, checked
|
||||
# against the published checksums, not a rebuild that merely ought to
|
||||
# match them. awk matches the manifest filename column exactly: a
|
||||
# substring grep would also pull in the archive's .sbom.json line,
|
||||
# whose file is deliberately not downloaded in this lane.
|
||||
cd "${assets_dir}"
|
||||
awk -v name="${amd64_archive}" '$2 == name' "${checksums}" | sha256sum --check
|
||||
awk -v name="${arm64_archive}" '$2 == name' "${checksums}" | sha256sum --check
|
||||
|
||||
# The checksum manifest and both archives must have provenance from
|
||||
# this repository's release workflow at the exact checked-out tag.
|
||||
for artifact in "${checksums}" "${amd64_archive}" "${arm64_archive}"; do
|
||||
gh attestation verify "${artifact}" \
|
||||
--repo "${GITHUB_REPOSITORY}" \
|
||||
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/release.yml" \
|
||||
--source-digest "$(git -C "${GITHUB_WORKSPACE}" rev-parse HEAD)" \
|
||||
--source-ref "refs/tags/${RELEASE_TAG}" >/dev/null
|
||||
done
|
||||
cd "${GITHUB_WORKSPACE}"
|
||||
|
||||
# Dockerfile.goreleaser expects the binary at the context root and
|
||||
# the entrypoint scripts under dockerscripts/, which is the layout
|
||||
# GoReleaser used to assemble via extra_files.
|
||||
for arch in amd64 arm64; do
|
||||
context="docker-release/${arch}"
|
||||
archive="${assets_dir}/silo_${PKG_VERSION}_linux_${arch}.tar.gz"
|
||||
mkdir -p "${context}/dockerscripts"
|
||||
tar -xzf "${archive}" -C "${context}" silo
|
||||
cp Dockerfile.goreleaser Dockerfile.distroless LICENSE NOTICE CREDITS "${context}/"
|
||||
cp dockerscripts/docker-entrypoint.sh dockerscripts/download-static-curl.sh \
|
||||
"${context}/dockerscripts/"
|
||||
done
|
||||
|
||||
echo "RELEASE_REVISION=$(git rev-parse HEAD)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Install Syft
|
||||
uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
|
||||
with:
|
||||
syft-version: v1.50.0
|
||||
|
||||
- name: Build and push amd64 image
|
||||
id: build-amd64
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: docker-release/amd64
|
||||
file: docker-release/amd64/Dockerfile.goreleaser
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: |
|
||||
pgsty/silo:${{ env.RELEASE_TAG }}-amd64
|
||||
pgsty/silo:latest-amd64
|
||||
labels: |
|
||||
org.opencontainers.image.version=${{ env.RELEASE_TAG }}
|
||||
org.opencontainers.image.created=${{ env.PUBLISHED_AT }}
|
||||
org.opencontainers.image.revision=${{ env.RELEASE_REVISION }}
|
||||
|
||||
- name: Build and push arm64 image
|
||||
id: build-arm64
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: docker-release/arm64
|
||||
file: docker-release/arm64/Dockerfile.goreleaser
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
pgsty/silo:${{ env.RELEASE_TAG }}-arm64
|
||||
pgsty/silo:latest-arm64
|
||||
labels: |
|
||||
org.opencontainers.image.version=${{ env.RELEASE_TAG }}
|
||||
org.opencontainers.image.created=${{ env.PUBLISHED_AT }}
|
||||
org.opencontainers.image.revision=${{ env.RELEASE_REVISION }}
|
||||
|
||||
# The distroless variant is a pilot published alongside the classic
|
||||
# image; it ships the silo binary alone and relies on the native
|
||||
# `silo healthcheck` subcommand for container health.
|
||||
# Design: https://silo.pgsty.com/compatibility/feature/healthcheck/
|
||||
- name: Build and push amd64 distroless image
|
||||
id: build-amd64-distroless
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: docker-release/amd64
|
||||
file: docker-release/amd64/Dockerfile.distroless
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: |
|
||||
pgsty/silo:${{ env.RELEASE_TAG }}-distroless-amd64
|
||||
pgsty/silo:distroless-amd64
|
||||
labels: |
|
||||
org.opencontainers.image.version=${{ env.RELEASE_TAG }}
|
||||
org.opencontainers.image.created=${{ env.PUBLISHED_AT }}
|
||||
org.opencontainers.image.revision=${{ env.RELEASE_REVISION }}
|
||||
|
||||
- name: Build and push arm64 distroless image
|
||||
id: build-arm64-distroless
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: docker-release/arm64
|
||||
file: docker-release/arm64/Dockerfile.distroless
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
pgsty/silo:${{ env.RELEASE_TAG }}-distroless-arm64
|
||||
pgsty/silo:distroless-arm64
|
||||
labels: |
|
||||
org.opencontainers.image.version=${{ env.RELEASE_TAG }}
|
||||
org.opencontainers.image.created=${{ env.PUBLISHED_AT }}
|
||||
org.opencontainers.image.revision=${{ env.RELEASE_REVISION }}
|
||||
|
||||
- name: Verify HEALTHCHECK survived the distroless push
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# HEALTHCHECK is a Docker extension absent from the OCI image
|
||||
# spec, and a publish path can drop it silently. Check the pushed
|
||||
# architecture image now, before the versioned and rolling
|
||||
# multi-arch manifests are created, so a broken health config
|
||||
# stops their promotion. (The architecture-suffixed tags above
|
||||
# are already public by this point - full staging-then-promote
|
||||
# would be a workflow-wide redesign shared with the classic
|
||||
# image lanes.)
|
||||
docker pull "pgsty/silo:${RELEASE_TAG}-distroless-amd64" >/dev/null
|
||||
test "$(docker inspect -f '{{json .Config.Healthcheck.Test}}' "pgsty/silo:${RELEASE_TAG}-distroless-amd64")" \
|
||||
= '["CMD","/usr/bin/silo","healthcheck","ready"]'
|
||||
|
||||
- name: Publish multi-architecture manifests
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p docker-release/metadata
|
||||
docker buildx imagetools create \
|
||||
--tag "pgsty/silo:${RELEASE_TAG}" \
|
||||
--metadata-file docker-release/metadata/release.json \
|
||||
"pgsty/silo:${RELEASE_TAG}-amd64" \
|
||||
"pgsty/silo:${RELEASE_TAG}-arm64"
|
||||
docker buildx imagetools create \
|
||||
--tag "pgsty/silo:latest" \
|
||||
--metadata-file docker-release/metadata/latest.json \
|
||||
"pgsty/silo:latest-amd64" \
|
||||
"pgsty/silo:latest-arm64"
|
||||
docker buildx imagetools inspect "pgsty/silo:${RELEASE_TAG}"
|
||||
docker buildx imagetools inspect "pgsty/silo:latest"
|
||||
|
||||
docker buildx imagetools create \
|
||||
--tag "pgsty/silo:${RELEASE_TAG}-distroless" \
|
||||
--metadata-file docker-release/metadata/release-distroless.json \
|
||||
"pgsty/silo:${RELEASE_TAG}-distroless-amd64" \
|
||||
"pgsty/silo:${RELEASE_TAG}-distroless-arm64"
|
||||
docker buildx imagetools create \
|
||||
--tag "pgsty/silo:distroless" \
|
||||
--metadata-file docker-release/metadata/distroless.json \
|
||||
"pgsty/silo:distroless-amd64" \
|
||||
"pgsty/silo:distroless-arm64"
|
||||
docker buildx imagetools inspect "pgsty/silo:${RELEASE_TAG}-distroless"
|
||||
docker buildx imagetools inspect "pgsty/silo:distroless"
|
||||
|
||||
RELEASE_DIGEST="$(jq -r '."containerimage.descriptor".digest' docker-release/metadata/release.json)"
|
||||
LATEST_DIGEST="$(jq -r '."containerimage.descriptor".digest' docker-release/metadata/latest.json)"
|
||||
if ! [[ "${RELEASE_DIGEST}" =~ ^sha256:[0-9a-f]{64}$ ]]; then
|
||||
echo "Invalid release manifest digest: ${RELEASE_DIGEST}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "${RELEASE_DIGEST}" != "${LATEST_DIGEST}" ]; then
|
||||
echo "Release and latest tags resolved to different manifests" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "SILO_IMAGE_DIGEST=${RELEASE_DIGEST}" >> "${GITHUB_ENV}"
|
||||
|
||||
DISTROLESS_RELEASE_DIGEST="$(jq -r '."containerimage.descriptor".digest' docker-release/metadata/release-distroless.json)"
|
||||
DISTROLESS_ROLLING_DIGEST="$(jq -r '."containerimage.descriptor".digest' docker-release/metadata/distroless.json)"
|
||||
if ! [[ "${DISTROLESS_RELEASE_DIGEST}" =~ ^sha256:[0-9a-f]{64}$ ]]; then
|
||||
echo "Invalid distroless manifest digest: ${DISTROLESS_RELEASE_DIGEST}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "${DISTROLESS_RELEASE_DIGEST}" != "${DISTROLESS_ROLLING_DIGEST}" ]; then
|
||||
echo "Distroless release and rolling tags resolved to different manifests" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "SILO_DISTROLESS_DIGEST=${DISTROLESS_RELEASE_DIGEST}" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Generate architecture image SBOMs
|
||||
env:
|
||||
AMD64_DIGEST: ${{ steps.build-amd64.outputs.digest }}
|
||||
ARM64_DIGEST: ${{ steps.build-arm64.outputs.digest }}
|
||||
DISTROLESS_AMD64_DIGEST: ${{ steps.build-amd64-distroless.outputs.digest }}
|
||||
DISTROLESS_ARM64_DIGEST: ${{ steps.build-arm64-distroless.outputs.digest }}
|
||||
SYFT_CHECK_FOR_APP_UPDATE: "false"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p docker-release/sbom
|
||||
# Each per-architecture digest names an OCI index (image plus the
|
||||
# provenance attestation buildx attaches), and Syft's platform
|
||||
# default on an index follows the amd64 runner - an arm64-only
|
||||
# index would fail outright. Select the platform explicitly.
|
||||
syft "registry:index.docker.io/pgsty/silo@${AMD64_DIGEST}" \
|
||||
--platform linux/amd64 \
|
||||
--output "spdx-json=docker-release/sbom/linux-amd64.spdx.json"
|
||||
syft "registry:index.docker.io/pgsty/silo@${ARM64_DIGEST}" \
|
||||
--platform linux/arm64 \
|
||||
--output "spdx-json=docker-release/sbom/linux-arm64.spdx.json"
|
||||
syft "registry:index.docker.io/pgsty/silo@${DISTROLESS_AMD64_DIGEST}" \
|
||||
--platform linux/amd64 \
|
||||
--output "spdx-json=docker-release/sbom/linux-amd64-distroless.spdx.json"
|
||||
syft "registry:index.docker.io/pgsty/silo@${DISTROLESS_ARM64_DIGEST}" \
|
||||
--platform linux/arm64 \
|
||||
--output "spdx-json=docker-release/sbom/linux-arm64-distroless.spdx.json"
|
||||
|
||||
- name: Attest amd64 image SBOM
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: index.docker.io/pgsty/silo
|
||||
subject-digest: ${{ steps.build-amd64.outputs.digest }}
|
||||
sbom-path: docker-release/sbom/linux-amd64.spdx.json
|
||||
push-to-registry: true
|
||||
|
||||
- name: Attest arm64 image SBOM
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: index.docker.io/pgsty/silo
|
||||
subject-digest: ${{ steps.build-arm64.outputs.digest }}
|
||||
sbom-path: docker-release/sbom/linux-arm64.spdx.json
|
||||
push-to-registry: true
|
||||
|
||||
- name: Attest amd64 distroless image SBOM
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: index.docker.io/pgsty/silo
|
||||
subject-digest: ${{ steps.build-amd64-distroless.outputs.digest }}
|
||||
sbom-path: docker-release/sbom/linux-amd64-distroless.spdx.json
|
||||
push-to-registry: true
|
||||
|
||||
- name: Attest arm64 distroless image SBOM
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: index.docker.io/pgsty/silo
|
||||
subject-digest: ${{ steps.build-arm64-distroless.outputs.digest }}
|
||||
sbom-path: docker-release/sbom/linux-arm64-distroless.spdx.json
|
||||
push-to-registry: true
|
||||
|
||||
- name: Attest multi-architecture image provenance
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: index.docker.io/pgsty/silo
|
||||
subject-digest: ${{ env.SILO_IMAGE_DIGEST }}
|
||||
push-to-registry: true
|
||||
|
||||
- name: Attest multi-architecture distroless image provenance
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-name: index.docker.io/pgsty/silo
|
||||
subject-digest: ${{ env.SILO_DISTROLESS_DIGEST }}
|
||||
push-to-registry: true
|
||||
@@ -0,0 +1,234 @@
|
||||
name: Finalize Release Packages
|
||||
|
||||
# Manual-only lane that runs AFTER the maintainer has GPG-signed the RPMs in a
|
||||
# Draft release (buildscripts/sign-release-rpms.sh --upload) and BEFORE the
|
||||
# release is published. GPG signing rewrites the RPM bytes, which strands the
|
||||
# SBOMs, the packages checksum manifest, and the attestations that release.yml
|
||||
# generated from the as-built packages. This lane regenerates those materials
|
||||
# from the published (signed) bytes under the workflow identity, so the
|
||||
# sigstore layer describes exactly what the release ships.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Draft RELEASE.* tag whose signed RPMs need refreshed SBOMs and checksums"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
artifact-metadata: write
|
||||
|
||||
concurrency:
|
||||
group: finalize-release
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
finalize:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout release tag
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ inputs.tag }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Verify workflow identity matches release source
|
||||
run: |
|
||||
set -euo pipefail
|
||||
CHECKED_OUT_REVISION="$(git rev-parse HEAD)"
|
||||
if [ "${CHECKED_OUT_REVISION}" != "${GITHUB_SHA}" ]; then
|
||||
echo "Checked out ${CHECKED_OUT_REVISION}, but workflow identity is ${GITHUB_SHA}. Dispatch from the release tag." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Compute release variables
|
||||
env:
|
||||
# Environment passthrough keeps the dispatch input out of the script
|
||||
# source, mirroring release.yml.
|
||||
INPUT_TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="${INPUT_TAG}"
|
||||
if [[ ! "${TAG}" =~ ^RELEASE\.[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}Z$ ]]; then
|
||||
echo "Invalid release tag format: ${TAG}" >&2
|
||||
exit 1
|
||||
fi
|
||||
VERSION_HYPHEN="${TAG#RELEASE.}"
|
||||
PKG_VERSION="$(echo "${VERSION_HYPHEN}" | sed -E 's/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2})-([0-9]{2})-([0-9]{2})Z$/\1\2\3\4\5\6.0.0/')"
|
||||
{
|
||||
echo "RELEASE_TAG=${TAG}"
|
||||
echo "PKG_VERSION=${PKG_VERSION}"
|
||||
} >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Refuse to touch a published release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ "$(gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --json isDraft --jq .isDraft)" != "true" ]; then
|
||||
echo "${RELEASE_TAG} is not a Draft release; finalize runs only before publishing." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Install Syft
|
||||
uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
|
||||
with:
|
||||
syft-version: v1.50.0
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
with:
|
||||
cosign-release: v3.1.2
|
||||
|
||||
- name: Download and verify the package set
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# sign-release-rpms.sh owns the package identity; import its values
|
||||
# the same way test-release.yml does so the lanes cannot drift.
|
||||
eval "$(grep -E '^expected_(release|fingerprint)=' buildscripts/sign-release-rpms.sh)"
|
||||
test -n "${expected_release}"
|
||||
test -n "${expected_fingerprint}"
|
||||
|
||||
packages_dir="finalize/packages"
|
||||
sidecar_dir="finalize/sidecars"
|
||||
mkdir -p "${packages_dir}" "${sidecar_dir}"
|
||||
|
||||
manifest="silo_${PKG_VERSION}_packages_checksums.txt"
|
||||
|
||||
# Exactly the twelve manifest subjects land in packages_dir; the
|
||||
# tarball SBOMs in the release root do not match these patterns.
|
||||
gh release download "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --dir "${packages_dir}" \
|
||||
--pattern '*.rpm' --pattern '*.deb' --pattern '*.apk' \
|
||||
--pattern '*.rpm.sbom.json' --pattern '*.deb.sbom.json' --pattern '*.apk.sbom.json'
|
||||
gh release download "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --dir "${sidecar_dir}" \
|
||||
--pattern '*.rpm.sha256sum' --pattern "${manifest}"
|
||||
|
||||
cd "${packages_dir}"
|
||||
subject_count="$(find . -maxdepth 1 -type f | wc -l | tr -d ' ')"
|
||||
if [ "${subject_count}" -ne 12 ]; then
|
||||
echo "Expected twelve package subjects, found ${subject_count}" >&2
|
||||
find . -maxdepth 1 -type f >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The signed RPMs must match the .sha256sum sidecars the signing
|
||||
# script regenerated and uploaded alongside them.
|
||||
for rpm_file in "silo-${PKG_VERSION}-${expected_release}.x86_64.rpm" \
|
||||
"silo-${PKG_VERSION}-${expected_release}.aarch64.rpm"; do
|
||||
test -s "${rpm_file}"
|
||||
actual="$(sha256sum "${rpm_file}" | awk '{print $1}')"
|
||||
recorded="$(awk '{print $1}' "../sidecars/${rpm_file}.sha256sum")"
|
||||
if [ "${actual}" != "${recorded}" ]; then
|
||||
echo "Digest mismatch for ${rpm_file}: sidecar ${recorded}, asset ${actual}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Everything the maintainer did not re-sign must still match the
|
||||
# manifest release.yml generated: this lane refreshes RPM materials,
|
||||
# it does not accept drift anywhere else.
|
||||
for package_file in *.deb *.apk *.deb.sbom.json *.apk.sbom.json; do
|
||||
awk -v name="${package_file}" '$2 == name' "../sidecars/${manifest}" | sha256sum --check
|
||||
done
|
||||
|
||||
- name: Verify RPM GPG signatures
|
||||
run: |
|
||||
set -euo pipefail
|
||||
eval "$(grep -E '^expected_fingerprint=' buildscripts/sign-release-rpms.sh)"
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes rpm
|
||||
sudo rpmkeys --import buildscripts/pgsty-rpm-signing-key.asc
|
||||
key_id="$(printf '%s' "${expected_fingerprint}" | tail -c 8 | tr '[:upper:]' '[:lower:]')"
|
||||
for rpm_file in finalize/packages/*.rpm; do
|
||||
signature_output="$(sudo rpmkeys --checksig --verbose "${rpm_file}")"
|
||||
printf '%s\n' "${signature_output}"
|
||||
if ! printf '%s\n' "${signature_output}" | tr '[:upper:]' '[:lower:]' | grep -q "key id ${key_id}: ok"; then
|
||||
echo "Signature verification failed for ${rpm_file}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Regenerate RPM SBOMs and the packages checksum manifest
|
||||
env:
|
||||
SYFT_CHECK_FOR_APP_UPDATE: "false"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd finalize/packages
|
||||
for rpm_file in *.rpm; do
|
||||
rm -f "${rpm_file}.sbom.json"
|
||||
syft "${rpm_file}" --output "spdx-json=${rpm_file}.sbom.json"
|
||||
done
|
||||
|
||||
manifest="silo_${PKG_VERSION}_packages_checksums.txt"
|
||||
mapfile -t subjects < <(find . -maxdepth 1 -type f \
|
||||
\( -name '*.rpm' -o -name '*.deb' -o -name '*.apk' -o -name '*.sbom.json' \) | sort)
|
||||
if [ "${#subjects[@]}" -ne 12 ]; then
|
||||
echo "Expected six packages and six SBOMs, found ${#subjects[@]} subjects" >&2
|
||||
exit 1
|
||||
fi
|
||||
sha256sum "${subjects[@]}" | sed 's# \./# #' > "${manifest}"
|
||||
cosign sign-blob --bundle="${manifest}.sigstore.json" "${manifest}" --yes
|
||||
|
||||
- name: Attest the finalized package artifacts
|
||||
id: attest-finalize
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-path: |
|
||||
finalize/packages/*.rpm
|
||||
finalize/packages/*.rpm.sbom.json
|
||||
finalize/packages/*_checksums.txt
|
||||
finalize/packages/*_checksums.txt.sigstore.json
|
||||
finalize/sidecars/*.rpm.sha256sum
|
||||
|
||||
- name: Preserve finalize provenance bundle as a release asset
|
||||
env:
|
||||
BUNDLE_PATH: ${{ steps.attest-finalize.outputs.bundle-path }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -s "${BUNDLE_PATH}"
|
||||
cp "${BUNDLE_PATH}" "finalize/packages/silo_${PKG_VERSION}_packages_provenance.sigstore.json"
|
||||
|
||||
- name: Upload finalized assets to the Draft release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
eval "$(grep -E '^expected_release=' buildscripts/sign-release-rpms.sh)"
|
||||
manifest="silo_${PKG_VERSION}_packages_checksums.txt"
|
||||
cd finalize/packages
|
||||
files=(
|
||||
"silo-${PKG_VERSION}-${expected_release}.x86_64.rpm.sbom.json"
|
||||
"silo-${PKG_VERSION}-${expected_release}.aarch64.rpm.sbom.json"
|
||||
"${manifest}"
|
||||
"${manifest}.sigstore.json"
|
||||
"silo_${PKG_VERSION}_packages_provenance.sigstore.json"
|
||||
)
|
||||
gh release upload "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --clobber "${files[@]}"
|
||||
|
||||
for asset in "${files[@]}"; do
|
||||
local_digest="sha256:$(sha256sum "${asset}" | awk '{print $1}')"
|
||||
remote_digest=""
|
||||
for attempt in 1 2 3 4 5; do
|
||||
remote_digest="$(gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --json assets \
|
||||
--jq ".assets[] | select(.name == \"${asset}\") | .digest")"
|
||||
if [ "${local_digest}" = "${remote_digest}" ]; then
|
||||
break
|
||||
fi
|
||||
if [ "${attempt}" -lt 5 ]; then
|
||||
sleep 2
|
||||
fi
|
||||
done
|
||||
if [ "${local_digest}" != "${remote_digest}" ]; then
|
||||
echo "GitHub asset digest mismatch for ${asset}" >&2
|
||||
echo "Local: ${local_digest}" >&2
|
||||
echo "Remote: ${remote_digest}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Verified GitHub asset: ${asset} ${remote_digest}"
|
||||
done
|
||||
@@ -1,39 +0,0 @@
|
||||
name: Crosscompile
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Tests with Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Build on ${{ matrix.os }}
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GO111MODULE: on
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make crosscompile
|
||||
@@ -1,59 +0,0 @@
|
||||
name: FIPS Build Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go BoringCrypto ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Setup dockerfile for build test
|
||||
run: |
|
||||
GO_VERSION=$(go version | cut -d ' ' -f 3 | sed 's/go//')
|
||||
echo Detected go version $GO_VERSION
|
||||
cat > Dockerfile.fips.test <<EOF
|
||||
FROM golang:${GO_VERSION}
|
||||
COPY . /minio
|
||||
WORKDIR /minio
|
||||
ENV GOEXPERIMENT=boringcrypto
|
||||
RUN make
|
||||
EOF
|
||||
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.fips.test
|
||||
push: false
|
||||
load: true
|
||||
tags: minio/fips-test:latest
|
||||
|
||||
# This should fail if grep returns non-zero exit
|
||||
- name: Test binary
|
||||
run: |
|
||||
docker run --rm minio/fips-test:latest ./minio --version
|
||||
docker run --rm -i minio/fips-test:latest /bin/bash -c 'go tool nm ./minio | grep FIPS | grep -q FIPS'
|
||||
@@ -1,44 +0,0 @@
|
||||
name: Healing Functional Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Build on ${{ matrix.os }}
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GO111MODULE: on
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:oyArl7zlPECEduNbB1KXgdzDn2Bdpvvw0l8VO51HQnY="
|
||||
MINIO_KMS_AUTO_ENCRYPTION: on
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make verify-healing
|
||||
make verify-healing-inconsistent-versions
|
||||
make verify-healing-with-root-disks
|
||||
make verify-healing-with-rewrite
|
||||
@@ -1,42 +0,0 @@
|
||||
name: Linters and Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Build on ${{ matrix.os }}
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GO111MODULE: on
|
||||
run: |
|
||||
sudo apt install jq -y
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make
|
||||
make test
|
||||
make test-race
|
||||
@@ -1,39 +0,0 @@
|
||||
name: Resiliency Functional Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Build on ${{ matrix.os }}
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GO111MODULE: on
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-resiliency
|
||||
+137
-23
@@ -1,42 +1,156 @@
|
||||
name: Functional Tests
|
||||
name: Go CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
# Cancel superseded runs for the same PR; never cancel main push runs.
|
||||
# Keyed on PR number (not head_ref) so fork PRs sharing a branch name
|
||||
# do not collide.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }} - healing
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
os: [ubuntu-latest]
|
||||
verify:
|
||||
name: Format, Build, Vet
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Build on ${{ matrix.os }}
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Check gofmt
|
||||
run: |
|
||||
mapfile -t unformatted < <(gofmt -l main.go cmd internal \
|
||||
buildscripts/rebrand-guard buildscripts/helm-migration-guard)
|
||||
if [ "${#unformatted[@]}" -ne 0 ]; then
|
||||
echo "The following files are not gofmt-formatted:"
|
||||
printf '%s\n' "${unformatted[@]}"
|
||||
gofmt -d "${unformatted[@]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GO111MODULE: on
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw="
|
||||
MINIO_KMS_AUTO_ENCRYPTION: on
|
||||
run: go build ./...
|
||||
|
||||
- name: Vet
|
||||
run: go vet ./...
|
||||
|
||||
- name: Verify rebrand compatibility contracts
|
||||
run: |
|
||||
go run ./buildscripts/rebrand-guard
|
||||
buildscripts/verify-rebrand.sh
|
||||
dockerscripts/docker-entrypoint_test.sh
|
||||
|
||||
quality:
|
||||
name: Lint, Generated Files
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Lint
|
||||
run: make lint
|
||||
|
||||
- name: Check generated files
|
||||
run: make check-gen
|
||||
|
||||
race-s3select:
|
||||
name: Race, S3 Select
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Run S3 Select tests under race detector
|
||||
run: go test -race ./internal/s3select/... -count=1
|
||||
|
||||
crosscompile:
|
||||
name: Cross Compile
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Build supported targets
|
||||
run: make crosscompile
|
||||
|
||||
test-internal:
|
||||
name: Test internal/
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
# internal/http listener tests bind [::1]; runners disable IPv6 by default.
|
||||
- name: Enable IPv6
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make verify
|
||||
make test-timeout
|
||||
|
||||
- name: Run internal tests
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
MINIO_API_REQUESTS_MAX: "10000"
|
||||
run: go test ./internal/... -count=1
|
||||
|
||||
test-cmd:
|
||||
name: Test cmd/
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 35
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
# Some server tests bind IPv6 listeners; runners disable IPv6 by default.
|
||||
- name: Enable IPv6
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
|
||||
- name: Run cmd tests
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
MINIO_API_REQUESTS_MAX: "10000"
|
||||
# cmd/ is one large package; raise go test's default 10m per-package
|
||||
# timeout so slower runners fail on the job timeout, not a panic.
|
||||
run: go test ./cmd/ -count=1 -timeout 30m
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
name: Helm Chart linting
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v4
|
||||
|
||||
- name: Run helm lint
|
||||
run: |
|
||||
cd helm/minio
|
||||
helm lint .
|
||||
@@ -1,161 +0,0 @@
|
||||
name: IAM integration
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
iam-matrix-test:
|
||||
name: "[Go=${{ matrix.go-version }}|ldap=${{ matrix.ldap }}|etcd=${{ matrix.etcd }}|openid=${{ matrix.openid }}]"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
openldap:
|
||||
image: quay.io/minio/openldap
|
||||
ports:
|
||||
- "389:389"
|
||||
- "636:636"
|
||||
env:
|
||||
LDAP_ORGANIZATION: "MinIO Inc"
|
||||
LDAP_DOMAIN: "min.io"
|
||||
LDAP_ADMIN_PASSWORD: "admin"
|
||||
etcd:
|
||||
image: "quay.io/coreos/etcd:v3.5.1"
|
||||
env:
|
||||
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
|
||||
ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2379"
|
||||
ports:
|
||||
- "2379:2379"
|
||||
options: >-
|
||||
--health-cmd "etcdctl endpoint health"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
openid:
|
||||
image: quay.io/minio/dex
|
||||
ports:
|
||||
- "5556:5556"
|
||||
env:
|
||||
DEX_LDAP_SERVER: "openldap:389"
|
||||
openid2:
|
||||
image: quay.io/minio/dex
|
||||
ports:
|
||||
- "5557:5557"
|
||||
env:
|
||||
DEX_LDAP_SERVER: "openldap:389"
|
||||
DEX_ISSUER: "http://127.0.0.1:5557/dex"
|
||||
DEX_WEB_HTTP: "0.0.0.0:5557"
|
||||
|
||||
strategy:
|
||||
# When ldap, etcd or openid vars are empty below, those external servers
|
||||
# are turned off - i.e. if ldap="", then ldap server is not enabled for
|
||||
# the tests.
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
ldap: ["", "localhost:389"]
|
||||
etcd: ["", "http://localhost:2379"]
|
||||
openid: ["", "http://127.0.0.1:5556/dex"]
|
||||
exclude:
|
||||
# exclude combos where all are empty.
|
||||
- ldap: ""
|
||||
etcd: ""
|
||||
openid: ""
|
||||
# exclude combos where both ldap and openid IDPs are specified.
|
||||
- ldap: "localhost:389"
|
||||
openid: "http://127.0.0.1:5556/dex"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Test LDAP/OpenID/Etcd combo
|
||||
env:
|
||||
_MINIO_LDAP_TEST_SERVER: ${{ matrix.ldap }}
|
||||
_MINIO_ETCD_TEST_SERVER: ${{ matrix.etcd }}
|
||||
_MINIO_OPENID_TEST_SERVER: ${{ matrix.openid }}
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-iam
|
||||
- name: Test with multiple OpenID providers
|
||||
if: matrix.openid == 'http://127.0.0.1:5556/dex'
|
||||
env:
|
||||
_MINIO_LDAP_TEST_SERVER: ${{ matrix.ldap }}
|
||||
_MINIO_ETCD_TEST_SERVER: ${{ matrix.etcd }}
|
||||
_MINIO_OPENID_TEST_SERVER: ${{ matrix.openid }}
|
||||
_MINIO_OPENID_TEST_SERVER_2: "http://127.0.0.1:5557/dex"
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-iam
|
||||
- name: Test with Access Management Plugin enabled
|
||||
env:
|
||||
_MINIO_LDAP_TEST_SERVER: ${{ matrix.ldap }}
|
||||
_MINIO_ETCD_TEST_SERVER: ${{ matrix.etcd }}
|
||||
_MINIO_OPENID_TEST_SERVER: ${{ matrix.openid }}
|
||||
_MINIO_POLICY_PLUGIN_TEST_ENDPOINT: "http://127.0.0.1:8080"
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
go run docs/iam/access-manager-plugin.go &
|
||||
make test-iam
|
||||
- name: Test MinIO Old Version data to IAM import current version
|
||||
if: matrix.ldap == 'ldaphost:389'
|
||||
env:
|
||||
_MINIO_LDAP_TEST_SERVER: ${{ matrix.ldap }}
|
||||
run: |
|
||||
make test-iam-ldap-upgrade-import
|
||||
- name: Test LDAP for automatic site replication
|
||||
if: matrix.ldap == 'localhost:389'
|
||||
run: |
|
||||
make test-site-replication-ldap
|
||||
- name: Test OIDC for automatic site replication
|
||||
if: matrix.openid == 'http://127.0.0.1:5556/dex'
|
||||
run: |
|
||||
make test-site-replication-oidc
|
||||
iam-import-with-missing-entities:
|
||||
name: Test IAM import in new cluster with missing entities
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Checkout minio-iam-testing
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: minio/minio-iam-testing
|
||||
path: minio-iam-testing
|
||||
- name: Test import of IAM artifacts when in fresh cluster there are missing groups etc
|
||||
run: |
|
||||
make test-iam-import-with-missing-entities
|
||||
iam-import-with-openid:
|
||||
name: Test IAM import in new cluster with opendid configurations
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Checkout minio-iam-testing
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: minio/minio-iam-testing
|
||||
path: minio-iam-testing
|
||||
- name: Test import of IAM artifacts when in fresh cluster with openid configurations
|
||||
run: |
|
||||
make test-iam-import-with-openid
|
||||
@@ -1,18 +0,0 @@
|
||||
# @format
|
||||
|
||||
name: Issue Workflow
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add issue to project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v0.5.0
|
||||
with:
|
||||
project-url: https://github.com/orgs/miniohq/projects/2
|
||||
github-token: ${{ secrets.BOT_PAT }}
|
||||
@@ -1,24 +0,0 @@
|
||||
name: 'Lock Threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
concurrency:
|
||||
group: lock
|
||||
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v3
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '365'
|
||||
exclude-any-issue-labels: 'do-not-close'
|
||||
issue-lock-reason: 'resolved'
|
||||
log-output: true
|
||||
@@ -1,81 +0,0 @@
|
||||
name: Mint Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
mint-test:
|
||||
runs-on: mint
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- name: cleanup #https://github.com/actions/checkout/issues/273
|
||||
run: |
|
||||
sudo -S rm -rf ${GITHUB_WORKSPACE}
|
||||
mkdir ${GITHUB_WORKSPACE}
|
||||
- name: checkout-step
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-go-step
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.23.x
|
||||
|
||||
- name: github sha short
|
||||
id: vars
|
||||
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: build-minio
|
||||
run: |
|
||||
TAG="quay.io/minio/minio:${{ steps.vars.outputs.sha_short }}" make docker
|
||||
|
||||
- name: multipart uploads test
|
||||
run: |
|
||||
${GITHUB_WORKSPACE}/.github/workflows/multipart/migrate.sh "${{ steps.vars.outputs.sha_short }}"
|
||||
|
||||
- name: compress and encrypt
|
||||
run: |
|
||||
${GITHUB_WORKSPACE}/.github/workflows/run-mint.sh "compress-encrypt" "minio" "minio123" "${{ steps.vars.outputs.sha_short }}"
|
||||
|
||||
- name: multiple pools
|
||||
run: |
|
||||
${GITHUB_WORKSPACE}/.github/workflows/run-mint.sh "pools" "minio" "minio123" "${{ steps.vars.outputs.sha_short }}"
|
||||
|
||||
- name: standalone erasure
|
||||
run: |
|
||||
${GITHUB_WORKSPACE}/.github/workflows/run-mint.sh "erasure" "minio" "minio123" "${{ steps.vars.outputs.sha_short }}"
|
||||
|
||||
# FIXME: renable this back when we have a valid way to add deadlines for PUT()s (internode CreateFile)
|
||||
# - name: resiliency
|
||||
# run: |
|
||||
# ${GITHUB_WORKSPACE}/.github/workflows/run-mint.sh "resiliency" "minio" "minio123" "${{ steps.vars.outputs.sha_short }}"
|
||||
|
||||
- name: The job must cleanup
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
export JOB_NAME=${{ steps.vars.outputs.sha_short }}
|
||||
for mode in $(echo compress-encrypt pools erasure); do
|
||||
docker-compose -f ${GITHUB_WORKSPACE}/.github/workflows/mint/minio-${mode}.yaml down || true
|
||||
docker-compose -f ${GITHUB_WORKSPACE}/.github/workflows/mint/minio-${mode}.yaml rm || true
|
||||
done
|
||||
|
||||
docker-compose -f ${GITHUB_WORKSPACE}/.github/workflows/multipart/docker-compose-site1.yaml rm -s -f || true
|
||||
docker-compose -f ${GITHUB_WORKSPACE}/.github/workflows/multipart/docker-compose-site2.yaml rm -s -f || true
|
||||
for volume in $(docker volume ls -q | grep minio); do
|
||||
docker volume rm ${volume} || true
|
||||
done
|
||||
|
||||
docker rmi -f quay.io/minio/minio:${{ steps.vars.outputs.sha_short }}
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -q -f dangling=true) || true
|
||||
@@ -1,80 +0,0 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${JOB_NAME}
|
||||
command: server --console-address ":9001" http://minio{1...4}/cdata{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
- "9001"
|
||||
environment:
|
||||
MINIO_CI_CD: "on"
|
||||
MINIO_ROOT_USER: "minio"
|
||||
MINIO_ROOT_PASSWORD: "minio123"
|
||||
MINIO_COMPRESSION_ENABLE: "on"
|
||||
MINIO_COMPRESSION_MIME_TYPES: "*"
|
||||
MINIO_COMPRESSION_ALLOW_ENCRYPTION: "on"
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw="
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
minio1:
|
||||
<<: *minio-common
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- cdata1-1:/cdata1
|
||||
- cdata1-2:/cdata2
|
||||
|
||||
minio2:
|
||||
<<: *minio-common
|
||||
hostname: minio2
|
||||
volumes:
|
||||
- cdata2-1:/cdata1
|
||||
- cdata2-2:/cdata2
|
||||
|
||||
minio3:
|
||||
<<: *minio-common
|
||||
hostname: minio3
|
||||
volumes:
|
||||
- cdata3-1:/cdata1
|
||||
- cdata3-2:/cdata2
|
||||
|
||||
minio4:
|
||||
<<: *minio-common
|
||||
hostname: minio4
|
||||
volumes:
|
||||
- cdata4-1:/cdata1
|
||||
- cdata4-2:/cdata2
|
||||
|
||||
nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: nginx
|
||||
volumes:
|
||||
- ./nginx-4-node.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- minio1
|
||||
- minio2
|
||||
- minio3
|
||||
- minio4
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
cdata1-1:
|
||||
cdata1-2:
|
||||
cdata2-1:
|
||||
cdata2-2:
|
||||
cdata3-1:
|
||||
cdata3-2:
|
||||
cdata4-1:
|
||||
cdata4-2:
|
||||
@@ -1,51 +0,0 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${JOB_NAME}
|
||||
command: server --console-address ":9001" edata{1...4}
|
||||
expose:
|
||||
- "9000"
|
||||
- "9001"
|
||||
environment:
|
||||
MINIO_CI_CD: "on"
|
||||
MINIO_ROOT_USER: "minio"
|
||||
MINIO_ROOT_PASSWORD: "minio123"
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw="
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
minio1:
|
||||
<<: *minio-common
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- edata1-1:/edata1
|
||||
- edata1-2:/edata2
|
||||
- edata1-3:/edata3
|
||||
- edata1-4:/edata4
|
||||
|
||||
nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: nginx
|
||||
volumes:
|
||||
- ./nginx-1-node.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- minio1
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
edata1-1:
|
||||
edata1-2:
|
||||
edata1-3:
|
||||
edata1-4:
|
||||
@@ -1,117 +0,0 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${JOB_NAME}
|
||||
command: server --console-address ":9001" http://minio{1...4}/pdata{1...2} http://minio{5...8}/pdata{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
- "9001"
|
||||
environment:
|
||||
MINIO_CI_CD: "on"
|
||||
MINIO_ROOT_USER: "minio"
|
||||
MINIO_ROOT_PASSWORD: "minio123"
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw="
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
minio1:
|
||||
<<: *minio-common
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- pdata1-1:/pdata1
|
||||
- pdata1-2:/pdata2
|
||||
|
||||
minio2:
|
||||
<<: *minio-common
|
||||
hostname: minio2
|
||||
volumes:
|
||||
- pdata2-1:/pdata1
|
||||
- pdata2-2:/pdata2
|
||||
|
||||
minio3:
|
||||
<<: *minio-common
|
||||
hostname: minio3
|
||||
volumes:
|
||||
- pdata3-1:/pdata1
|
||||
- pdata3-2:/pdata2
|
||||
|
||||
minio4:
|
||||
<<: *minio-common
|
||||
hostname: minio4
|
||||
volumes:
|
||||
- pdata4-1:/pdata1
|
||||
- pdata4-2:/pdata2
|
||||
|
||||
minio5:
|
||||
<<: *minio-common
|
||||
hostname: minio5
|
||||
volumes:
|
||||
- pdata5-1:/pdata1
|
||||
- pdata5-2:/pdata2
|
||||
|
||||
minio6:
|
||||
<<: *minio-common
|
||||
hostname: minio6
|
||||
volumes:
|
||||
- pdata6-1:/pdata1
|
||||
- pdata6-2:/pdata2
|
||||
|
||||
minio7:
|
||||
<<: *minio-common
|
||||
hostname: minio7
|
||||
volumes:
|
||||
- pdata7-1:/pdata1
|
||||
- pdata7-2:/pdata2
|
||||
|
||||
minio8:
|
||||
<<: *minio-common
|
||||
hostname: minio8
|
||||
volumes:
|
||||
- pdata8-1:/pdata1
|
||||
- pdata8-2:/pdata2
|
||||
|
||||
nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: nginx
|
||||
volumes:
|
||||
- ./nginx-8-node.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- minio1
|
||||
- minio2
|
||||
- minio3
|
||||
- minio4
|
||||
- minio5
|
||||
- minio6
|
||||
- minio7
|
||||
- minio8
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
pdata1-1:
|
||||
pdata1-2:
|
||||
pdata2-1:
|
||||
pdata2-2:
|
||||
pdata3-1:
|
||||
pdata3-2:
|
||||
pdata4-1:
|
||||
pdata4-2:
|
||||
pdata5-1:
|
||||
pdata5-2:
|
||||
pdata6-1:
|
||||
pdata6-2:
|
||||
pdata7-1:
|
||||
pdata7-2:
|
||||
pdata8-1:
|
||||
pdata8-2:
|
||||
@@ -1,78 +0,0 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${JOB_NAME}
|
||||
command: server --console-address ":9001" http://minio{1...4}/rdata{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
- "9001"
|
||||
environment:
|
||||
MINIO_CI_CD: "on"
|
||||
MINIO_ROOT_USER: "minio"
|
||||
MINIO_ROOT_PASSWORD: "minio123"
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw="
|
||||
MINIO_DRIVE_MAX_TIMEOUT: "5s"
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
minio1:
|
||||
<<: *minio-common
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- rdata1-1:/rdata1
|
||||
- rdata1-2:/rdata2
|
||||
|
||||
minio2:
|
||||
<<: *minio-common
|
||||
hostname: minio2
|
||||
volumes:
|
||||
- rdata2-1:/rdata1
|
||||
- rdata2-2:/rdata2
|
||||
|
||||
minio3:
|
||||
<<: *minio-common
|
||||
hostname: minio3
|
||||
volumes:
|
||||
- rdata3-1:/rdata1
|
||||
- rdata3-2:/rdata2
|
||||
|
||||
minio4:
|
||||
<<: *minio-common
|
||||
hostname: minio4
|
||||
volumes:
|
||||
- rdata4-1:/rdata1
|
||||
- rdata4-2:/rdata2
|
||||
|
||||
nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: nginx
|
||||
volumes:
|
||||
- ./nginx-4-node.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- minio1
|
||||
- minio2
|
||||
- minio3
|
||||
- minio4
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
rdata1-1:
|
||||
rdata1-2:
|
||||
rdata2-1:
|
||||
rdata2-2:
|
||||
rdata3-1:
|
||||
rdata3-2:
|
||||
rdata4-1:
|
||||
rdata4-2:
|
||||
@@ -1,100 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server minio1:9000;
|
||||
}
|
||||
|
||||
upstream console {
|
||||
ip_hash;
|
||||
server minio1:9001;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9000;
|
||||
listen [::]:9000;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9001;
|
||||
listen [::]:9001;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
|
||||
# This is necessary to pass the correct IP to be hashed
|
||||
real_ip_header X-Real-IP;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
|
||||
# To support websocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://console;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server minio1:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio2:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio3:9000 max_fails=1 fail_timeout=10s;
|
||||
}
|
||||
|
||||
upstream console {
|
||||
ip_hash;
|
||||
server minio1:9001;
|
||||
server minio2:9001;
|
||||
server minio3:9001;
|
||||
server minio4:9001;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9000;
|
||||
listen [::]:9000;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9001;
|
||||
listen [::]:9001;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
|
||||
# This is necessary to pass the correct IP to be hashed
|
||||
real_ip_header X-Real-IP;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
|
||||
# To support websocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://console;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server minio1:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio2:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio3:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio4:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio5:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio6:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio7:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio8:9000 max_fails=1 fail_timeout=10s;
|
||||
}
|
||||
|
||||
upstream console {
|
||||
ip_hash;
|
||||
server minio1:9001;
|
||||
server minio2:9001;
|
||||
server minio3:9001;
|
||||
server minio4:9001;
|
||||
server minio5:9001;
|
||||
server minio6:9001;
|
||||
server minio7:9001;
|
||||
server minio8:9001;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9000;
|
||||
listen [::]:9000;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9001;
|
||||
listen [::]:9001;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
|
||||
# This is necessary to pass the correct IP to be hashed
|
||||
real_ip_header X-Real-IP;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
|
||||
# To support websocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://console;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server minio1:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio2:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio3:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio4:9000 max_fails=1 fail_timeout=10s;
|
||||
}
|
||||
|
||||
upstream console {
|
||||
ip_hash;
|
||||
server minio1:9001;
|
||||
server minio2:9001;
|
||||
server minio3:9001;
|
||||
server minio4:9001;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9000;
|
||||
listen [::]:9000;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9001;
|
||||
listen [::]:9001;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
|
||||
# This is necessary to pass the correct IP to be hashed
|
||||
real_ip_header X-Real-IP;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
|
||||
# To support websocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://console;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${RELEASE}
|
||||
command: server http://site1-minio{1...4}/data{1...2}
|
||||
environment:
|
||||
- MINIO_PROMETHEUS_AUTH_TYPE=public
|
||||
- CI=true
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
site1-minio1:
|
||||
<<: *minio-common
|
||||
hostname: site1-minio1
|
||||
volumes:
|
||||
- site1-data1-1:/data1
|
||||
- site1-data1-2:/data2
|
||||
|
||||
site1-minio2:
|
||||
<<: *minio-common
|
||||
hostname: site1-minio2
|
||||
volumes:
|
||||
- site1-data2-1:/data1
|
||||
- site1-data2-2:/data2
|
||||
|
||||
site1-minio3:
|
||||
<<: *minio-common
|
||||
hostname: site1-minio3
|
||||
volumes:
|
||||
- site1-data3-1:/data1
|
||||
- site1-data3-2:/data2
|
||||
|
||||
site1-minio4:
|
||||
<<: *minio-common
|
||||
hostname: site1-minio4
|
||||
volumes:
|
||||
- site1-data4-1:/data1
|
||||
- site1-data4-2:/data2
|
||||
|
||||
site1-nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: site1-nginx
|
||||
volumes:
|
||||
- ./nginx-site1.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- site1-minio1
|
||||
- site1-minio2
|
||||
- site1-minio3
|
||||
- site1-minio4
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
site1-data1-1:
|
||||
site1-data1-2:
|
||||
site1-data2-1:
|
||||
site1-data2-2:
|
||||
site1-data3-1:
|
||||
site1-data3-2:
|
||||
site1-data4-1:
|
||||
site1-data4-2:
|
||||
@@ -1,66 +0,0 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${RELEASE}
|
||||
command: server http://site2-minio{1...4}/data{1...2}
|
||||
environment:
|
||||
- MINIO_PROMETHEUS_AUTH_TYPE=public
|
||||
- CI=true
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
site2-minio1:
|
||||
<<: *minio-common
|
||||
hostname: site2-minio1
|
||||
volumes:
|
||||
- site2-data1-1:/data1
|
||||
- site2-data1-2:/data2
|
||||
|
||||
site2-minio2:
|
||||
<<: *minio-common
|
||||
hostname: site2-minio2
|
||||
volumes:
|
||||
- site2-data2-1:/data1
|
||||
- site2-data2-2:/data2
|
||||
|
||||
site2-minio3:
|
||||
<<: *minio-common
|
||||
hostname: site2-minio3
|
||||
volumes:
|
||||
- site2-data3-1:/data1
|
||||
- site2-data3-2:/data2
|
||||
|
||||
site2-minio4:
|
||||
<<: *minio-common
|
||||
hostname: site2-minio4
|
||||
volumes:
|
||||
- site2-data4-1:/data1
|
||||
- site2-data4-2:/data2
|
||||
|
||||
site2-nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: site2-nginx
|
||||
volumes:
|
||||
- ./nginx-site2.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9002:9002"
|
||||
depends_on:
|
||||
- site2-minio1
|
||||
- site2-minio2
|
||||
- site2-minio3
|
||||
- site2-minio4
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
site2-data1-1:
|
||||
site2-data1-2:
|
||||
site2-data2-1:
|
||||
site2-data2-2:
|
||||
site2-data3-1:
|
||||
site2-data3-2:
|
||||
site2-data4-1:
|
||||
site2-data4-2:
|
||||
@@ -1,147 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
## change working directory
|
||||
cd .github/workflows/multipart/
|
||||
|
||||
function cleanup() {
|
||||
docker-compose -f docker-compose-site1.yaml rm -s -f || true
|
||||
docker-compose -f docker-compose-site2.yaml rm -s -f || true
|
||||
for volume in $(docker volume ls -q | grep minio); do
|
||||
docker volume rm ${volume} || true
|
||||
done
|
||||
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -q -f dangling=true) || true
|
||||
}
|
||||
|
||||
cleanup
|
||||
|
||||
if [ ! -f ./mc ]; then
|
||||
wget --quiet -O mc https://dl.minio.io/client/mc/release/linux-amd64/mc &&
|
||||
chmod +x mc
|
||||
fi
|
||||
|
||||
export RELEASE=RELEASE.2023-08-29T23-07-35Z
|
||||
|
||||
docker-compose -f docker-compose-site1.yaml up -d
|
||||
docker-compose -f docker-compose-site2.yaml up -d
|
||||
|
||||
sleep 30s
|
||||
|
||||
./mc alias set site1 http://site1-nginx:9001 minioadmin minioadmin --api s3v4
|
||||
./mc alias set site2 http://site2-nginx:9002 minioadmin minioadmin --api s3v4
|
||||
|
||||
./mc ready site1/
|
||||
./mc ready site2/
|
||||
|
||||
./mc admin replicate add site1 site2
|
||||
./mc mb site1/testbucket/
|
||||
./mc cp -r --quiet /usr/bin site1/testbucket/
|
||||
|
||||
sleep 5
|
||||
|
||||
./s3-check-md5 -h
|
||||
|
||||
failed_count_site1=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site1-nginx:9001 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
failed_count_site2=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site2-nginx:9002 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
|
||||
if [ $failed_count_site1 -ne 0 ]; then
|
||||
echo "failed with multipart on site1 uploads"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $failed_count_site2 -ne 0 ]; then
|
||||
echo "failed with multipart on site2 uploads"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./mc cp -r --quiet /usr/bin site1/testbucket/
|
||||
|
||||
sleep 5
|
||||
|
||||
failed_count_site1=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site1-nginx:9001 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
failed_count_site2=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site2-nginx:9002 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
|
||||
## we do not need to fail here, since we are going to test
|
||||
## upgrading to master, healing and being able to recover
|
||||
## the last version.
|
||||
if [ $failed_count_site1 -ne 0 ]; then
|
||||
echo "failed with multipart on site1 uploads ${failed_count_site1}"
|
||||
fi
|
||||
|
||||
if [ $failed_count_site2 -ne 0 ]; then
|
||||
echo "failed with multipart on site2 uploads ${failed_count_site2}"
|
||||
fi
|
||||
|
||||
export RELEASE=${1}
|
||||
|
||||
docker-compose -f docker-compose-site1.yaml up -d
|
||||
docker-compose -f docker-compose-site2.yaml up -d
|
||||
|
||||
./mc ready site1/
|
||||
./mc ready site2/
|
||||
|
||||
for i in $(seq 1 10); do
|
||||
# mc admin heal -r --remove when used against a LB endpoint
|
||||
# behaves flaky, let this run 10 times before giving up
|
||||
./mc admin heal -r --remove --json site1/ 2>&1 >/dev/null
|
||||
./mc admin heal -r --remove --json site2/ 2>&1 >/dev/null
|
||||
done
|
||||
|
||||
failed_count_site1=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site1-nginx:9001 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
failed_count_site2=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site2-nginx:9002 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
|
||||
if [ $failed_count_site1 -ne 0 ]; then
|
||||
echo "failed with multipart on site1 uploads"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $failed_count_site2 -ne 0 ]; then
|
||||
echo "failed with multipart on site2 uploads"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Add user group test
|
||||
./mc admin user add site1 site-replication-issue-user site-replication-issue-password
|
||||
./mc admin group add site1 site-replication-issue-group site-replication-issue-user
|
||||
|
||||
max_wait_attempts=30
|
||||
wait_interval=5
|
||||
|
||||
attempt=1
|
||||
while true; do
|
||||
diff <(./mc admin group info site1 site-replication-issue-group) <(./mc admin group info site2 site-replication-issue-group)
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "Outputs are consistent."
|
||||
break
|
||||
fi
|
||||
|
||||
remaining_attempts=$((max_wait_attempts - attempt))
|
||||
if ((attempt >= max_wait_attempts)); then
|
||||
echo "Outputs remain inconsistent after $max_wait_attempts attempts. Exiting with error."
|
||||
exit 1
|
||||
else
|
||||
echo "Outputs are inconsistent. Waiting for $wait_interval seconds (attempt $attempt/$max_wait_attempts)."
|
||||
sleep $wait_interval
|
||||
fi
|
||||
|
||||
((attempt++))
|
||||
done
|
||||
|
||||
status=$(./mc admin group info site1 site-replication-issue-group --json | jq .groupStatus | tr -d '"')
|
||||
|
||||
if [[ $status == "enabled" ]]; then
|
||||
echo "Success"
|
||||
else
|
||||
echo "Expected status: enabled, actual status: $status"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup
|
||||
|
||||
## change working directory
|
||||
cd ../../../
|
||||
@@ -1,61 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server site1-minio1:9000;
|
||||
server site1-minio2:9000;
|
||||
server site1-minio3:9000;
|
||||
server site1-minio4:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9001;
|
||||
listen [::]:9001;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server site2-minio1:9000;
|
||||
server site2-minio2:9000;
|
||||
server site2-minio3:9000;
|
||||
server site2-minio4:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9002;
|
||||
listen [::]:9002;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "RELEASE.*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Release tag (e.g. RELEASE.2026-03-24T12-00-00Z)"
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
artifact-metadata: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Build the code at the tag being released, not whatever branch the
|
||||
# dispatch ran from. On a tag push this is the tag ref already; on
|
||||
# workflow_dispatch it pins the checkout to the requested tag so the
|
||||
# artifacts cannot be built from one ref and published under another.
|
||||
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Verify clean checkout
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# GitHub's OIDC certificate records GITHUB_SHA, not the ref passed to
|
||||
# actions/checkout. A manual dispatch must therefore be launched from
|
||||
# the release tag itself; otherwise the provenance identity would
|
||||
# describe different source from the bytes being published.
|
||||
CHECKED_OUT_REVISION="$(git rev-parse HEAD)"
|
||||
if [ "${CHECKED_OUT_REVISION}" != "${GITHUB_SHA}" ]; then
|
||||
echo "Checked out ${CHECKED_OUT_REVISION}, but workflow identity is ${GITHUB_SHA}. Dispatch from the release tag." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "Refusing to release from a dirty working tree:" >&2
|
||||
git status --porcelain >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify rebrand compatibility contracts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
go run ./buildscripts/rebrand-guard
|
||||
buildscripts/verify-rebrand.sh
|
||||
dockerscripts/docker-entrypoint_test.sh
|
||||
|
||||
- name: Compute release variables
|
||||
env:
|
||||
# Passed through the environment, never interpolated into the script
|
||||
# body: a dispatch input reaches bash as data, so it cannot inject
|
||||
# commands the way a `${{ ... }}` splice into the source would.
|
||||
INPUT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="${INPUT_TAG:-${GITHUB_REF_NAME}}"
|
||||
# Whitelist the exact tag shape before the value is used anywhere. bash
|
||||
# =~ anchors ^...$ to the whole string (not per line, as sed would), so
|
||||
# a tag carrying a newline cannot pass and then smuggle extra lines into
|
||||
# $GITHUB_ENV below.
|
||||
if [[ ! "${TAG}" =~ ^RELEASE\.[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}Z$ ]]; then
|
||||
echo "Invalid release tag format: ${TAG}" >&2
|
||||
exit 1
|
||||
fi
|
||||
VERSION_HYPHEN="${TAG#RELEASE.}"
|
||||
PKG_VERSION="$(echo "${VERSION_HYPHEN}" | sed -E 's/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2})-([0-9]{2})-([0-9]{2})Z$/\1\2\3\4\5\6.0.0/')"
|
||||
VERSION_COLON="$(echo "${VERSION_HYPHEN}" | sed -E 's/T([0-9]{2})-([0-9]{2})-([0-9]{2})Z$/T\1:\2:\3Z/')"
|
||||
LDFLAGS="$(MINIO_RELEASE=RELEASE go run buildscripts/gen-ldflags.go "${VERSION_COLON}")"
|
||||
|
||||
{
|
||||
echo "RELEASE_TAG=${TAG}"
|
||||
echo "PKG_VERSION=${PKG_VERSION}"
|
||||
echo "LDFLAGS=${LDFLAGS}"
|
||||
} >> "${GITHUB_ENV}"
|
||||
|
||||
echo "Release tag: ${TAG}"
|
||||
echo "Package version: ${PKG_VERSION}"
|
||||
echo "LDFLAGS: ${LDFLAGS}"
|
||||
|
||||
# Both installer actions are pinned to immutable commits. The explicit
|
||||
# tool versions keep the release format reproducible across workflow
|
||||
# reruns while the installers verify the downloaded executables.
|
||||
- name: Install Syft
|
||||
uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
|
||||
with:
|
||||
syft-version: v1.50.0
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
with:
|
||||
cosign-release: v3.1.2
|
||||
|
||||
- name: Build Draft release with GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v7
|
||||
with:
|
||||
version: "~> v2"
|
||||
args: release --clean --skip=validate --config .github/goreleaser.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
LDFLAGS: ${{ env.LDFLAGS }}
|
||||
PKG_VERSION: ${{ env.PKG_VERSION }}
|
||||
|
||||
- name: Verify binary provenance stamps
|
||||
run: |
|
||||
set -euo pipefail
|
||||
buildscripts/verify-build-provenance.sh
|
||||
|
||||
- name: Install nFPM
|
||||
run: |
|
||||
set -euo pipefail
|
||||
go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.47.0
|
||||
echo "$(go env GOPATH)/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: Build nFPM packages
|
||||
run: |
|
||||
set -euo pipefail
|
||||
buildscripts/package-release.sh
|
||||
|
||||
- name: Generate package SBOMs and signed checksum manifest
|
||||
env:
|
||||
SYFT_CHECK_FOR_APP_UPDATE: "false"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
packages_dir="dist/packages"
|
||||
mapfile -t packages < <(find "${packages_dir}" -maxdepth 1 -type f \
|
||||
\( -name '*.rpm' -o -name '*.deb' -o -name '*.apk' \) | sort)
|
||||
if [ "${#packages[@]}" -ne 6 ]; then
|
||||
echo "Expected six Linux packages, found ${#packages[@]}" >&2
|
||||
printf '%s\n' "${packages[@]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for package in "${packages[@]}"; do
|
||||
syft "${package}" --output "spdx-json=${package}.sbom.json"
|
||||
done
|
||||
|
||||
manifest="${packages_dir}/silo_${PKG_VERSION}_packages_checksums.txt"
|
||||
(
|
||||
cd "${packages_dir}"
|
||||
mapfile -t subjects < <(find . -maxdepth 1 -type f \
|
||||
\( -name '*.rpm' -o -name '*.deb' -o -name '*.apk' -o -name '*.sbom.json' \) | sort)
|
||||
if [ "${#subjects[@]}" -ne 12 ]; then
|
||||
echo "Expected six packages and six SBOMs, found ${#subjects[@]} subjects" >&2
|
||||
exit 1
|
||||
fi
|
||||
sha256sum "${subjects[@]}" | sed 's# \./# #' > "$(basename "${manifest}")"
|
||||
)
|
||||
cosign sign-blob --bundle="${manifest}.sigstore.json" "${manifest}" --yes
|
||||
|
||||
- name: Attest downloadable release artifacts
|
||||
id: attest-release
|
||||
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
||||
with:
|
||||
subject-path: |
|
||||
dist/*.tar.gz
|
||||
dist/*.zip
|
||||
dist/*.sbom.json
|
||||
dist/*_checksums.txt
|
||||
dist/*.sigstore.json
|
||||
dist/packages/*.rpm
|
||||
dist/packages/*.deb
|
||||
dist/packages/*.apk
|
||||
dist/packages/*.sha256sum
|
||||
dist/packages/*.sbom.json
|
||||
dist/packages/*_checksums.txt
|
||||
dist/packages/*.sigstore.json
|
||||
|
||||
- name: Preserve provenance bundle as a release asset
|
||||
env:
|
||||
BUNDLE_PATH: ${{ steps.attest-release.outputs.bundle-path }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -s "${BUNDLE_PATH}"
|
||||
cp "${BUNDLE_PATH}" "dist/silo_${PKG_VERSION}_provenance.sigstore.json"
|
||||
|
||||
- name: Upload nFPM packages to Draft release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mapfile -t files < <(find dist/packages -maxdepth 1 -type f \
|
||||
\( -name '*.rpm' -o -name '*.deb' -o -name '*.apk' -o -name '*.sha256sum' \
|
||||
-o -name '*.sbom.json' -o -name '*_checksums.txt' -o -name '*.sigstore.json' \) | sort)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No packages were generated."
|
||||
exit 1
|
||||
fi
|
||||
gh release upload "${RELEASE_TAG}" "${files[@]}" \
|
||||
"dist/silo_${PKG_VERSION}_provenance.sigstore.json"
|
||||
|
||||
- name: Upload dist artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
@@ -1,79 +0,0 @@
|
||||
name: MinIO advanced tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
replication-test:
|
||||
name: Advanced Tests with Go ${{ matrix.go-version }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Test Decom
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-decom
|
||||
|
||||
- name: Test ILM
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-ilm
|
||||
make test-ilm-transition
|
||||
|
||||
- name: Test PBAC
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-pbac
|
||||
|
||||
- name: Test Config File
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-configfile
|
||||
|
||||
- name: Test Replication
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-replication
|
||||
|
||||
- name: Test MinIO IDP for automatic site replication
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-site-replication-minio
|
||||
|
||||
- name: Test Versioning
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-versioning
|
||||
|
||||
- name: Test Multipart upload with failures
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-multipart
|
||||
@@ -1,34 +0,0 @@
|
||||
name: Root lockdown tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Start root lockdown tests
|
||||
run: |
|
||||
make test-root-disable
|
||||
@@ -1,9 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBKDCB26ADAgECAhB6vebGMUfKnmBKyqoApRSOMAUGAytlcDAbMRkwFwYDVQQD
|
||||
DBByb290QHBsYXkubWluLmlvMB4XDTIwMDQzMDE1MjIyNVoXDTI1MDQyOTE1MjIy
|
||||
NVowGzEZMBcGA1UEAwwQcm9vdEBwbGF5Lm1pbi5pbzAqMAUGAytlcAMhALzn735W
|
||||
fmSH/ghKs+4iPWziZMmWdiWr/sqvqeW+WwSxozUwMzAOBgNVHQ8BAf8EBAMCB4Aw
|
||||
EwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAFBgMrZXADQQDZOrGK
|
||||
b2ATkDlu2pTcP3LyhSBDpYh7V4TvjRkBTRgjkacCzwFLm+mh+7US8V4dBpIDsJ4u
|
||||
uWoF0y6vbLVGIlkG
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,3 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VwBCIEID9E7FSYWrMD+VjhI6q545cYT9YOyFxZb7UnjEepYDRc
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -1,64 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
export MODE="$1"
|
||||
export ACCESS_KEY="$2"
|
||||
export SECRET_KEY="$3"
|
||||
export JOB_NAME="$4"
|
||||
export MINT_MODE="full"
|
||||
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -f dangling=true) || true
|
||||
|
||||
## change working directory
|
||||
cd .github/workflows/mint
|
||||
|
||||
## always pull latest
|
||||
docker pull docker.io/minio/mint:edge
|
||||
|
||||
docker-compose -f minio-${MODE}.yaml up -d
|
||||
sleep 1m
|
||||
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -q -f dangling=true) || true
|
||||
|
||||
# Stop two nodes, one of each pool, to check that all S3 calls work while quorum is still there
|
||||
[ "${MODE}" == "pools" ] && docker-compose -f minio-${MODE}.yaml stop minio2
|
||||
[ "${MODE}" == "pools" ] && docker-compose -f minio-${MODE}.yaml stop minio6
|
||||
|
||||
# Pause one node, to check that all S3 calls work while one node goes wrong
|
||||
[ "${MODE}" == "resiliency" ] && docker-compose -f minio-${MODE}.yaml pause minio4
|
||||
|
||||
docker run --rm --net=mint_default \
|
||||
--name="mint-${MODE}-${JOB_NAME}" \
|
||||
-e SERVER_ENDPOINT="nginx:9000" \
|
||||
-e ACCESS_KEY="${ACCESS_KEY}" \
|
||||
-e SECRET_KEY="${SECRET_KEY}" \
|
||||
-e ENABLE_HTTPS=0 \
|
||||
-e MINT_MODE="${MINT_MODE}" \
|
||||
docker.io/minio/mint:edge
|
||||
|
||||
# FIXME: enable this after fixing aws-sdk-java-v2 tests
|
||||
# # unpause the node, to check that all S3 calls work while one node goes wrong
|
||||
# [ "${MODE}" == "resiliency" ] && docker-compose -f minio-${MODE}.yaml unpause minio4
|
||||
# [ "${MODE}" == "resiliency" ] && docker run --rm --net=mint_default \
|
||||
# --name="mint-${MODE}-${JOB_NAME}" \
|
||||
# -e SERVER_ENDPOINT="nginx:9000" \
|
||||
# -e ACCESS_KEY="${ACCESS_KEY}" \
|
||||
# -e SECRET_KEY="${SECRET_KEY}" \
|
||||
# -e ENABLE_HTTPS=0 \
|
||||
# -e MINT_MODE="${MINT_MODE}" \
|
||||
# docker.io/minio/mint:edge
|
||||
|
||||
docker-compose -f minio-${MODE}.yaml down || true
|
||||
sleep 10s
|
||||
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -q -f dangling=true) || true
|
||||
|
||||
## change working directory
|
||||
cd ../../../
|
||||
@@ -1,22 +0,0 @@
|
||||
name: Shell formatting checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: runner / shfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: luizm/action-sh-checker@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SHFMT_OPTS: "-s"
|
||||
with:
|
||||
sh_checker_shellcheck_disable: true # disable for now
|
||||
@@ -0,0 +1,501 @@
|
||||
name: Test Release Pipeline
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/goreleaser.yml"
|
||||
- ".github/nfpm.yml"
|
||||
- "Dockerfile.goreleaser"
|
||||
- "Dockerfile.distroless"
|
||||
- "cmd/healthcheck-main.go"
|
||||
- "cmd/main.go"
|
||||
- "dockerscripts/download-static-curl.sh"
|
||||
- "dockerscripts/docker-entrypoint.sh"
|
||||
- "dockerscripts/docker-entrypoint_test.sh"
|
||||
- "silo.service"
|
||||
- "silo.env"
|
||||
- "silo.sysusers"
|
||||
- "buildscripts/package-release.sh"
|
||||
- "buildscripts/package/postinstall.sh"
|
||||
- "buildscripts/package/preremove.sh"
|
||||
- "buildscripts/package/lifecycle_test.sh"
|
||||
- "buildscripts/minio-upgrade.sh"
|
||||
- "buildscripts/sign-release-rpms.sh"
|
||||
- "buildscripts/verify-build-provenance.sh"
|
||||
- "buildscripts/verify-rebrand.sh"
|
||||
- "buildscripts/verify-helm-migration.sh"
|
||||
- "buildscripts/helm-migration-guard/**"
|
||||
- "helm/silo/**"
|
||||
- "buildscripts/rebrand-guard/**"
|
||||
- "buildscripts/gen-ldflags.go"
|
||||
- ".github/workflows/release.yml"
|
||||
- ".github/workflows/docker-release.yml"
|
||||
- ".github/workflows/finalize-release.yml"
|
||||
- ".github/workflows/test-release.yml"
|
||||
- ".gitignore"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Compute test variables
|
||||
run: |
|
||||
set -euo pipefail
|
||||
RELEASE_TAG="RELEASE.2026-02-14T12-00-00Z"
|
||||
VERSION_COLON="2026-02-14T12:00:00Z"
|
||||
PKG_VERSION="20260214120000.0.0"
|
||||
LDFLAGS="$(MINIO_RELEASE=RELEASE go run buildscripts/gen-ldflags.go "${VERSION_COLON}")"
|
||||
{
|
||||
echo "RELEASE_TAG=${RELEASE_TAG}"
|
||||
echo "PKG_VERSION=${PKG_VERSION}"
|
||||
echo "LDFLAGS=${LDFLAGS}"
|
||||
} >> "${GITHUB_ENV}"
|
||||
echo "PKG_VERSION: ${PKG_VERSION}"
|
||||
echo "LDFLAGS: ${LDFLAGS}"
|
||||
|
||||
- name: GoReleaser config check
|
||||
uses: goreleaser/goreleaser-action@v7
|
||||
with:
|
||||
version: "~> v2"
|
||||
args: check --config .github/goreleaser.yml
|
||||
|
||||
- name: Validate Helm chart and legacy upgrade identity
|
||||
run: buildscripts/verify-helm-migration.sh
|
||||
|
||||
- name: Install Syft
|
||||
uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
|
||||
with:
|
||||
syft-version: v1.50.0
|
||||
|
||||
- name: Build snapshot artifacts
|
||||
uses: goreleaser/goreleaser-action@v7
|
||||
with:
|
||||
version: "~> v2"
|
||||
# A pull-request snapshot has no trusted release identity. Exercise
|
||||
# the SBOM/checksum pipeline here, and reserve keyless signing for
|
||||
# the tag-triggered release workflow with GitHub OIDC.
|
||||
args: release --snapshot --clean --skip=publish,docker,sign --config .github/goreleaser.yml
|
||||
env:
|
||||
LDFLAGS: ${{ env.LDFLAGS }}
|
||||
PKG_VERSION: ${{ env.PKG_VERSION }}
|
||||
|
||||
- name: Verify archive SBOM and checksum coverage
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mapfile -t archives < <(find dist -maxdepth 1 -type f -name 'silo_*.tar.gz' | sort)
|
||||
mapfile -t sboms < <(find dist -maxdepth 1 -type f -name 'silo_*.tar.gz.sbom.json' | sort)
|
||||
test "${#archives[@]}" -eq 6
|
||||
test "${#sboms[@]}" -eq 6
|
||||
manifest="dist/silo_${PKG_VERSION}_checksums.txt"
|
||||
test -s "${manifest}"
|
||||
(
|
||||
cd dist
|
||||
sha256sum --check "$(basename "${manifest}")"
|
||||
)
|
||||
test "$(wc -l < "${manifest}" | tr -d ' ')" -eq 12
|
||||
|
||||
- name: Verify binary provenance stamps
|
||||
run: |
|
||||
set -euo pipefail
|
||||
buildscripts/verify-build-provenance.sh
|
||||
|
||||
- name: Install package validation tools
|
||||
run: |
|
||||
set -euo pipefail
|
||||
go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.47.0
|
||||
echo "$(go env GOPATH)/bin" >> "${GITHUB_PATH}"
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes rpm binutils
|
||||
|
||||
- name: Package snapshot binaries with nFPM
|
||||
run: |
|
||||
set -euo pipefail
|
||||
buildscripts/package-release.sh
|
||||
|
||||
- name: Validate package names, checksums, metadata, and payload
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd dist/packages
|
||||
|
||||
# The signing script asserts these same values, but it runs on the
|
||||
# maintainer's machine after the release workflow has already built
|
||||
# and uploaded. Take its expectations as the single source of truth
|
||||
# so nfpm.yml and the signing script cannot drift apart without
|
||||
# failing here first, while a fix is still cheap.
|
||||
#
|
||||
# This grep is deliberately limited to the eight identity variables,
|
||||
# all of which are single-line. That is what makes the eval safe:
|
||||
# should one ever become multi-line, the grep captures an
|
||||
# unterminated quote and the eval aborts on a syntax error under
|
||||
# set -e rather than quietly binding an empty value and comparing
|
||||
# against nothing. expected_payload is multi-line by design and must
|
||||
# stay out of this set for the same reason.
|
||||
eval "$(grep -E '^expected_(release|vendor|packager|url|summary|description|license|group)=' \
|
||||
../../buildscripts/sign-release-rpms.sh)"
|
||||
for value in "${expected_release}" "${expected_vendor}" "${expected_packager}" \
|
||||
"${expected_url}" "${expected_summary}" "${expected_description}" \
|
||||
"${expected_license}" "${expected_group}"; do
|
||||
test -n "${value}"
|
||||
done
|
||||
|
||||
# These are the public download names; a drift here breaks every
|
||||
# script that fetches packages by URL. RPM and DEB carry the PGSTY
|
||||
# release segment; APK cannot (Alpine pkgrel admits only -r<integer>),
|
||||
# so it stays bare. package-release.sh builds the same three shapes.
|
||||
expected=(
|
||||
"silo-${PKG_VERSION}-${expected_release}.aarch64.rpm"
|
||||
"silo-${PKG_VERSION}-${expected_release}.x86_64.rpm"
|
||||
"silo_${PKG_VERSION}-${expected_release}_amd64.deb"
|
||||
"silo_${PKG_VERSION}-${expected_release}_arm64.deb"
|
||||
"silo_${PKG_VERSION}_aarch64.apk"
|
||||
"silo_${PKG_VERSION}_x86_64.apk"
|
||||
)
|
||||
|
||||
for package in "${expected[@]}"; do
|
||||
test -s "${package}"
|
||||
test -s "${package}.sha256sum"
|
||||
sha256sum --check "${package}.sha256sum"
|
||||
done
|
||||
|
||||
test "$(find . -maxdepth 1 -type f \( -name '*.rpm' -o -name '*.deb' -o -name '*.apk' \) | wc -l)" -eq 6
|
||||
|
||||
service_sha="$(sha256sum ../../silo.service | awk '{print $1}')"
|
||||
defaults_sha="$(sha256sum ../../silo.env | awk '{print $1}')"
|
||||
sysusers_sha="$(sha256sum ../../silo.sysusers | awk '{print $1}')"
|
||||
license_sha="$(sha256sum ../../LICENSE | awk '{print $1}')"
|
||||
notice_sha="$(sha256sum ../../NOTICE | awk '{print $1}')"
|
||||
|
||||
rpm_file="silo-${PKG_VERSION}-${expected_release}.x86_64.rpm"
|
||||
test "$(rpm -qp --queryformat '%{RELEASE}' "${rpm_file}")" = "${expected_release}"
|
||||
test "$(rpm -qp --queryformat '%{VENDOR}' "${rpm_file}")" = "${expected_vendor}"
|
||||
test "$(rpm -qp --queryformat '%{PACKAGER}' "${rpm_file}")" = "${expected_packager}"
|
||||
test "$(rpm -qp --queryformat '%{URL}' "${rpm_file}")" = "${expected_url}"
|
||||
test "$(rpm -qp --queryformat '%{SUMMARY}' "${rpm_file}")" = "${expected_summary}"
|
||||
test "$(rpm -qp --queryformat '%{DESCRIPTION}' "${rpm_file}")" = "${expected_description}"
|
||||
test "$(rpm -qp --queryformat '%{LICENSE}' "${rpm_file}")" = "${expected_license}"
|
||||
test "$(rpm -qp --queryformat '%{GROUP}' "${rpm_file}")" = "${expected_group}"
|
||||
rpm -qpl "${rpm_file}" | grep -Fx '/usr/bin/silo'
|
||||
rpm -qpl "${rpm_file}" | grep -Fx '/usr/lib/systemd/system/silo.service'
|
||||
rpm -qpl "${rpm_file}" | grep -Fx '/etc/default/silo'
|
||||
rpm -qpl "${rpm_file}" | grep -Fx '/usr/lib/sysusers.d/silo.conf'
|
||||
rpm -qpl "${rpm_file}" | grep -Fx '/usr/share/doc/silo/LICENSE'
|
||||
rpm -qpl "${rpm_file}" | grep -Fx '/usr/share/doc/silo/NOTICE'
|
||||
test "$(rpm -qpl "${rpm_file}" | wc -l)" -eq 6
|
||||
# nfpm only honors type: license on rpm, which is why nfpm.yml
|
||||
# declares the license materials once per packager. Pin the rpm
|
||||
# %license flag so that split cannot silently regress.
|
||||
rpm -qp --queryformat '[%{FILEFLAGS:fflags} %{FILENAMES}\n]' "${rpm_file}" \
|
||||
| grep -Fx 'l /usr/share/doc/silo/LICENSE'
|
||||
rpm -qp --queryformat '[%{FILEFLAGS:fflags} %{FILENAMES}\n]' "${rpm_file}" \
|
||||
| grep -Fx 'l /usr/share/doc/silo/NOTICE'
|
||||
if rpm -qp --conflicts "${rpm_file}" | grep -qi minio; then
|
||||
echo "RPM must not declare a cross-name conflict with MinIO" >&2
|
||||
exit 1
|
||||
fi
|
||||
if rpm -qp --obsoletes "${rpm_file}" | grep -qi minio; then
|
||||
echo "RPM must not obsolete a MinIO package" >&2
|
||||
exit 1
|
||||
fi
|
||||
if rpm -qp --provides "${rpm_file}" | grep -qi minio; then
|
||||
echo "RPM must not provide a MinIO package alias" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
deb_file="silo_${PKG_VERSION}-${expected_release}_amd64.deb"
|
||||
test "$(dpkg-deb --field "${deb_file}" Maintainer)" = "${expected_packager}"
|
||||
test "$(dpkg-deb --field "${deb_file}" Version)" = "${PKG_VERSION}-${expected_release}"
|
||||
test "$(dpkg-deb --field "${deb_file}" License)" = "${expected_license}"
|
||||
test "$(dpkg-deb --field "${deb_file}" Section)" = "utils"
|
||||
test "$(dpkg-deb --field "${deb_file}" Homepage)" = "${expected_url}"
|
||||
test "$(dpkg-deb --field "${deb_file}" Description)" = "${expected_description}"
|
||||
dpkg-deb --contents "${deb_file}" | grep -E 'usr/bin/silo$'
|
||||
dpkg-deb --contents "${deb_file}" | grep -E 'usr/lib/systemd/system/silo\.service$'
|
||||
dpkg-deb --contents "${deb_file}" | grep -E 'etc/default/silo$'
|
||||
dpkg-deb --contents "${deb_file}" | grep -E 'usr/lib/sysusers\.d/silo\.conf$'
|
||||
dpkg-deb --contents "${deb_file}" | grep -E 'usr/share/doc/silo/LICENSE$'
|
||||
dpkg-deb --contents "${deb_file}" | grep -E 'usr/share/doc/silo/NOTICE$'
|
||||
test "$(dpkg-deb --contents "${deb_file}" | awk '$1 !~ /^d/ { count++ } END { print count + 0 }')" -eq 6
|
||||
test -z "$(dpkg-deb --field "${deb_file}" Conflicts)"
|
||||
test -z "$(dpkg-deb --field "${deb_file}" Replaces)"
|
||||
test -z "$(dpkg-deb --field "${deb_file}" Provides)"
|
||||
|
||||
apk_info="$(tar -xOzf "silo_${PKG_VERSION}_x86_64.apk" .PKGINFO)"
|
||||
grep -Fx "pkgver = ${PKG_VERSION}" <<< "${apk_info}"
|
||||
grep -Fx "url = ${expected_url}" <<< "${apk_info}"
|
||||
grep -Fx "maintainer = ${expected_packager}" <<< "${apk_info}"
|
||||
grep -Fx "license = ${expected_license}" <<< "${apk_info}"
|
||||
grep -Fx "pkgdesc = ${expected_description}" <<< "${apk_info}"
|
||||
tar -tzf "silo_${PKG_VERSION}_x86_64.apk" | grep -Fx 'usr/bin/silo'
|
||||
tar -tzf "silo_${PKG_VERSION}_x86_64.apk" | grep -Fx 'usr/lib/systemd/system/silo.service'
|
||||
tar -tzf "silo_${PKG_VERSION}_x86_64.apk" | grep -Fx 'etc/default/silo'
|
||||
tar -tzf "silo_${PKG_VERSION}_x86_64.apk" | grep -Fx 'usr/lib/sysusers.d/silo.conf'
|
||||
tar -tzf "silo_${PKG_VERSION}_x86_64.apk" | grep -Fx 'usr/share/doc/silo/LICENSE'
|
||||
tar -tzf "silo_${PKG_VERSION}_x86_64.apk" | grep -Fx 'usr/share/doc/silo/NOTICE'
|
||||
test "$(tar -tzf "silo_${PKG_VERSION}_x86_64.apk" | awk '$0 !~ /^\./ && $0 !~ /\/$/ { count++ } END { print count + 0 }')" -eq 6
|
||||
if grep -Ei '^provides = .*minio' <<< "${apk_info}"; then
|
||||
echo "APK must not provide a MinIO package alias" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for arch in amd64 arm64; do
|
||||
if [ "${arch}" = amd64 ]; then
|
||||
rpm_arch=x86_64
|
||||
deb_arch=amd64
|
||||
apk_arch=x86_64
|
||||
else
|
||||
rpm_arch=aarch64
|
||||
deb_arch=arm64
|
||||
apk_arch=aarch64
|
||||
fi
|
||||
|
||||
test "$(rpm -qp --queryformat '%{ARCH}' "silo-${PKG_VERSION}-${expected_release}.${rpm_arch}.rpm")" = "${rpm_arch}"
|
||||
test "$(dpkg-deb --field "silo_${PKG_VERSION}-${expected_release}_${deb_arch}.deb" Architecture)" = "${deb_arch}"
|
||||
grep -Fx "arch = ${apk_arch}" <<< "$(tar -xOzf "silo_${PKG_VERSION}_${apk_arch}.apk" .PKGINFO)"
|
||||
|
||||
# Accepted weakness: this takes the first match, unsorted, where
|
||||
# find_binary in package-release.sh demands exactly one. It cannot
|
||||
# be reached with an ambiguous match today, because packaging runs
|
||||
# earlier in this same job and hard-fails on one. Revisit if
|
||||
# goamd64 gains a second level, or if find_binary's exactly-one
|
||||
# contract is ever relaxed -- at that point this weak copy would be
|
||||
# the only one left choosing silently.
|
||||
source_binary="$(find .. -maxdepth 2 -type f -path "../silo_linux_${arch}*/silo" | head -n 1)"
|
||||
source_sha="$(sha256sum "${source_binary}" | awk '{print $1}')"
|
||||
|
||||
# Do not pipe rpm2cpio here: Debian's build exits non-zero even when
|
||||
# it writes a correct payload, which trips `set -o pipefail`. Use
|
||||
# rpm's own digests instead -- -K checks the payload against the
|
||||
# header, and FILEDIGESTS is the sha256 rpm itself verifies on
|
||||
# install.
|
||||
rpm -K "silo-${PKG_VERSION}-${expected_release}.${rpm_arch}.rpm"
|
||||
rpm_sha="$(rpm -qp --queryformat '[%{FILENAMES} %{FILEDIGESTS}\n]' \
|
||||
"silo-${PKG_VERSION}-${expected_release}.${rpm_arch}.rpm" | awk '$1 == "/usr/bin/silo" { print $2 }')"
|
||||
rpm_service_sha="$(rpm -qp --queryformat '[%{FILENAMES} %{FILEDIGESTS}\n]' \
|
||||
"silo-${PKG_VERSION}-${expected_release}.${rpm_arch}.rpm" | awk '$1 == "/usr/lib/systemd/system/silo.service" { print $2 }')"
|
||||
rpm_defaults_sha="$(rpm -qp --queryformat '[%{FILENAMES} %{FILEDIGESTS}\n]' \
|
||||
"silo-${PKG_VERSION}-${expected_release}.${rpm_arch}.rpm" | awk '$1 == "/etc/default/silo" { print $2 }')"
|
||||
rpm_sysusers_sha="$(rpm -qp --queryformat '[%{FILENAMES} %{FILEDIGESTS}\n]' \
|
||||
"silo-${PKG_VERSION}-${expected_release}.${rpm_arch}.rpm" | awk '$1 == "/usr/lib/sysusers.d/silo.conf" { print $2 }')"
|
||||
rpm_license_sha="$(rpm -qp --queryformat '[%{FILENAMES} %{FILEDIGESTS}\n]' \
|
||||
"silo-${PKG_VERSION}-${expected_release}.${rpm_arch}.rpm" | awk '$1 == "/usr/share/doc/silo/LICENSE" { print $2 }')"
|
||||
rpm_notice_sha="$(rpm -qp --queryformat '[%{FILENAMES} %{FILEDIGESTS}\n]' \
|
||||
"silo-${PKG_VERSION}-${expected_release}.${rpm_arch}.rpm" | awk '$1 == "/usr/share/doc/silo/NOTICE" { print $2 }')"
|
||||
deb_sha="$(ar p "silo_${PKG_VERSION}-${expected_release}_${deb_arch}.deb" data.tar.gz | tar -xzOf - ./usr/bin/silo | sha256sum | awk '{print $1}')"
|
||||
deb_service_sha="$(ar p "silo_${PKG_VERSION}-${expected_release}_${deb_arch}.deb" data.tar.gz | tar -xzOf - ./usr/lib/systemd/system/silo.service | sha256sum | awk '{print $1}')"
|
||||
deb_defaults_sha="$(ar p "silo_${PKG_VERSION}-${expected_release}_${deb_arch}.deb" data.tar.gz | tar -xzOf - ./etc/default/silo | sha256sum | awk '{print $1}')"
|
||||
deb_sysusers_sha="$(ar p "silo_${PKG_VERSION}-${expected_release}_${deb_arch}.deb" data.tar.gz | tar -xzOf - ./usr/lib/sysusers.d/silo.conf | sha256sum | awk '{print $1}')"
|
||||
deb_license_sha="$(ar p "silo_${PKG_VERSION}-${expected_release}_${deb_arch}.deb" data.tar.gz | tar -xzOf - ./usr/share/doc/silo/LICENSE | sha256sum | awk '{print $1}')"
|
||||
deb_notice_sha="$(ar p "silo_${PKG_VERSION}-${expected_release}_${deb_arch}.deb" data.tar.gz | tar -xzOf - ./usr/share/doc/silo/NOTICE | sha256sum | awk '{print $1}')"
|
||||
apk_sha="$(tar -xzOf "silo_${PKG_VERSION}_${apk_arch}.apk" usr/bin/silo | sha256sum | awk '{print $1}')"
|
||||
apk_service_sha="$(tar -xzOf "silo_${PKG_VERSION}_${apk_arch}.apk" usr/lib/systemd/system/silo.service | sha256sum | awk '{print $1}')"
|
||||
apk_defaults_sha="$(tar -xzOf "silo_${PKG_VERSION}_${apk_arch}.apk" etc/default/silo | sha256sum | awk '{print $1}')"
|
||||
apk_sysusers_sha="$(tar -xzOf "silo_${PKG_VERSION}_${apk_arch}.apk" usr/lib/sysusers.d/silo.conf | sha256sum | awk '{print $1}')"
|
||||
apk_license_sha="$(tar -xzOf "silo_${PKG_VERSION}_${apk_arch}.apk" usr/share/doc/silo/LICENSE | sha256sum | awk '{print $1}')"
|
||||
apk_notice_sha="$(tar -xzOf "silo_${PKG_VERSION}_${apk_arch}.apk" usr/share/doc/silo/NOTICE | sha256sum | awk '{print $1}')"
|
||||
|
||||
test "${source_sha}" = "${rpm_sha}"
|
||||
test "${source_sha}" = "${deb_sha}"
|
||||
test "${source_sha}" = "${apk_sha}"
|
||||
test "${service_sha}" = "${rpm_service_sha}"
|
||||
test "${service_sha}" = "${deb_service_sha}"
|
||||
test "${service_sha}" = "${apk_service_sha}"
|
||||
test "${defaults_sha}" = "${rpm_defaults_sha}"
|
||||
test "${defaults_sha}" = "${deb_defaults_sha}"
|
||||
test "${defaults_sha}" = "${apk_defaults_sha}"
|
||||
test "${sysusers_sha}" = "${rpm_sysusers_sha}"
|
||||
test "${sysusers_sha}" = "${deb_sysusers_sha}"
|
||||
test "${sysusers_sha}" = "${apk_sysusers_sha}"
|
||||
test "${license_sha}" = "${rpm_license_sha}"
|
||||
test "${license_sha}" = "${deb_license_sha}"
|
||||
test "${license_sha}" = "${apk_license_sha}"
|
||||
test "${notice_sha}" = "${rpm_notice_sha}"
|
||||
test "${notice_sha}" = "${deb_notice_sha}"
|
||||
test "${notice_sha}" = "${apk_notice_sha}"
|
||||
done
|
||||
|
||||
find . -maxdepth 1 -type f | sort
|
||||
|
||||
- name: Build release runtime image and verify graceful shutdown
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# docker-release.yml is workflow_dispatch only, so this is the only
|
||||
# automated build of the release runtime layer and entrypoint before a
|
||||
# real publish. Assemble a minimal image from the linux/amd64 binary
|
||||
# goreleaser already produced; the mcli-download build stage is skipped
|
||||
# on purpose to keep this gate offline and deterministic.
|
||||
ctx="$(mktemp -d)"
|
||||
tar -xzf "dist/silo_${PKG_VERSION}_linux_amd64.tar.gz" -C "${ctx}" silo
|
||||
cp dockerscripts/docker-entrypoint.sh "${ctx}/docker-entrypoint.sh"
|
||||
{
|
||||
echo "FROM registry.access.redhat.com/ubi9/ubi-micro:latest"
|
||||
echo "COPY silo /usr/bin/silo"
|
||||
echo "COPY docker-entrypoint.sh /usr/bin/docker-entrypoint.sh"
|
||||
echo "RUN mkdir -p /data && chmod 0777 /data && chmod +x /usr/bin/silo /usr/bin/docker-entrypoint.sh"
|
||||
echo 'ENV HOME=/tmp'
|
||||
echo 'ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]'
|
||||
echo 'CMD ["silo"]'
|
||||
} > "${ctx}/Dockerfile"
|
||||
docker build -t silo-runtime-test:snapshot "${ctx}"
|
||||
|
||||
# PID 1 must be silo, not the entry shell, on every privilege path, so
|
||||
# a SIGTERM from docker stop reaches the server and it exits gracefully
|
||||
# instead of being killed at the stop timeout. Regression guard for the
|
||||
# exec-into-chroot entrypoint fix.
|
||||
assert_graceful() {
|
||||
name="$1"; shift
|
||||
docker rm -f "${name}" >/dev/null 2>&1 || true
|
||||
docker run -d --name "${name}" \
|
||||
-e MINIO_CI_CD=1 -e MINIO_ROOT_USER=ciadmin -e MINIO_ROOT_PASSWORD=ciadmin-secret-123 \
|
||||
"$@" silo-runtime-test:snapshot silo server /data --address :9000 >/dev/null
|
||||
up=""
|
||||
for _ in $(seq 1 60); do
|
||||
if docker logs "${name}" 2>&1 | grep -q "API:"; then up=1; break; fi
|
||||
if [ "$(docker inspect -f '{{.State.Running}}' "${name}")" != "true" ]; then break; fi
|
||||
sleep 1
|
||||
done
|
||||
if [ -z "${up}" ]; then echo "server did not start (${name}):"; docker logs "${name}" | tail -5; exit 1; fi
|
||||
pid1="$(docker exec "${name}" cat /proc/1/comm 2>/dev/null || echo '?')"
|
||||
start="$(date +%s)"; docker stop -t 15 "${name}" >/dev/null; end="$(date +%s)"
|
||||
code="$(docker inspect -f '{{.State.ExitCode}}' "${name}")"
|
||||
elapsed=$((end - start))
|
||||
echo "${name}: pid1=${pid1} stop=${elapsed}s exit=${code}"
|
||||
graceful=0; docker logs "${name}" 2>&1 | grep -q "Exiting on signal" && graceful=1
|
||||
docker rm -f "${name}" >/dev/null 2>&1 || true
|
||||
[ "${graceful}" = "1" ] || { echo "no graceful-shutdown log (${name}) - signal not forwarded"; exit 1; }
|
||||
[ "${code}" = "0" ] || { echo "non-zero exit (${name}): ${code}"; exit 1; }
|
||||
[ "${elapsed}" -lt 10 ] || { echo "shutdown too slow (${name}): ${elapsed}s - signal not forwarded"; exit 1; }
|
||||
}
|
||||
assert_graceful silo-rt-default
|
||||
assert_graceful silo-rt-dropuser -e MINIO_USERNAME=silo-user -e MINIO_GROUPNAME=silo-group
|
||||
assert_graceful silo-rt-rootless --user 1001:1001
|
||||
|
||||
# The compatibility shim translates only the legacy first argv token;
|
||||
# the image contains no /usr/bin/minio file.
|
||||
docker run --rm silo-runtime-test:snapshot sh -c 'test ! -e /usr/bin/minio'
|
||||
docker run --rm -d --name silo-rt-legacy \
|
||||
-e MINIO_CI_CD=1 -e MINIO_ROOT_USER=ciadmin -e MINIO_ROOT_PASSWORD=ciadmin-secret-123 \
|
||||
silo-runtime-test:snapshot minio server /data --address :9000 >/dev/null
|
||||
sleep 2
|
||||
test "$(docker exec silo-rt-legacy cat /proc/1/comm)" = silo
|
||||
docker rm -f silo-rt-legacy >/dev/null
|
||||
|
||||
- name: Build distroless runtime image and verify native healthcheck
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Unlike the classic image, Dockerfile.distroless has no release
|
||||
# download stages, so the real shipped file can be built and gated
|
||||
# here. It must keep working with nothing in it but the silo
|
||||
# binary: no shell, no mc, no entrypoint script.
|
||||
ctx="$(mktemp -d)"
|
||||
tar -xzf "dist/silo_${PKG_VERSION}_linux_amd64.tar.gz" -C "${ctx}" silo
|
||||
cp Dockerfile.distroless LICENSE NOTICE CREDITS "${ctx}/"
|
||||
docker build -t silo-distroless-test:snapshot -f "${ctx}/Dockerfile.distroless" "${ctx}"
|
||||
|
||||
# HEALTHCHECK is a Docker extension absent from the OCI image
|
||||
# spec; assert the exact probe command survived into the image
|
||||
# config, not merely a substring of it.
|
||||
test "$(docker inspect -f '{{json .Config.Healthcheck.Test}}' silo-distroless-test:snapshot)" \
|
||||
= '["CMD","/usr/bin/silo","healthcheck","ready"]'
|
||||
|
||||
# /data ships in the image layer world-writable (issue #55):
|
||||
# there is no entrypoint left to repair ownership at runtime.
|
||||
# Export the rootfs once, then assert each required and each
|
||||
# forbidden entry individually: tar's member-argument mode exits
|
||||
# non-zero on any missing name, which under pipefail masks a
|
||||
# found forbidden file, and -tv prints symlinks as 'name ->
|
||||
# target' which defeats $-anchored greps.
|
||||
probe="$(docker create silo-distroless-test:snapshot server /data)"
|
||||
docker export "${probe}" -o "${ctx}/rootfs.tar"
|
||||
docker rm "${probe}" >/dev/null
|
||||
tar -tf "${ctx}/rootfs.tar" > "${ctx}/names.txt"
|
||||
tar -tvf "${ctx}/rootfs.tar" > "${ctx}/verbose.txt"
|
||||
grep -E '^drwxrwxrwx.* data/$' "${ctx}/verbose.txt" >/dev/null
|
||||
for want in usr/bin/silo licenses/LICENSE licenses/NOTICE licenses/CREDITS; do
|
||||
grep -Fxq "${want}" "${ctx}/names.txt" || { echo "missing ${want}"; exit 1; }
|
||||
done
|
||||
for forbid in bin/sh usr/bin/sh busybox/sh usr/bin/minio usr/bin/mc usr/bin/mcli; do
|
||||
if grep -Fxq "${forbid}" "${ctx}/names.txt"; then
|
||||
echo "distroless image unexpectedly contains ${forbid}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# The baked-in healthcheck must drive Docker's health state on its
|
||||
# own, the probe binary must be directly exec-able without any
|
||||
# shell, and SIGTERM must still reach PID 1 (the server binary is
|
||||
# the entrypoint) for a graceful stop.
|
||||
assert_distroless() {
|
||||
name="$1"; shift
|
||||
docker rm -f "${name}" >/dev/null 2>&1 || true
|
||||
docker run -d --name "${name}" \
|
||||
-e MINIO_CI_CD=1 -e MINIO_ROOT_USER=ciadmin -e MINIO_ROOT_PASSWORD=ciadmin-secret-123 \
|
||||
"$@" silo-distroless-test:snapshot server /data --address :9000 >/dev/null
|
||||
status=""
|
||||
for _ in $(seq 1 90); do
|
||||
status="$(docker inspect -f '{{.State.Health.Status}}' "${name}" 2>/dev/null || echo '?')"
|
||||
if [ "${status}" = "healthy" ]; then break; fi
|
||||
if [ "$(docker inspect -f '{{.State.Running}}' "${name}")" != "true" ]; then break; fi
|
||||
sleep 1
|
||||
done
|
||||
if [ "${status}" != "healthy" ]; then
|
||||
echo "container never became healthy (${name}): status=${status}"
|
||||
docker logs "${name}" 2>&1 | tail -5
|
||||
exit 1
|
||||
fi
|
||||
docker exec "${name}" /usr/bin/silo healthcheck ready
|
||||
docker exec "${name}" /usr/bin/silo healthcheck cluster
|
||||
start="$(date +%s)"; docker stop -t 15 "${name}" >/dev/null; end="$(date +%s)"
|
||||
code="$(docker inspect -f '{{.State.ExitCode}}' "${name}")"
|
||||
elapsed=$((end - start))
|
||||
graceful=0; docker logs "${name}" 2>&1 | grep -q "Exiting on signal" && graceful=1
|
||||
echo "${name}: health=${status} stop=${elapsed}s exit=${code}"
|
||||
docker rm -f "${name}" >/dev/null 2>&1 || true
|
||||
[ "${graceful}" = "1" ] || { echo "no graceful-shutdown log (${name}) - signal not forwarded"; exit 1; }
|
||||
[ "${code}" = "0" ] || { echo "non-zero exit (${name}): ${code}"; exit 1; }
|
||||
[ "${elapsed}" -lt 10 ] || { echo "shutdown too slow (${name}): ${elapsed}s - signal not forwarded"; exit 1; }
|
||||
}
|
||||
assert_distroless silo-dl-default
|
||||
assert_distroless silo-dl-rootless --user 1001:1001
|
||||
|
||||
- name: Validate release scripts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bash -n buildscripts/package-release.sh
|
||||
bash -n buildscripts/minio-upgrade.sh
|
||||
bash -n buildscripts/sign-release-rpms.sh
|
||||
bash -n buildscripts/verify-build-provenance.sh
|
||||
bash -n buildscripts/verify-rebrand.sh
|
||||
bash -n buildscripts/verify-helm-migration.sh
|
||||
sh -n buildscripts/package/postinstall.sh
|
||||
sh -n buildscripts/package/preremove.sh
|
||||
bash -n buildscripts/package/lifecycle_test.sh
|
||||
buildscripts/package/lifecycle_test.sh
|
||||
bash -n dockerscripts/docker-entrypoint_test.sh
|
||||
bash -n dockerscripts/download-static-curl.sh
|
||||
dockerscripts/docker-entrypoint_test.sh
|
||||
go run ./buildscripts/rebrand-guard
|
||||
buildscripts/verify-rebrand.sh
|
||||
test -x buildscripts/package-release.sh
|
||||
test -x buildscripts/sign-release-rpms.sh
|
||||
test -x buildscripts/verify-build-provenance.sh
|
||||
test -x buildscripts/verify-rebrand.sh
|
||||
test -x buildscripts/verify-helm-migration.sh
|
||||
test -x buildscripts/package/postinstall.sh
|
||||
test -x buildscripts/package/preremove.sh
|
||||
test -x buildscripts/package/lifecycle_test.sh
|
||||
test -x dockerscripts/docker-entrypoint_test.sh
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
name: Spelling
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check spelling of repo
|
||||
uses: crate-ci/typos@master
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
name: Upgrade old version tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Start upgrade tests
|
||||
run: |
|
||||
make test-upgrade
|
||||
@@ -1,30 +1,36 @@
|
||||
name: VulnCheck
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
- main
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
vulncheck:
|
||||
name: Analysis
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: 1.23.5
|
||||
- name: Get official govulncheck
|
||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
shell: bash
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Install govulncheck
|
||||
run: |
|
||||
go install golang.org/x/vuln/cmd/govulncheck@v1.6.0
|
||||
echo "$(go env GOPATH)/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: Run govulncheck
|
||||
run: govulncheck -show verbose ./...
|
||||
shell: bash
|
||||
|
||||
+16
@@ -2,6 +2,7 @@
|
||||
cover.out
|
||||
*~
|
||||
minio
|
||||
silo
|
||||
!*/
|
||||
site/
|
||||
**/*.test
|
||||
@@ -53,3 +54,18 @@ s3-check-md5
|
||||
s3-verify
|
||||
xattr
|
||||
xl-meta
|
||||
|
||||
.gitignore
|
||||
|
||||
dist/
|
||||
|
||||
|
||||
.claude/
|
||||
.codex/
|
||||
AGENTS.md
|
||||
CLAUDE.md
|
||||
_bmad/
|
||||
_bmad-output/
|
||||
docs/security/
|
||||
docs/rebranding.md
|
||||
.release-sign/
|
||||
|
||||
+13
-3
@@ -1,7 +1,17 @@
|
||||
# AGPLv3 Compliance
|
||||
|
||||
We have designed MinIO as an Open Source software for the Open Source software community. This requires applications to consider whether their usage of MinIO is in compliance with the GNU AGPLv3 [license](https://github.com/minio/minio/blob/master/LICENSE).
|
||||
Silo is distributed under the [GNU Affero General Public License v3.0](LICENSE).
|
||||
It incorporates source code from the MinIO project and preserves the original
|
||||
copyright, license, and attribution notices in [`NOTICE`](NOTICE),
|
||||
[`CREDITS`](CREDITS), and source-file headers.
|
||||
|
||||
MinIO cannot make the determination as to whether your application's usage of MinIO is in compliance with the AGPLv3 license requirements. You should instead rely on your own legal counsel or licensing specialists to audit and ensure your application is in compliance with the licenses of MinIO and all other open-source projects with which your application integrates or interacts. We understand that AGPLv3 licensing is complex and nuanced. It is for that reason we strongly encourage using experts in licensing to make any such determinations around compliance instead of relying on apocryphal or anecdotal advice.
|
||||
You are responsible for determining how the AGPLv3 applies to your use,
|
||||
modification, deployment, and distribution of Silo and its dependencies. The
|
||||
Silo maintainers cannot provide legal advice or determine whether a particular
|
||||
application or service satisfies the license. Consult qualified counsel when
|
||||
the obligations are material to your deployment.
|
||||
|
||||
[MinIO Commercial Licensing](https://min.io/pricing) is the best option for applications that trigger AGPLv3 obligations (e.g. open sourcing your application). Applications using MinIO - or any other OSS-licensed code - without validating their usage do so at their own risk.
|
||||
If you convey modified binaries or provide network access to a modified
|
||||
version, review the complete AGPLv3 text and ensure that the corresponding
|
||||
source and notices are made available as required. Dependency licenses and
|
||||
separate notices continue to apply independently.
|
||||
|
||||
+95
-30
@@ -1,55 +1,61 @@
|
||||
# MinIO Contribution Guide [](https://slack.min.io) [](https://hub.docker.com/r/minio/minio/)
|
||||
# Contributing to Silo
|
||||
|
||||
``MinIO`` community welcomes your contribution. To make the process as seamless as possible, we recommend you read this contribution guide.
|
||||
Silo welcomes focused contributions that improve security, reliability,
|
||||
compatibility, packaging, tests, or maintainability. This repository preserves
|
||||
MinIO-compatible interfaces and storage formats, so changes must identify and
|
||||
test any compatibility impact.
|
||||
|
||||
## Development Workflow
|
||||
|
||||
Start by forking the MinIO GitHub repository, make changes in a branch and then send a pull request. We encourage pull requests to discuss code changes. Here are the steps in details:
|
||||
Fork the current Silo source repository, create a topic branch, and submit a
|
||||
pull request. Discuss broad or compatibility-sensitive changes in an issue
|
||||
before implementation.
|
||||
|
||||
### Setup your MinIO GitHub Repository
|
||||
|
||||
Fork [MinIO upstream](https://github.com/minio/minio/fork) source repository to your own personal repository. Copy the URL of your MinIO fork (you will need it for the `git clone` command below).
|
||||
### Set up a checkout
|
||||
|
||||
```sh
|
||||
git clone https://github.com/minio/minio
|
||||
cd minio
|
||||
go install -v
|
||||
ls $(go env GOPATH)/bin/minio
|
||||
git clone https://github.com/pgsty/silo
|
||||
cd silo
|
||||
go build -o silo .
|
||||
./silo --version
|
||||
```
|
||||
|
||||
### Set up git remote as ``upstream``
|
||||
### Keep the lineage remote separate
|
||||
|
||||
```sh
|
||||
$ cd minio
|
||||
$ git remote add upstream https://github.com/minio/minio
|
||||
$ git fetch upstream
|
||||
$ git merge upstream/master
|
||||
...
|
||||
git remote add lineage https://github.com/minio/minio
|
||||
git fetch lineage
|
||||
```
|
||||
|
||||
Do not merge an upstream branch into a pull request unless the maintainers have
|
||||
agreed on the scope. Silo intentionally carries a small downstream delta.
|
||||
|
||||
### Create your feature branch
|
||||
|
||||
Before making code changes, make sure you create a separate branch for these changes
|
||||
Create a separate branch before making code changes:
|
||||
|
||||
```
|
||||
git checkout -b my-new-feature
|
||||
```
|
||||
|
||||
### Test MinIO server changes
|
||||
### Test Silo server changes
|
||||
|
||||
After your code changes, make sure
|
||||
Before opening a pull request:
|
||||
|
||||
- To add test cases for the new code. If you have questions about how to do it, please ask on our [Slack](https://slack.min.io) channel.
|
||||
- To run `make verifiers`
|
||||
- To squash your commits into a single commit. `git rebase -i`. It's okay to force update your pull request.
|
||||
- To run `make test` and `make build` completes.
|
||||
- Add or update tests for changed behavior.
|
||||
- Run `make verifiers`.
|
||||
- Run the smallest relevant package tests, then `make test` when practical.
|
||||
- Run `make build` and confirm the generated executable is `silo`.
|
||||
- Explain any preserved `MINIO_*`, `minio_*`, `x-minio-*`, `/minio/*`,
|
||||
`.minio.sys`, ARN, module/import-path, or serialized compatibility name.
|
||||
|
||||
### Commit changes
|
||||
|
||||
After verification, commit your changes. This is a [great post](https://chris.beams.io/posts/git-commit/) on how to write useful commit messages
|
||||
After verification, commit your changes with a concise message and a DCO
|
||||
sign-off (see [Licensing of Contributions](#licensing-of-contributions)):
|
||||
|
||||
```
|
||||
git commit -am 'Add some feature'
|
||||
git commit -s -am 'Fix object replication retry handling'
|
||||
```
|
||||
|
||||
### Push to the branch
|
||||
@@ -62,13 +68,71 @@ git push origin my-new-feature
|
||||
|
||||
### Create a Pull Request
|
||||
|
||||
Pull requests can be created via GitHub. Refer to [this document](https://help.github.com/articles/creating-a-pull-request/) for detailed steps on how to create a pull request. After a Pull Request gets peer reviewed and approved, it will be merged.
|
||||
Pull requests should include motivation, reproduction steps where applicable,
|
||||
test evidence, compatibility notes, and documentation impact. Public product
|
||||
documentation is owned by the separate
|
||||
[`pgsty/silo.pgsty.com`](https://github.com/pgsty/silo.pgsty.com) repository.
|
||||
|
||||
## Licensing of Contributions
|
||||
|
||||
Silo is licensed under the [GNU AGPL v3.0 or later](LICENSE). Its core is
|
||||
Copyright (c) MinIO, Inc.; the combined work can never be relicensed, and this
|
||||
fork does not try to.
|
||||
|
||||
* **No CLA.** We do not ask you to sign a Contributor License Agreement and we
|
||||
do not take your copyright. Contributions are accepted inbound=outbound: you
|
||||
keep the copyright to your changes and license them under the same
|
||||
AGPL-3.0-or-later as the project itself. The maintainers receive no rights
|
||||
beyond the project license.
|
||||
|
||||
* **DCO sign-off required.** Every commit must carry a
|
||||
`Signed-off-by: Your Name <you@example.com>` trailer certifying the
|
||||
[Developer Certificate of Origin 1.1](https://developercertificate.org/) —
|
||||
your statement that you have the right to submit the code under the project
|
||||
license. Sign each commit with:
|
||||
|
||||
```
|
||||
git commit -s
|
||||
```
|
||||
|
||||
Forgot some? Repair your branch with `git rebase --signoff` and force-push.
|
||||
CI rejects pull requests containing unsigned commits; the sign-off email
|
||||
must match the commit author email. (Lowercase `-s` is the plain-text DCO
|
||||
sign-off; cryptographic `-S`/GPG signing is welcome but independent.)
|
||||
|
||||
* **Provenance.** Only submit code you are entitled to submit. This matters
|
||||
more here than in most projects: Silo carries a downstream delta over an
|
||||
upstream code base, and cherry-picks from the lineage remote or other forks
|
||||
are routine. When relaying a patch written by someone else, preserve original
|
||||
authorship (`git cherry-pick -x`, keep the author field and any existing
|
||||
`Signed-off-by` trailers) and add your own sign-off as the person passing it
|
||||
along. Never import code from a proprietary distribution.
|
||||
|
||||
* **File headers.** Files derived from upstream keep the original MinIO
|
||||
copyright header unchanged. New files added by this fork use the dual
|
||||
header, followed by the standard AGPL boilerplate:
|
||||
|
||||
```
|
||||
// Copyright (c) 2015-2025 MinIO, Inc.
|
||||
// Copyright (c) 2025-2026 PGSTY
|
||||
```
|
||||
|
||||
* **Squash merges** must keep the `Signed-off-by:` trailers in the resulting
|
||||
commit message.
|
||||
|
||||
* **Authorship and tooling.** The human contributor is the author of the commit
|
||||
and the sole signatory of its DCO sign-off. Attribution trailers for
|
||||
assistive tooling (for example `Co-Authored-By:` naming an AI assistant) are
|
||||
informational only: they record which tools were used, and do not create
|
||||
authorship, co-authorship, or any copyright claim. Whoever signs off remains
|
||||
responsible for the content of the commit, whatever produced it.
|
||||
|
||||
## FAQs
|
||||
|
||||
### How does ``MinIO`` manage dependencies?
|
||||
### How does Silo manage dependencies?
|
||||
|
||||
``MinIO`` uses `go mod` to manage its dependencies.
|
||||
Silo uses Go modules. Preserve the compatibility module and import paths in
|
||||
`go.mod`; downstream forks are selected with explicit `replace` directives.
|
||||
|
||||
- Run `go get foo/bar` in the source folder to add the dependency to `go.mod` file.
|
||||
|
||||
@@ -77,6 +141,7 @@ To remove a dependency
|
||||
- Edit your code and remove the import reference.
|
||||
- Run `go mod tidy` in the source folder to remove dependency from `go.mod` file.
|
||||
|
||||
### What are the coding guidelines for MinIO?
|
||||
### What are the coding guidelines?
|
||||
|
||||
``MinIO`` is fully conformant with Golang style. Refer: [Effective Go](https://github.com/golang/go/wiki/CodeReviewComments) article from Golang project. If you observe offending code, please feel free to send a pull request or ping us on [Slack](https://slack.min.io).
|
||||
Follow the existing Go style, run `gofmt` on changed Go files, and keep changes
|
||||
compact. See the Go project's [code review comments](https://go.dev/wiki/CodeReviewComments).
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
FROM minio/minio:latest
|
||||
|
||||
RUN chmod -R 777 /usr/bin
|
||||
|
||||
COPY ./minio /usr/bin/minio
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
|
||||
VOLUME ["/data"]
|
||||
|
||||
CMD ["minio"]
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM minio/minio:edge
|
||||
|
||||
CMD ["minio", "server", "/data"]
|
||||
@@ -0,0 +1,50 @@
|
||||
# The distroless variant ships exactly one program: the silo binary.
|
||||
# No shell, no mc, no curl, no entrypoint script; health checking is
|
||||
# provided by the binary itself (`silo healthcheck`).
|
||||
# Design note: https://silo.pgsty.com/compatibility/feature/healthcheck/
|
||||
|
||||
# A distroless final stage cannot RUN anything, so /data is prepared in a
|
||||
# throwaway stage. It ships world-writable (see pgsty/silo#55): Docker
|
||||
# seeds fresh volumes from the image-layer mountpoint, no entrypoint
|
||||
# exists to repair ownership at runtime, and 0777 is what keeps every
|
||||
# privilege mode working, --user included.
|
||||
FROM busybox:1.37.0 AS prep
|
||||
RUN mkdir -p /prep/data && chmod 0777 /prep/data
|
||||
|
||||
FROM gcr.io/distroless/static-debian12:latest
|
||||
|
||||
LABEL org.opencontainers.image.title="Silo" \
|
||||
org.opencontainers.image.description="S3-Interface Libre Object Storage (distroless)" \
|
||||
org.opencontainers.image.url="https://silo.pgsty.com" \
|
||||
org.opencontainers.image.source="https://github.com/pgsty/silo" \
|
||||
org.opencontainers.image.licenses="AGPL-3.0-or-later" \
|
||||
maintainer="PGSTY <https://silo.pgsty.com>"
|
||||
|
||||
ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MINIO_SECRET_KEY_FILE=secret_key \
|
||||
MINIO_ROOT_USER_FILE=access_key \
|
||||
MINIO_ROOT_PASSWORD_FILE=secret_key \
|
||||
MINIO_KMS_SECRET_KEY_FILE=kms_master_key \
|
||||
MINIO_CONFIG_ENV_FILE=config.env \
|
||||
HOME=/tmp
|
||||
|
||||
COPY --chmod=0755 silo /usr/bin/silo
|
||||
# COPY of a directory copies its contents, not the directory entry, so an
|
||||
# empty /prep/data would arrive as a default root:0755 /data and non-root
|
||||
# runs would fail storage init. Copying the parent makes data/ itself a
|
||||
# copied entry, which --chmod then actually applies to.
|
||||
COPY --from=prep --chmod=0777 /prep/ /
|
||||
COPY LICENSE NOTICE CREDITS /licenses/
|
||||
|
||||
EXPOSE 9000
|
||||
VOLUME ["/data"]
|
||||
|
||||
# Exec form is mandatory: there is no /bin/sh in this image. `ready`
|
||||
# rather than `live` because Docker health feeds start-order gating
|
||||
# (readiness semantics); the two are identical unless KMS/etcd are used.
|
||||
# The outer timeout stays above the probe's own 5s deadline so the
|
||||
# probe can report its diagnostic line instead of being SIGKILLed.
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=2m --start-interval=2s --retries=3 \
|
||||
CMD ["/usr/bin/silo", "healthcheck", "ready"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/silo"]
|
||||
@@ -0,0 +1,104 @@
|
||||
FROM golang:1.26.5-alpine AS build
|
||||
|
||||
ARG TARGETARCH
|
||||
|
||||
ENV GOPATH=/go
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
ARG MC_REPO=pgsty/mc
|
||||
ARG MC_VERSION=RELEASE.2026-08-06T00-00-00Z
|
||||
ARG MC_AMD64_SHA256=4b488bd30af54ad4214e5b654746677c79cd93dc6cad4be3aa2d09dbb48370ff
|
||||
ARG MC_ARM64_SHA256=83f6fedb16ed9c1e8efa8aea6776203dff132bc474214543d5c0767ed2066c2f
|
||||
|
||||
RUN apk add -U --no-cache \
|
||||
ca-certificates \
|
||||
bash \
|
||||
curl \
|
||||
jq && \
|
||||
case "${TARGETARCH}" in \
|
||||
amd64) MC_ARCH=amd64; MC_PINNED_SHA256="${MC_AMD64_SHA256}" ;; \
|
||||
arm64) MC_ARCH=arm64; MC_PINNED_SHA256="${MC_ARM64_SHA256}" ;; \
|
||||
*) echo "Unsupported TARGETARCH=${TARGETARCH}"; exit 1 ;; \
|
||||
esac && \
|
||||
if [ "${MC_VERSION}" = "latest" ]; then \
|
||||
MC_RELEASE_URL="https://api.github.com/repos/${MC_REPO}/releases/latest"; \
|
||||
else \
|
||||
MC_RELEASE_URL="https://api.github.com/repos/${MC_REPO}/releases/tags/${MC_VERSION}"; \
|
||||
fi && \
|
||||
curl -fsSL "${MC_RELEASE_URL}" -o /tmp/mc-release.json && \
|
||||
MC_ARCHIVE_URL=$(jq -r --arg arch "${MC_ARCH}" \
|
||||
'.assets[] | select(.name | endswith("_linux_" + $arch + ".tar.gz")) | .browser_download_url' \
|
||||
/tmp/mc-release.json | head -n 1) && \
|
||||
MC_CHECKSUM_URL=$(jq -r \
|
||||
'.assets[] | select(.name | endswith("_checksums.txt")) | .browser_download_url' \
|
||||
/tmp/mc-release.json | head -n 1) && \
|
||||
[ -n "${MC_ARCHIVE_URL}" ] || { echo "Cannot find mcli archive for linux/${MC_ARCH}"; exit 1; } && \
|
||||
[ -n "${MC_CHECKSUM_URL}" ] || { echo "Cannot find mcli checksums file"; exit 1; } && \
|
||||
ARCHIVE_NAME=$(basename "${MC_ARCHIVE_URL}") && \
|
||||
echo "Downloading ${ARCHIVE_NAME} ..." && \
|
||||
curl -fsSL "${MC_ARCHIVE_URL}" -o /tmp/mcli.tar.gz && \
|
||||
curl -fsSL "${MC_CHECKSUM_URL}" -o /tmp/mcli_checksums.txt && \
|
||||
EXPECTED=$(grep " ${ARCHIVE_NAME}$" /tmp/mcli_checksums.txt | awk '{print $1}') && \
|
||||
ACTUAL=$(sha256sum /tmp/mcli.tar.gz | awk '{print $1}') && \
|
||||
[ -n "${EXPECTED}" ] || { echo "Checksum entry not found for ${ARCHIVE_NAME}"; exit 1; } && \
|
||||
[ "${EXPECTED}" = "${MC_PINNED_SHA256}" ] || { echo "Published checksum drift for ${ARCHIVE_NAME}"; exit 1; } && \
|
||||
[ "${MC_PINNED_SHA256}" = "${ACTUAL}" ] || { echo "Checksum mismatch: expected ${MC_PINNED_SHA256}, got ${ACTUAL}"; exit 1; } && \
|
||||
echo "Checksum OK: ${ACTUAL}" && \
|
||||
mkdir -p /tmp/mcli-extract && \
|
||||
tar -xzf /tmp/mcli.tar.gz -C /tmp/mcli-extract/ && \
|
||||
if [ -f /tmp/mcli-extract/mcli ]; then \
|
||||
cp /tmp/mcli-extract/mcli /go/bin/mcli; \
|
||||
elif [ -f /tmp/mcli-extract/mc ]; then \
|
||||
cp /tmp/mcli-extract/mc /go/bin/mcli; \
|
||||
else \
|
||||
echo "No mc or mcli binary found in archive:"; ls -la /tmp/mcli-extract/; exit 1; \
|
||||
fi && \
|
||||
chmod +x /go/bin/mcli && \
|
||||
ln -sf mcli /go/bin/mc
|
||||
|
||||
COPY dockerscripts/download-static-curl.sh /build/download-static-curl
|
||||
RUN chmod +x /build/download-static-curl && \
|
||||
/build/download-static-curl
|
||||
|
||||
FROM registry.access.redhat.com/ubi9/ubi:latest AS certs
|
||||
RUN dnf -y install ca-certificates && \
|
||||
update-ca-trust && \
|
||||
cp /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem /tmp/ca-certificates.crt && \
|
||||
dnf clean all && \
|
||||
rm -rf /var/cache/dnf
|
||||
|
||||
FROM registry.access.redhat.com/ubi9/ubi-micro:latest
|
||||
|
||||
LABEL org.opencontainers.image.title="Silo" \
|
||||
org.opencontainers.image.description="S3-Interface Libre Object Storage" \
|
||||
org.opencontainers.image.url="https://silo.pgsty.com" \
|
||||
org.opencontainers.image.source="https://github.com/pgsty/silo" \
|
||||
org.opencontainers.image.licenses="AGPL-3.0-or-later" \
|
||||
maintainer="PGSTY <https://silo.pgsty.com>"
|
||||
|
||||
ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MINIO_SECRET_KEY_FILE=secret_key \
|
||||
MINIO_ROOT_USER_FILE=access_key \
|
||||
MINIO_ROOT_PASSWORD_FILE=secret_key \
|
||||
MINIO_KMS_SECRET_KEY_FILE=kms_master_key \
|
||||
MINIO_CONFIG_ENV_FILE=config.env \
|
||||
HOME=/tmp \
|
||||
MC_CONFIG_DIR=/tmp/.mc
|
||||
|
||||
COPY --from=certs /tmp/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
COPY silo /usr/bin/silo
|
||||
COPY --from=build /go/bin/mcli /usr/bin/mcli
|
||||
COPY --from=build /go/bin/curl* /usr/bin/
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
COPY LICENSE /licenses/LICENSE
|
||||
COPY NOTICE /licenses/NOTICE
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
|
||||
RUN chmod +x /usr/bin/silo /usr/bin/mcli /usr/bin/docker-entrypoint.sh && \
|
||||
ln -sf mcli /usr/bin/mc
|
||||
|
||||
EXPOSE 9000
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
CMD ["silo"]
|
||||
@@ -1,71 +0,0 @@
|
||||
FROM golang:1.23-alpine as build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH=/go
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
apk add -U --no-cache curl && \
|
||||
go install aead.dev/minisign/cmd/minisign@v0.2.1
|
||||
|
||||
# Download minio binary and signature files
|
||||
RUN curl -s -q https://dl.min.io/server/minio/hotfixes/linux-${TARGETARCH}/archive/minio.${RELEASE} -o /go/bin/minio && \
|
||||
curl -s -q https://dl.min.io/server/minio/hotfixes/linux-${TARGETARCH}/archive/minio.${RELEASE}.minisig -o /go/bin/minio.minisig && \
|
||||
curl -s -q https://dl.min.io/server/minio/hotfixes/linux-${TARGETARCH}/archive/minio.${RELEASE}.sha256sum -o /go/bin/minio.sha256sum && \
|
||||
chmod +x /go/bin/minio
|
||||
|
||||
# Download mc binary and signature files
|
||||
RUN curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc -o /go/bin/mc && \
|
||||
curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.minisig -o /go/bin/mc.minisig && \
|
||||
curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.sha256sum -o /go/bin/mc.sha256sum && \
|
||||
chmod +x /go/bin/mc
|
||||
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
curl -L -s -q https://github.com/moparisthebest/static-curl/releases/latest/download/curl-${TARGETARCH} -o /go/bin/curl; \
|
||||
chmod +x /go/bin/curl; \
|
||||
fi
|
||||
|
||||
# Verify binary signature using public key "RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGavRUN"
|
||||
RUN minisign -Vqm /go/bin/minio -x /go/bin/minio.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav && \
|
||||
minisign -Vqm /go/bin/mc -x /go/bin/mc.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav
|
||||
|
||||
FROM registry.access.redhat.com/ubi9/ubi-micro:latest
|
||||
|
||||
ARG RELEASE
|
||||
|
||||
LABEL name="MinIO" \
|
||||
vendor="MinIO Inc <dev@min.io>" \
|
||||
maintainer="MinIO Inc <dev@min.io>" \
|
||||
version="${RELEASE}" \
|
||||
release="${RELEASE}" \
|
||||
summary="MinIO is a High Performance Object Storage, API compatible with Amazon S3 cloud storage service." \
|
||||
description="MinIO object storage is fundamentally different. Designed for performance and the S3 API, it is 100% open-source. MinIO is ideal for large, private cloud environments with stringent security requirements and delivers mission-critical availability across a diverse range of workloads."
|
||||
|
||||
ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MINIO_SECRET_KEY_FILE=secret_key \
|
||||
MINIO_ROOT_USER_FILE=access_key \
|
||||
MINIO_ROOT_PASSWORD_FILE=secret_key \
|
||||
MINIO_KMS_SECRET_KEY_FILE=kms_master_key \
|
||||
MINIO_UPDATE_MINISIGN_PUBKEY="RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav" \
|
||||
MINIO_CONFIG_ENV_FILE=config.env \
|
||||
MC_CONFIG_DIR=/tmp/.mc
|
||||
|
||||
RUN chmod -R 777 /usr/bin
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio* /usr/bin/
|
||||
COPY --from=build /go/bin/mc* /usr/bin/
|
||||
COPY --from=build /go/bin/cur* /usr/bin/
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
COPY LICENSE /licenses/LICENSE
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 9000
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
CMD ["minio"]
|
||||
@@ -1,73 +0,0 @@
|
||||
FROM golang:1.23-alpine AS build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH=/go
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
apk add -U --no-cache curl && \
|
||||
apk add -U --no-cache bash && \
|
||||
go install aead.dev/minisign/cmd/minisign@v0.2.1
|
||||
|
||||
# Download minio binary and signature files
|
||||
RUN curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE} -o /go/bin/minio && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.minisig -o /go/bin/minio.minisig && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.sha256sum -o /go/bin/minio.sha256sum && \
|
||||
chmod +x /go/bin/minio
|
||||
|
||||
# Download mc binary and signature files
|
||||
RUN curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc -o /go/bin/mc && \
|
||||
curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.minisig -o /go/bin/mc.minisig && \
|
||||
curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.sha256sum -o /go/bin/mc.sha256sum && \
|
||||
chmod +x /go/bin/mc
|
||||
|
||||
# Verify binary signature using public key "RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGavRUN"
|
||||
RUN minisign -Vqm /go/bin/minio -x /go/bin/minio.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav && \
|
||||
minisign -Vqm /go/bin/mc -x /go/bin/mc.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav
|
||||
|
||||
COPY dockerscripts/download-static-curl.sh /build/download-static-curl
|
||||
RUN chmod +x /build/download-static-curl && \
|
||||
/build/download-static-curl
|
||||
|
||||
FROM registry.access.redhat.com/ubi9/ubi-micro:latest
|
||||
|
||||
ARG RELEASE
|
||||
|
||||
LABEL name="MinIO" \
|
||||
vendor="MinIO Inc <dev@min.io>" \
|
||||
maintainer="MinIO Inc <dev@min.io>" \
|
||||
version="${RELEASE}" \
|
||||
release="${RELEASE}" \
|
||||
summary="MinIO is a High Performance Object Storage, API compatible with Amazon S3 cloud storage service." \
|
||||
description="MinIO object storage is fundamentally different. Designed for performance and the S3 API, it is 100% open-source. MinIO is ideal for large, private cloud environments with stringent security requirements and delivers mission-critical availability across a diverse range of workloads."
|
||||
|
||||
ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MINIO_SECRET_KEY_FILE=secret_key \
|
||||
MINIO_ROOT_USER_FILE=access_key \
|
||||
MINIO_ROOT_PASSWORD_FILE=secret_key \
|
||||
MINIO_KMS_SECRET_KEY_FILE=kms_master_key \
|
||||
MINIO_UPDATE_MINISIGN_PUBKEY="RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav" \
|
||||
MINIO_CONFIG_ENV_FILE=config.env \
|
||||
MC_CONFIG_DIR=/tmp/.mc
|
||||
|
||||
RUN chmod -R 777 /usr/bin
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio* /usr/bin/
|
||||
COPY --from=build /go/bin/mc* /usr/bin/
|
||||
COPY --from=build /go/bin/curl* /usr/bin/
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
COPY LICENSE /licenses/LICENSE
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 9000
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
CMD ["minio"]
|
||||
@@ -1,71 +0,0 @@
|
||||
FROM golang:1.23-alpine AS build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH=/go
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
apk add -U --no-cache curl && \
|
||||
go install aead.dev/minisign/cmd/minisign@v0.2.1
|
||||
|
||||
# Download minio binary and signature files
|
||||
RUN curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE} -o /go/bin/minio && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.minisig -o /go/bin/minio.minisig && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.sha256sum -o /go/bin/minio.sha256sum && \
|
||||
chmod +x /go/bin/minio
|
||||
|
||||
# Download mc binary and signature files
|
||||
RUN curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc -o /go/bin/mc && \
|
||||
curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.minisig -o /go/bin/mc.minisig && \
|
||||
curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.sha256sum -o /go/bin/mc.sha256sum && \
|
||||
chmod +x /go/bin/mc
|
||||
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
curl -L -s -q https://github.com/moparisthebest/static-curl/releases/latest/download/curl-${TARGETARCH} -o /go/bin/curl; \
|
||||
chmod +x /go/bin/curl; \
|
||||
fi
|
||||
|
||||
# Verify binary signature using public key "RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGavRUN"
|
||||
RUN minisign -Vqm /go/bin/minio -x /go/bin/minio.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav && \
|
||||
minisign -Vqm /go/bin/mc -x /go/bin/mc.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav
|
||||
|
||||
FROM registry.access.redhat.com/ubi8/ubi-micro:latest
|
||||
|
||||
ARG RELEASE
|
||||
|
||||
LABEL name="MinIO" \
|
||||
vendor="MinIO Inc <dev@min.io>" \
|
||||
maintainer="MinIO Inc <dev@min.io>" \
|
||||
version="${RELEASE}" \
|
||||
release="${RELEASE}" \
|
||||
summary="MinIO is a High Performance Object Storage, API compatible with Amazon S3 cloud storage service." \
|
||||
description="MinIO object storage is fundamentally different. Designed for performance and the S3 API, it is 100% open-source. MinIO is ideal for large, private cloud environments with stringent security requirements and delivers mission-critical availability across a diverse range of workloads."
|
||||
|
||||
ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MINIO_SECRET_KEY_FILE=secret_key \
|
||||
MINIO_ROOT_USER_FILE=access_key \
|
||||
MINIO_ROOT_PASSWORD_FILE=secret_key \
|
||||
MINIO_KMS_SECRET_KEY_FILE=kms_master_key \
|
||||
MINIO_UPDATE_MINISIGN_PUBKEY="RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav" \
|
||||
MINIO_CONFIG_ENV_FILE=config.env \
|
||||
MC_CONFIG_DIR=/tmp/.mc
|
||||
|
||||
RUN chmod -R 777 /usr/bin
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio* /usr/bin/
|
||||
COPY --from=build /go/bin/mc* /usr/bin/
|
||||
COPY --from=build /go/bin/cur* /usr/bin/
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
COPY LICENSE /licenses/LICENSE
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 9000
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
CMD ["minio"]
|
||||
@@ -1,5 +0,0 @@
|
||||
FROM scratch
|
||||
|
||||
COPY minio /minio
|
||||
|
||||
CMD ["/minio"]
|
||||
@@ -4,10 +4,11 @@ LDFLAGS := $(shell go run buildscripts/gen-ldflags.go)
|
||||
|
||||
GOOS ?= $(shell go env GOOS)
|
||||
GOARCH ?= $(shell go env GOARCH)
|
||||
GOLANGCI_VERSION ?= v2.11.3
|
||||
|
||||
VERSION ?= $(shell git describe --tags)
|
||||
REPO ?= quay.io/minio
|
||||
TAG ?= $(REPO)/minio:$(VERSION)
|
||||
REPO ?= docker.io/pgsty
|
||||
TAG ?= $(REPO)/silo:$(VERSION)
|
||||
|
||||
GOLANGCI_DIR = .bin/golangci/$(GOLANGCI_VERSION)
|
||||
GOLANGCI = $(GOLANGCI_DIR)/golangci-lint
|
||||
@@ -23,37 +24,61 @@ help: ## print this help
|
||||
|
||||
getdeps: ## fetch necessary dependencies
|
||||
@mkdir -p ${GOPATH}/bin
|
||||
@echo "Installing golangci-lint" && curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOLANGCI_DIR)
|
||||
@echo "Installing msgp" && go install -v github.com/tinylib/msgp@v1.2.5
|
||||
@echo "Installing stringer" && go install -v golang.org/x/tools/cmd/stringer@latest
|
||||
@if [ ! -x "$(GOLANGCI)" ]; then \
|
||||
set -e; \
|
||||
echo "Installing golangci-lint $(GOLANGCI_VERSION)"; \
|
||||
script=$$(mktemp); \
|
||||
trap 'rm -f "$$script"' EXIT; \
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/$(GOLANGCI_VERSION)/install.sh -o "$$script"; \
|
||||
sh "$$script" -b $(GOLANGCI_DIR) $(GOLANGCI_VERSION); \
|
||||
fi
|
||||
|
||||
crosscompile: ## cross compile minio
|
||||
crosscompile: ## cross compile Silo
|
||||
@(env bash $(PWD)/buildscripts/cross-compile.sh)
|
||||
|
||||
verifiers: lint check-gen
|
||||
verifiers: lint check-gen rebrand-guard
|
||||
|
||||
rebrand-guard: ## verify Silo branding and protected compatibility identifiers
|
||||
@go run ./buildscripts/rebrand-guard
|
||||
@env bash $(PWD)/buildscripts/verify-rebrand.sh
|
||||
@env bash $(PWD)/dockerscripts/docker-entrypoint_test.sh
|
||||
|
||||
credits: ## regenerate CREDITS from the licenses of Go modules linked into the binary
|
||||
@env bash $(PWD)/buildscripts/gen-credits.sh
|
||||
|
||||
check-gen: ## check for updated autogenerated files
|
||||
@go generate ./... >/dev/null
|
||||
@go mod tidy -compat=1.21
|
||||
@(! git diff --name-only | grep '_gen.go$$') || (echo "Non-committed changes in auto-generated code is detected, please commit them to proceed." && false)
|
||||
@(! git diff --name-only | grep 'go.sum') || (echo "Non-committed changes in auto-generated go.sum is detected, please commit them to proceed." && false)
|
||||
@go mod tidy -compat=1.26
|
||||
@env bash $(PWD)/buildscripts/gen-credits.sh
|
||||
@changed=$$(git diff --name-only -- '*_gen.go' '*_gen_test.go' '*_msgp_test.go' '*_string.go' go.mod go.sum CREDITS); \
|
||||
if [ -n "$$changed" ]; then \
|
||||
echo "Non-committed generated changes detected:"; \
|
||||
echo "$$changed"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@untracked=$$(git ls-files --others --exclude-standard -- '*_gen.go' '*_gen_test.go' '*_msgp_test.go' '*_string.go'); \
|
||||
if [ -n "$$untracked" ]; then \
|
||||
echo "Untracked generated files detected:"; \
|
||||
echo "$$untracked"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
lint: getdeps ## runs golangci-lint suite of linters
|
||||
@echo "Running $@ check"
|
||||
@$(GOLANGCI) run --build-tags kqueue --timeout=10m --config ./.golangci.yml
|
||||
@command typos && typos ./ || echo "typos binary is not found.. skipping.."
|
||||
@if command -v typos >/dev/null 2>&1; then typos ./; else echo "typos binary is not found.. skipping.."; fi
|
||||
|
||||
lint-fix: getdeps ## runs golangci-lint suite of linters with automatic fixes
|
||||
@echo "Running $@ check"
|
||||
@$(GOLANGCI) run --build-tags kqueue --timeout=10m --config ./.golangci.yml --fix
|
||||
|
||||
check: test
|
||||
test: verifiers build ## builds minio, runs linters, tests
|
||||
test: verifiers build ## builds Silo, runs linters, tests
|
||||
@echo "Running unit tests"
|
||||
@MINIO_API_REQUESTS_MAX=10000 CGO_ENABLED=0 go test -v -tags kqueue,dev ./...
|
||||
|
||||
test-root-disable: install-race
|
||||
@echo "Running minio root lockdown tests"
|
||||
@echo "Running Silo root lockdown tests"
|
||||
@env bash $(PWD)/buildscripts/disable-root.sh
|
||||
|
||||
test-ilm: install-race
|
||||
@@ -69,7 +94,7 @@ test-pbac: install-race
|
||||
@env bash $(PWD)/docs/iam/policies/pbac-tests.sh
|
||||
|
||||
test-decom: install-race
|
||||
@echo "Running minio decom tests"
|
||||
@echo "Running Silo decom tests"
|
||||
@env bash $(PWD)/docs/distributed/decom.sh
|
||||
@env bash $(PWD)/docs/distributed/decom-encrypted.sh
|
||||
@env bash $(PWD)/docs/distributed/decom-encrypted-sse-s3.sh
|
||||
@@ -77,17 +102,17 @@ test-decom: install-race
|
||||
@env bash $(PWD)/docs/distributed/decom-encrypted-kes.sh
|
||||
|
||||
test-versioning: install-race
|
||||
@echo "Running minio versioning tests"
|
||||
@echo "Running Silo versioning tests"
|
||||
@env bash $(PWD)/docs/bucket/versioning/versioning-tests.sh
|
||||
|
||||
test-configfile: install-race
|
||||
@env bash $(PWD)/docs/distributed/distributed-from-config-file.sh
|
||||
|
||||
test-upgrade: install-race
|
||||
@echo "Running minio upgrade tests"
|
||||
test-upgrade:
|
||||
@echo "Running MinIO-to-Silo upgrade tests"
|
||||
@(env bash $(PWD)/buildscripts/minio-upgrade.sh)
|
||||
|
||||
test-race: verifiers build ## builds minio, runs linters, tests (race)
|
||||
test-race: verifiers build ## builds Silo, runs linters, tests (race)
|
||||
@echo "Running unit tests under -race"
|
||||
@(env bash $(PWD)/buildscripts/race.sh)
|
||||
|
||||
@@ -135,9 +160,9 @@ test-site-replication-oidc: install-race ## verify automatic site replication
|
||||
@echo "Running tests for automatic site replication of IAM (with OIDC)"
|
||||
@(env bash $(PWD)/docs/site-replication/run-multi-site-oidc.sh)
|
||||
|
||||
test-site-replication-minio: install-race ## verify automatic site replication
|
||||
@echo "Running tests for automatic site replication of IAM (with MinIO IDP)"
|
||||
@(env bash $(PWD)/docs/site-replication/run-multi-site-minio-idp.sh)
|
||||
test-site-replication-silo: install-race ## verify automatic site replication
|
||||
@echo "Running tests for automatic site replication of IAM (with Silo IDP)"
|
||||
@(env bash $(PWD)/docs/site-replication/run-multi-site-silo-idp.sh)
|
||||
@echo "Running tests for automatic site replication of SSE-C objects"
|
||||
@(env bash $(PWD)/docs/site-replication/run-ssec-object-replication.sh)
|
||||
@echo "Running tests for automatic site replication of SSE-C objects with SSE-KMS enabled for bucket"
|
||||
@@ -153,11 +178,11 @@ test-timeout: install-race ## test multipart
|
||||
@echo "Test server timeout"
|
||||
@(env bash $(PWD)/buildscripts/test-timeout.sh)
|
||||
|
||||
verify: install-race ## verify minio various setups
|
||||
verify: install-race ## verify Silo in various setups
|
||||
@echo "Verifying build with race"
|
||||
@(env bash $(PWD)/buildscripts/verify-build.sh)
|
||||
|
||||
verify-healing: install-race ## verify healing and replacing disks with minio binary
|
||||
verify-healing: install-race ## verify healing and replacing disks with the Silo binary
|
||||
@echo "Verify healing build with race"
|
||||
@(env bash $(PWD)/buildscripts/verify-healing.sh)
|
||||
@(env bash $(PWD)/buildscripts/verify-healing-empty-erasure-set.sh)
|
||||
@@ -178,59 +203,49 @@ verify-healing-inconsistent-versions: install-race ## verify resolving inconsist
|
||||
build-debugging:
|
||||
@(env bash $(PWD)/docs/debugging/build.sh)
|
||||
|
||||
build: checks build-debugging ## builds minio to $(PWD)
|
||||
@echo "Building minio binary to './minio'"
|
||||
@CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -tags kqueue -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/minio 1>/dev/null
|
||||
build: checks build-debugging ## builds Silo to $(PWD)
|
||||
@echo "Building Silo binary to './silo'"
|
||||
@CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -tags kqueue -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/silo 1>/dev/null
|
||||
|
||||
hotfix-vars:
|
||||
$(eval LDFLAGS := $(shell MINIO_RELEASE="RELEASE" MINIO_HOTFIX="hotfix.$(shell git rev-parse --short HEAD)" go run buildscripts/gen-ldflags.go $(shell git describe --tags --abbrev=0 | \
|
||||
sed 's#RELEASE\.\([0-9]\+\)-\([0-9]\+\)-\([0-9]\+\)T\([0-9]\+\)-\([0-9]\+\)-\([0-9]\+\)Z#\1-\2-\3T\4:\5:\6Z#')))
|
||||
$(eval VERSION := $(shell git describe --tags --abbrev=0).hotfix.$(shell git rev-parse --short HEAD))
|
||||
docker: checks build-debugging ## builds the local Linux Silo container image
|
||||
@echo "Building Silo container image '$(TAG)'"
|
||||
@set -e; \
|
||||
context=$$(mktemp -d); \
|
||||
trap 'rm -rf "$$context"' EXIT; \
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -tags kqueue -trimpath \
|
||||
--ldflags "$(LDFLAGS)" -o "$$context/silo"; \
|
||||
mkdir -p "$$context/dockerscripts"; \
|
||||
cp Dockerfile.goreleaser LICENSE NOTICE CREDITS "$$context/"; \
|
||||
cp dockerscripts/docker-entrypoint.sh dockerscripts/download-static-curl.sh \
|
||||
"$$context/dockerscripts/"; \
|
||||
docker build -q --no-cache --platform linux/$(GOARCH) -t $(TAG) --build-arg TARGETARCH=$(GOARCH) \
|
||||
-f "$$context/Dockerfile.goreleaser" "$$context"
|
||||
|
||||
hotfix: hotfix-vars clean install ## builds minio binary with hotfix tags
|
||||
@wget -q -c https://github.com/minio/pkger/releases/download/v2.3.10/pkger_2.3.10_linux_amd64.deb
|
||||
@wget -q -c https://raw.githubusercontent.com/minio/minio-service/v1.1.0/linux-systemd/distributed/minio.service
|
||||
@sudo apt install ./pkger_2.3.10_linux_amd64.deb --yes
|
||||
@mkdir -p minio-release/$(GOOS)-$(GOARCH)/archive
|
||||
@cp -af ./minio minio-release/$(GOOS)-$(GOARCH)/minio
|
||||
@cp -af ./minio minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION)
|
||||
@minisign -qQSm minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION) -s "${CRED_DIR}/minisign.key" < "${CRED_DIR}/minisign-passphrase"
|
||||
@sha256sum < minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION) | sed 's, -,minio.$(VERSION),g' > minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION).sha256sum
|
||||
@cp -af minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION)* minio-release/$(GOOS)-$(GOARCH)/archive/
|
||||
@pkger -r $(VERSION) --ignore
|
||||
|
||||
hotfix-push: hotfix
|
||||
@scp -q -r minio-release/$(GOOS)-$(GOARCH)/* minio@dl-0.minio.io:~/releases/server/minio/hotfixes/linux-$(GOOS)/
|
||||
@scp -q -r minio-release/$(GOOS)-$(GOARCH)/* minio@dl-0.minio.io:~/releases/server/minio/hotfixes/linux-$(GOOS)/archive
|
||||
@scp -q -r minio-release/$(GOOS)-$(GOARCH)/* minio@dl-1.minio.io:~/releases/server/minio/hotfixes/linux-$(GOOS)/
|
||||
@scp -q -r minio-release/$(GOOS)-$(GOARCH)/* minio@dl-1.minio.io:~/releases/server/minio/hotfixes/linux-$(GOOS)/archive
|
||||
@echo "Published new hotfix binaries at https://dl.min.io/server/minio/hotfixes/linux-$(GOOS)/archive/minio.$(VERSION)"
|
||||
|
||||
docker-hotfix-push: docker-hotfix
|
||||
@docker push -q $(TAG) && echo "Published new container $(TAG)"
|
||||
|
||||
docker-hotfix: hotfix-push checks ## builds minio docker container with hotfix tags
|
||||
@echo "Building minio docker image '$(TAG)'"
|
||||
@docker build -q --no-cache -t $(TAG) --build-arg RELEASE=$(VERSION) . -f Dockerfile.hotfix
|
||||
|
||||
docker: build ## builds minio docker container
|
||||
@echo "Building minio docker image '$(TAG)'"
|
||||
@docker build -q --no-cache -t $(TAG) . -f Dockerfile
|
||||
docker-distroless: checks build-debugging ## builds the local Linux Silo distroless container image
|
||||
@echo "Building Silo distroless container image '$(TAG)-distroless'"
|
||||
@set -e; \
|
||||
context=$$(mktemp -d); \
|
||||
trap 'rm -rf "$$context"' EXIT; \
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -tags kqueue -trimpath \
|
||||
--ldflags "$(LDFLAGS)" -o "$$context/silo"; \
|
||||
cp Dockerfile.distroless LICENSE NOTICE CREDITS "$$context/"; \
|
||||
docker build -q --no-cache --platform linux/$(GOARCH) -t $(TAG)-distroless \
|
||||
-f "$$context/Dockerfile.distroless" "$$context"
|
||||
|
||||
test-resiliency: build
|
||||
@echo "Running resiliency tests"
|
||||
@(DOCKER_COMPOSE_FILE=$(PWD)/docs/resiliency/docker-compose.yaml env bash $(PWD)/docs/resiliency/resiliency-tests.sh)
|
||||
|
||||
install-race: checks build-debugging ## builds minio to $(PWD)
|
||||
@echo "Building minio binary with -race to './minio'"
|
||||
@GORACE=history_size=7 CGO_ENABLED=1 go build -tags kqueue,dev -race -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/minio 1>/dev/null
|
||||
@echo "Installing minio binary with -race to '$(GOPATH)/bin/minio'"
|
||||
@mkdir -p $(GOPATH)/bin && cp -af $(PWD)/minio $(GOPATH)/bin/minio
|
||||
install-race: checks build-debugging ## builds Silo to $(PWD)
|
||||
@echo "Building Silo binary with -race to './silo'"
|
||||
@GORACE=history_size=7 CGO_ENABLED=1 go build -tags kqueue,dev -race -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/silo 1>/dev/null
|
||||
@echo "Installing Silo binary with -race to '$(GOPATH)/bin/silo'"
|
||||
@mkdir -p $(GOPATH)/bin && cp -af $(PWD)/silo $(GOPATH)/bin/silo
|
||||
|
||||
install: build ## builds minio and installs it to $GOPATH/bin.
|
||||
@echo "Installing minio binary to '$(GOPATH)/bin/minio'"
|
||||
@mkdir -p $(GOPATH)/bin && cp -af $(PWD)/minio $(GOPATH)/bin/minio
|
||||
@echo "Installation successful. To learn more, try \"minio --help\"."
|
||||
install: build ## builds Silo and installs it to $GOPATH/bin.
|
||||
@echo "Installing Silo binary to '$(GOPATH)/bin/silo'"
|
||||
@mkdir -p $(GOPATH)/bin && cp -af $(PWD)/silo $(GOPATH)/bin/silo
|
||||
@echo "Installation successful. To learn more, try \"silo --help\"."
|
||||
|
||||
clean: ## cleanup all generated assets
|
||||
@echo "Cleaning up all the generated files"
|
||||
@@ -238,10 +253,8 @@ clean: ## cleanup all generated assets
|
||||
@find . -name '*~' | xargs rm -fv
|
||||
@find . -name '.#*#' | xargs rm -fv
|
||||
@find . -name '#*#' | xargs rm -fv
|
||||
@rm -rvf minio
|
||||
@rm -rvf silo
|
||||
@rm -rvf build
|
||||
@rm -rvf release
|
||||
@rm -rvf .verify*
|
||||
@rm -rvf minio-release
|
||||
@rm -rvf minio.RELEASE*.hotfix.*
|
||||
@rm -rvf pkger_*.deb
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
MinIO Project, (C) 2015-2023 MinIO, Inc.
|
||||
MinIO Project, (C) 2015-2025 MinIO, Inc.
|
||||
|
||||
This product includes software developed at MinIO, Inc.
|
||||
(https://min.io/).
|
||||
@@ -7,3 +7,9 @@ The MinIO project contains unmodified/modified subcomponents too with
|
||||
separate copyright notices and license terms. Your use of the source
|
||||
code for these subcomponents is subject to the terms and conditions
|
||||
of GNU Affero General Public License 3.0.
|
||||
|
||||
Silo Project modifications, (C) 2025-2026 PGSTY.
|
||||
|
||||
Silo is an independent community-maintained project incorporating MinIO
|
||||
source code. It is not affiliated with or endorsed by MinIO, Inc. Modified
|
||||
source and Silo release artifacts are maintained by the Silo project.
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# Silo Pull Request Guidelines
|
||||
|
||||
These guidelines ensure high-quality commits in Silo's GitHub repositories, maintaining
|
||||
a clear, valuable commit history for our open-source projects. They apply to all contributors,
|
||||
fostering efficient reviews and robust code.
|
||||
|
||||
## Why Pull Requests?
|
||||
|
||||
Pull Requests (PRs) drive quality in Silo's codebase by:
|
||||
- Enabling peer review without pair programming.
|
||||
- Documenting changes for future reference.
|
||||
- Ensuring commits tell a clear story of development.
|
||||
|
||||
**A poor commit lasts forever, even if code is refactored.**
|
||||
|
||||
## Crafting a Quality PR
|
||||
|
||||
A strong Silo PR:
|
||||
- Delivers a complete, valuable change (feature, bug fix, or improvement).
|
||||
- Has a concise title (e.g., `[S3] Fix bucket policy parsing #1234`) and a summary with context, referencing issues (e.g., `#1234`).
|
||||
- Contains well-written, logical commits explaining *why* changes were made (e.g., “Add S3 bucket tagging support so that users can organize resources efficiently”).
|
||||
- Is small, focused, and easy to review—ideally one commit, unless multiple commits better narrate complex work.
|
||||
- Adheres to Silo's coding standards (e.g., Go style, error handling, testing).
|
||||
|
||||
PRs must flow smoothly through review to reach production. Large PRs should be split into smaller, manageable ones.
|
||||
|
||||
## Submitting PRs
|
||||
|
||||
1. **Title and Summary**:
|
||||
- Use a scannable title: `[Subsystem] Action Description #Issue` (e.g., `[IAM] Add role-based access control #567`).
|
||||
- Include context in the summary: what changed, why, and any issue references.
|
||||
- Use `[WIP]` for in-progress PRs to avoid premature merging or choose GitHub draft PRs.
|
||||
|
||||
2. **Commits**:
|
||||
- Write clear messages: what changed and why (e.g., “Refactor S3 API handler to reduce latency so that requests process 20% faster”).
|
||||
- Rebase to tidy commits before submitting (e.g., `git rebase -i main` to squash typos or reword messages), unless multiple contributors worked on the branch.
|
||||
- Keep PRs focused—one feature or fix. Split large changes into multiple PRs.
|
||||
|
||||
3. **Testing**:
|
||||
- Include unit tests for new functionality or bug fixes.
|
||||
- Ensure existing tests pass (`make test`).
|
||||
- Document testing steps in the PR summary if manual testing was performed.
|
||||
|
||||
4. **Before Submitting**:
|
||||
- Run `make verify` to check formatting, linting, and tests.
|
||||
- Reference related issues (e.g., “Closes #1234”).
|
||||
- Notify team members via GitHub `@mentions` if urgent or complex.
|
||||
|
||||
## Reviewing PRs
|
||||
|
||||
Reviewers ensure Silo's commit history remains a clear, reliable record. Responsibilities include:
|
||||
|
||||
1. **Commit Quality**:
|
||||
- Verify each commit explains *why* the change was made (e.g., “So that…”).
|
||||
- Request rebasing if commits are unclear, redundant, or lack context (e.g., “Please squash typo fixes into the parent commit”).
|
||||
|
||||
2. **Code Quality**:
|
||||
- Check adherence to Silo's Go standards (e.g., error handling, documentation).
|
||||
- Ensure tests cover new code and pass CI.
|
||||
- Flag bugs or critical issues for immediate fixes; suggest non-blocking improvements as follow-up issues.
|
||||
|
||||
3. **Flow**:
|
||||
- Review promptly to avoid blocking progress.
|
||||
- Balance quality and speed—minor issues can be addressed later via issues, not PR blocks.
|
||||
- If unable to complete the review, tag another reviewer (e.g., `@username please take over`).
|
||||
|
||||
4. **Shared Responsibility**:
|
||||
- All Silo contributors are reviewers. The first commenter on a PR owns the review unless they delegate.
|
||||
- Multiple reviewers are encouraged for complex PRs.
|
||||
|
||||
5. **No Self-Edits**:
|
||||
- Don’t modify the PR directly (e.g., fixing bugs). Request changes from the submitter or create a follow-up PR.
|
||||
- If you edit, you’re a collaborator, not a reviewer, and cannot merge.
|
||||
|
||||
6. **Testing**:
|
||||
- Assume the submitter tested the code. If testing is unclear, ask for details (e.g., “How was this tested?”).
|
||||
- Reject untested PRs unless testing is infeasible, then assist with test setup.
|
||||
|
||||
## Tips for Success
|
||||
|
||||
- **Small PRs**: Easier to review, faster to merge. Split large changes logically.
|
||||
- **Clear Commits**: Use `git rebase -i` to refine history before submitting.
|
||||
- **Engage Early**: Discuss complex changes in a GitHub issue before coding.
|
||||
- **Be Responsive**: Address reviewer feedback promptly to keep PRs moving.
|
||||
- **Learn from Reviews**: Use feedback to improve future contributions.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Silo Contribution Guide](CONTRIBUTING.md)
|
||||
- [Effective Commit Messages](https://mislav.net/2014/02/hidden-documentation/)
|
||||
- [GitHub PR Tips](https://github.com/blog/1943-how-to-write-the-perfect-pull-request)
|
||||
|
||||
By following these guidelines, we ensure Silo's codebase remains high-quality, maintainable, and a joy to contribute to. Happy coding!
|
||||
@@ -1,7 +0,0 @@
|
||||
# MinIO FIPS Builds
|
||||
|
||||
MinIO creates FIPS builds using a patched version of the Go compiler (that uses BoringCrypto, from BoringSSL, which is [FIPS 140-2 validated](https://csrc.nist.gov/csrc/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp2964.pdf)) published by the Golang Team [here](https://github.com/golang/go/tree/dev.boringcrypto/misc/boring).
|
||||
|
||||
MinIO FIPS executables are available at <http://dl.min.io> - they are only published for `linux-amd64` architecture as binary files with the suffix `.fips`. We also publish corresponding container images to our official image repositories.
|
||||
|
||||
We are not making any statements or representations about the suitability of this code or build in relation to the FIPS 140-2 standard. Interested users will have to evaluate for themselves whether this is useful for their own purposes.
|
||||
@@ -1,259 +1,266 @@
|
||||
# MinIO Quickstart Guide
|
||||
<h1 align="center">
|
||||
<a href="https://silo.pgsty.com/">
|
||||
<img src=".github/silo-logo.svg" alt="Silo" width="160">
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
[](https://slack.min.io) [](https://hub.docker.com/r/minio/minio/) [](https://github.com/minio/minio/blob/master/LICENSE)
|
||||
|
||||
[](https://min.io)
|
||||
<p align="center">
|
||||
<strong>S3-compatible object storage — a MinIO fork maintained by PIGSTY</strong>
|
||||
</p>
|
||||
|
||||
MinIO is a High Performance Object Storage released under GNU Affero General Public License v3.0. It is API compatible with Amazon S3 cloud storage service. Use MinIO to build high performance infrastructure for machine learning, analytics and application data workloads. To learn more about what MinIO is doing for AI storage, go to [AI storage documentation](https://min.io/solutions/object-storage-for-ai).
|
||||
<p align="center">
|
||||
<a href="https://silo.pgsty.com/">Website</a> ·
|
||||
<a href="https://silo.pgsty.com/docs/">Documentation</a> ·
|
||||
<a href="https://silo.pgsty.com/download/">Download</a> ·
|
||||
<a href="https://silo.pgsty.com/tags/silo/">Release Notes</a> ·
|
||||
<a href="https://silo.pgsty.com/compatibility/server/">Compatibility</a> ·
|
||||
<a href="SECURITY.md">Security</a> ·
|
||||
<a href="README_ZH.md">中文</a>
|
||||
</p>
|
||||
|
||||
This README provides quickstart instructions on running MinIO on bare metal hardware, including container-based installations. For Kubernetes environments, use the [MinIO Kubernetes Operator](https://github.com/minio/operator/blob/master/README.md).
|
||||
<p align="center">
|
||||
<a href="https://silo.pgsty.com/"><img alt="Website" src="https://img.shields.io/badge/Website-silo.pgsty.com-1d588c"></a>
|
||||
<a href="https://github.com/pgsty/silo/releases"><img alt="GitHub Release" src="https://img.shields.io/github/v/release/pgsty/silo?include_prereleases&label=release&logo=github"></a>
|
||||
<a href="https://hub.docker.com/r/pgsty/silo"><img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/pgsty/minio?logo=docker"></a>
|
||||
<a href="go.mod"><img alt="Go Version" src="https://img.shields.io/github/go-mod/go-version/pgsty/silo?logo=go"></a>
|
||||
<a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-AGPLv3-blue"></a>
|
||||
</p>
|
||||
|
||||
## Container Installation
|
||||
> [!IMPORTANT]
|
||||
> Silo is an independent, community-maintained fork of the open-source MinIO server, published by [Pigsty](https://pigsty.io) from [`pgsty/silo`](https://github.com/pgsty/silo). It is not affiliated with, endorsed by, or sponsored by MinIO, Inc. “MinIO” is used only to identify the upstream project and compatibility lineage.
|
||||
|
||||
Use the following commands to run a standalone MinIO server as a container.
|
||||
> [!NOTE]
|
||||
> This repository was renamed from `pgsty/minio` to `pgsty/silo`, and its default branch from `master` to `main`, on 2026-08-06. If you need the artifacts maintained under the original MinIO identity, they are on the archived [`minio`](https://github.com/pgsty/silo/tree/minio) branch and in the releases up to [`RELEASE.2026-08-04T00-00-00Z`](https://github.com/pgsty/silo/releases/tag/RELEASE.2026-08-04T00-00-00Z); those assets and the `docker.io/pgsty/minio` image stay published and unmodified. The rename changed product and artifact names only — `MINIO_*` variables, `minio_*` metrics, `x-minio-*` headers, `/minio/*` routes, and the on-disk format are unchanged.
|
||||
|
||||
Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication
|
||||
require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically,
|
||||
with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html)
|
||||
for more complete documentation.
|
||||
## Overview
|
||||
|
||||
### Stable
|
||||
Silo maintains one downstream release line derived from the open-source MinIO server. It provides maintained builds and release artifacts for existing MinIO-compatible deployments after upstream community distribution ended. Pigsty uses Silo for object storage, including as an optional PostgreSQL backup repository.
|
||||
|
||||
Run the following command to run the latest stable image of MinIO as a container using an ephemeral data volume:
|
||||
The fork follows one rule: **the product and its delivery surfaces are renamed; the protocol and your data are not.** The `silo` executable, packages, service, and container image carry the new name, while the S3 API, `MINIO_*` variables, `minio_*` metrics, `x-minio-*` headers, reserved routes, and the on-disk format stay unchanged. The differences are recorded in the [compatibility notes](https://silo.pgsty.com/compatibility/server/).
|
||||
|
||||
```sh
|
||||
podman run -p 9000:9000 -p 9001:9001 \
|
||||
quay.io/minio/minio server /data --console-address ":9001"
|
||||
The official project portal is [silo.pgsty.com](https://silo.pgsty.com/). It brings documentation, downloads, release and security notes, and project background together.
|
||||
|
||||
## Find the Right Resource
|
||||
|
||||
| Looking for | Canonical location |
|
||||
| :-- | :-- |
|
||||
| Project overview and navigation | [Silo Website](https://silo.pgsty.com/) |
|
||||
| Installation methods and downloads | [Download & Install](https://silo.pgsty.com/download/) |
|
||||
| Operations, administration, development, and reference | [Documentation](https://silo.pgsty.com/docs/) |
|
||||
| Release notes for this server | [Silo release notes](https://silo.pgsty.com/tags/silo/) |
|
||||
| Differences from upstream MinIO | [Silo vs. MinIO compatibility notes](https://silo.pgsty.com/compatibility/server/) |
|
||||
| Project news and security advisories | [Blog](https://silo.pgsty.com/blog/) · [release](https://silo.pgsty.com/blog/release/) and [security](https://silo.pgsty.com/blog/security/) notes |
|
||||
| Versioned binaries, checksums, and source archives | [GitHub Releases](https://github.com/pgsty/silo/releases) |
|
||||
| Bug reports and feature discussions | [GitHub Issues](https://github.com/pgsty/silo/issues) |
|
||||
| Vulnerability reporting | [`SECURITY.md`](SECURITY.md) and [`VULNERABILITY_REPORT.md`](VULNERABILITY_REPORT.md) |
|
||||
| License, attribution, and trademark information | [`LICENSE`](LICENSE) · [`NOTICE`](NOTICE) · [`CREDITS`](CREDITS) · portal [license](https://silo.pgsty.com/about/license/), [attribution](https://silo.pgsty.com/about/attribution/), and [trademark](https://silo.pgsty.com/about/trademark/) pages |
|
||||
|
||||
## Related Projects
|
||||
|
||||
| Repository | Description |
|
||||
| :-- | :-- |
|
||||
| [`pgsty/silo`](https://github.com/pgsty/silo) | This repository — the Silo object storage server, an S3-compatible MinIO fork |
|
||||
| [`pgsty/mc`](https://github.com/pgsty/mc) | The Silo command-line client, shipped as `mcli` with the `mc` command name |
|
||||
| [`pgsty/silo-console`](https://github.com/pgsty/silo-console) | Admin web console, embedded in the server build |
|
||||
| [`pgsty/silo-pkg`](https://github.com/pgsty/silo-pkg) | Shared Go packages maintained for the Silo forks |
|
||||
| [`pgsty/pigsty`](https://github.com/pgsty/pigsty) | Pigsty — the PostgreSQL distribution that ships Silo as its object storage |
|
||||
|
||||
## Maintenance Policy
|
||||
|
||||
The active release line covers:
|
||||
|
||||
- build and dependency maintenance;
|
||||
- applicable security fixes and advisories;
|
||||
- focused fixes for reproducible defects;
|
||||
- versioned binaries, packages, checksums, and multi-architecture images;
|
||||
- the web console, client, documentation, and Pigsty integration.
|
||||
|
||||
Changes are kept narrow and tested where practical. Maintenance is best effort; no response, remediation, or release schedule is guaranteed.
|
||||
|
||||
### Out of scope
|
||||
|
||||
- a separate product roadmap, new storage engine, or speculative S3 features;
|
||||
- broad rewrites or changes that materially expand the downstream delta;
|
||||
- historical releases or multiple support branches;
|
||||
- commercial support, SLAs, 24×7 coverage, or SUBNET access;
|
||||
- deployment design, access control, monitoring, backup, or recovery.
|
||||
|
||||
## Governance
|
||||
|
||||
The server, client, console, and shared packages are maintained under one release process: DCO-signed commits, reviewed pull requests, and versioned `RELEASE.YYYY-MM-DDTHH-MM-SSZ` tags whose artifacts ship with checksums, SPDX SBOMs, Sigstore-signed manifests, and GitHub build attestations. Each release is announced with a [release note](https://silo.pgsty.com/tags/silo/) on the portal; security handling follows [`SECURITY.md`](SECURITY.md) and the [advisory log](docs/security/advisories.md). Upstream copyright, license, and third-party notices are preserved in [`LICENSE`](LICENSE), [`NOTICE`](NOTICE), and [`CREDITS`](CREDITS).
|
||||
|
||||
## Compatibility
|
||||
|
||||
Silo aims to preserve:
|
||||
|
||||
- the `github.com/minio/minio` module path and `github.com/minio/*` import paths;
|
||||
- MinIO-compatible S3 APIs, wire behavior, `MINIO_*` environment variables, metrics, protocol headers, reserved routes, and storage metadata;
|
||||
- `RELEASE.YYYY-MM-DDTHH-MM-SSZ` tags and legacy `minio …` container argv translation.
|
||||
|
||||
Silo-owned delivery surfaces use the `silo` executable, package, service, Helm chart, and `pgsty/silo` container image. Native artifacts intentionally do not install a `minio` binary alias.
|
||||
|
||||
Compatibility is the default constraint. Silo preserves existing wire, client, configuration, and operational behavior whenever doing so remains safe. Compatibility is broken only when necessary to close a major security issue, and the release notes must identify the affected behavior and migration path. Treat each release as a downstream upgrade: pin versions, review the [release notes](https://silo.pgsty.com/tags/silo/), [compatibility notes](https://silo.pgsty.com/compatibility/server/), and [security advisories](docs/security/advisories.md), keep a rollback path, and test before production use.
|
||||
|
||||
## Downloads and Release Artifacts
|
||||
|
||||
Use [Download & Install](https://silo.pgsty.com/download/) to choose an installation method. GitHub Releases remains the source for versioned server binaries, checksums, and source archives.
|
||||
|
||||
| Artifact | Location |
|
||||
| :-- | :-- |
|
||||
| Source | [`github.com/pgsty/silo`](https://github.com/pgsty/silo) |
|
||||
| Container image | [`pgsty/silo`](https://hub.docker.com/r/pgsty/silo), multi-arch for `linux/amd64` and `linux/arm64` |
|
||||
| Server binaries and checksums | [GitHub Releases](https://github.com/pgsty/silo/releases) for Linux, macOS, and Windows on `amd64` and `arm64` |
|
||||
| Linux packages | RPM, DEB, and APK artifacts, also distributed through the [Pigsty repository](https://pigsty.io/docs/repo/) |
|
||||
| Client | [`pgsty/mc`](https://github.com/pgsty/mc), bundled in the container as `mcli` with an `mc` compatibility alias |
|
||||
| Console | [`pgsty/silo-console`](https://github.com/pgsty/silo-console), embedded through the compatibility import path `github.com/minio/console` |
|
||||
| Shared library | [`pgsty/silo-pkg`](https://github.com/pgsty/silo-pkg) v3.11.0, consumed through a `replace` directive while preserving the `github.com/minio/pkg/v3` import path |
|
||||
|
||||
Each new release publishes per-archive and per-package SPDX JSON SBOMs. The archive and package checksum manifests have detached keyless Sigstore bundles, while GitHub artifact attestations record signed provenance for every downloadable artifact and the multi-architecture container image.
|
||||
|
||||
After downloading an archive and its release files, verify integrity, the
|
||||
published SBOM, the signed manifest, and build provenance independently:
|
||||
|
||||
```bash
|
||||
# Integrity: choose the line for the artifact you downloaded.
|
||||
grep -F ' silo_<version>_linux_amd64.tar.gz' \
|
||||
silo_<version>_checksums.txt | sha256sum --check
|
||||
|
||||
# The archive SBOM is a separate checksummed release artifact.
|
||||
grep -F ' silo_<version>_linux_amd64.tar.gz.sbom.json' \
|
||||
silo_<version>_checksums.txt | sha256sum --check
|
||||
|
||||
# Signature over the archive/SBOM checksum manifest.
|
||||
cosign verify-blob \
|
||||
--bundle silo_<version>_checksums.txt.sigstore.json \
|
||||
--certificate-identity-regexp \
|
||||
'^https://github.com/pgsty/(minio|silo)/\.github/workflows/release\.yml@refs/(tags/RELEASE\..+|heads/(master|main))$' \
|
||||
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
||||
silo_<version>_checksums.txt
|
||||
|
||||
# Signed build provenance (online verification against this repository).
|
||||
gh attestation verify silo_<version>_linux_amd64.tar.gz \
|
||||
--repo pgsty/silo
|
||||
```
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded
|
||||
object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the
|
||||
root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
For packages, use `silo_<version>_packages_checksums.txt` and its adjacent
|
||||
Sigstore bundle with the same identity and issuer constraints. Inspect the
|
||||
verified SPDX JSON SBOM with your preferred SPDX tooling. Verify the
|
||||
multi-architecture container provenance by digest:
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See
|
||||
[Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers,
|
||||
see <https://min.io/docs/minio/linux/developers/minio-drivers.html> to view MinIO SDKs for supported languages.
|
||||
|
||||
> NOTE: To deploy MinIO on with persistent storage, you must map local persistent directories from the host OS to the container using the `podman -v` option. For example, `-v /mnt/data:/data` maps the host OS drive at `/mnt/data` to `/data` on the container.
|
||||
|
||||
## macOS
|
||||
|
||||
Use the following commands to run a standalone MinIO server on macOS.
|
||||
|
||||
Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html) for more complete documentation.
|
||||
|
||||
### Homebrew (recommended)
|
||||
|
||||
Run the following command to install the latest stable MinIO package using [Homebrew](https://brew.sh/). Replace ``/data`` with the path to the drive or directory in which you want MinIO to store data.
|
||||
|
||||
```sh
|
||||
brew install minio/stable/minio
|
||||
minio server /data
|
||||
```bash
|
||||
gh attestation verify \
|
||||
oci://index.docker.io/pgsty/silo@sha256:<manifest-digest> \
|
||||
--repo pgsty/silo
|
||||
```
|
||||
|
||||
> NOTE: If you previously installed minio using `brew install minio` then it is recommended that you reinstall minio from `minio/stable/minio` official repo instead.
|
||||
The platform SBOM attestations are attached to the `amd64` and `arm64` image
|
||||
digests rather than the multi-architecture manifest. Verify each one explicitly:
|
||||
|
||||
```sh
|
||||
brew uninstall minio
|
||||
brew install minio/stable/minio
|
||||
```bash
|
||||
gh attestation verify \
|
||||
oci://index.docker.io/pgsty/silo@sha256:<platform-digest> \
|
||||
--repo pgsty/silo \
|
||||
--predicate-type https://spdx.dev/Document/v2.3
|
||||
```
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
Verification by digest avoids trusting a mutable image tag.
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see <https://min.io/docs/minio/linux/developers/minio-drivers.html/> to view MinIO SDKs for supported languages.
|
||||
### Native package migration
|
||||
|
||||
### Binary Download
|
||||
The `silo` RPM, DEB, and APK do not declare `Provides`, `Obsoletes`,
|
||||
`Replaces`, or package-level `Conflicts` against `minio`. They can therefore be
|
||||
installed beside an existing MinIO package without silently replacing it. The
|
||||
two systemd units conflict at runtime, so switch them explicitly rather than
|
||||
starting both.
|
||||
|
||||
Use the following command to download and run a standalone MinIO server on macOS. Replace ``/data`` with the path to the drive or directory in which you want MinIO to store data.
|
||||
Before switching, record the old unit's enabled/active state and `User`/`Group`,
|
||||
and back up `/etc/default/minio`. Silo reads that legacy defaults file first and
|
||||
then `/etc/default/silo`; administrator-set values in the latter take
|
||||
precedence. If the existing data must continue to run under its original
|
||||
UID/GID, create `/etc/systemd/system/silo.service.d/10-legacy-user.conf`:
|
||||
|
||||
```sh
|
||||
wget https://dl.min.io/server/minio/release/darwin-amd64/minio
|
||||
chmod +x minio
|
||||
./minio server /data
|
||||
```ini
|
||||
[Service]
|
||||
User=<legacy-user>
|
||||
Group=<legacy-group>
|
||||
```
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
Run `systemctl daemon-reload`, then disable and stop `minio.service` before
|
||||
enabling and starting `silo.service`. Verify health, S3, Admin API, metrics, and
|
||||
logs before masking or uninstalling the old service. Do not recursively change
|
||||
data ownership as part of the package migration; keep the old package and unit
|
||||
available during the rollback window.
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see <https://min.io/docs/minio/linux/developers/minio-drivers.html> to view MinIO SDKs for supported languages.
|
||||
## Quick Start
|
||||
|
||||
## GNU/Linux
|
||||
For local evaluation:
|
||||
|
||||
Use the following command to run a standalone MinIO server on Linux hosts running 64-bit Intel/AMD architectures. Replace ``/data`` with the path to the drive or directory in which you want MinIO to store data.
|
||||
```bash
|
||||
mkdir -p data
|
||||
|
||||
```sh
|
||||
wget https://dl.min.io/server/minio/release/linux-amd64/minio
|
||||
chmod +x minio
|
||||
./minio server /data
|
||||
export MINIO_ROOT_USER=minioadmin
|
||||
export MINIO_ROOT_PASSWORD=change-me-long-password
|
||||
|
||||
docker run -d --name silo \
|
||||
-p 9000:9000 \
|
||||
-p 9001:9001 \
|
||||
-e MINIO_ROOT_USER \
|
||||
-e MINIO_ROOT_PASSWORD \
|
||||
-v "$PWD/data:/data" \
|
||||
docker.io/pgsty/silo:latest server /data --console-address ":9001"
|
||||
```
|
||||
|
||||
The following table lists supported architectures. Replace the `wget` URL with the architecture for your Linux host.
|
||||
Open the console at <http://localhost:9001>; the S3 API listens on <http://localhost:9000>.
|
||||
|
||||
| Architecture | URL |
|
||||
| -------- | ------ |
|
||||
| 64-bit Intel/AMD | <https://dl.min.io/server/minio/release/linux-amd64/minio> |
|
||||
| 64-bit ARM | <https://dl.min.io/server/minio/release/linux-arm64/minio> |
|
||||
| 64-bit PowerPC LE (ppc64le) | <https://dl.min.io/server/minio/release/linux-ppc64le/minio> |
|
||||
The image includes the compatible client as `mcli`:
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see <https://min.io/docs/minio/linux/developers/minio-drivers.html> to view MinIO SDKs for supported languages.
|
||||
|
||||
> NOTE: Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html#) for more complete documentation.
|
||||
|
||||
## Microsoft Windows
|
||||
|
||||
To run MinIO on 64-bit Windows hosts, download the MinIO executable from the following URL:
|
||||
|
||||
```sh
|
||||
https://dl.min.io/server/minio/release/windows-amd64/minio.exe
|
||||
```bash
|
||||
docker exec silo mcli alias set local http://127.0.0.1:9000 \
|
||||
"$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
|
||||
docker exec silo mcli mb local/demo
|
||||
docker exec silo mcli ls local
|
||||
```
|
||||
|
||||
Use the following command to run a standalone MinIO server on the Windows host. Replace ``D:\`` with the path to the drive or directory in which you want MinIO to store data. You must change the terminal or powershell directory to the location of the ``minio.exe`` executable, *or* add the path to that directory to the system ``$PATH``:
|
||||
> [!WARNING]
|
||||
> For production, pin a release, use unique credentials and TLS, monitor the service, keep independent backups, and test recovery.
|
||||
|
||||
```sh
|
||||
minio.exe server D:\
|
||||
Build the server from source:
|
||||
|
||||
```bash
|
||||
go build -o silo .
|
||||
./silo --version
|
||||
```
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
For other installation paths—including native packages, binaries, Podman, Kubernetes, source, and Pigsty Ansible—use [Download & Install](https://silo.pgsty.com/download/). For production deployment and administration, start with the [Silo documentation](https://silo.pgsty.com/docs/). Pigsty users can also use the [Pigsty MinIO module](https://pigsty.io/docs/minio/).
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see <https://min.io/docs/minio/linux/developers/minio-drivers.html> to view MinIO SDKs for supported languages.
|
||||
## Security
|
||||
|
||||
> NOTE: Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html#) for more complete documentation.
|
||||
Security fixes target the active development branch and are recorded in the [advisory log](docs/security/advisories.md) and the portal's [security notes](https://silo.pgsty.com/blog/security/). Report vulnerabilities privately as described in [`SECURITY.md`](SECURITY.md) and [`VULNERABILITY_REPORT.md`](VULNERABILITY_REPORT.md). Report issues that also affect upstream MinIO there as well.
|
||||
|
||||
## Install from Source
|
||||
## Contributing
|
||||
|
||||
Use the following commands to compile and run a standalone MinIO server from source. Source installation is only intended for developers and advanced users. If you do not have a working Golang environment, please follow [How to install Golang](https://golang.org/doc/install). Minimum version required is [go1.21](https://golang.org/dl/#stable)
|
||||
Useful contributions include security and dependency updates, reproducible bug fixes, tests, release automation, packaging, and documentation.
|
||||
|
||||
```sh
|
||||
go install github.com/minio/minio@latest
|
||||
```
|
||||
Issues and pull requests should include the affected version, reproduction steps, impact, expected behavior, tests, and compatibility notes. Discuss large changes in an issue first.
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
There is no CLA: contributions are accepted inbound=outbound under the project license (AGPL-3.0-or-later) and contributors keep their copyright. Every commit must be signed off (`git commit -s`) per the [Developer Certificate of Origin](https://developercertificate.org/); see [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see <https://min.io/docs/minio/linux/developers/minio-drivers.html> to view MinIO SDKs for supported languages.
|
||||
## Background
|
||||
|
||||
> NOTE: Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html) for more complete documentation.
|
||||
This project was created in response to changes in the upstream community distribution and maintenance model. The maintainer’s analysis, alternatives considered, and early maintenance record are documented below:
|
||||
|
||||
MinIO strongly recommends *against* using compiled-from-source MinIO servers for production environments.
|
||||
| Essay | Subject |
|
||||
| :-- | :-- |
|
||||
| [MinIO Is Dead](https://silo.pgsty.com/blog/post/minio-is-dead/) | Changes to the upstream project and distribution model |
|
||||
| [MinIO Is Dead, Who Takes Over?](https://silo.pgsty.com/blog/post/minio-alternative/) | Alternatives considered |
|
||||
| [MinIO Is Dead, Long Live MinIO](https://silo.pgsty.com/blog/post/minio-resurrect/) | Establishing the fork and its release pipeline |
|
||||
| [Two months into maintaining a MinIO fork](https://silo.pgsty.com/blog/post/minio-promise-kept/) | Initial security and maintenance work |
|
||||
|
||||
## Deployment Recommendations
|
||||
## License and Trademark
|
||||
|
||||
### Allow port access for Firewalls
|
||||
The server source is distributed under the [GNU Affero General Public License v3.0 or later](LICENSE). This fork derives from [`minio/minio`](https://github.com/minio/minio): [`NOTICE`](NOTICE) retains the upstream product notice, [`CREDITS`](CREDITS) records licenses and notices for included third-party components, and the Git history records downstream modifications.
|
||||
|
||||
By default MinIO uses the port 9000 to listen for incoming connections. If your platform blocks the port by default, you may need to enable access to the port.
|
||||
MinIO is a trademark of MinIO, Inc. The name is used here only to identify the upstream project and compatibility lineage. Pigsty, Silo, and `pgsty/silo` are independent community efforts and are not affiliated with, endorsed by, or sponsored by MinIO, Inc.
|
||||
|
||||
### ufw
|
||||
|
||||
For hosts with ufw enabled (Debian based distros), you can use `ufw` command to allow traffic to specific ports. Use below command to allow access to port 9000
|
||||
|
||||
```sh
|
||||
ufw allow 9000
|
||||
```
|
||||
|
||||
Below command enables all incoming traffic to ports ranging from 9000 to 9010.
|
||||
|
||||
```sh
|
||||
ufw allow 9000:9010/tcp
|
||||
```
|
||||
|
||||
### firewall-cmd
|
||||
|
||||
For hosts with firewall-cmd enabled (CentOS), you can use `firewall-cmd` command to allow traffic to specific ports. Use below commands to allow access to port 9000
|
||||
|
||||
```sh
|
||||
firewall-cmd --get-active-zones
|
||||
```
|
||||
|
||||
This command gets the active zone(s). Now, apply port rules to the relevant zones returned above. For example if the zone is `public`, use
|
||||
|
||||
```sh
|
||||
firewall-cmd --zone=public --add-port=9000/tcp --permanent
|
||||
```
|
||||
|
||||
Note that `permanent` makes sure the rules are persistent across firewall start, restart or reload. Finally reload the firewall for changes to take effect.
|
||||
|
||||
```sh
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
### iptables
|
||||
|
||||
For hosts with iptables enabled (RHEL, CentOS, etc), you can use `iptables` command to enable all traffic coming to specific ports. Use below command to allow
|
||||
access to port 9000
|
||||
|
||||
```sh
|
||||
iptables -A INPUT -p tcp --dport 9000 -j ACCEPT
|
||||
service iptables restart
|
||||
```
|
||||
|
||||
Below command enables all incoming traffic to ports ranging from 9000 to 9010.
|
||||
|
||||
```sh
|
||||
iptables -A INPUT -p tcp --dport 9000:9010 -j ACCEPT
|
||||
service iptables restart
|
||||
```
|
||||
|
||||
## Test MinIO Connectivity
|
||||
|
||||
### Test using MinIO Console
|
||||
|
||||
MinIO Server comes with an embedded web based object browser. Point your web browser to <http://127.0.0.1:9000> to ensure your server has started successfully.
|
||||
|
||||
> NOTE: MinIO runs console on random port by default, if you wish to choose a specific port use `--console-address` to pick a specific interface and port.
|
||||
|
||||
### Things to consider
|
||||
|
||||
MinIO redirects browser access requests to the configured server port (i.e. `127.0.0.1:9000`) to the configured Console port. MinIO uses the hostname or IP address specified in the request when building the redirect URL. The URL and port *must* be accessible by the client for the redirection to work.
|
||||
|
||||
For deployments behind a load balancer, proxy, or ingress rule where the MinIO host IP address or port is not public, use the `MINIO_BROWSER_REDIRECT_URL` environment variable to specify the external hostname for the redirect. The LB/Proxy must have rules for directing traffic to the Console port specifically.
|
||||
|
||||
For example, consider a MinIO deployment behind a proxy `https://minio.example.net`, `https://console.minio.example.net` with rules for forwarding traffic on port :9000 and :9001 to MinIO and the MinIO Console respectively on the internal network. Set `MINIO_BROWSER_REDIRECT_URL` to `https://console.minio.example.net` to ensure the browser receives a valid reachable URL.
|
||||
|
||||
| Dashboard | Creating a bucket |
|
||||
| ------------- | ------------- |
|
||||
|  |  |
|
||||
|
||||
## Test using MinIO Client `mc`
|
||||
|
||||
`mc` provides a modern alternative to UNIX commands like ls, cat, cp, mirror, diff etc. It supports filesystems and Amazon S3 compatible cloud storage services. Follow the MinIO Client [Quickstart Guide](https://min.io/docs/minio/linux/reference/minio-mc.html#quickstart) for further instructions.
|
||||
|
||||
## Upgrading MinIO
|
||||
|
||||
Upgrades require zero downtime in MinIO, all upgrades are non-disruptive, all transactions on MinIO are atomic. So upgrading all the servers simultaneously is the recommended way to upgrade MinIO.
|
||||
|
||||
> NOTE: requires internet access to update directly from <https://dl.min.io>, optionally you can host any mirrors at <https://my-artifactory.example.com/minio/>
|
||||
|
||||
- For deployments that installed the MinIO server binary by hand, use [`mc admin update`](https://min.io/docs/minio/linux/reference/minio-mc-admin/mc-admin-update.html)
|
||||
|
||||
```sh
|
||||
mc admin update <minio alias, e.g., myminio>
|
||||
```
|
||||
|
||||
- For deployments without external internet access (e.g. airgapped environments), download the binary from <https://dl.min.io> and replace the existing MinIO binary let's say for example `/opt/bin/minio`, apply executable permissions `chmod +x /opt/bin/minio` and proceed to perform `mc admin service restart alias/`.
|
||||
|
||||
- For installations using Systemd MinIO service, upgrade via RPM/DEB packages **parallelly** on all servers or replace the binary lets say `/opt/bin/minio` on all nodes, apply executable permissions `chmod +x /opt/bin/minio` and process to perform `mc admin service restart alias/`.
|
||||
|
||||
### Upgrade Checklist
|
||||
|
||||
- Test all upgrades in a lower environment (DEV, QA, UAT) before applying to production. Performing blind upgrades in production environments carries significant risk.
|
||||
- Read the release notes for MinIO *before* performing any upgrade, there is no forced requirement to upgrade to latest release upon every release. Some release may not be relevant to your setup, avoid upgrading production environments unnecessarily.
|
||||
- If you plan to use `mc admin update`, MinIO process must have write access to the parent directory where the binary is present on the host system.
|
||||
- `mc admin update` is not supported and should be avoided in kubernetes/container environments, please upgrade containers by upgrading relevant container images.
|
||||
- **We do not recommend upgrading one MinIO server at a time, the product is designed to support parallel upgrades please follow our recommended guidelines.**
|
||||
|
||||
## Explore Further
|
||||
|
||||
- [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html)
|
||||
- [Use `mc` with MinIO Server](https://min.io/docs/minio/linux/reference/minio-mc.html)
|
||||
- [Use `minio-go` SDK with MinIO Server](https://min.io/docs/minio/linux/developers/go/minio-go.html)
|
||||
- [The MinIO documentation website](https://min.io/docs/minio/linux/index.html)
|
||||
|
||||
## Contribute to MinIO Project
|
||||
|
||||
Please follow MinIO [Contributor's Guide](https://github.com/minio/minio/blob/master/CONTRIBUTING.md)
|
||||
|
||||
## License
|
||||
|
||||
- MinIO source is licensed under the [GNU AGPLv3](https://github.com/minio/minio/blob/master/LICENSE).
|
||||
- MinIO [documentation](https://github.com/minio/minio/tree/master/docs) is licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
|
||||
- [License Compliance](https://github.com/minio/minio/blob/master/COMPLIANCE.md)
|
||||
The portal separately publishes the project [license summary](https://silo.pgsty.com/about/license/), [documentation attribution](https://silo.pgsty.com/about/attribution/), and [trademark notice](https://silo.pgsty.com/about/trademark/).
|
||||
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
<h1 align="center">
|
||||
<a href="https://silo.pgsty.com/zh/">
|
||||
<img src=".github/silo-logo.svg" alt="Silo" width="112"><br>
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<strong>S3 兼容对象存储 —— 由 PIGSTY 维护的 MinIO 社区分支</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://silo.pgsty.com/zh/">官网</a> ·
|
||||
<a href="https://silo.pgsty.com/zh/docs/">文档</a> ·
|
||||
<a href="https://silo.pgsty.com/zh/download/">下载</a> ·
|
||||
<a href="https://silo.pgsty.com/zh/tags/silo/">版本说明</a> ·
|
||||
<a href="https://silo.pgsty.com/zh/compatibility/server/">兼容性</a> ·
|
||||
<a href="SECURITY.md">安全策略</a> ·
|
||||
<a href="README.md">English</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://silo.pgsty.com/zh/"><img alt="官网" src="https://img.shields.io/badge/%E5%AE%98%E7%BD%91-silo.pgsty.com%2Fzh-1d588c"></a>
|
||||
<a href="https://github.com/pgsty/silo/releases"><img alt="GitHub Release" src="https://img.shields.io/github/v/release/pgsty/silo?include_prereleases&label=release&logo=github"></a>
|
||||
<a href="https://hub.docker.com/r/pgsty/silo"><img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/pgsty/minio?logo=docker"></a>
|
||||
<a href="go.mod"><img alt="Go Version" src="https://img.shields.io/github/go-mod/go-version/pgsty/silo?logo=go"></a>
|
||||
<a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-AGPLv3-blue"></a>
|
||||
</p>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Silo 是由 [Pigsty](https://pigsty.cc) 独立维护、从 [`pgsty/silo`](https://github.com/pgsty/silo) 发布的开源 MinIO 社区分支。本项目与 MinIO, Inc. 不存在隶属、背书或赞助关系;文中使用 “MinIO” 仅用于说明上游项目及兼容谱系。
|
||||
|
||||
> [!NOTE]
|
||||
> 2026-08-06,本仓库由 `pgsty/minio` 更名为 `pgsty/silo`,默认分支由 `master` 更名为 `main`。如果你需要以原本 MinIO 形态维持的归档构件,它们位于归档的 [`minio`](https://github.com/pgsty/silo/tree/minio) 分支,以及截止到 [`RELEASE.2026-08-04T00-00-00Z`](https://github.com/pgsty/silo/releases/tag/RELEASE.2026-08-04T00-00-00Z) 的历次发布中;这些资产与 `docker.io/pgsty/minio` 镜像保持已发布状态且不做改动。本次更名只改变产品与交付物名称 —— `MINIO_*` 变量、`minio_*` 指标、`x-minio-*` 头、`/minio/*` 路由与磁盘格式均保持不变。
|
||||
|
||||
## 概述
|
||||
|
||||
Silo 维护一条源自开源 MinIO 服务端的下游版本线,为上游停止社区发行后仍在运行 MinIO 兼容部署的用户提供持续构建与发行产物。Pigsty 使用 Silo 提供对象存储,包括作为可选的 PostgreSQL 备份仓库。
|
||||
|
||||
本分支遵循一条原则:**改名的是产品与交付物,不是协议与你的数据。** `silo` 可执行文件、软件包、服务与容器镜像使用新名称,而 S3 API、`MINIO_*` 变量、`minio_*` 指标、`x-minio-*` 头、保留路由与磁盘格式保持不变。具体差异记录在[兼容性说明](https://silo.pgsty.com/zh/compatibility/server/)中。
|
||||
|
||||
项目统一门户为 [silo.pgsty.com](https://silo.pgsty.com/zh/),集中提供文档、下载安装、版本与安全动态及项目背景。
|
||||
|
||||
## 按需求选择入口
|
||||
|
||||
| 需求 | 权威入口 |
|
||||
| :-- | :-- |
|
||||
| 项目概览与全站导航 | [Silo 官网](https://silo.pgsty.com/zh/) |
|
||||
| 安装方式与软件下载 | [下载与安装](https://silo.pgsty.com/zh/download/) |
|
||||
| 运维、管理、开发与参考指南 | [文档](https://silo.pgsty.com/zh/docs/) |
|
||||
| 本服务端的版本说明 | [Silo 版本说明](https://silo.pgsty.com/zh/tags/silo/) |
|
||||
| 与上游 MinIO 的差异 | [Silo vs. MinIO 兼容性说明](https://silo.pgsty.com/zh/compatibility/server/) |
|
||||
| 项目动态与安全通告 | [博客](https://silo.pgsty.com/zh/blog/) · [版本发布](https://silo.pgsty.com/zh/blog/release/)与[安全通告](https://silo.pgsty.com/zh/blog/security/) |
|
||||
| 带版本的二进制、校验和与源码归档 | [GitHub Releases](https://github.com/pgsty/silo/releases) |
|
||||
| 缺陷报告与功能讨论 | [GitHub Issues](https://github.com/pgsty/silo/issues) |
|
||||
| 私密漏洞报告 | [`SECURITY.md`](SECURITY.md) 与 [`VULNERABILITY_REPORT.md`](VULNERABILITY_REPORT.md) |
|
||||
| 许可证、署名与商标信息 | [`LICENSE`](LICENSE) · [`NOTICE`](NOTICE) · [`CREDITS`](CREDITS) · 门户的[许可证](https://silo.pgsty.com/zh/about/license/)、[署名归属](https://silo.pgsty.com/zh/about/attribution/)与[商标政策](https://silo.pgsty.com/zh/about/trademark/)页面 |
|
||||
|
||||
## 相关项目
|
||||
|
||||
| 仓库 | 说明 |
|
||||
| :-- | :-- |
|
||||
| [`pgsty/silo`](https://github.com/pgsty/silo) | 本仓库 —— Silo 对象存储服务器,S3 兼容的 MinIO fork |
|
||||
| [`pgsty/mc`](https://github.com/pgsty/mc) | Silo 命令行客户端,以 `mcli` 发行、保留 `mc` 命令名 |
|
||||
| [`pgsty/silo-console`](https://github.com/pgsty/silo-console) | Web 管理控制台,内嵌于服务端构建 |
|
||||
| [`pgsty/silo-pkg`](https://github.com/pgsty/silo-pkg) | 为 Silo 系 fork 维护的共享 Go 包 |
|
||||
| [`pgsty/pigsty`](https://github.com/pgsty/pigsty) | Pigsty —— 内置 Silo 作为对象存储的 PostgreSQL 发行版 |
|
||||
|
||||
## 维护政策
|
||||
|
||||
活跃版本线的维护范围包括:
|
||||
|
||||
- 构建与依赖项维护;
|
||||
- 适用的安全修复与公告;
|
||||
- 针对可复现缺陷的范围明确的修复;
|
||||
- 带版本的二进制、软件包、校验和与多架构镜像;
|
||||
- Web Console、客户端、文档与 Pigsty 集成。
|
||||
|
||||
改动保持克制,并在可行时提供测试。所有维护均为尽力而为,不承诺固定的响应、修复或发布时间。
|
||||
|
||||
### 范围之外
|
||||
|
||||
- 独立产品路线图、新存储引擎或假设性的 S3 新特性;
|
||||
- 大规模重写或显著扩大下游差异的改动;
|
||||
- 历史版本或多条支持分支;
|
||||
- 商业支持、SLA、7×24 服务或 SUBNET 服务;
|
||||
- 部署设计、访问控制、监控、备份与恢复。
|
||||
|
||||
## 治理
|
||||
|
||||
服务端、客户端、控制台与共享包在同一套发布流程下共同维护:提交必须签署 DCO、Pull Request 经过评审、以 `RELEASE.YYYY-MM-DDTHH-MM-SSZ` 标签发布,产物附带校验和、SPDX SBOM、Sigstore 签名清单与 GitHub 构建证明。每个版本都会在门户发布[版本说明](https://silo.pgsty.com/zh/tags/silo/);安全问题按照 [`SECURITY.md`](SECURITY.md) 与仓库[安全公告](docs/security/advisories.md)处理。上游版权、许可证与第三方声明完整保留于 [`LICENSE`](LICENSE)、[`NOTICE`](NOTICE) 与 [`CREDITS`](CREDITS)。
|
||||
|
||||
## 兼容策略
|
||||
|
||||
Silo 尽量保留:
|
||||
|
||||
- `github.com/minio/minio` module path 与 `github.com/minio/*` 导入路径;
|
||||
- MinIO 兼容的 S3 API、线协议、`MINIO_*` 环境变量、指标、协议头、保留路由与存储元数据;
|
||||
- `RELEASE.YYYY-MM-DDTHH-MM-SSZ` 标签,以及容器入口对旧式 `minio …` 参数的转换。
|
||||
|
||||
Silo 自有交付面统一使用 `silo` 可执行文件、软件包、服务、Helm Chart 与 `pgsty/silo` 容器镜像;原生交付物不会安装 `minio` 二进制别名。
|
||||
|
||||
兼容性是默认约束。只要不会留下安全问题,Silo 就保留既有的协议、客户端、配置与运维行为;只有在修复重大安全问题确有必要时才会打破兼容,并在版本说明中明确受影响行为与迁移方式。每个版本仍应视为下游升级:锁定版本,阅读[版本说明](https://silo.pgsty.com/zh/tags/silo/)、[兼容性说明](https://silo.pgsty.com/zh/compatibility/server/)与[安全公告](docs/security/advisories.md),保留回滚路径,并在生产使用前完成测试。
|
||||
|
||||
## 下载与发行产物
|
||||
|
||||
请先在[下载与安装](https://silo.pgsty.com/zh/download/)页面选择合适的安装方式;GitHub Releases 仍是带版本服务端二进制、校验和与源码归档的获取位置。
|
||||
|
||||
| 产物 | 位置 |
|
||||
| :-- | :-- |
|
||||
| 源码 | [`github.com/pgsty/silo`](https://github.com/pgsty/silo) |
|
||||
| 容器镜像 | [`pgsty/silo`](https://hub.docker.com/r/pgsty/silo),支持 `linux/amd64` 与 `linux/arm64` 多架构清单 |
|
||||
| 服务端二进制与校验和 | [GitHub Releases](https://github.com/pgsty/silo/releases),覆盖 Linux、macOS、Windows 的 `amd64` 与 `arm64` |
|
||||
| Linux 软件包 | RPM、DEB、APK,并通过 [Pigsty 软件仓库](https://pigsty.cc/docs/repo/) 分发 |
|
||||
| 客户端 | [`pgsty/mc`](https://github.com/pgsty/mc),容器内以 `mcli` 提供,并保留 `mc` 兼容别名 |
|
||||
| 管理控制台 | [`pgsty/silo-console`](https://github.com/pgsty/silo-console),通过兼容导入路径 `github.com/minio/console` 嵌入服务端构建 |
|
||||
| 共享库 | [`pgsty/silo-pkg`](https://github.com/pgsty/silo-pkg) v3.11.0,通过 `replace` 指令使用,同时保留 `github.com/minio/pkg/v3` 导入路径 |
|
||||
|
||||
每个新版本都会为各平台归档和软件包发布 SPDX JSON SBOM。归档与软件包的校验和清单分别带有无长期密钥的 Sigstore 签名包;GitHub 制品证明则为全部可下载产物及多架构容器镜像记录已签名的构建来源。
|
||||
|
||||
下载归档及配套文件后,请分别验证完整性、已发布 SBOM、签名清单与构建来源:
|
||||
|
||||
```bash
|
||||
# 完整性:选择与你下载产物相符的一行。
|
||||
grep -F ' silo_<version>_linux_amd64.tar.gz' \
|
||||
silo_<version>_checksums.txt | sha256sum --check
|
||||
|
||||
# 归档 SBOM 是另一个独立校验的 Release 产物。
|
||||
grep -F ' silo_<version>_linux_amd64.tar.gz.sbom.json' \
|
||||
silo_<version>_checksums.txt | sha256sum --check
|
||||
|
||||
# 验证归档/SBOM 校验和清单的签名。
|
||||
cosign verify-blob \
|
||||
--bundle silo_<version>_checksums.txt.sigstore.json \
|
||||
--certificate-identity-regexp \
|
||||
'^https://github.com/pgsty/(minio|silo)/\.github/workflows/release\.yml@refs/(tags/RELEASE\..+|heads/(master|main))$' \
|
||||
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
||||
silo_<version>_checksums.txt
|
||||
|
||||
# 在线验证本仓库签发的构建来源。
|
||||
gh attestation verify silo_<version>_linux_amd64.tar.gz \
|
||||
--repo pgsty/silo
|
||||
```
|
||||
|
||||
软件包使用 `silo_<version>_packages_checksums.txt` 及其相邻的 Sigstore
|
||||
签名包,并使用相同的 identity 与 issuer 约束;请用 SPDX 工具检查已验证的
|
||||
SPDX JSON SBOM。按 digest 验证多架构容器清单的构建来源:
|
||||
|
||||
```bash
|
||||
gh attestation verify \
|
||||
oci://index.docker.io/pgsty/silo@sha256:<manifest-digest> \
|
||||
--repo pgsty/silo
|
||||
```
|
||||
|
||||
分架构 SBOM 证明附在 `amd64` 与 `arm64` 平台镜像的 digest 上,而非多架构
|
||||
清单上,需要分别显式验证:
|
||||
|
||||
```bash
|
||||
gh attestation verify \
|
||||
oci://index.docker.io/pgsty/silo@sha256:<platform-digest> \
|
||||
--repo pgsty/silo \
|
||||
--predicate-type https://spdx.dev/Document/v2.3
|
||||
```
|
||||
|
||||
按 digest 验证可避免信任可变镜像标签。
|
||||
|
||||
### 原生软件包迁移
|
||||
|
||||
`silo` RPM、DEB 与 APK 不针对 `minio` 声明 `Provides`、`Obsoletes`、
|
||||
`Replaces` 或包级 `Conflicts`,因此可以与已有 MinIO 软件包并存安装,
|
||||
不会被普通升级静默替换。两个 systemd unit 在运行时互斥,应由管理员显式
|
||||
切换,不能同时启动。
|
||||
|
||||
切换前请记录旧 unit 的 enabled/active 状态与 `User`/`Group`,并备份
|
||||
`/etc/default/minio`。Silo 先读取该旧配置,再读取 `/etc/default/silo`;后者
|
||||
中由管理员设置的同名变量优先。如现有数据必须继续使用原 UID/GID,请创建
|
||||
`/etc/systemd/system/silo.service.d/10-legacy-user.conf`:
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
User=<legacy-user>
|
||||
Group=<legacy-group>
|
||||
```
|
||||
|
||||
执行 `systemctl daemon-reload`,先停用并停止 `minio.service`,再启用并启动
|
||||
`silo.service`。确认健康检查、S3、Admin API、指标与日志后,才 mask 或卸载
|
||||
旧服务。软件包迁移期间不要递归修改数据属主;在回滚观察窗口内保留旧包与
|
||||
旧 unit。
|
||||
|
||||
## 快速开始
|
||||
|
||||
本地体验:
|
||||
|
||||
```bash
|
||||
mkdir -p data
|
||||
|
||||
export MINIO_ROOT_USER=minioadmin
|
||||
export MINIO_ROOT_PASSWORD=change-me-long-password
|
||||
|
||||
docker run -d --name silo \
|
||||
-p 9000:9000 \
|
||||
-p 9001:9001 \
|
||||
-e MINIO_ROOT_USER \
|
||||
-e MINIO_ROOT_PASSWORD \
|
||||
-v "$PWD/data:/data" \
|
||||
docker.io/pgsty/silo:latest server /data --console-address ":9001"
|
||||
```
|
||||
|
||||
管理控制台位于 <http://localhost:9001>,S3 API 位于 <http://localhost:9000>。
|
||||
|
||||
镜像内置兼容客户端 `mcli`:
|
||||
|
||||
```bash
|
||||
docker exec silo mcli alias set local http://127.0.0.1:9000 \
|
||||
"$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
|
||||
docker exec silo mcli mb local/demo
|
||||
docker exec silo mcli ls local
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> 生产环境应锁定版本,使用独立凭据与 TLS,配置监控,保留独立备份,并验证恢复流程。
|
||||
|
||||
从源码构建服务端:
|
||||
|
||||
```bash
|
||||
go build -o silo .
|
||||
./silo --version
|
||||
```
|
||||
|
||||
其他安装方式(包括原生软件包、二进制、Podman、Kubernetes、源码构建与 Pigsty Ansible)请前往[下载与安装](https://silo.pgsty.com/zh/download/);生产部署与管理请从 [Silo 文档](https://silo.pgsty.com/zh/docs/)开始。Pigsty 用户也可以直接使用 [Pigsty MinIO 模块](https://pigsty.cc/docs/minio/)。
|
||||
|
||||
## 安全
|
||||
|
||||
安全修复面向当前活跃开发分支,并记录在仓库[安全公告](docs/security/advisories.md)与门户[安全通告](https://silo.pgsty.com/zh/blog/security/)中。请按照 [`SECURITY.md`](SECURITY.md) 与 [`VULNERABILITY_REPORT.md`](VULNERABILITY_REPORT.md) 私密报告漏洞;同时影响上游 MinIO 的问题也应向上游报告。
|
||||
|
||||
## 贡献
|
||||
|
||||
欢迎安全与依赖项更新、可复现缺陷修复、测试、发布自动化、打包与文档改进。
|
||||
|
||||
Issue 与 Pull Request 应说明受影响版本、复现步骤、影响、预期行为、测试与兼容性说明。大型改动请先提交 Issue 讨论。
|
||||
|
||||
本项目不要求签署 CLA:贡献按项目许可证(AGPL-3.0-or-later,inbound=outbound)接收,贡献者保留自己的版权。每个提交都必须按照 [DCO](https://developercertificate.org/) 签署(`git commit -s`),详见 [`CONTRIBUTING.md`](CONTRIBUTING.md)。
|
||||
|
||||
## 背景
|
||||
|
||||
本项目源于上游社区发行与维护模式的变化。维护者对相关变化的分析、替代方案评估与早期维护记录见以下文章:
|
||||
|
||||
| 文章 | 主题 |
|
||||
| :-- | :-- |
|
||||
| [MinIO已死](https://silo.pgsty.com/zh/blog/post/minio-is-dead/) | 上游项目与发行模式的变化 |
|
||||
| [MinIO已死,谁能接盘?](https://silo.pgsty.com/zh/blog/post/minio-alternative/) | 可选替代方案评估 |
|
||||
| [MinIO 已死,MinIO 复生](https://silo.pgsty.com/zh/blog/post/minio-resurrect/) | 建立分支及其发行流水线 |
|
||||
| [续命 MinIO:承诺兑现](https://silo.pgsty.com/zh/blog/post/minio-promise-kept/) | 初期安全与维护工作 |
|
||||
|
||||
## 许可证与商标
|
||||
|
||||
服务端源码依据 [GNU Affero 通用公共许可证第 3 版或任何后续版本](LICENSE)分发。本分支衍生自 [`minio/minio`](https://github.com/minio/minio):[`NOTICE`](NOTICE) 保留上游产品声明,[`CREDITS`](CREDITS) 记录所含第三方组件的许可证与声明,Git 历史记录下游修改。
|
||||
|
||||
MinIO 是 MinIO, Inc. 的商标;本文仅为标识上游项目与兼容谱系而使用该名称。Pigsty、Silo 与 `pgsty/silo` 均为独立社区项目,与 MinIO, Inc. 不存在隶属、背书或赞助关系。
|
||||
|
||||
门户另行发布项目的[许可证摘要](https://silo.pgsty.com/zh/about/license/)、[文档署名归属](https://silo.pgsty.com/zh/about/attribution/)与[商标声明](https://silo.pgsty.com/zh/about/trademark/)。
|
||||
+16
-32
@@ -1,42 +1,26 @@
|
||||
# Security Policy
|
||||
|
||||
Silo is an independent, community-maintained object-storage server derived from
|
||||
the open-source MinIO server. Upstream MinIO security contacts do not handle
|
||||
Silo-specific fixes or release notes.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We always provide security updates for the [latest release](https://github.com/minio/minio/releases/latest).
|
||||
Whenever there is a security update you just need to upgrade to the latest version.
|
||||
Security fixes are tracked on the active development branch and summarized in
|
||||
[docs/security/advisories.md](docs/security/advisories.md). Only the current
|
||||
Silo release line is supported unless an advisory says otherwise.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
All security bugs in [minio/minio](https://github,com/minio/minio) (or other minio/* repositories)
|
||||
should be reported by email to security@min.io. Your email will be acknowledged within 48 hours,
|
||||
and you'll receive a more detailed response to your email within 72 hours indicating the next steps
|
||||
in handling your report.
|
||||
For vulnerabilities in this fork:
|
||||
|
||||
Please, provide a detailed explanation of the issue. In particular, outline the type of the security
|
||||
issue (DoS, authentication bypass, information disclose, ...) and the assumptions you're making (e.g. do
|
||||
you need access credentials for a successful exploit).
|
||||
1. Follow the fork-specific expectations in [VULNERABILITY_REPORT.md](VULNERABILITY_REPORT.md).
|
||||
2. Prefer this repository's [private GitHub security advisory](https://github.com/pgsty/silo/security/advisories/new) workflow.
|
||||
3. If private reporting is unavailable, contact the maintainers through the
|
||||
repository without publishing exploit details until a private channel is
|
||||
established.
|
||||
4. If you confirm the issue also affects upstream `minio/minio`, report it upstream separately.
|
||||
|
||||
If you have not received a reply to your email within 48 hours or you have not heard from the security team
|
||||
for the past five days please contact the security team directly:
|
||||
## Disclosure Process
|
||||
|
||||
- Primary security coordinator: aead@min.io
|
||||
- Secondary coordinator: harsha@min.io
|
||||
- If you receive no response: dev@min.io
|
||||
|
||||
### Disclosure Process
|
||||
|
||||
MinIO uses the following disclosure process:
|
||||
|
||||
1. Once the security report is received one member of the security team tries to verify and reproduce
|
||||
the issue and determines the impact it has.
|
||||
2. A member of the security team will respond and either confirm or reject the security report.
|
||||
If the report is rejected the response explains why.
|
||||
3. Code is audited to find any potential similar problems.
|
||||
4. Fixes are prepared for the latest release.
|
||||
5. On the date that the fixes are applied a security advisory will be published on <https://blog.min.io>.
|
||||
Please inform us in your report email whether MinIO should mention your contribution w.r.t. fixing
|
||||
the security issue. By default MinIO will **not** publish this information to protect your privacy.
|
||||
|
||||
This process can take some time, especially when coordination is required with maintainers of other projects.
|
||||
Every effort will be made to handle the bug in as timely a manner as possible, however it's important that we
|
||||
follow the process described above to ensure that disclosures are handled consistently.
|
||||
Fork-specific fixes and user-visible upgrade notes are published in [docs/security/advisories.md](docs/security/advisories.md). The fork-specific triage and remediation process is described in [VULNERABILITY_REPORT.md](VULNERABILITY_REPORT.md).
|
||||
|
||||
+19
-20
@@ -1,38 +1,37 @@
|
||||
# Vulnerability Management Policy
|
||||
|
||||
This document formally describes the process of addressing and managing a
|
||||
reported vulnerability that has been found in the MinIO server code base,
|
||||
any directly connected ecosystem component or a direct / indirect dependency
|
||||
of the code base.
|
||||
This document describes how the Silo maintainers investigate,
|
||||
assess, and remediate reported vulnerabilities affecting this fork, any
|
||||
directly shipped component, or a direct / indirect dependency used by this
|
||||
repository.
|
||||
|
||||
## Scope
|
||||
|
||||
The vulnerability management policy described in this document covers the
|
||||
process of investigating, assessing and resolving a vulnerability report
|
||||
opened by a MinIO employee or an external third party.
|
||||
This policy covers vulnerability reports opened by repository maintainers or
|
||||
external third parties against Silo itself, its release artifacts, or
|
||||
dependencies that materially affect this fork.
|
||||
|
||||
Therefore, it lists pre-conditions and actions that should be performed to
|
||||
resolve and fix a reported vulnerability.
|
||||
It defines the information needed for triage and the expected remediation
|
||||
workflow for supported fixes.
|
||||
|
||||
## Vulnerability Management Process
|
||||
|
||||
The vulnerability management process requires that the vulnerability report
|
||||
contains the following information:
|
||||
A useful vulnerability report should contain the following information:
|
||||
|
||||
- The project / component that contains the reported vulnerability.
|
||||
- A description of the vulnerability. In particular, the type of the
|
||||
reported vulnerability and how it might be exploited. Alternatively,
|
||||
a well-established vulnerability identifier, e.g. CVE number, can be
|
||||
used instead.
|
||||
reported vulnerability and how it might be exploited. Alternatively,
|
||||
a well-established vulnerability identifier, such as a CVE or GHSA ID, can
|
||||
be used instead.
|
||||
|
||||
Based on the description mentioned above, a MinIO engineer or security team
|
||||
member investigates:
|
||||
Based on the report, the Silo maintainers investigate:
|
||||
|
||||
- Whether the reported vulnerability exists.
|
||||
- The conditions that are required such that the vulnerability can be exploited.
|
||||
- Which releases, branches, or deployment paths are affected.
|
||||
- The steps required to fix the vulnerability.
|
||||
|
||||
In general, if the vulnerability exists in one of the MinIO code bases
|
||||
itself - not in a code dependency - then MinIO will, if possible, fix
|
||||
the vulnerability or implement reasonable countermeasures such that the
|
||||
vulnerability cannot be exploited anymore.
|
||||
If the vulnerability exists in this fork itself, the maintainers will, when
|
||||
feasible, fix the issue or implement reasonable countermeasures such that the
|
||||
vulnerability can no longer be exploited. Fork-specific upgrade notes and
|
||||
security advisories are published in `docs/security/advisories.md`.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
theme: jekyll-theme-minimal
|
||||
@@ -74,11 +74,11 @@ check_minimum_version() {
|
||||
|
||||
assert_is_supported_arch() {
|
||||
case "${ARCH}" in
|
||||
x86_64 | amd64 | aarch64 | ppc64le | arm* | s390x | loong64 | loongarch64)
|
||||
x86_64 | amd64 | aarch64 | ppc64le | arm* | s390x | loong64 | loongarch64 | riscv64)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
echo "Arch '${ARCH}' is not supported. Supported Arch: [x86_64, amd64, aarch64, ppc64le, arm*, s390x, loong64, loongarch64]"
|
||||
echo "Arch '${ARCH}' is not supported. Supported Arch: [x86_64, amd64, aarch64, ppc64le, arm*, s390x, loong64, loongarch64, riscv64]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -8,8 +8,11 @@ function _init() {
|
||||
## All binaries are static make sure to disable CGO.
|
||||
export CGO_ENABLED=0
|
||||
|
||||
## List of architectures and OS to test coss compilation.
|
||||
SUPPORTED_OSARCH="linux/ppc64le linux/mips64 linux/amd64 linux/arm64 linux/s390x darwin/arm64 darwin/amd64 freebsd/amd64 windows/amd64 linux/arm linux/386 netbsd/amd64 linux/mips openbsd/amd64"
|
||||
## Cross-compile only the OS/arch combinations we actually publish, kept in
|
||||
## sync with the goos/goarch matrix in .github/goreleaser.yml. Compile-checking
|
||||
## targets we never ship (ppc64le, s390x, mips*, riscv64, 386, arm, the BSDs)
|
||||
## spent CI minutes on unshipped code and timed the gate out on a cold cache.
|
||||
SUPPORTED_OSARCH="linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64"
|
||||
}
|
||||
|
||||
function _build() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set -x
|
||||
|
||||
export MINIO_CI_CD=1
|
||||
killall -9 minio
|
||||
killall -9 silo
|
||||
|
||||
rm -rf ${HOME}/tmp/dist
|
||||
|
||||
@@ -19,28 +19,27 @@ done
|
||||
echo $args
|
||||
|
||||
for ((i = 0; i < $((nr_servers)); i++)); do
|
||||
(minio server --address ":$((9100 + i))" $args 2>&1 >/tmp/log$i.txt) &
|
||||
(silo server --address ":$((9100 + i))" $args 2>&1 >/tmp/log$i.txt) &
|
||||
done
|
||||
|
||||
sleep 10s
|
||||
|
||||
if [ ! -f ./mc ]; then
|
||||
wget --quiet -O ./mc https://dl.minio.io/client/mc/release/linux-amd64/./mc &&
|
||||
chmod +x mc
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-mcli.sh" ./mc
|
||||
fi
|
||||
|
||||
set +e
|
||||
|
||||
export MC_HOST_minioadm=http://minioadmin:minioadmin@localhost:9100/
|
||||
./mc ready minioadm
|
||||
export MC_HOST_siloadm=http://minioadmin:minioadmin@localhost:9100/
|
||||
./mc ready siloadm
|
||||
|
||||
./mc ls minioadm/
|
||||
./mc ls siloadm/
|
||||
|
||||
./mc admin config set minioadm/ api root_access=off
|
||||
./mc admin config set siloadm/ api root_access=off
|
||||
|
||||
sleep 3s # let things settle a little
|
||||
|
||||
./mc ls minioadm/
|
||||
./mc ls siloadm/
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "listing succeeded, 'minioadmin' was not disabled"
|
||||
exit 1
|
||||
@@ -48,38 +47,38 @@ fi
|
||||
|
||||
set -e
|
||||
|
||||
killall -9 minio
|
||||
killall -9 silo
|
||||
|
||||
export MINIO_API_ROOT_ACCESS=on
|
||||
for ((i = 0; i < $((nr_servers)); i++)); do
|
||||
(minio server --address ":$((9100 + i))" $args 2>&1 >/tmp/log$i.txt) &
|
||||
(silo server --address ":$((9100 + i))" $args 2>&1 >/tmp/log$i.txt) &
|
||||
done
|
||||
|
||||
set +e
|
||||
|
||||
./mc ready minioadm/
|
||||
./mc ready siloadm/
|
||||
|
||||
./mc ls minioadm/
|
||||
./mc ls siloadm/
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "listing failed, 'minioadmin' should be enabled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
killall -9 minio
|
||||
killall -9 silo
|
||||
|
||||
rm -rf /tmp/multisitea/
|
||||
rm -rf /tmp/multisiteb/
|
||||
|
||||
echo "Setup site-replication and then disable root credentials"
|
||||
|
||||
minio server --address 127.0.0.1:9001 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
silo server --address 127.0.0.1:9001 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9002/tmp/multisitea/data/disterasure/xl{5...8}" >/tmp/sitea_1.log 2>&1 &
|
||||
minio server --address 127.0.0.1:9002 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
silo server --address 127.0.0.1:9002 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9002/tmp/multisitea/data/disterasure/xl{5...8}" >/tmp/sitea_2.log 2>&1 &
|
||||
|
||||
minio server --address 127.0.0.1:9003 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
silo server --address 127.0.0.1:9003 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9004/tmp/multisiteb/data/disterasure/xl{5...8}" >/tmp/siteb_1.log 2>&1 &
|
||||
minio server --address 127.0.0.1:9004 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
silo server --address 127.0.0.1:9004 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9004/tmp/multisiteb/data/disterasure/xl{5...8}" >/tmp/siteb_2.log 2>&1 &
|
||||
|
||||
export MC_HOST_sitea=http://minioadmin:minioadmin@127.0.0.1:9001
|
||||
@@ -96,19 +95,19 @@ export MC_HOST_siteb=http://minioadmin:minioadmin@127.0.0.1:9004
|
||||
|
||||
./mc admin user info siteb foobar
|
||||
|
||||
killall -9 minio
|
||||
killall -9 silo
|
||||
|
||||
echo "turning off root access, however site replication must continue"
|
||||
export MINIO_API_ROOT_ACCESS=off
|
||||
|
||||
minio server --address 127.0.0.1:9001 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
silo server --address 127.0.0.1:9001 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9002/tmp/multisitea/data/disterasure/xl{5...8}" >/tmp/sitea_1.log 2>&1 &
|
||||
minio server --address 127.0.0.1:9002 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
silo server --address 127.0.0.1:9002 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9002/tmp/multisitea/data/disterasure/xl{5...8}" >/tmp/sitea_2.log 2>&1 &
|
||||
|
||||
minio server --address 127.0.0.1:9003 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
silo server --address 127.0.0.1:9003 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9004/tmp/multisiteb/data/disterasure/xl{5...8}" >/tmp/siteb_1.log 2>&1 &
|
||||
minio server --address 127.0.0.1:9004 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
silo server --address 127.0.0.1:9004 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9004/tmp/multisiteb/data/disterasure/xl{5...8}" >/tmp/siteb_2.log 2>&1 &
|
||||
|
||||
export MC_HOST_sitea=http://foobar:foo12345@127.0.0.1:9001
|
||||
|
||||
Executable
+125
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Regenerates CREDITS from the license text of every Go module linked into the
|
||||
# silo binary. The module set is what `go list -deps` reports for the main
|
||||
# package, so test-only and tool dependencies stay out: CREDITS documents what
|
||||
# a shipped binary actually contains. check-gen runs this and fails on a diff,
|
||||
# which keeps CREDITS from drifting when go.mod changes.
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_dir="$(cd "${script_dir}/.." && pwd)"
|
||||
cd "${repo_dir}"
|
||||
|
||||
out_file="${1:-${repo_dir}/CREDITS}"
|
||||
tmp_file="${out_file}.tmp"
|
||||
trap 'rm -f "${tmp_file}"' EXIT
|
||||
|
||||
rule_dash='----------------------------------------------------------------'
|
||||
rule_equal='================================================================'
|
||||
|
||||
# These modules repackage Go standard library code and publish no license
|
||||
# file; their source files carry the Go Authors' BSD-style header pointing at
|
||||
# the Go project license, so that text is reproduced for them.
|
||||
stdlib_derived='github.com/minio/colorjson github.com/minio/csvparser github.com/minio/filepath'
|
||||
|
||||
is_stdlib_derived() {
|
||||
case " ${stdlib_derived} " in
|
||||
*" $1 "*) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Command substitution strips trailing newlines; printf adds exactly one back,
|
||||
# so every entry ends the same way regardless of how the license file ends.
|
||||
emit_text() {
|
||||
printf '%s\n' "$(cat "$1")"
|
||||
}
|
||||
|
||||
# The module cache must hold every dependency before .Dir can resolve.
|
||||
go mod download
|
||||
|
||||
# The Go project license text is taken from the pinned golang.org/x/sys module
|
||||
# rather than GOROOT: Homebrew's Go does not ship GOROOT/LICENSE, and the
|
||||
# module copy is version-locked by go.mod, so the output cannot vary with the
|
||||
# machine's toolchain packaging.
|
||||
go_license="$(go list -m -f '{{.Dir}}' golang.org/x/sys)/LICENSE"
|
||||
if [ ! -f "${go_license}" ]; then
|
||||
echo "Missing Go license text: ${go_license}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
{
|
||||
printf '%s\n' \
|
||||
'Silo bundles third-party software under the licenses reproduced below.' \
|
||||
'Generated by buildscripts/gen-credits.sh (make credits) from the Go' \
|
||||
'modules linked into the silo binary. Do not edit by hand.' \
|
||||
'' \
|
||||
"${rule_equal}" \
|
||||
''
|
||||
printf '%s\n%s\n%s\n' 'Go (the standard library)' 'https://golang.org/' "${rule_dash}"
|
||||
emit_text "${go_license}"
|
||||
printf '\n%s\n\n' "${rule_equal}"
|
||||
|
||||
# The dependency closure is GOOS/GOARCH-specific: platform-only modules
|
||||
# (darwin's go-m1cpu, windows' wmi, ...) enter and leave it with the host.
|
||||
# Pin the primary shipped target and the release build tags so regenerating
|
||||
# CREDITS produces identical output on every machine, including CI.
|
||||
GOOS=linux GOARCH=amd64 go list -deps -tags kqueue \
|
||||
-f '{{if and (not .Standard) .Module}}{{.Module.Path}}{{end}}' . \
|
||||
| LC_ALL=C sort -u \
|
||||
| grep -vx 'github.com/minio/minio' \
|
||||
| xargs go list -m -f '{{.Path}}|{{with .Replace}}{{.Path}}{{end}}|{{.Dir}}' \
|
||||
| while IFS='|' read -r path replacement dir; do
|
||||
if [ -z "${dir}" ] || [ ! -d "${dir}" ]; then
|
||||
echo "Module cache directory missing for ${path}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
name="${path}"
|
||||
url="https://${path}"
|
||||
# A same-path replace only pins a version; the annotation is for
|
||||
# dependencies actually served from a different repository.
|
||||
if [ -n "${replacement}" ] && [ "${replacement}" != "${path}" ]; then
|
||||
name="${path} (replaced by ${replacement})"
|
||||
url="https://${replacement}"
|
||||
fi
|
||||
|
||||
printf '%s\n%s\n%s\n' "${name}" "${url}" "${rule_dash}"
|
||||
|
||||
if is_stdlib_derived "${path}"; then
|
||||
printf '%s\n%s\n\n' \
|
||||
'This module repackages Go standard library code and publishes no' \
|
||||
'license file; the Go project license below applies per its file headers.'
|
||||
emit_text "${go_license}"
|
||||
else
|
||||
license_file=''
|
||||
for candidate in LICENSE LICENSE.txt LICENSE.md COPYING COPYING.txt LICENCE UNLICENSE; do
|
||||
if [ -f "${dir}/${candidate}" ]; then
|
||||
license_file="${dir}/${candidate}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "${license_file}" ]; then
|
||||
echo "No license file found for ${path} in ${dir}" >&2
|
||||
exit 1
|
||||
fi
|
||||
emit_text "${license_file}"
|
||||
fi
|
||||
|
||||
# Apache License 2.0 section 4(d) requires redistributing the NOTICE
|
||||
# file contents alongside the licensed work.
|
||||
for notice in NOTICE NOTICE.txt; do
|
||||
if [ -f "${dir}/${notice}" ]; then
|
||||
printf '\n%s\n\n' 'Bundled NOTICE file:'
|
||||
emit_text "${dir}/${notice}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
printf '\n%s\n\n' "${rule_equal}"
|
||||
done
|
||||
} > "${tmp_file}"
|
||||
|
||||
mv "${tmp_file}" "${out_file}"
|
||||
@@ -38,8 +38,12 @@ func genLDFlags(version string) string {
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.ReleaseTag=" + releaseTag
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.CommitID=" + commitID()
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.ShortCommitID=" + commitID()[:12]
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.GOPATH=" + os.Getenv("GOPATH")
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.GOROOT=" + os.Getenv("GOROOT")
|
||||
// GOPATH/GOROOT are deliberately not stamped in. They only seed the logger's
|
||||
// source-path trim list, which -trimpath already makes moot (paths are
|
||||
// relative in the binary, so there is no build-machine prefix left to trim),
|
||||
// and stamping them baked the builder's absolute paths into the released
|
||||
// binary - defeating -trimpath and reproducible builds. cmd.GOPATH/GOROOT
|
||||
// keep their empty defaults, exactly as a plain `go build` leaves them.
|
||||
return ldflagsStr
|
||||
}
|
||||
|
||||
|
||||
@@ -5,45 +5,34 @@ set -o pipefail
|
||||
set -x
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
SILO_CONFIG_DIR="$WORK_DIR/.silo"
|
||||
SILO=("$PWD/silo" --config-dir "$SILO_CONFIG_DIR" server)
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
if [ ! -x "$PWD/silo" ]; then
|
||||
echo "Silo executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
if [ ! -x "$PWD/silo" ]; then
|
||||
echo "Silo executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function start_minio_4drive() {
|
||||
function start_silo_4drive() {
|
||||
start_port=$1
|
||||
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
export MC_HOST_minio="http://minio:minio123@127.0.0.1:${start_port}/"
|
||||
export MINIO_ROOT_USER=silo
|
||||
export MINIO_ROOT_PASSWORD=silo123
|
||||
export MC_HOST_silo="http://silo:silo123@127.0.0.1:${start_port}/"
|
||||
unset MINIO_KMS_AUTO_ENCRYPTION # do not auto-encrypt objects
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
mkdir ${WORK_DIR}
|
||||
C_PWD=${PWD}
|
||||
if [ ! -x "$PWD/mc" ]; then
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "$C_PWD/mc")
|
||||
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-mcli.sh" "$PWD/mc"
|
||||
fi
|
||||
|
||||
"${MINIO[@]}" --address ":$start_port" "${WORK_DIR}/disk{1...4}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
"${SILO[@]}" --address ":$start_port" "${WORK_DIR}/disk{1...4}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
pid=$!
|
||||
disown $pid
|
||||
sleep 5
|
||||
@@ -56,30 +45,30 @@ function start_minio_4drive() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"${PWD}/mc" mb --with-versioning minio/bucket
|
||||
"${PWD}/mc" mb --with-versioning silo/bucket
|
||||
|
||||
for i in $(seq 1 4); do
|
||||
"${PWD}/mc" cp /etc/hosts minio/bucket/testobj
|
||||
"${PWD}/mc" cp /etc/hosts silo/bucket/testobj
|
||||
|
||||
sudo chown -R root. "${WORK_DIR}/disk${i}"
|
||||
|
||||
"${PWD}/mc" cp /etc/hosts minio/bucket/testobj
|
||||
"${PWD}/mc" cp /etc/hosts silo/bucket/testobj
|
||||
|
||||
sudo chown -R ${USER}. "${WORK_DIR}/disk${i}"
|
||||
done
|
||||
|
||||
for vid in $("${PWD}/mc" ls --json --versions minio/bucket/testobj | jq -r .versionId); do
|
||||
"${PWD}/mc" cat --vid "${vid}" minio/bucket/testobj | md5sum
|
||||
for vid in $("${PWD}/mc" ls --json --versions silo/bucket/testobj | jq -r .versionId); do
|
||||
"${PWD}/mc" cat --vid "${vid}" silo/bucket/testobj | md5sum
|
||||
done
|
||||
|
||||
pkill minio
|
||||
pkill silo
|
||||
sleep 3
|
||||
}
|
||||
|
||||
function main() {
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
|
||||
start_minio_4drive ${start_port}
|
||||
start_silo_4drive ${start_port}
|
||||
}
|
||||
|
||||
function purge() {
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
// Copyright 2026 PGSTY contributors.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// helm-migration-guard compares a rendered legacy MinIO chart with the Silo
|
||||
// upgrade candidate. Product labels, images, and commands may change; resource
|
||||
// identity, selectors, PVCs, storage mounts, ports, secrets, and service-account
|
||||
// references must not.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
type resource struct {
|
||||
key string
|
||||
doc map[string]any
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 3 {
|
||||
fatal(errors.New("usage: helm-migration-guard OLD_RENDER NEW_RENDER"))
|
||||
}
|
||||
oldResources, err := readResources(os.Args[1])
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
newResources, err := readResources(os.Args[2])
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err := compare(oldResources, newResources); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
fmt.Printf("Silo Helm migration identity is stable across %d rendered resources\n", len(oldResources))
|
||||
}
|
||||
|
||||
func readResources(path string) (map[string]resource, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open %s: %w", path, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
resources := make(map[string]resource)
|
||||
decoder := yaml.NewDecoder(file)
|
||||
for document := 1; ; document++ {
|
||||
var doc map[string]any
|
||||
err = decoder.Decode(&doc)
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode %s document %d: %w", path, document, err)
|
||||
}
|
||||
if len(doc) == 0 || text(doc["kind"]) == "" {
|
||||
continue
|
||||
}
|
||||
metadata := object(doc["metadata"])
|
||||
key := strings.Join([]string{text(doc["kind"]), text(metadata["namespace"]), text(metadata["name"])}, "/")
|
||||
if _, exists := resources[key]; exists {
|
||||
return nil, fmt.Errorf("%s contains duplicate resource %s", path, key)
|
||||
}
|
||||
resources[key] = resource{key: key, doc: doc}
|
||||
}
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
func compare(oldResources, newResources map[string]resource) error {
|
||||
for key := range oldResources {
|
||||
if _, ok := newResources[key]; !ok {
|
||||
return fmt.Errorf("legacy resource would be removed or renamed: %s", key)
|
||||
}
|
||||
}
|
||||
for key := range newResources {
|
||||
if _, ok := oldResources[key]; !ok {
|
||||
return fmt.Errorf("upgrade candidate unexpectedly adds a resource: %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(oldResources))
|
||||
for key := range oldResources {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
oldDoc := oldResources[key].doc
|
||||
newDoc := newResources[key].doc
|
||||
kind := text(oldDoc["kind"])
|
||||
switch kind {
|
||||
case "Service":
|
||||
if err := same(key, "Service selector", at(oldDoc, "spec", "selector"), at(newDoc, "spec", "selector")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := same(key, "Service ports", at(oldDoc, "spec", "ports"), at(newDoc, "spec", "ports")); err != nil {
|
||||
return err
|
||||
}
|
||||
case "Deployment", "StatefulSet":
|
||||
if err := same(key, "workload selector", at(oldDoc, "spec", "selector"), at(newDoc, "spec", "selector")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if kind == "StatefulSet" {
|
||||
if err := same(key, "StatefulSet serviceName", at(oldDoc, "spec", "serviceName"), at(newDoc, "spec", "serviceName")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := same(key, "volume claim templates", claimTemplates(oldDoc), claimTemplates(newDoc)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if kind == "PersistentVolumeClaim" {
|
||||
if err := same(key, "PVC specification", at(oldDoc, "spec"), at(newDoc, "spec")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if kind == "Secret" {
|
||||
if err := same(key, "Secret keys", secretKeys(oldDoc), secretKeys(newDoc)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if kind == "Deployment" || kind == "StatefulSet" || kind == "Job" {
|
||||
if err := comparePod(key, kind, oldDoc, newDoc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func comparePod(key, kind string, oldDoc, newDoc map[string]any) error {
|
||||
oldPod := object(at(oldDoc, "spec", "template", "spec"))
|
||||
newPod := object(at(newDoc, "spec", "template", "spec"))
|
||||
if err := same(key, "service account", oldPod["serviceAccountName"], newPod["serviceAccountName"]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := same(key, "referenced volume sources", volumeSources(oldPod), volumeSources(newPod)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldContainers := containers(oldPod)
|
||||
newContainers := containers(newPod)
|
||||
if err := same(key, "container identities", sortedKeys(oldContainers), sortedKeys(newContainers)); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, name := range sortedKeys(oldContainers) {
|
||||
oldContainer := oldContainers[name]
|
||||
newContainer := newContainers[name]
|
||||
if err := same(key, name+" ports", oldContainer["ports"], newContainer["ports"]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := same(key, name+" environment", oldContainer["env"], newContainer["env"]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := same(key, name+" envFrom", oldContainer["envFrom"], newContainer["envFrom"]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := same(key, name+" storage mounts", normalizedMounts(oldContainer, oldPod), normalizedMounts(newContainer, newPod)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
image := text(newContainer["image"])
|
||||
if strings.HasPrefix(image, "pgsty/minio:") || strings.HasPrefix(image, "docker.io/pgsty/minio:") {
|
||||
return fmt.Errorf("%s container %s still uses frozen image %s", key, name, image)
|
||||
}
|
||||
command := commandText(newContainer)
|
||||
if strings.Contains(command, "/usr/bin/minio") {
|
||||
return fmt.Errorf("%s container %s still invokes /usr/bin/minio", key, name)
|
||||
}
|
||||
if (kind == "Deployment" || kind == "StatefulSet") && strings.Contains(image, "pgsty/silo:") {
|
||||
if !strings.Contains(command, "silo") || !strings.Contains(command, "server") {
|
||||
return fmt.Errorf("%s container %s does not invoke the Silo server: %q", key, name, command)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func claimTemplates(doc map[string]any) []string {
|
||||
var result []string
|
||||
for _, raw := range list(at(doc, "spec", "volumeClaimTemplates")) {
|
||||
claim := object(raw)
|
||||
metadata := object(claim["metadata"])
|
||||
result = append(result, text(metadata["name"])+"="+canonical(claim["spec"]))
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func secretKeys(doc map[string]any) []string {
|
||||
var result []string
|
||||
for _, section := range []string{"data", "stringData"} {
|
||||
for key := range object(doc[section]) {
|
||||
result = append(result, section+":"+key)
|
||||
}
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func volumeSources(pod map[string]any) []string {
|
||||
var result []string
|
||||
for _, raw := range list(pod["volumes"]) {
|
||||
volume := cloneObject(object(raw))
|
||||
delete(volume, "name")
|
||||
result = append(result, canonical(volume))
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func volumeSourceByName(pod map[string]any) map[string]string {
|
||||
result := make(map[string]string)
|
||||
for _, raw := range list(pod["volumes"]) {
|
||||
volume := cloneObject(object(raw))
|
||||
name := text(volume["name"])
|
||||
delete(volume, "name")
|
||||
result[name] = canonical(volume)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func normalizedMounts(container, pod map[string]any) []string {
|
||||
sources := volumeSourceByName(pod)
|
||||
var result []string
|
||||
for _, raw := range list(container["volumeMounts"]) {
|
||||
mount := cloneObject(object(raw))
|
||||
name := text(mount["name"])
|
||||
delete(mount, "name")
|
||||
mount["source"] = sources[name]
|
||||
result = append(result, canonical(mount))
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func containers(pod map[string]any) map[string]map[string]any {
|
||||
result := make(map[string]map[string]any)
|
||||
for _, section := range []string{"initContainers", "containers"} {
|
||||
for _, raw := range list(pod[section]) {
|
||||
container := object(raw)
|
||||
result[section+":"+text(container["name"])] = container
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func commandText(container map[string]any) string {
|
||||
var parts []string
|
||||
for _, field := range []string{"command", "args"} {
|
||||
for _, value := range list(container[field]) {
|
||||
parts = append(parts, text(value))
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
func at(root map[string]any, path ...string) any {
|
||||
var current any = root
|
||||
for _, part := range path {
|
||||
current = object(current)[part]
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
func object(value any) map[string]any {
|
||||
if value == nil {
|
||||
return map[string]any{}
|
||||
}
|
||||
result, _ := value.(map[string]any)
|
||||
return result
|
||||
}
|
||||
|
||||
func cloneObject(value map[string]any) map[string]any {
|
||||
result := make(map[string]any, len(value))
|
||||
for key, item := range value {
|
||||
result[key] = item
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func list(value any) []any {
|
||||
result, _ := value.([]any)
|
||||
return result
|
||||
}
|
||||
|
||||
func text(value any) string {
|
||||
result, _ := value.(string)
|
||||
return result
|
||||
}
|
||||
|
||||
func sortedKeys[T any](values map[string]T) []string {
|
||||
result := make([]string, 0, len(values))
|
||||
for key := range values {
|
||||
result = append(result, key)
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func same(resourceKey, field string, oldValue, newValue any) error {
|
||||
oldCanonical := canonical(oldValue)
|
||||
newCanonical := canonical(newValue)
|
||||
if oldCanonical != newCanonical {
|
||||
return fmt.Errorf("%s changes %s\nold: %s\nnew: %s", resourceKey, field, oldCanonical, newCanonical)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func canonical(value any) string {
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<unmarshalable %T: %v>", value, err)
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func fatal(err error) {
|
||||
fmt.Fprintf(os.Stderr, "Silo Helm migration check failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "usage: $0 TARGET" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
target=$1
|
||||
target_dir=$(dirname "${target}")
|
||||
if [ ! -d "${target_dir}" ]; then
|
||||
echo "target directory does not exist: ${target_dir}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sha256_file() {
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum "$1" | awk '{print $1}'
|
||||
else
|
||||
shasum -a 256 "$1" | awk '{print $1}'
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -n "${MCLI_BIN:-}" ]; then
|
||||
if [ ! -f "${MCLI_BIN}" ]; then
|
||||
echo "MCLI_BIN is not a regular file: ${MCLI_BIN}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! printf '%s\n' "${MCLI_SHA256:-}" | grep -Eq '^[0-9a-fA-F]{64}$'; then
|
||||
echo "MCLI_SHA256 must contain the expected SHA-256 for MCLI_BIN" >&2
|
||||
exit 1
|
||||
fi
|
||||
actual=$(sha256_file "${MCLI_BIN}")
|
||||
if [ "${actual}" != "${MCLI_SHA256,,}" ]; then
|
||||
echo "MCLI_BIN checksum mismatch: expected ${MCLI_SHA256,,}, got ${actual}" >&2
|
||||
exit 1
|
||||
fi
|
||||
install -m 0755 "${MCLI_BIN}" "${target}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
release=${MCLI_RELEASE:-RELEASE.2026-08-06T00-00-00Z}
|
||||
version_hyphen=${release#RELEASE.}
|
||||
package_version=$(printf '%s\n' "${version_hyphen}" | sed -E 's/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2})-([0-9]{2})-([0-9]{2})Z$/\1\2\3\4\5\6.0.0/')
|
||||
if [ "${package_version}" = "${version_hyphen}" ]; then
|
||||
echo "invalid MCLI_RELEASE: ${release}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case $(uname -s) in
|
||||
Linux) os=linux ;;
|
||||
Darwin) os=darwin ;;
|
||||
*) echo "unsupported mcli host OS: $(uname -s)" >&2; exit 1 ;;
|
||||
esac
|
||||
case $(uname -m) in
|
||||
x86_64 | amd64) arch=amd64 ;;
|
||||
aarch64 | arm64) arch=arm64 ;;
|
||||
*) echo "unsupported mcli host architecture: $(uname -m)" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
archive="mcli_${package_version}_${os}_${arch}.tar.gz"
|
||||
checksums="mcli_${package_version}_checksums.txt"
|
||||
base_url="https://github.com/pgsty/mc/releases/download/${release}"
|
||||
tmp_dir=$(mktemp -d "${TMPDIR:-/tmp}/silo-mcli.XXXXXX")
|
||||
trap 'rm -rf "${tmp_dir}"' EXIT
|
||||
|
||||
curl --fail --location --retry 3 --silent --show-error \
|
||||
"${base_url}/${checksums}" --output "${tmp_dir}/${checksums}"
|
||||
curl --fail --location --retry 3 --silent --show-error \
|
||||
"${base_url}/${archive}" --output "${tmp_dir}/${archive}"
|
||||
|
||||
expected=$(awk -v asset="${archive}" '
|
||||
{
|
||||
name=$2
|
||||
sub(/^\*/, "", name)
|
||||
if (name == asset && $1 ~ /^[0-9a-fA-F]{64}$/) print tolower($1)
|
||||
}
|
||||
' "${tmp_dir}/${checksums}")
|
||||
if ! printf '%s\n' "${expected}" | grep -Eq '^[0-9a-f]{64}$'; then
|
||||
echo "checksum manifest does not contain exactly one valid entry for ${archive}" >&2
|
||||
exit 1
|
||||
fi
|
||||
actual=$(sha256_file "${tmp_dir}/${archive}")
|
||||
if [ "${actual}" != "${expected}" ]; then
|
||||
echo "downloaded ${archive} checksum mismatch" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -xzf "${tmp_dir}/${archive}" -C "${tmp_dir}" mcli
|
||||
install -m 0755 "${tmp_dir}/mcli" "${target}"
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 3 ]; then
|
||||
echo "usage: $0 SOURCE SHA256 TARGET" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
source_ref=$1
|
||||
expected=${2,,}
|
||||
target=$3
|
||||
if ! printf '%s\n' "${expected}" | grep -Eq '^[0-9a-f]{64}$'; then
|
||||
echo "expected checksum must be a lowercase SHA-256 digest" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -d "$(dirname "${target}")" ]; then
|
||||
echo "target directory does not exist: $(dirname "${target}")" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tmp_file=$(mktemp "${TMPDIR:-/tmp}/silo-fixture.XXXXXX")
|
||||
trap 'rm -f "${tmp_file}"' EXIT
|
||||
case ${source_ref} in
|
||||
https://*)
|
||||
curl --fail --location --retry 3 --silent --show-error \
|
||||
"${source_ref}" --output "${tmp_file}"
|
||||
;;
|
||||
*)
|
||||
if [ ! -f "${source_ref}" ]; then
|
||||
echo "fixture is not a regular file: ${source_ref}" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp "${source_ref}" "${tmp_file}"
|
||||
;;
|
||||
esac
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
actual=$(sha256sum "${tmp_file}" | awk '{print $1}')
|
||||
else
|
||||
actual=$(shasum -a 256 "${tmp_file}" | awk '{print $1}')
|
||||
fi
|
||||
if [ "${actual}" != "${expected}" ]; then
|
||||
echo "fixture checksum mismatch: expected ${expected}, got ${actual}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
install -m 0755 "${tmp_file}" "${target}"
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is used to test the migration of IAM content from old minio
|
||||
# instance to new minio instance.
|
||||
# This script tests IAM migration from an old MinIO compatibility fixture into
|
||||
# the current Silo server.
|
||||
#
|
||||
# To run it locally, start the LDAP server in github.com/minio/minio-iam-testing
|
||||
# repo (e.g. make podman-run), and then run this script.
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
OLD_VERSION=RELEASE.2024-03-26T22-10-45Z
|
||||
OLD_BINARY_LINK=https://dl.min.io/server/minio/release/linux-amd64/archive/minio.${OLD_VERSION}
|
||||
OLD_BINARY_SHA256=2050199d89e3057571620a1d453118fed5bd2de9d4f3b266b11365fdf984d676
|
||||
|
||||
__init__() {
|
||||
if which curl &>/dev/null; then
|
||||
@@ -27,17 +28,16 @@ __init__() {
|
||||
export GOPATH=/tmp/gopath
|
||||
export PATH="${PATH}":"${GOPATH}"/bin
|
||||
|
||||
if which mc &>/dev/null; then
|
||||
echo "mc is already installed"
|
||||
else
|
||||
echo "Installing mc:"
|
||||
go install github.com/minio/mc@latest
|
||||
if [ ! -x "${GOPATH}/bin/mc" ]; then
|
||||
echo "Installing verified compatible client fixture"
|
||||
mkdir -p "${GOPATH}/bin"
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-mcli.sh" "${GOPATH}/bin/mc"
|
||||
fi
|
||||
|
||||
if [ ! -x ./minio.${OLD_VERSION} ]; then
|
||||
echo "Downloading minio.${OLD_VERSION} binary"
|
||||
curl -o minio.${OLD_VERSION} ${OLD_BINARY_LINK}
|
||||
chmod +x minio.${OLD_VERSION}
|
||||
echo "Installing verified upstream compatibility fixture minio.${OLD_VERSION}"
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-verified-fixture.sh" \
|
||||
"${OLD_BINARY_LINK}" "${OLD_BINARY_SHA256}" "minio.${OLD_VERSION}"
|
||||
fi
|
||||
|
||||
if [ -z "$_MINIO_LDAP_TEST_SERVER" ]; then
|
||||
@@ -49,7 +49,7 @@ __init__() {
|
||||
}
|
||||
|
||||
create_iam_content_in_old_minio() {
|
||||
echo "Creating IAM content in old minio instance."
|
||||
echo "Creating IAM content in the old MinIO compatibility fixture."
|
||||
|
||||
MINIO_CI_CD=1 ./minio.${OLD_VERSION} server /tmp/data/{1...4} &
|
||||
sleep 5
|
||||
@@ -80,9 +80,9 @@ create_iam_content_in_old_minio() {
|
||||
}
|
||||
|
||||
import_iam_content_in_new_minio() {
|
||||
echo "Importing IAM content in new minio instance."
|
||||
# Assume current minio binary exists.
|
||||
MINIO_CI_CD=1 ./minio server /tmp/data/{1...4} &
|
||||
echo "Importing IAM content into the current Silo instance."
|
||||
# Assume the current Silo binary exists.
|
||||
MINIO_CI_CD=1 ./silo server /tmp/data/{1...4} &
|
||||
sleep 5
|
||||
|
||||
set -x
|
||||
|
||||
+113
-100
@@ -1,111 +1,124 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
trap 'cleanup $LINENO' ERR
|
||||
set -euo pipefail
|
||||
|
||||
# Exercise both directions of the on-disk compatibility contract using an
|
||||
# immutable pre-rebrand image and a container built from the current checkout.
|
||||
# Every Docker resource is uniquely named and removed explicitly; this test
|
||||
# never prunes unrelated images, containers, networks, or volumes.
|
||||
|
||||
repo_dir="$(git rev-parse --show-toplevel)"
|
||||
old_image="${OLD_IMAGE:-docker.io/pgsty/minio@sha256:b6bfe7239bfc83fb90d31612d9704d86039dd714f7904b3f1ad68f211e602372}"
|
||||
new_image="${NEW_IMAGE:-silo-upgrade-test:dev}"
|
||||
suffix="$(date +%s)-$$"
|
||||
network="silo-upgrade-net-${suffix}"
|
||||
volume="silo-upgrade-data-${suffix}"
|
||||
old_container="silo-upgrade-old-${suffix}"
|
||||
new_container="silo-upgrade-new-${suffix}"
|
||||
rollback_container="silo-upgrade-rollback-${suffix}"
|
||||
root_user=silo-upgrade-admin
|
||||
root_password=silo-upgrade-secret-123
|
||||
|
||||
# shellcheck disable=SC2120
|
||||
cleanup() {
|
||||
MINIO_VERSION=dev /tmp/gopath/bin/docker-compose \
|
||||
-f "buildscripts/upgrade-tests/compose.yml" \
|
||||
down || true
|
||||
|
||||
MINIO_VERSION=dev /tmp/gopath/bin/docker-compose \
|
||||
-f "buildscripts/upgrade-tests/compose.yml" \
|
||||
rm || true
|
||||
|
||||
for volume in $(docker volume ls -q | grep upgrade); do
|
||||
docker volume rm ${volume} || true
|
||||
done
|
||||
|
||||
docker volume prune -f
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -q -f dangling=true) || true
|
||||
}
|
||||
|
||||
verify_checksum_after_heal() {
|
||||
local sum1
|
||||
sum1=$(curl -s "$2" | sha256sum)
|
||||
mc admin heal --json -r "$1" >/dev/null # test after healing
|
||||
local sum1_heal
|
||||
sum1_heal=$(curl -s "$2" | sha256sum)
|
||||
|
||||
if [ "${sum1_heal}" != "${sum1}" ]; then
|
||||
echo "mismatch expected ${sum1_heal}, got ${sum1}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
verify_checksum_mc() {
|
||||
local expected
|
||||
expected=$(mc cat "$1" | sha256sum)
|
||||
local got
|
||||
got=$(mc cat "$2" | sha256sum)
|
||||
|
||||
if [ "${expected}" != "${got}" ]; then
|
||||
echo "mismatch - expected ${expected}, got ${got}"
|
||||
exit 1
|
||||
fi
|
||||
echo "matches - ${expected}, got ${got}"
|
||||
}
|
||||
|
||||
add_alias() {
|
||||
for i in $(seq 1 4); do
|
||||
echo "... attempting to add alias $i"
|
||||
until (mc alias set minio http://127.0.0.1:9000 minioadmin minioadmin); do
|
||||
echo "...waiting... for 5secs" && sleep 5
|
||||
status=$?
|
||||
trap - EXIT
|
||||
if [ "${status}" -ne 0 ]; then
|
||||
for name in "${old_container}" "${new_container}" "${rollback_container}"; do
|
||||
docker logs "${name}" 2>/dev/null | tail -n 80 >&2 || true
|
||||
done
|
||||
fi
|
||||
if [ "${KEEP_UPGRADE_TEST_RESOURCES:-0}" = 1 ]; then
|
||||
printf 'Retained Docker resources for inspection: %s %s\n' "${network}" "${volume}" >&2
|
||||
exit "${status}"
|
||||
fi
|
||||
docker rm -f "${old_container}" "${new_container}" "${rollback_container}" >/dev/null 2>&1 || true
|
||||
docker network rm "${network}" >/dev/null 2>&1 || true
|
||||
docker volume rm "${volume}" >/dev/null 2>&1 || true
|
||||
exit "${status}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
trap 'exit 130' INT TERM
|
||||
|
||||
wait_ready() {
|
||||
name="$1"
|
||||
ready=""
|
||||
for _ in $(seq 1 90); do
|
||||
if docker logs "${name}" 2>&1 | grep -q 'API:'; then
|
||||
ready=1
|
||||
break
|
||||
fi
|
||||
if [ "$(docker inspect -f '{{.State.Running}}' "${name}")" != true ]; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Sleeping for nginx"
|
||||
sleep 20
|
||||
if [ -z "${ready}" ]; then
|
||||
echo "Server did not become ready: ${name}" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
__init__() {
|
||||
sudo apt install curl -y
|
||||
export GOPATH=/tmp/gopath
|
||||
export PATH=${PATH}:${GOPATH}/bin
|
||||
|
||||
go install github.com/minio/mc@latest
|
||||
|
||||
## this is needed because github actions don't have
|
||||
## docker-compose on all runners
|
||||
go install github.com/docker/compose/v2/cmd@latest
|
||||
mv -v /tmp/gopath/bin/cmd /tmp/gopath/bin/docker-compose
|
||||
|
||||
cleanup
|
||||
|
||||
TAG=minio/minio:dev make docker
|
||||
|
||||
MINIO_VERSION=RELEASE.2019-12-19T22-52-26Z docker-compose \
|
||||
-f "buildscripts/upgrade-tests/compose.yml" \
|
||||
up -d --build
|
||||
|
||||
add_alias
|
||||
|
||||
mc mb minio/minio-test/
|
||||
mc cp ./minio minio/minio-test/to-read/
|
||||
mc cp /etc/hosts minio/minio-test/to-read/hosts
|
||||
mc anonymous set download minio/minio-test
|
||||
|
||||
verify_checksum_mc ./minio minio/minio-test/to-read/minio
|
||||
|
||||
curl -s http://127.0.0.1:9000/minio-test/to-read/hosts | sha256sum
|
||||
|
||||
MINIO_VERSION=dev /tmp/gopath/bin/docker-compose -f "buildscripts/upgrade-tests/compose.yml" stop
|
||||
start_server() {
|
||||
name="$1"
|
||||
image="$2"
|
||||
shift 2
|
||||
docker run -d --name "${name}" --network "${network}" \
|
||||
--mount "source=${volume},target=/data" \
|
||||
-e MINIO_CI_CD=1 -e MINIO_ROOT_USER="${root_user}" -e MINIO_ROOT_PASSWORD="${root_password}" \
|
||||
"${image}" "$@" >/dev/null
|
||||
wait_ready "${name}"
|
||||
docker exec "${name}" mcli alias set local http://127.0.0.1:9000 "${root_user}" "${root_password}" >/dev/null
|
||||
}
|
||||
|
||||
main() {
|
||||
MINIO_VERSION=dev /tmp/gopath/bin/docker-compose -f "buildscripts/upgrade-tests/compose.yml" up -d --build
|
||||
|
||||
add_alias
|
||||
|
||||
verify_checksum_after_heal minio/minio-test http://127.0.0.1:9000/minio-test/to-read/hosts
|
||||
|
||||
verify_checksum_mc ./minio minio/minio-test/to-read/minio
|
||||
|
||||
verify_checksum_mc /etc/hosts minio/minio-test/to-read/hosts
|
||||
|
||||
cleanup
|
||||
stop_server() {
|
||||
name="$1"
|
||||
docker stop -t 20 "${name}" >/dev/null
|
||||
test "$(docker inspect -f '{{.State.ExitCode}}' "${name}")" = 0
|
||||
docker logs "${name}" 2>&1 | grep -q 'Exiting on signal'
|
||||
docker rm "${name}" >/dev/null
|
||||
}
|
||||
|
||||
(__init__ "$@" && main "$@")
|
||||
command -v docker >/dev/null
|
||||
docker info >/dev/null
|
||||
if ! docker image inspect "${old_image}" >/dev/null 2>&1; then
|
||||
docker pull "${old_image}"
|
||||
fi
|
||||
|
||||
if [ "${SILO_UPGRADE_SKIP_BUILD:-0}" != 1 ]; then
|
||||
make -C "${repo_dir}" docker TAG="${new_image}"
|
||||
fi
|
||||
docker image inspect "${new_image}" >/dev/null
|
||||
|
||||
docker network create "${network}" >/dev/null
|
||||
docker volume create "${volume}" >/dev/null
|
||||
|
||||
start_server "${old_container}" "${old_image}" minio server /data --address :9000
|
||||
docker exec "${old_container}" mcli mb local/compat >/dev/null
|
||||
docker exec "${old_container}" mcli version enable local/compat >/dev/null
|
||||
printf 'old-version-1\n' | docker exec -i "${old_container}" mcli pipe local/compat/versioned.txt >/dev/null
|
||||
printf 'old-version-2\n' | docker exec -i "${old_container}" mcli pipe local/compat/versioned.txt >/dev/null
|
||||
test "$(docker exec "${old_container}" mcli ls --versions local/compat/versioned.txt | grep -c 'versioned.txt')" -ge 2
|
||||
docker exec "${old_container}" mcli mb --with-lock local/locked >/dev/null
|
||||
printf 'locked-by-old\n' | docker exec -i "${old_container}" mcli pipe local/locked/object.txt >/dev/null
|
||||
dd if=/dev/zero bs=1048576 count=70 2>/dev/null | docker exec -i "${old_container}" mcli pipe local/compat/multipart.bin >/dev/null
|
||||
test "$(docker exec "${old_container}" mcli stat --json local/compat/multipart.bin | jq -r '.size')" = 73400320
|
||||
docker exec "${old_container}" mcli admin user add local migration-user migration-secret-123 >/dev/null
|
||||
docker exec "${old_container}" mcli admin policy attach local readwrite --user migration-user >/dev/null
|
||||
stop_server "${old_container}"
|
||||
|
||||
start_server "${new_container}" "${new_image}" silo server /data --address :9000
|
||||
test "$(docker exec "${new_container}" mcli cat local/compat/versioned.txt)" = old-version-2
|
||||
test "$(docker exec "${new_container}" mcli cat local/locked/object.txt)" = locked-by-old
|
||||
test "$(docker exec "${new_container}" mcli stat --json local/compat/multipart.bin | jq -r '.size')" = 73400320
|
||||
docker exec "${new_container}" mcli admin user info local migration-user >/dev/null
|
||||
docker exec "${new_container}" mcli alias set migrated http://127.0.0.1:9000 migration-user migration-secret-123 >/dev/null
|
||||
printf 'written-by-silo\n' | docker exec -i "${new_container}" mcli pipe migrated/compat/silo.txt >/dev/null
|
||||
stop_server "${new_container}"
|
||||
|
||||
start_server "${rollback_container}" "${old_image}" minio server /data --address :9000
|
||||
test "$(docker exec "${rollback_container}" mcli cat local/compat/versioned.txt)" = old-version-2
|
||||
test "$(docker exec "${rollback_container}" mcli cat local/compat/silo.txt)" = written-by-silo
|
||||
test "$(docker exec "${rollback_container}" mcli stat --json local/compat/multipart.bin | jq -r '.size')" = 73400320
|
||||
stop_server "${rollback_container}"
|
||||
|
||||
echo "MinIO-to-Silo data upgrade and rollback checks passed"
|
||||
|
||||
@@ -5,16 +5,16 @@ if [ -n "$TEST_DEBUG" ]; then
|
||||
fi
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
SILO_CONFIG_DIR="$WORK_DIR/.silo"
|
||||
SILO=("$PWD/silo" --config-dir "$SILO_CONFIG_DIR" server)
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
if [ ! -x "$PWD/silo" ]; then
|
||||
echo "Silo executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
if [ ! -x "$PWD/silo" ]; then
|
||||
echo "Silo executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -30,9 +30,9 @@ catch() {
|
||||
echo "error on line $1"
|
||||
fi
|
||||
|
||||
echo "Cleaning up instances of MinIO"
|
||||
pkill minio || true
|
||||
pkill -9 minio || true
|
||||
echo "Cleaning up instances of Silo"
|
||||
pkill silo || true
|
||||
pkill -9 silo || true
|
||||
purge "$WORK_DIR"
|
||||
if [ $# -ne 0 ]; then
|
||||
exit $#
|
||||
@@ -41,32 +41,21 @@ catch() {
|
||||
|
||||
catch
|
||||
|
||||
function start_minio_10drive() {
|
||||
function start_silo_10drive() {
|
||||
start_port=$1
|
||||
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
export MC_HOST_minio="http://minio:minio123@127.0.0.1:${start_port}/"
|
||||
export MINIO_ROOT_USER=silo
|
||||
export MINIO_ROOT_PASSWORD=silo123
|
||||
export MC_HOST_silo="http://silo:silo123@127.0.0.1:${start_port}/"
|
||||
unset MINIO_KMS_AUTO_ENCRYPTION # do not auto-encrypt objects
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
mkdir ${WORK_DIR}
|
||||
C_PWD=${PWD}
|
||||
if [ ! -x "$PWD/mc" ]; then
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "$C_PWD/mc")
|
||||
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-mcli.sh" "$PWD/mc"
|
||||
fi
|
||||
|
||||
"${MINIO[@]}" --address ":$start_port" "${WORK_DIR}/disk{1...10}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
"${SILO[@]}" --address ":$start_port" "${WORK_DIR}/disk{1...10}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
pid=$!
|
||||
disown $pid
|
||||
sleep 5
|
||||
@@ -79,10 +68,10 @@ function start_minio_10drive() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"${PWD}/mc" mb --with-versioning minio/bucket
|
||||
"${PWD}/mc" mb --with-versioning silo/bucket
|
||||
|
||||
export AWS_ACCESS_KEY_ID=minio
|
||||
export AWS_SECRET_ACCESS_KEY=minio123
|
||||
export AWS_ACCESS_KEY_ID=silo
|
||||
export AWS_SECRET_ACCESS_KEY=silo123
|
||||
aws --endpoint-url http://localhost:"$start_port" s3api create-multipart-upload --bucket bucket --key obj-1 >upload-id.json
|
||||
uploadId=$(jq -r '.UploadId' upload-id.json)
|
||||
|
||||
@@ -120,7 +109,7 @@ EOF
|
||||
|
||||
function main() {
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
start_minio_10drive ${start_port}
|
||||
start_silo_10drive ${start_port}
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Executable
+157
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_dir="$(cd "${script_dir}/.." && pwd)"
|
||||
dist_dir="${DIST_DIR:-${repo_dir}/dist}"
|
||||
nfpm_config="${NFPM_CONFIG:-${repo_dir}/.github/nfpm.yml}"
|
||||
|
||||
if [ -z "${PKG_VERSION:-}" ]; then
|
||||
echo "PKG_VERSION is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Re-validate the shape release.yml derived from the tag. The packages are
|
||||
# named from this, so a malformed value would ship under a name no repository
|
||||
# can order against.
|
||||
if ! [[ "${PKG_VERSION}" =~ ^[0-9]{14}\.0\.0$ ]]; then
|
||||
echo "Invalid PKG_VERSION: ${PKG_VERSION}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The PGSTY release segment, PGDG-style. sign-release-rpms.sh declares the
|
||||
# same value as expected_release, and test-release.yml asserts the two agree.
|
||||
PKG_RELEASE="1PGSTY"
|
||||
|
||||
if ! command -v nfpm >/dev/null 2>&1; then
|
||||
echo "nfpm is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${nfpm_config}" ]; then
|
||||
echo "Missing nFPM config: ${nfpm_config}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# nfpm resolves a relative content src against the current directory, not
|
||||
# against the config file, so the unit path is passed in absolute. Otherwise
|
||||
# this only works when invoked from the repository root and fails elsewhere on
|
||||
# a message that names the file rather than the cause.
|
||||
unit_file="${repo_dir}/silo.service"
|
||||
defaults_file="${repo_dir}/silo.env"
|
||||
sysusers_file="${repo_dir}/silo.sysusers"
|
||||
license_file="${repo_dir}/LICENSE"
|
||||
notice_file="${repo_dir}/NOTICE"
|
||||
postinstall_file="${repo_dir}/buildscripts/package/postinstall.sh"
|
||||
preremove_file="${repo_dir}/buildscripts/package/preremove.sh"
|
||||
if [ ! -f "${unit_file}" ]; then
|
||||
echo "Missing systemd unit: ${unit_file}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "${defaults_file}" ]; then
|
||||
echo "Missing defaults file: ${defaults_file}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "${sysusers_file}" ]; then
|
||||
echo "Missing sysusers file: ${sysusers_file}" >&2
|
||||
exit 1
|
||||
fi
|
||||
for distributed_doc in "${license_file}" "${notice_file}"; do
|
||||
if [ ! -s "${distributed_doc}" ]; then
|
||||
echo "Missing license material: ${distributed_doc}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
for lifecycle_script in "${postinstall_file}" "${preremove_file}"; do
|
||||
if [ ! -x "${lifecycle_script}" ]; then
|
||||
echo "Missing executable package lifecycle script: ${lifecycle_script}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
packages_dir="${dist_dir}/packages"
|
||||
mkdir -p "${packages_dir}"
|
||||
|
||||
# Two spaces, no trailing newline: sign-release-rpms.sh parses these files to
|
||||
# check download integrity before it signs, and regenerates them afterwards in
|
||||
# the same shape.
|
||||
sha256_file() {
|
||||
local file="$1"
|
||||
local digest
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
digest="$(sha256sum "${file}" | awk '{print $1}')"
|
||||
else
|
||||
digest="$(shasum -a 256 "${file}" | awk '{print $1}')"
|
||||
fi
|
||||
|
||||
printf '%s %s' "${digest}" "$(basename "${file}")" > "${file}.sha256sum"
|
||||
}
|
||||
|
||||
find_binary() {
|
||||
local goarch="$1"
|
||||
local matches
|
||||
local count
|
||||
|
||||
# Must resolve to exactly one binary. Picking the first of several build
|
||||
# variants (an added goamd64 level, a stale dist entry) would silently ship a
|
||||
# package whose contents do not match its name.
|
||||
matches="$(find "${dist_dir}" -maxdepth 2 -type f \
|
||||
-path "${dist_dir}/silo_linux_${goarch}*/silo" | sort)"
|
||||
count="$(printf '%s' "${matches}" | grep -c . || true)"
|
||||
|
||||
if [ "${count}" -eq 0 ]; then
|
||||
echo "Missing GoReleaser binary for linux/${goarch}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "${count}" -ne 1 ]; then
|
||||
echo "Expected exactly one GoReleaser binary for linux/${goarch}, found ${count}:" >&2
|
||||
printf '%s\n' "${matches}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "${matches}"
|
||||
}
|
||||
|
||||
build_arch() {
|
||||
local goarch="$1"
|
||||
local rpm_arch="$2"
|
||||
local deb_arch="$3"
|
||||
local apk_arch="$4"
|
||||
local source
|
||||
local rpm_file
|
||||
local deb_file
|
||||
local apk_file
|
||||
|
||||
source="$(find_binary "${goarch}")"
|
||||
|
||||
# These names are the public download names and must not drift; RPM and DEB
|
||||
# carry the PGSTY release number (nfpm renders it as the RPM Release tag and
|
||||
# as the Debian revision after a dash). APK stays bare: Alpine pkgrel only
|
||||
# admits -r<integer>, so a lettered release cannot ride along there.
|
||||
rpm_file="${packages_dir}/silo-${PKG_VERSION}-${PKG_RELEASE}.${rpm_arch}.rpm"
|
||||
deb_file="${packages_dir}/silo_${PKG_VERSION}-${PKG_RELEASE}_${deb_arch}.deb"
|
||||
apk_file="${packages_dir}/silo_${PKG_VERSION}_${apk_arch}.apk"
|
||||
|
||||
(
|
||||
cd "${repo_dir}"
|
||||
export NFPM_UNIT="${unit_file}" NFPM_DEFAULTS="${defaults_file}" NFPM_SYSUSERS="${sysusers_file}" \
|
||||
NFPM_LICENSE="${license_file}" NFPM_NOTICE="${notice_file}"
|
||||
PKG_VERSION="${PKG_VERSION}" NFPM_RELEASE="${PKG_RELEASE}" NFPM_ARCH="${goarch}" NFPM_SOURCE="${source}" \
|
||||
nfpm package --config "${nfpm_config}" --packager rpm --target "${rpm_file}"
|
||||
PKG_VERSION="${PKG_VERSION}" NFPM_RELEASE="${PKG_RELEASE}" NFPM_ARCH="${goarch}" NFPM_SOURCE="${source}" \
|
||||
nfpm package --config "${nfpm_config}" --packager deb --target "${deb_file}"
|
||||
PKG_VERSION="${PKG_VERSION}" NFPM_RELEASE='' NFPM_ARCH="${goarch}" NFPM_SOURCE="${source}" \
|
||||
nfpm package --config "${nfpm_config}" --packager apk --target "${apk_file}"
|
||||
)
|
||||
|
||||
sha256_file "${rpm_file}"
|
||||
sha256_file "${deb_file}"
|
||||
sha256_file "${apk_file}"
|
||||
}
|
||||
|
||||
build_arch amd64 x86_64 amd64 x86_64
|
||||
build_arch arm64 aarch64 arm64 aarch64
|
||||
|
||||
find "${packages_dir}" -maxdepth 1 -type f | sort
|
||||
Executable
+172
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_dir="$(cd "${script_dir}/../.." && pwd)"
|
||||
postinstall="${script_dir}/postinstall.sh"
|
||||
preremove="${script_dir}/preremove.sh"
|
||||
test_dir="$(mktemp -d)"
|
||||
fakebin="${test_dir}/bin"
|
||||
log_file="${test_dir}/calls.log"
|
||||
useradd_shell=/usr/sbin/nologin
|
||||
[ -x "${useradd_shell}" ] || useradd_shell=/sbin/nologin
|
||||
busybox_shell=/sbin/nologin
|
||||
[ -x "${busybox_shell}" ] || busybox_shell=/bin/false
|
||||
|
||||
cleanup() {
|
||||
rm -rf "${test_dir}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
mkdir -p "${fakebin}"
|
||||
touch "${log_file}"
|
||||
|
||||
# One dispatcher represents every external command used by the lifecycle
|
||||
# scripts. The tested scripts run with no host utilities in PATH, so a green
|
||||
# result cannot create a real account or touch the host service manager.
|
||||
cat > "${fakebin}/fake-command" <<'EOF'
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
command_name=${0##*/}
|
||||
case "${command_name}" in
|
||||
id)
|
||||
[ "${PACKAGE_TEST_USER_EXISTS:-0}" = 1 ]
|
||||
;;
|
||||
getent)
|
||||
[ "${PACKAGE_TEST_GROUP_EXISTS:-0}" = 1 ]
|
||||
;;
|
||||
systemd-sysusers|useradd|addgroup|adduser|systemctl)
|
||||
{
|
||||
printf '%s' "${command_name}"
|
||||
for argument in "$@"; do
|
||||
printf ' %s' "${argument}"
|
||||
done
|
||||
printf '\n'
|
||||
} >> "${PACKAGE_TEST_LOG}"
|
||||
;;
|
||||
*)
|
||||
echo "unexpected fake command: ${command_name}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "${fakebin}/fake-command"
|
||||
|
||||
link_command() {
|
||||
ln -sf fake-command "${fakebin}/$1"
|
||||
}
|
||||
|
||||
unlink_optional_commands() {
|
||||
rm -f \
|
||||
"${fakebin}/systemd-sysusers" \
|
||||
"${fakebin}/useradd" \
|
||||
"${fakebin}/adduser" \
|
||||
"${fakebin}/addgroup"
|
||||
}
|
||||
|
||||
reset_log() {
|
||||
: > "${log_file}"
|
||||
}
|
||||
|
||||
run_postinstall() {
|
||||
PACKAGE_TEST_LOG="${log_file}" \
|
||||
PACKAGE_TEST_USER_EXISTS="${1}" \
|
||||
PACKAGE_TEST_GROUP_EXISTS="${2}" \
|
||||
PATH="${fakebin}" \
|
||||
/bin/sh "${postinstall}"
|
||||
}
|
||||
|
||||
run_preremove() {
|
||||
PACKAGE_TEST_LOG="${log_file}" PATH="${fakebin}" \
|
||||
/bin/sh "${preremove}" "$@"
|
||||
}
|
||||
|
||||
assert_log_line() {
|
||||
grep -Fx -- "$1" "${log_file}" >/dev/null
|
||||
}
|
||||
|
||||
reject_log_text() {
|
||||
if grep -F -- "$1" "${log_file}" >/dev/null; then
|
||||
echo "unexpected lifecycle call containing '$1':" >&2
|
||||
cat "${log_file}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
link_command id
|
||||
link_command getent
|
||||
link_command systemctl
|
||||
|
||||
# Clean install through systemd-sysusers. Side-by-side safety is represented
|
||||
# by the fact that the only service-manager operation is daemon-reload: no old
|
||||
# service is stopped, disabled, enabled, masked, or restarted.
|
||||
unlink_optional_commands
|
||||
link_command systemd-sysusers
|
||||
reset_log
|
||||
run_postinstall 0 0
|
||||
assert_log_line "systemd-sysusers /usr/lib/sysusers.d/silo.conf"
|
||||
assert_log_line "systemctl daemon-reload"
|
||||
test "$(wc -l < "${log_file}" | tr -d ' ')" -eq 2
|
||||
|
||||
# An existing service account is preserved without modification.
|
||||
reset_log
|
||||
run_postinstall 1 0
|
||||
test "$(cat "${log_file}")" = "systemctl daemon-reload"
|
||||
|
||||
# useradd creates a private group only when one does not already exist. An
|
||||
# administrator may pre-create group silo with the legacy GID; that group must
|
||||
# be reused rather than causing installation to fail.
|
||||
unlink_optional_commands
|
||||
link_command useradd
|
||||
reset_log
|
||||
run_postinstall 0 0
|
||||
assert_log_line "useradd --system --user-group --no-create-home --shell ${useradd_shell} --comment Silo object storage service silo"
|
||||
reject_log_text "--gid silo"
|
||||
|
||||
reset_log
|
||||
run_postinstall 0 1
|
||||
assert_log_line "useradd --system --gid silo --no-create-home --shell ${useradd_shell} --comment Silo object storage service silo"
|
||||
reject_log_text "--user-group"
|
||||
|
||||
# BusyBox follows the same existing-group contract.
|
||||
unlink_optional_commands
|
||||
link_command adduser
|
||||
link_command addgroup
|
||||
reset_log
|
||||
run_postinstall 0 0
|
||||
assert_log_line "addgroup -S silo"
|
||||
assert_log_line "adduser -S -D -H -G silo -s ${busybox_shell} silo"
|
||||
|
||||
reset_log
|
||||
run_postinstall 0 1
|
||||
reject_log_text "addgroup"
|
||||
assert_log_line "adduser -S -D -H -G silo -s ${busybox_shell} silo"
|
||||
|
||||
# Debian remove, RPM erase, and Alpine deinstall stop the Silo unit. Upgrade
|
||||
# arguments must leave the running service alone.
|
||||
for removal_argument in remove 0 20260214120000.0.0-r0; do
|
||||
reset_log
|
||||
run_preremove "${removal_argument}"
|
||||
test "$(cat "${log_file}")" = "systemctl disable --now silo.service"
|
||||
done
|
||||
|
||||
for upgrade_argument in upgrade 1; do
|
||||
reset_log
|
||||
run_preremove "${upgrade_argument}"
|
||||
test ! -s "${log_file}"
|
||||
done
|
||||
|
||||
# The package deliberately leaves legacy ownership changes to an explicit
|
||||
# systemd drop-in. Lifecycle scripts must never rewrite ownership or touch the
|
||||
# old unit, and the base unit must expose overridable User/Group directives.
|
||||
grep -Fx 'User=silo' "${repo_dir}/silo.service" >/dev/null
|
||||
grep -Fx 'Group=silo' "${repo_dir}/silo.service" >/dev/null
|
||||
if grep -Ein '\b(chown|chgrp|usermod|groupmod)\b|minio\.service' \
|
||||
"${postinstall}" "${preremove}"; then
|
||||
echo "package lifecycle scripts must not mutate data ownership or the legacy service" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Silo package lifecycle checks passed"
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
sysusers_file=/usr/lib/sysusers.d/silo.conf
|
||||
|
||||
group_exists() {
|
||||
if command -v getent >/dev/null 2>&1; then
|
||||
getent group silo >/dev/null 2>&1
|
||||
return
|
||||
fi
|
||||
|
||||
[ -r /etc/group ] || return 1
|
||||
while IFS=: read -r group_name _; do
|
||||
[ "${group_name}" = silo ] && return 0
|
||||
done < /etc/group
|
||||
return 1
|
||||
}
|
||||
|
||||
if ! id -u silo >/dev/null 2>&1; then
|
||||
if command -v systemd-sysusers >/dev/null 2>&1; then
|
||||
systemd-sysusers "${sysusers_file}"
|
||||
elif command -v useradd >/dev/null 2>&1; then
|
||||
nologin_shell=/usr/sbin/nologin
|
||||
[ -x "${nologin_shell}" ] || nologin_shell=/sbin/nologin
|
||||
if group_exists; then
|
||||
useradd --system --gid silo --no-create-home --shell "${nologin_shell}" --comment "Silo object storage service" silo
|
||||
else
|
||||
useradd --system --user-group --no-create-home --shell "${nologin_shell}" --comment "Silo object storage service" silo
|
||||
fi
|
||||
elif command -v adduser >/dev/null 2>&1 && command -v addgroup >/dev/null 2>&1; then
|
||||
nologin_shell=/sbin/nologin
|
||||
[ -x "${nologin_shell}" ] || nologin_shell=/bin/false
|
||||
group_exists || addgroup -S silo
|
||||
adduser -S -D -H -G silo -s "${nologin_shell}" silo
|
||||
else
|
||||
echo "Unable to create the silo system account: systemd-sysusers, useradd, or BusyBox adduser is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
fi
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
# Debian passes "remove" for an actual removal, RPM passes 0 to %preun, and
|
||||
# Alpine runs pre-deinstall only for removal and passes the old dotted version.
|
||||
# Upgrade paths deliberately leave the running service untouched.
|
||||
case "${1:-}" in
|
||||
remove|0|*.*)
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl disable --now silo.service >/dev/null 2>&1 || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,52 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBGaV5PwBEACbErI+7yOrsXTT3mR83O6Fw9WyHJqozhyNPF3dA1gAtWpfWqd4
|
||||
S9x6vBjVwUbIRn21jYgov0hDiaLABNQhRzifvVr0r1IjBW8lhA8zJGaO42Uz0aBW
|
||||
YIkajOklsXgYMX+gSmy5WXzM31sDQVMnzptHh9dwW067hMM5pJKDslu2pLMwSb9K
|
||||
QgIFcYsaR0taBkcDg4dNu1gncriD/GcdXIS0/V4R82DIYeIqj2S0lt0jDTACbUz3
|
||||
C6esrTw2XerCeHKHb9c/V+KMhqvLJOOpy/aJWLrTGBoaH7xw6v0qg32OYiBxlUj9
|
||||
VEzoQbDfbRkR+jlxiuYP3scUs/ziKrSh+0mshVbeuLRSNfuHLa7C4xTEnATcgD1J
|
||||
MZeMaJXIcDt+DN+1aHVQjY5YNvr5wA3ykxW51uReZf7/odgqVW3+1rhW5pd8NQKQ
|
||||
qoVUHOtIrC9KaiGfrczEtJTNUxcNZV9eBgcKHYDXB2hmR2pIf7WvydgXTs/qIsXg
|
||||
SIzfKjisi795Dd5GrvdLYXVnu9YzylWlkJ5rjod1wnSxkI/CcCJaoPLnXZA9KV7A
|
||||
cpMWWaUEXP/XBIwIU+vxDd1taBIaPIOv1KIdzvG7QqAQtf5Lphi5HfaGvBud/CVt
|
||||
mvWhRPJMr1J0ER2xAgU2iZR7dN0vSF6zDqc0W09RAoC0nDS3tupDX2BrOwARAQAB
|
||||
tCRSdW9oYW5nIEZlbmcgKFBpZ3N0eSkgPHJoQHZvbm5nLmNvbT6JAlEEEwEIADsW
|
||||
IQSVkqe8emguczM3bgnnk12Nub2LIAUCZpXk/AIbAwULCQgHAgIiAgYVCgkICwIE
|
||||
FgIDAQIeBwIXgAAKCRDnk12Nub2LIOMuEACBLVc09O4icFwc45R3KMvOMu14Egpn
|
||||
UkpmBKhErjup0TIunzI0zZH6HG8LGuf6XEdH4ItCJeLg5349UE00BUHNmxk2coo2
|
||||
u4Wtu28LPqmxb6sqpuRAaefedU6vqfs7YN6WWp52pVF1KdOHkIOcgAQ9z3ZHdosM
|
||||
I/Y/UxO2t4pjdCAfJHOmGPrbgLcHSMpoLLxjuf3YIwS5NSfjNDd0Y8sKFUcMGLCF
|
||||
5P0lv5feLLdZvh2Una34UmHKhZlXC5E3vlY9bf/LgsRzXRFQosD0RsCXbz3Tk+zF
|
||||
+j/eP3WhUvJshqIDuY6eJYCzMjiA8sM5gety+htVJuD0mewp+qAhjxE0d4bIr4qO
|
||||
BKQzBt9tT2ackCPdgW42VPS+IZymm1oMET0hgZfKiVpwsKO6qxeWn4RW2jJ0zkUJ
|
||||
MsrrxOPFdZQAtuFcLwa5PUAHHs6XQT2vzxDpeE9lInQ14lshofU5ZKIeb9sbvb/w
|
||||
P+xnDqvZ1pcotEIBvDK0S0jHbHHqtioIUdDFvdCBlBlYP1TQRNPlJ7TJDBBvhj8i
|
||||
fmjQsYSV1u36aHOJVGYNHv+SyJpVd3nHCZn97ADM9qHnDm7xljyHXPzIx4FMmBGJ
|
||||
UTiLH5yxa1xhWr42Iv3TykaQJVbpydmBuegFR8WbWitAvVqI3HvRG+FalLsjJruc
|
||||
8YDAf7gHdj/937kCDQRmleT8ARAAmJxscC76NZzqFBiaeq2+aJxOt1HGPqKb4pbz
|
||||
jLKRX9sFkeXuzhfZaNDljnr2yrnQ75rit9Aah/loEhbSHanNUDCNmvOeSEISr9yA
|
||||
yfOnqlcVOtcwWQK57n6MvlCSM8Js3jdoSmCFHVtdFFwxejE5ok0dk1VFYDIg6DRk
|
||||
ZBMuxGO7ZJW7TzCxhK4AL+NNYA2wX6b+IVMn6CA9kwNwCNrrnGHR1sblSxZp7lPo
|
||||
+GsqzYY0LXGR2eEicgKd4lk38gaO8Q4d1mlpX95vgdhGKxR+CM26y9QU0qrO1hXP
|
||||
Fw6lX9HfIUkVNrqAa1mzgneYXivnLvcj8gc7bFAdweX4MyBHsmiPm32WqjUJFAmw
|
||||
kcKYaiyfDJ+1wusa/b+7RCnshWc8B9udYbXfvcpOGgphpUuvomKT8at3ToJfEWmR
|
||||
BzToYYTsgAAX8diY/X53BHCE/+MhLccglEUYNZyBRkTwDLrS9QgNkhrADaTwxsv1
|
||||
8PwnVKve/ZxwOU0QGf4ZOhA2YQOE5hkRDR5uY2OHsOS5vHsd9Y6kNNnO8EBy99d1
|
||||
QiBJOW3AP0nr4Cj1/NhdigAujsYRKiCAuPT7dgqART58VU4bZ3PgonMlziLe7+ht
|
||||
YYxV+wyP6LVqicDd0MLLvG7r/JOiWuABOUxsFFaRecehoPJjeAEQxnWJjedokXKL
|
||||
HVOFaEkAEQEAAYkCNgQYAQgAIBYhBJWSp7x6aC5zMzduCeeTXY25vYsgBQJmleT8
|
||||
AhsMAAoJEOeTXY25vYsgG8sP/3UdsWuiwTsf/x4BTW82K+Uk9YwZDnUNH+4dUMED
|
||||
bKT1C6CbuSZ7Mnbi2rVsmGzOMs9MehIx6Ko8/iCR2OCeWi8Q+wM+iffAfWuT1GK6
|
||||
7f/VIfoYBUWEa+kvDcPgEbd5Tu7ZdUO/jROVBSlXRSjzK9LpIj7GozBTJ8Vqy5x7
|
||||
oqbWPPEYtGDVHime8o6f5/wfhNgL3mFnoq6srK7KhwACwfTXlNqAlGiXGa30Yj+b
|
||||
Cj6IvmxoII49E67/ovMEmzDCb3RXiaL6OATy25P+HQJvWvAam7Qq5Xn+bZg65Mup
|
||||
vXq3zoX0a7EKXc5vsJVNtTlXO1ATdYszKP5uNzkHrNAN52VRYaowq1vPy/MVMbSI
|
||||
rL/hTFKr7ZNhmC7jmS3OuJyCYQsfEerubtBUuc/W6JDc2oTI3xOG1S2Zj8f4PxLl
|
||||
H7vMG4E+p6eOrUGw6VQXjFsH9GtwhkPh/ZGMKENb2+JztJ02674Cok4s5c/lZFKz
|
||||
mmRUcNjX2bm2K0GfGG5/hAog/CHCeUZvwIh4hZLkdeJ1QsIYpN8xbvY7QP6yh4VB
|
||||
XrL18+2sontZ45MsGResrRibB35x7IrCrxZsVtRJZthHqshiORPatgy+AiWcAtEv
|
||||
UWEnnC1xBSasNebw4fSE8AJg9JMCRw+3GAetlotOeW9q7PN6yrXD9rGuV/QquQNd
|
||||
/c7w
|
||||
=4rRi
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,480 @@
|
||||
// Copyright 2026 PGSTY contributors.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// rebrand-guard records the compatibility identifiers that a product rebrand
|
||||
// must not accidentally rename. It intentionally excludes product branding and
|
||||
// delivery names, which are validated by buildscripts/verify-rebrand.sh.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const manifestVersion = 3
|
||||
|
||||
var (
|
||||
minioImportRE = regexp.MustCompile(`github\.com/minio/[A-Za-z0-9_./-]+`)
|
||||
envRE = regexp.MustCompile(`\b_?MINIO_[A-Z0-9_]+\b`)
|
||||
metricRE = regexp.MustCompile(`\bminio_[A-Za-z0-9_]+\b`)
|
||||
headerRE = regexp.MustCompile(`(?i)\bx-minio-[a-z0-9_-]+\b`)
|
||||
routeRE = regexp.MustCompile(`^/[A-Za-z0-9._~!$&'()*+,;=:@%/?{}=-]*`)
|
||||
storageRE = regexp.MustCompile(`\.minio\.sys(?:/[A-Za-z0-9._${}-]+)*`)
|
||||
policyRE = regexp.MustCompile(`(?:arn:minio|minio:s3)[A-Za-z0-9_:/.*${}-]*`)
|
||||
brandRE = regexp.MustCompile(`(?i)(^|[^a-z0-9_])minio([^a-z0-9_]|$)`)
|
||||
)
|
||||
|
||||
type manifest struct {
|
||||
Version int `json:"version"`
|
||||
ModulePath string `json:"module_path"`
|
||||
MinioImports []string `json:"minio_imports"`
|
||||
Environment []string `json:"environment"`
|
||||
Metrics []string `json:"metrics"`
|
||||
Headers []string `json:"headers"`
|
||||
Routes []string `json:"routes"`
|
||||
RouteRoots []string `json:"route_roots"`
|
||||
GridRoutes []string `json:"grid_routes"`
|
||||
StorageMarkers []string `json:"storage_markers"`
|
||||
PolicyValues []string `json:"policy_values"`
|
||||
ExportedSymbols []string `json:"exported_symbols"`
|
||||
BrandAllowlist []string `json:"brand_allowlist"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
write := flag.Bool("write", false, "replace the checked-in compatibility baseline")
|
||||
flag.Parse()
|
||||
|
||||
repo, err := gitOutput("rev-parse", "--show-toplevel")
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
repo = strings.TrimSpace(repo)
|
||||
baselinePath := filepath.Join(repo, "buildscripts", "rebrand-guard", "compat-baseline.json")
|
||||
|
||||
current, err := collect(repo)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if *write {
|
||||
if err := writeManifest(baselinePath, current); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
fmt.Printf("wrote %s\n", baselinePath)
|
||||
printSummary(current)
|
||||
return
|
||||
}
|
||||
|
||||
want, err := readManifest(baselinePath)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err := compare(want, current); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
printSummary(current)
|
||||
fmt.Println("Silo rebrand compatibility baseline is unchanged")
|
||||
}
|
||||
|
||||
func collect(repo string) (manifest, error) {
|
||||
files, err := trackedFiles(repo)
|
||||
if err != nil {
|
||||
return manifest{}, err
|
||||
}
|
||||
|
||||
sets := map[string]map[string]struct{}{
|
||||
"imports": {},
|
||||
"env": {},
|
||||
"metrics": {},
|
||||
"headers": {},
|
||||
"routes": {},
|
||||
"roots": {},
|
||||
"grid": {},
|
||||
"storage": {},
|
||||
"policy": {},
|
||||
"exported": {},
|
||||
"brand": {},
|
||||
}
|
||||
modulePath := ""
|
||||
fset := token.NewFileSet()
|
||||
|
||||
for _, rel := range files {
|
||||
if rel == "SILO_REBRANDING_MIGRATION.md" ||
|
||||
strings.HasPrefix(rel, "buildscripts/rebrand-guard/") ||
|
||||
strings.HasPrefix(rel, "buildscripts/helm-migration-guard/") {
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(repo, filepath.FromSlash(rel))
|
||||
data, err := os.ReadFile(path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return manifest{}, fmt.Errorf("read %s: %w", rel, err)
|
||||
}
|
||||
if bytes.IndexByte(data, 0) >= 0 {
|
||||
continue
|
||||
}
|
||||
text := string(data)
|
||||
|
||||
addMatches(sets["env"], envRE, text, false)
|
||||
addMatches(sets["headers"], headerRE, text, true)
|
||||
addMatches(sets["storage"], storageRE, text, false)
|
||||
addMatches(sets["policy"], policyRE, text, false)
|
||||
if strings.HasSuffix(rel, ".go") && (strings.HasPrefix(rel, "cmd/") || strings.HasPrefix(rel, "internal/")) {
|
||||
addMatches(sets["metrics"], metricRE, text, false)
|
||||
}
|
||||
if rel == "go.mod" {
|
||||
addMatches(sets["imports"], minioImportRE, text, false)
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 2 && fields[0] == "module" {
|
||||
modulePath = fields[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.HasSuffix(rel, ".go") {
|
||||
file, err := parser.ParseFile(fset, path, data, parser.SkipObjectResolution)
|
||||
if err != nil {
|
||||
return manifest{}, fmt.Errorf("parse %s: %w", rel, err)
|
||||
}
|
||||
for _, spec := range file.Imports {
|
||||
value, err := strconv.Unquote(spec.Path.Value)
|
||||
if err == nil && strings.HasPrefix(value, "github.com/minio/") {
|
||||
sets["imports"][value] = struct{}{}
|
||||
}
|
||||
}
|
||||
collectStringMatches(sets["routes"], routeRE, file)
|
||||
collectNamedStringValues(sets["roots"], rel, file, "minioReservedBucket")
|
||||
if rel == "internal/grid/manager.go" {
|
||||
collectStringMatches(sets["grid"], routeRE, file)
|
||||
}
|
||||
if !strings.HasSuffix(rel, "_test.go") {
|
||||
collectExported(sets["exported"], filepath.ToSlash(filepath.Dir(rel)), file)
|
||||
if strings.HasPrefix(rel, "cmd/") || strings.HasPrefix(rel, "internal/") {
|
||||
collectBrandStrings(sets["brand"], rel, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// This was a shell-local PID variable in the generated inspect script,
|
||||
// never a supported environment setting.
|
||||
delete(sets["env"], "MINIO_SRVR_PID")
|
||||
|
||||
if modulePath == "" {
|
||||
return manifest{}, errors.New("go.mod module path was not found")
|
||||
}
|
||||
return manifest{
|
||||
Version: manifestVersion,
|
||||
ModulePath: modulePath,
|
||||
MinioImports: sorted(sets["imports"]),
|
||||
Environment: sorted(sets["env"]),
|
||||
Metrics: sorted(sets["metrics"]),
|
||||
Headers: sorted(sets["headers"]),
|
||||
Routes: sorted(sets["routes"]),
|
||||
RouteRoots: sorted(sets["roots"]),
|
||||
GridRoutes: sorted(sets["grid"]),
|
||||
StorageMarkers: sorted(sets["storage"]),
|
||||
PolicyValues: sorted(sets["policy"]),
|
||||
ExportedSymbols: sorted(sets["exported"]),
|
||||
BrandAllowlist: sorted(sets["brand"]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func collectBrandStrings(dst map[string]struct{}, rel string, file *ast.File) {
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
literal, ok := node.(*ast.BasicLit)
|
||||
if !ok || literal.Kind != token.STRING {
|
||||
return true
|
||||
}
|
||||
value, err := strconv.Unquote(literal.Value)
|
||||
if err != nil || !brandRE.MatchString(value) || strings.HasPrefix(value, "github.com/minio/") {
|
||||
return true
|
||||
}
|
||||
dst[filepath.ToSlash(rel)+"="+strconv.Quote(value)] = struct{}{}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func collectNamedStringValues(dst map[string]struct{}, rel string, file *ast.File, names ...string) {
|
||||
wanted := make(map[string]struct{}, len(names))
|
||||
for _, name := range names {
|
||||
wanted[name] = struct{}{}
|
||||
}
|
||||
for _, decl := range file.Decls {
|
||||
gen, ok := decl.(*ast.GenDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, rawSpec := range gen.Specs {
|
||||
spec, ok := rawSpec.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for i, name := range spec.Names {
|
||||
if _, ok := wanted[name.Name]; !ok || i >= len(spec.Values) {
|
||||
continue
|
||||
}
|
||||
literal, ok := spec.Values[i].(*ast.BasicLit)
|
||||
if !ok || literal.Kind != token.STRING {
|
||||
continue
|
||||
}
|
||||
value, err := strconv.Unquote(literal.Value)
|
||||
if err == nil {
|
||||
dst[filepath.ToSlash(rel)+":"+name.Name+"="+value] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func collectStringMatches(dst map[string]struct{}, re *regexp.Regexp, file *ast.File) {
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
literal, ok := node.(*ast.BasicLit)
|
||||
if !ok || literal.Kind != token.STRING {
|
||||
return true
|
||||
}
|
||||
value, err := strconv.Unquote(literal.Value)
|
||||
if err == nil {
|
||||
addMatches(dst, re, value, false)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func trackedFiles(repo string) ([]string, error) {
|
||||
cmd := exec.Command("git", "-C", repo, "ls-files", "--cached", "--others", "--exclude-standard", "-z")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("git ls-files: %w", err)
|
||||
}
|
||||
parts := bytes.Split(out, []byte{0})
|
||||
files := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
if len(part) > 0 {
|
||||
files = append(files, string(part))
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func addMatches(dst map[string]struct{}, re *regexp.Regexp, text string, lower bool) {
|
||||
for _, match := range re.FindAllString(text, -1) {
|
||||
if lower {
|
||||
match = strings.ToLower(match)
|
||||
}
|
||||
dst[match] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func collectExported(dst map[string]struct{}, dir string, file *ast.File) {
|
||||
prefix := dir + ":" + file.Name.Name + ":"
|
||||
for _, decl := range file.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.FuncDecl:
|
||||
if !ast.IsExported(decl.Name.Name) {
|
||||
continue
|
||||
}
|
||||
if decl.Recv == nil {
|
||||
dst[prefix+"func:"+decl.Name.Name] = struct{}{}
|
||||
continue
|
||||
}
|
||||
receiver := receiverName(decl.Recv.List[0].Type)
|
||||
dst[prefix+"method:"+receiver+"."+decl.Name.Name] = struct{}{}
|
||||
case *ast.GenDecl:
|
||||
for _, spec := range decl.Specs {
|
||||
switch spec := spec.(type) {
|
||||
case *ast.TypeSpec:
|
||||
if !ast.IsExported(spec.Name.Name) {
|
||||
continue
|
||||
}
|
||||
dst[prefix+"type:"+spec.Name.Name] = struct{}{}
|
||||
collectExportedFields(dst, prefix, spec.Name.Name, spec.Type)
|
||||
case *ast.ValueSpec:
|
||||
kind := strings.ToLower(decl.Tok.String())
|
||||
for _, name := range spec.Names {
|
||||
if ast.IsExported(name.Name) {
|
||||
dst[prefix+kind+":"+name.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func collectExportedFields(dst map[string]struct{}, prefix, typeName string, expr ast.Expr) {
|
||||
var fields *ast.FieldList
|
||||
switch typed := expr.(type) {
|
||||
case *ast.StructType:
|
||||
fields = typed.Fields
|
||||
case *ast.InterfaceType:
|
||||
fields = typed.Methods
|
||||
default:
|
||||
return
|
||||
}
|
||||
for _, field := range fields.List {
|
||||
for _, name := range field.Names {
|
||||
if ast.IsExported(name.Name) {
|
||||
dst[prefix+"field:"+typeName+"."+name.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func receiverName(expr ast.Expr) string {
|
||||
switch expr := expr.(type) {
|
||||
case *ast.Ident:
|
||||
return expr.Name
|
||||
case *ast.StarExpr:
|
||||
return receiverName(expr.X)
|
||||
case *ast.IndexExpr:
|
||||
return receiverName(expr.X)
|
||||
case *ast.IndexListExpr:
|
||||
return receiverName(expr.X)
|
||||
case *ast.SelectorExpr:
|
||||
return receiverName(expr.X) + "." + expr.Sel.Name
|
||||
default:
|
||||
return fmt.Sprintf("%T", expr)
|
||||
}
|
||||
}
|
||||
|
||||
func sorted(set map[string]struct{}) []string {
|
||||
values := make([]string, 0, len(set))
|
||||
for value := range set {
|
||||
values = append(values, value)
|
||||
}
|
||||
sort.Strings(values)
|
||||
return values
|
||||
}
|
||||
|
||||
func writeManifest(path string, value manifest) error {
|
||||
data, err := json.MarshalIndent(value, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = append(data, '\n')
|
||||
return os.WriteFile(path, data, 0o644)
|
||||
}
|
||||
|
||||
func readManifest(path string) (manifest, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return manifest{}, fmt.Errorf("read compatibility baseline (run go run ./buildscripts/rebrand-guard --write once): %w", err)
|
||||
}
|
||||
var value manifest
|
||||
if err := json.Unmarshal(data, &value); err != nil {
|
||||
return manifest{}, err
|
||||
}
|
||||
if value.Version != manifestVersion {
|
||||
return manifest{}, fmt.Errorf("unsupported compatibility baseline version %d", value.Version)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func compare(want, got manifest) error {
|
||||
var failures []string
|
||||
if want.ModulePath != got.ModulePath {
|
||||
failures = append(failures, fmt.Sprintf("module_path: want %q, got %q", want.ModulePath, got.ModulePath))
|
||||
}
|
||||
checks := []struct {
|
||||
name string
|
||||
want, got []string
|
||||
}{
|
||||
{"minio_imports", want.MinioImports, got.MinioImports},
|
||||
{"environment", want.Environment, got.Environment},
|
||||
{"metrics", want.Metrics, got.Metrics},
|
||||
{"headers", want.Headers, got.Headers},
|
||||
{"routes", want.Routes, got.Routes},
|
||||
{"route_roots", want.RouteRoots, got.RouteRoots},
|
||||
{"grid_routes", want.GridRoutes, got.GridRoutes},
|
||||
{"storage_markers", want.StorageMarkers, got.StorageMarkers},
|
||||
{"policy_values", want.PolicyValues, got.PolicyValues},
|
||||
{"exported_symbols", want.ExportedSymbols, got.ExportedSymbols},
|
||||
{"brand_allowlist", want.BrandAllowlist, got.BrandAllowlist},
|
||||
}
|
||||
for _, check := range checks {
|
||||
if missing, added := setDiff(check.want, check.got); len(missing) > 0 || len(added) > 0 {
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, "%s compatibility set changed", check.name)
|
||||
for _, value := range missing {
|
||||
fmt.Fprintf(&b, "\n - %s", value)
|
||||
}
|
||||
for _, value := range added {
|
||||
fmt.Fprintf(&b, "\n + %s", value)
|
||||
}
|
||||
failures = append(failures, b.String())
|
||||
}
|
||||
}
|
||||
if len(failures) > 0 {
|
||||
return errors.New(strings.Join(failures, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setDiff(want, got []string) (missing, added []string) {
|
||||
wantSet := make(map[string]struct{}, len(want))
|
||||
gotSet := make(map[string]struct{}, len(got))
|
||||
for _, value := range want {
|
||||
wantSet[value] = struct{}{}
|
||||
}
|
||||
for _, value := range got {
|
||||
gotSet[value] = struct{}{}
|
||||
}
|
||||
for _, value := range want {
|
||||
if _, ok := gotSet[value]; !ok {
|
||||
missing = append(missing, value)
|
||||
}
|
||||
}
|
||||
for _, value := range got {
|
||||
if _, ok := wantSet[value]; !ok {
|
||||
added = append(added, value)
|
||||
}
|
||||
}
|
||||
return missing, added
|
||||
}
|
||||
|
||||
func printSummary(value manifest) {
|
||||
fmt.Printf("compatibility manifest: imports=%d env=%d metrics=%d headers=%d routes=%d roots=%d grid=%d storage=%d policy=%d exported=%d brand=%d sha256=%s\n",
|
||||
len(value.MinioImports), len(value.Environment), len(value.Metrics), len(value.Headers),
|
||||
len(value.Routes), len(value.RouteRoots), len(value.GridRoutes), len(value.StorageMarkers), len(value.PolicyValues),
|
||||
len(value.ExportedSymbols), len(value.BrandAllowlist), manifestDigest(value))
|
||||
}
|
||||
|
||||
func manifestDigest(value manifest) string {
|
||||
data, _ := json.Marshal(value)
|
||||
sum := sha256.Sum256(data)
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func gitOutput(args ...string) (string, error) {
|
||||
out, err := exec.Command("git", args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("git %s: %s: %w", strings.Join(args, " "), strings.TrimSpace(string(out)), err)
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func fatal(err error) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -6,38 +6,29 @@ set -x
|
||||
set -e
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
SILO_CONFIG_DIR="$WORK_DIR/.silo"
|
||||
SILO=("$PWD/silo" --config-dir "$SILO_CONFIG_DIR" server)
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
if [ ! -x "$PWD/silo" ]; then
|
||||
echo "Silo executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function start_minio_5drive() {
|
||||
function start_silo_5drive() {
|
||||
start_port=$1
|
||||
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
export MC_HOST_minio="http://minio:minio123@127.0.0.1:${start_port}/"
|
||||
export MINIO_ROOT_USER=silo
|
||||
export MINIO_ROOT_PASSWORD=silo123
|
||||
export MC_HOST_silo="http://silo:silo123@127.0.0.1:${start_port}/"
|
||||
unset MINIO_KMS_AUTO_ENCRYPTION # do not auto-encrypt objects
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "$WORK_DIR/mc")
|
||||
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
mkdir -p "${WORK_DIR}"
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-mcli.sh" "${WORK_DIR}/mc"
|
||||
|
||||
"${WORK_DIR}/mc" cp --quiet -r "buildscripts/cicd-corpus/" "${WORK_DIR}/cicd-corpus/"
|
||||
|
||||
"${MINIO[@]}" --address ":$start_port" "${WORK_DIR}/cicd-corpus/disk{1...5}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
"${SILO[@]}" --address ":$start_port" "${WORK_DIR}/cicd-corpus/disk{1...5}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
pid=$!
|
||||
disown $pid
|
||||
sleep 5
|
||||
@@ -50,16 +41,16 @@ function start_minio_5drive() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"${WORK_DIR}/mc" stat minio/bucket/testobj
|
||||
"${WORK_DIR}/mc" stat silo/bucket/testobj
|
||||
|
||||
pkill minio
|
||||
pkill silo
|
||||
sleep 3
|
||||
}
|
||||
|
||||
function main() {
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
|
||||
start_minio_5drive ${start_port}
|
||||
start_silo_5drive ${start_port}
|
||||
}
|
||||
|
||||
function purge() {
|
||||
|
||||
@@ -5,48 +5,42 @@ set -o pipefail
|
||||
set -x
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO_OLD=("$PWD/minio.RELEASE.2020-10-28T08-16-50Z" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
SILO_CONFIG_DIR="$WORK_DIR/.silo"
|
||||
MINIO_OLD=("$PWD/minio.RELEASE.2020-10-28T08-16-50Z" --config-dir "$SILO_CONFIG_DIR" server)
|
||||
SILO=("$PWD/silo" --config-dir "$SILO_CONFIG_DIR" server)
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
if [ ! -x "$PWD/silo" ]; then
|
||||
echo "Silo executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function download_old_release() {
|
||||
if [ ! -f minio.RELEASE.2020-10-28T08-16-50Z ]; then
|
||||
curl --silent -O https://dl.minio.io/server/minio/release/linux-amd64/archive/minio.RELEASE.2020-10-28T08-16-50Z
|
||||
chmod a+x minio.RELEASE.2020-10-28T08-16-50Z
|
||||
if [ ! -x minio.RELEASE.2020-10-28T08-16-50Z ]; then
|
||||
: "${SILO_LEGACY_FIXTURE_2020:?set SILO_LEGACY_FIXTURE_2020 to the audited legacy binary}"
|
||||
: "${SILO_LEGACY_SHA256_2020:?set SILO_LEGACY_SHA256_2020 to its audited SHA-256}"
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-verified-fixture.sh" \
|
||||
"${SILO_LEGACY_FIXTURE_2020}" "${SILO_LEGACY_SHA256_2020}" \
|
||||
"minio.RELEASE.2020-10-28T08-16-50Z"
|
||||
fi
|
||||
}
|
||||
|
||||
function verify_rewrite() {
|
||||
start_port=$1
|
||||
|
||||
export MINIO_ACCESS_KEY=minio
|
||||
export MINIO_SECRET_KEY=minio123
|
||||
export MC_HOST_minio="http://minio:minio123@127.0.0.1:${start_port}/"
|
||||
export MINIO_ACCESS_KEY=silo
|
||||
export MINIO_SECRET_KEY=silo123
|
||||
export MC_HOST_silo="http://silo:silo123@127.0.0.1:${start_port}/"
|
||||
unset MINIO_KMS_AUTO_ENCRYPTION # do not auto-encrypt objects
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "$WORK_DIR/mc")
|
||||
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
mkdir -p "${WORK_DIR}"
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-mcli.sh" "${WORK_DIR}/mc"
|
||||
|
||||
"${MINIO_OLD[@]}" --address ":$start_port" "${WORK_DIR}/xl{1...16}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
pid=$!
|
||||
disown $pid
|
||||
|
||||
"${WORK_DIR}/mc" ready minio/
|
||||
"${WORK_DIR}/mc" ready silo/
|
||||
|
||||
if ! ps -p ${pid} 1>&2 >/dev/null; then
|
||||
echo "server1 log:"
|
||||
@@ -56,30 +50,30 @@ function verify_rewrite() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"${WORK_DIR}/mc" mb minio/healing-rewrite-bucket --quiet --with-lock
|
||||
"${WORK_DIR}/mc" mb silo/healing-rewrite-bucket --quiet --with-lock
|
||||
"${WORK_DIR}/mc" cp \
|
||||
buildscripts/verify-build.sh \
|
||||
minio/healing-rewrite-bucket/ \
|
||||
silo/healing-rewrite-bucket/ \
|
||||
--disable-multipart --quiet
|
||||
|
||||
"${WORK_DIR}/mc" cp \
|
||||
buildscripts/verify-build.sh \
|
||||
minio/healing-rewrite-bucket/ \
|
||||
silo/healing-rewrite-bucket/ \
|
||||
--disable-multipart --quiet
|
||||
|
||||
"${WORK_DIR}/mc" cp \
|
||||
buildscripts/verify-build.sh \
|
||||
minio/healing-rewrite-bucket/ \
|
||||
silo/healing-rewrite-bucket/ \
|
||||
--disable-multipart --quiet
|
||||
|
||||
kill ${pid}
|
||||
sleep 3
|
||||
|
||||
"${MINIO[@]}" --address ":$start_port" "${WORK_DIR}/xl{1...16}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
"${SILO[@]}" --address ":$start_port" "${WORK_DIR}/xl{1...16}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
pid=$!
|
||||
disown $pid
|
||||
|
||||
"${WORK_DIR}/mc" ready minio/
|
||||
"${WORK_DIR}/mc" ready silo/
|
||||
|
||||
if ! ps -p ${pid} 1>&2 >/dev/null; then
|
||||
echo "server1 log:"
|
||||
@@ -92,8 +86,8 @@ function verify_rewrite() {
|
||||
if ! ./s3-check-md5 \
|
||||
-debug \
|
||||
-versions \
|
||||
-access-key minio \
|
||||
-secret-key minio123 \
|
||||
-access-key silo \
|
||||
-secret-key silo123 \
|
||||
-endpoint "http://127.0.0.1:${start_port}/" 2>&1 | grep INTACT; then
|
||||
echo "server1 log:"
|
||||
cat "${WORK_DIR}/server1.log"
|
||||
@@ -101,7 +95,7 @@ function verify_rewrite() {
|
||||
mkdir -p inspects
|
||||
(
|
||||
cd inspects
|
||||
"${WORK_DIR}/mc" admin inspect minio/healing-rewrite-bucket/verify-build.sh/**
|
||||
"${WORK_DIR}/mc" admin inspect silo/healing-rewrite-bucket/verify-build.sh/**
|
||||
)
|
||||
|
||||
"${WORK_DIR}/mc" mb play/inspects
|
||||
@@ -111,14 +105,14 @@ function verify_rewrite() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
go run ./buildscripts/heal-manual.go "127.0.0.1:${start_port}" "minio" "minio123"
|
||||
go run ./buildscripts/heal-manual.go "127.0.0.1:${start_port}" "silo" "silo123"
|
||||
sleep 1
|
||||
|
||||
if ! ./s3-check-md5 \
|
||||
-debug \
|
||||
-versions \
|
||||
-access-key minio \
|
||||
-secret-key minio123 \
|
||||
-access-key silo \
|
||||
-secret-key silo123 \
|
||||
-endpoint http://127.0.0.1:${start_port}/ 2>&1 | grep INTACT; then
|
||||
echo "server1 log:"
|
||||
cat "${WORK_DIR}/server1.log"
|
||||
@@ -126,7 +120,7 @@ function verify_rewrite() {
|
||||
mkdir -p inspects
|
||||
(
|
||||
cd inspects
|
||||
"${WORK_DIR}/mc" admin inspect minio/healing-rewrite-bucket/verify-build.sh/**
|
||||
"${WORK_DIR}/mc" admin inspect silo/healing-rewrite-bucket/verify-build.sh/**
|
||||
)
|
||||
|
||||
"${WORK_DIR}/mc" mb play/inspects
|
||||
|
||||
Executable
+287
@@ -0,0 +1,287 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# These are the single source of truth for the package identity: .github/nfpm.yml
|
||||
# must agree with them, and test-release.yml asserts that it does. Drift the
|
||||
# other way round would only surface here, on the maintainer's machine, after
|
||||
# the build has already run and uploaded.
|
||||
expected_fingerprint="9592A7BC7A682E7333376E09E7935D8DB9BD8B20"
|
||||
expected_release="1PGSTY"
|
||||
expected_vendor="PGSTY"
|
||||
expected_packager="Ruohang Feng (@Vonng) <rh@vonng.com>"
|
||||
expected_url="https://silo.pgsty.com"
|
||||
expected_summary="S3-Interface Libre Object Storage, a community-maintained S3-compatible server."
|
||||
expected_description="S3-Interface Libre Object Storage, a community-maintained S3-compatible server."
|
||||
expected_license="AGPL-3.0-or-later"
|
||||
expected_group="Applications/File"
|
||||
expected_payload="/etc/default/silo
|
||||
/usr/bin/silo
|
||||
/usr/lib/systemd/system/silo.service
|
||||
/usr/lib/sysusers.d/silo.conf
|
||||
/usr/share/doc/silo/LICENSE
|
||||
/usr/share/doc/silo/NOTICE"
|
||||
repository="${GH_REPO:-pgsty/silo}"
|
||||
container="${DNFUPDATE_CONTAINER:-dnfupdate}"
|
||||
upload=false
|
||||
release_tag=""
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: buildscripts/sign-release-rpms.sh RELEASE.TAG [--upload] [--repo OWNER/REPO] [--container NAME]
|
||||
|
||||
Downloads the two unsigned RPMs from a Draft GitHub Release, signs them with
|
||||
the expected Pigsty key in the local dnfupdate container, verifies the result,
|
||||
and regenerates their .sha256sum files. Nothing is uploaded unless --upload is
|
||||
provided.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--upload)
|
||||
upload=true
|
||||
;;
|
||||
--repo)
|
||||
shift
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo "--repo requires OWNER/REPO" >&2
|
||||
exit 1
|
||||
fi
|
||||
repository="$1"
|
||||
;;
|
||||
--container)
|
||||
shift
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo "--container requires a name" >&2
|
||||
exit 1
|
||||
fi
|
||||
container="$1"
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown option: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [ -n "${release_tag}" ]; then
|
||||
echo "Only one release tag may be specified" >&2
|
||||
exit 1
|
||||
fi
|
||||
release_tag="$1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -z "${release_tag}" ]; then
|
||||
usage >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for command in docker gh; do
|
||||
if ! command -v "${command}" >/dev/null 2>&1; then
|
||||
echo "${command} is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
version_hyphen="${release_tag#RELEASE.}"
|
||||
package_version="$(printf '%s\n' "${version_hyphen}" | sed -E \
|
||||
's/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2})-([0-9]{2})-([0-9]{2})Z$/\1\2\3\4\5\6.0.0/')"
|
||||
if [ "${package_version}" = "${version_hyphen}" ]; then
|
||||
echo "Invalid release tag: ${release_tag}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(gh release view "${release_tag}" --repo "${repository}" --json isDraft --jq .isDraft)" != "true" ]; then
|
||||
echo "Refusing to sign: ${release_tag} is not a Draft release" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(docker inspect --format '{{.State.Running}}' "${container}" 2>/dev/null || true)" != "true" ]; then
|
||||
echo "Signing container is not running: ${container}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
secret_fingerprints="$(docker exec "${container}" \
|
||||
gpg --batch --with-colons --list-secret-keys 2>/dev/null |
|
||||
awk -F: '$1 == "fpr" { print toupper($10) }')"
|
||||
if ! printf '%s\n' "${secret_fingerprints}" | grep -Fxq "${expected_fingerprint}"; then
|
||||
echo "Expected signing key is not available in ${container}: ${expected_fingerprint}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_dir="$(cd "${script_dir}/.." && pwd)"
|
||||
work_root="${SIGN_WORKDIR:-${repo_dir}/.release-sign}"
|
||||
mkdir -p "${work_root}"
|
||||
work_dir="$(mktemp -d "${work_root}/${release_tag}.XXXXXX")"
|
||||
unsigned_dir="${work_dir}/unsigned"
|
||||
signed_dir="${work_dir}/signed"
|
||||
mkdir -p "${unsigned_dir}" "${signed_dir}"
|
||||
chmod 700 "${work_dir}" "${unsigned_dir}" "${signed_dir}"
|
||||
|
||||
rpm_files=(
|
||||
"silo-${package_version}-${expected_release}.x86_64.rpm"
|
||||
"silo-${package_version}-${expected_release}.aarch64.rpm"
|
||||
)
|
||||
|
||||
download_patterns=()
|
||||
for rpm_file in "${rpm_files[@]}"; do
|
||||
download_patterns+=(--pattern "${rpm_file}" --pattern "${rpm_file}.sha256sum")
|
||||
done
|
||||
|
||||
echo "Downloading RPMs from Draft release ${repository}@${release_tag}"
|
||||
gh release download "${release_tag}" --repo "${repository}" \
|
||||
--dir "${unsigned_dir}" "${download_patterns[@]}"
|
||||
|
||||
sha256_digest() {
|
||||
local file="$1"
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum "${file}" | awk '{print $1}'
|
||||
else
|
||||
shasum -a 256 "${file}" | awk '{print $1}'
|
||||
fi
|
||||
}
|
||||
|
||||
for rpm_file in "${rpm_files[@]}"; do
|
||||
rpm_path="${unsigned_dir}/${rpm_file}"
|
||||
checksum_path="${rpm_path}.sha256sum"
|
||||
test -s "${rpm_path}"
|
||||
test -s "${checksum_path}"
|
||||
|
||||
actual_line="$(sha256_digest "${rpm_path}") ${rpm_file}"
|
||||
published_line="$(tr -d '\n' < "${checksum_path}")"
|
||||
if [ "${actual_line}" != "${published_line}" ]; then
|
||||
echo "Checksum mismatch for ${rpm_file}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
safe_tag="$(printf '%s' "${release_tag}" | tr -c 'A-Za-z0-9._-' '_')"
|
||||
container_dir="/tmp/silo-sign-${safe_tag}-$$"
|
||||
docker exec "${container}" mkdir -p "${container_dir}"
|
||||
|
||||
cleanup_container() {
|
||||
local rpm_file
|
||||
for rpm_file in "${rpm_files[@]}"; do
|
||||
docker exec "${container}" rm -f "${container_dir}/${rpm_file}" >/dev/null 2>&1 || true
|
||||
done
|
||||
docker exec "${container}" rmdir "${container_dir}" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup_container EXIT
|
||||
|
||||
assert_rpm_tag() {
|
||||
local rpm_path="$1"
|
||||
local tag="$2"
|
||||
local expected="$3"
|
||||
local actual
|
||||
|
||||
actual="$(docker exec "${container}" rpm -qp --queryformat "%{${tag}}" "${rpm_path}")"
|
||||
if [ "${actual}" != "${expected}" ]; then
|
||||
echo "Unexpected RPM ${tag}: ${actual}" >&2
|
||||
echo "Expected RPM ${tag}: ${expected}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
for rpm_file in "${rpm_files[@]}"; do
|
||||
case "${rpm_file}" in
|
||||
*.x86_64.rpm)
|
||||
expected_arch="x86_64"
|
||||
;;
|
||||
*.aarch64.rpm)
|
||||
expected_arch="aarch64"
|
||||
;;
|
||||
*)
|
||||
echo "Unexpected RPM filename: ${rpm_file}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Signing ${rpm_file} with ${expected_fingerprint}"
|
||||
docker cp "${unsigned_dir}/${rpm_file}" "${container}:${container_dir}/${rpm_file}" >/dev/null
|
||||
container_rpm="${container_dir}/${rpm_file}"
|
||||
|
||||
assert_rpm_tag "${container_rpm}" NAME silo
|
||||
assert_rpm_tag "${container_rpm}" VERSION "${package_version}"
|
||||
assert_rpm_tag "${container_rpm}" RELEASE "${expected_release}"
|
||||
assert_rpm_tag "${container_rpm}" ARCH "${expected_arch}"
|
||||
assert_rpm_tag "${container_rpm}" VENDOR "${expected_vendor}"
|
||||
assert_rpm_tag "${container_rpm}" PACKAGER "${expected_packager}"
|
||||
assert_rpm_tag "${container_rpm}" URL "${expected_url}"
|
||||
assert_rpm_tag "${container_rpm}" LICENSE "${expected_license}"
|
||||
assert_rpm_tag "${container_rpm}" GROUP "${expected_group}"
|
||||
assert_rpm_tag "${container_rpm}" SUMMARY "${expected_summary}"
|
||||
assert_rpm_tag "${container_rpm}" DESCRIPTION "${expected_description}"
|
||||
|
||||
rpm_payload="$(docker exec "${container}" rpm -qpl "${container_rpm}")"
|
||||
if [ "${rpm_payload}" != "${expected_payload}" ]; then
|
||||
echo "Unexpected RPM payload for ${rpm_file}:" >&2
|
||||
printf '%s\n' "${rpm_payload}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker exec "${container}" rpmsign \
|
||||
--define "_gpg_name ${expected_fingerprint}" \
|
||||
--addsign "${container_rpm}"
|
||||
|
||||
signature_output="$(docker exec "${container}" rpmkeys --checksig --verbose "${container_rpm}")"
|
||||
printf '%s\n' "${signature_output}"
|
||||
if ! printf '%s\n' "${signature_output}" | tr '[:upper:]' '[:lower:]' | grep -q 'key id b9bd8b20: ok'; then
|
||||
echo "Signature verification failed for ${rpm_file}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker cp "${container}:${container_dir}/${rpm_file}" "${signed_dir}/${rpm_file}" >/dev/null
|
||||
signed_digest="$(sha256_digest "${signed_dir}/${rpm_file}")"
|
||||
printf '%s %s' "${signed_digest}" "${rpm_file}" > "${signed_dir}/${rpm_file}.sha256sum"
|
||||
|
||||
docker exec "${container}" rpm -qp --queryformat \
|
||||
$'Name: %{NAME}\nVersion: %{VERSION}-%{RELEASE}\nArch: %{ARCH}\nVendor: %{VENDOR}\nPackager: %{PACKAGER}\nURL: %{URL}\n' \
|
||||
"${container_rpm}"
|
||||
echo "SHA256: ${signed_digest}"
|
||||
done
|
||||
|
||||
if [ "${upload}" = true ]; then
|
||||
upload_files=()
|
||||
for rpm_file in "${rpm_files[@]}"; do
|
||||
upload_files+=("${signed_dir}/${rpm_file}" "${signed_dir}/${rpm_file}.sha256sum")
|
||||
done
|
||||
|
||||
echo "Replacing RPMs in Draft release ${release_tag}"
|
||||
gh release upload "${release_tag}" --repo "${repository}" --clobber "${upload_files[@]}"
|
||||
|
||||
for rpm_file in "${rpm_files[@]}"; do
|
||||
for asset in "${rpm_file}" "${rpm_file}.sha256sum"; do
|
||||
local_digest="sha256:$(sha256_digest "${signed_dir}/${asset}")"
|
||||
remote_digest=""
|
||||
for attempt in 1 2 3 4 5; do
|
||||
remote_digest="$(gh release view "${release_tag}" --repo "${repository}" --json assets \
|
||||
--jq ".assets[] | select(.name == \"${asset}\") | .digest")"
|
||||
if [ "${local_digest}" = "${remote_digest}" ]; then
|
||||
break
|
||||
fi
|
||||
if [ "${attempt}" -lt 5 ]; then
|
||||
sleep 2
|
||||
fi
|
||||
done
|
||||
if [ "${local_digest}" != "${remote_digest}" ]; then
|
||||
echo "GitHub asset digest mismatch for ${asset}" >&2
|
||||
echo "Local: ${local_digest}" >&2
|
||||
echo "Remote: ${remote_digest}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Verified GitHub asset: ${asset} ${remote_digest}"
|
||||
done
|
||||
done
|
||||
else
|
||||
echo
|
||||
echo "Signed RPMs are ready for review in: ${signed_dir}"
|
||||
echo "Re-run with --upload to replace the RPM assets in the Draft release."
|
||||
fi
|
||||
@@ -5,16 +5,16 @@ if [ -n "$TEST_DEBUG" ]; then
|
||||
fi
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
SILO_CONFIG_DIR="$WORK_DIR/.silo"
|
||||
SILO=("$PWD/silo" --config-dir "$SILO_CONFIG_DIR" server)
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
if [ ! -x "$PWD/silo" ]; then
|
||||
echo "Silo executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
if [ ! -x "$PWD/silo" ]; then
|
||||
echo "Silo executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -30,9 +30,9 @@ catch() {
|
||||
echo "error on line $1"
|
||||
fi
|
||||
|
||||
echo "Cleaning up instances of MinIO"
|
||||
pkill minio || true
|
||||
pkill -9 minio || true
|
||||
echo "Cleaning up instances of Silo"
|
||||
pkill silo || true
|
||||
pkill -9 silo || true
|
||||
purge "$WORK_DIR"
|
||||
if [ $# -ne 0 ]; then
|
||||
exit $#
|
||||
@@ -70,31 +70,20 @@ function send_put_object_request() {
|
||||
return 0
|
||||
}
|
||||
|
||||
function test_minio_with_timeout() {
|
||||
function test_silo_with_timeout() {
|
||||
start_port=$1
|
||||
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
export MC_HOST_minio="http://minio:minio123@127.0.0.1:${start_port}/"
|
||||
export MINIO_ROOT_USER=silo
|
||||
export MINIO_ROOT_PASSWORD=silo123
|
||||
export MC_HOST_silo="http://silo:silo123@127.0.0.1:${start_port}/"
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
mkdir ${WORK_DIR}
|
||||
C_PWD=${PWD}
|
||||
if [ ! -x "$PWD/mc" ]; then
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "$C_PWD/mc")
|
||||
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-mcli.sh" "$PWD/mc"
|
||||
fi
|
||||
|
||||
"${MINIO[@]}" --address ":$start_port" --read-header-timeout ${srv_hdr_timeout}s --idle-timeout ${srv_idle_timeout}s "${WORK_DIR}/disk/" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
"${SILO[@]}" --address ":$start_port" --read-header-timeout ${srv_hdr_timeout}s --idle-timeout ${srv_idle_timeout}s "${WORK_DIR}/disk/" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
pid=$!
|
||||
disown $pid
|
||||
sleep 1
|
||||
@@ -109,20 +98,20 @@ function test_minio_with_timeout() {
|
||||
|
||||
set -e
|
||||
|
||||
"${PWD}/mc" mb minio/testbucket
|
||||
"${PWD}/mc" anonymous set public minio/testbucket
|
||||
"${PWD}/mc" mb silo/testbucket
|
||||
"${PWD}/mc" anonymous set public silo/testbucket
|
||||
|
||||
# slow header writing
|
||||
send_put_object_request 20 0 && exit -1
|
||||
"${PWD}/mc" stat minio/testbucket/testobject && exit -1
|
||||
"${PWD}/mc" stat silo/testbucket/testobject && exit -1
|
||||
|
||||
# quick header write and slow bodywrite
|
||||
send_put_object_request 0 40 && exit -1
|
||||
"${PWD}/mc" stat minio/testbucket/testobject && exit -1
|
||||
"${PWD}/mc" stat silo/testbucket/testobject && exit -1
|
||||
|
||||
# quick header and body write
|
||||
send_put_object_request 1 1 || exit -1
|
||||
"${PWD}/mc" stat minio/testbucket/testobject || exit -1
|
||||
"${PWD}/mc" stat silo/testbucket/testobject || exit -1
|
||||
}
|
||||
|
||||
function main() {
|
||||
@@ -131,7 +120,7 @@ function main() {
|
||||
export srv_idle_timeout=5
|
||||
export -f gen_put_request
|
||||
|
||||
test_minio_with_timeout ${start_port}
|
||||
test_silo_with_timeout ${start_port}
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: minio/minio:${MINIO_VERSION}
|
||||
command: server http://minio{1...4}/data{1...3}
|
||||
env_file:
|
||||
- ./minio.env
|
||||
expose:
|
||||
- "9000"
|
||||
- "9001"
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
minio1:
|
||||
<<: *minio-common
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- data1-1:/data1
|
||||
- data1-2:/data2
|
||||
- data1-3:/data3
|
||||
|
||||
minio2:
|
||||
<<: *minio-common
|
||||
hostname: minio2
|
||||
volumes:
|
||||
- data2-1:/data1
|
||||
- data2-2:/data2
|
||||
- data2-3:/data3
|
||||
|
||||
minio3:
|
||||
<<: *minio-common
|
||||
hostname: minio3
|
||||
volumes:
|
||||
- data3-1:/data1
|
||||
- data3-2:/data2
|
||||
- data3-3:/data3
|
||||
|
||||
minio4:
|
||||
<<: *minio-common
|
||||
hostname: minio4
|
||||
volumes:
|
||||
- data4-1:/data1
|
||||
- data4-2:/data2
|
||||
- data4-3:/data3
|
||||
|
||||
nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- minio1
|
||||
- minio2
|
||||
- minio3
|
||||
- minio4
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
data1-1:
|
||||
data1-2:
|
||||
data1-3:
|
||||
data2-1:
|
||||
data2-2:
|
||||
data2-3:
|
||||
data3-1:
|
||||
data3-2:
|
||||
data3-3:
|
||||
data4-1:
|
||||
data4-2:
|
||||
data4-3:
|
||||
@@ -1,3 +0,0 @@
|
||||
MINIO_ACCESS_KEY=minioadmin
|
||||
MINIO_SECRET_KEY=minioadmin
|
||||
MINIO_BROWSER=off
|
||||
@@ -1,68 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server minio1:9000;
|
||||
server minio2:9000;
|
||||
server minio3:9000;
|
||||
server minio4:9000;
|
||||
}
|
||||
|
||||
# main minio
|
||||
server {
|
||||
listen 9000;
|
||||
listen [::]:9000;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Asserts that every binary GoReleaser produced is stamped by the Go toolchain
|
||||
# as built from this exact commit with a clean working tree. A stray untracked
|
||||
# file (for example an un-ignored dist/) silently turns every release binary
|
||||
# into a "+dirty" pseudo-version, which destroys the link between a published
|
||||
# artifact and its tag. Catch that here instead of after publishing.
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_dir="$(cd "${script_dir}/.." && pwd)"
|
||||
dist_dir="${DIST_DIR:-${repo_dir}/dist}"
|
||||
expected_count="${EXPECTED_BINARY_COUNT:-6}"
|
||||
|
||||
if ! command -v go >/dev/null 2>&1; then
|
||||
echo "go is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${dist_dir}" ]; then
|
||||
echo "Missing GoReleaser dist directory: ${dist_dir}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
revision="$(git -C "${repo_dir}" rev-parse HEAD)"
|
||||
|
||||
count=0
|
||||
while IFS= read -r binary; do
|
||||
count=$((count + 1))
|
||||
info="$(go version -m "${binary}")"
|
||||
|
||||
if ! grep -qF "vcs.revision=${revision}" <<< "${info}"; then
|
||||
echo "Unexpected vcs.revision in ${binary} (expected ${revision})" >&2
|
||||
grep -F 'vcs.' <<< "${info}" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -qF 'vcs.modified=false' <<< "${info}"; then
|
||||
echo "Binary was built from a dirty working tree: ${binary}" >&2
|
||||
grep -F 'vcs.' <<< "${info}" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
done < <(find "${dist_dir}" -maxdepth 2 -type f \( -name 'silo' -o -name 'silo.exe' \) | sort)
|
||||
|
||||
if [ "${count}" -ne "${expected_count}" ]; then
|
||||
echo "Expected ${expected_count} release binaries, found ${count}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Verified ${count} binaries built from ${revision} with a clean tree"
|
||||
@@ -5,8 +5,8 @@ set -e
|
||||
set -E
|
||||
set -o pipefail
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
if [ ! -x "$PWD/silo" ]; then
|
||||
echo "Silo executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -15,196 +15,196 @@ WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
export MINT_MODE=core
|
||||
export MINT_DATA_DIR="$WORK_DIR/data"
|
||||
export SERVER_ENDPOINT="127.0.0.1:9000"
|
||||
export MC_HOST_verify="http://minio:minio123@${SERVER_ENDPOINT}/"
|
||||
export MC_HOST_verify_ipv6="http://minio:minio123@[::1]:9000/"
|
||||
export ACCESS_KEY="minio"
|
||||
export SECRET_KEY="minio123"
|
||||
export MC_HOST_verify="http://silo:silo123@${SERVER_ENDPOINT}/"
|
||||
export MC_HOST_verify_ipv6="http://silo:silo123@[::1]:9000/"
|
||||
export ACCESS_KEY="silo"
|
||||
export SECRET_KEY="silo123"
|
||||
export ENABLE_HTTPS=0
|
||||
export GO111MODULE=on
|
||||
export GOGC=25
|
||||
export ENABLE_ADMIN=1
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR")
|
||||
SILO_CONFIG_DIR="$WORK_DIR/.silo"
|
||||
SILO=("$PWD/silo" --config-dir "$SILO_CONFIG_DIR")
|
||||
|
||||
FILE_1_MB="$MINT_DATA_DIR/datafile-1-MB"
|
||||
FILE_65_MB="$MINT_DATA_DIR/datafile-65-MB"
|
||||
|
||||
FUNCTIONAL_TESTS="$WORK_DIR/functional-tests.sh"
|
||||
|
||||
function start_minio_fs() {
|
||||
function start_silo_fs() {
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
"${MINIO[@]}" server "${WORK_DIR}/fs-disk" >"$WORK_DIR/fs-minio.log" 2>&1 &
|
||||
"${SILO[@]}" server "${WORK_DIR}/fs-disk" >"$WORK_DIR/fs-silo.log" 2>&1 &
|
||||
|
||||
"${WORK_DIR}/mc" ready verify
|
||||
}
|
||||
|
||||
function start_minio_erasure() {
|
||||
"${MINIO[@]}" server "${WORK_DIR}/erasure-disk1" "${WORK_DIR}/erasure-disk2" "${WORK_DIR}/erasure-disk3" "${WORK_DIR}/erasure-disk4" >"$WORK_DIR/erasure-minio.log" 2>&1 &
|
||||
function start_silo_erasure() {
|
||||
"${SILO[@]}" server "${WORK_DIR}/erasure-disk1" "${WORK_DIR}/erasure-disk2" "${WORK_DIR}/erasure-disk3" "${WORK_DIR}/erasure-disk4" >"$WORK_DIR/erasure-silo.log" 2>&1 &
|
||||
|
||||
"${WORK_DIR}/mc" ready verify
|
||||
}
|
||||
|
||||
function start_minio_erasure_sets() {
|
||||
function start_silo_erasure_sets() {
|
||||
export MINIO_ENDPOINTS="${WORK_DIR}/erasure-disk-sets{1...32}"
|
||||
"${MINIO[@]}" server >"$WORK_DIR/erasure-minio-sets.log" 2>&1 &
|
||||
"${SILO[@]}" server >"$WORK_DIR/erasure-silo-sets.log" 2>&1 &
|
||||
|
||||
"${WORK_DIR}/mc" ready verify
|
||||
}
|
||||
|
||||
function start_minio_pool_erasure_sets() {
|
||||
function start_silo_pool_erasure_sets() {
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
export MINIO_ENDPOINTS="http://127.0.0.1:9000${WORK_DIR}/pool-disk-sets{1...4} http://127.0.0.1:9001${WORK_DIR}/pool-disk-sets{5...8}"
|
||||
"${MINIO[@]}" server --address ":9000" >"$WORK_DIR/pool-minio-9000.log" 2>&1 &
|
||||
"${MINIO[@]}" server --address ":9001" >"$WORK_DIR/pool-minio-9001.log" 2>&1 &
|
||||
"${SILO[@]}" server --address ":9000" >"$WORK_DIR/pool-silo-9000.log" 2>&1 &
|
||||
"${SILO[@]}" server --address ":9001" >"$WORK_DIR/pool-silo-9001.log" 2>&1 &
|
||||
|
||||
"${WORK_DIR}/mc" ready verify
|
||||
}
|
||||
|
||||
function start_minio_pool_erasure_sets_ipv6() {
|
||||
function start_silo_pool_erasure_sets_ipv6() {
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
export MINIO_ENDPOINTS="http://[::1]:9000${WORK_DIR}/pool-disk-sets-ipv6{1...4} http://[::1]:9001${WORK_DIR}/pool-disk-sets-ipv6{5...8}"
|
||||
"${MINIO[@]}" server --address="[::1]:9000" >"$WORK_DIR/pool-minio-ipv6-9000.log" 2>&1 &
|
||||
"${MINIO[@]}" server --address="[::1]:9001" >"$WORK_DIR/pool-minio-ipv6-9001.log" 2>&1 &
|
||||
"${SILO[@]}" server --address="[::1]:9000" >"$WORK_DIR/pool-silo-ipv6-9000.log" 2>&1 &
|
||||
"${SILO[@]}" server --address="[::1]:9001" >"$WORK_DIR/pool-silo-ipv6-9001.log" 2>&1 &
|
||||
|
||||
"${WORK_DIR}/mc" ready verify_ipv6
|
||||
}
|
||||
|
||||
function start_minio_dist_erasure() {
|
||||
function start_silo_dist_erasure() {
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
export MINIO_ENDPOINTS="http://127.0.0.1:9000${WORK_DIR}/dist-disk1 http://127.0.0.1:9001${WORK_DIR}/dist-disk2 http://127.0.0.1:9002${WORK_DIR}/dist-disk3 http://127.0.0.1:9003${WORK_DIR}/dist-disk4"
|
||||
for i in $(seq 0 3); do
|
||||
"${MINIO[@]}" server --address ":900${i}" >"$WORK_DIR/dist-minio-900${i}.log" 2>&1 &
|
||||
"${SILO[@]}" server --address ":900${i}" >"$WORK_DIR/dist-silo-900${i}.log" 2>&1 &
|
||||
done
|
||||
|
||||
"${WORK_DIR}/mc" ready verify
|
||||
}
|
||||
|
||||
function run_test_fs() {
|
||||
start_minio_fs
|
||||
start_silo_fs
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
pkill silo
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
cat "$WORK_DIR/fs-minio.log"
|
||||
cat "$WORK_DIR/fs-silo.log"
|
||||
fi
|
||||
rm -f "$WORK_DIR/fs-minio.log"
|
||||
rm -f "$WORK_DIR/fs-silo.log"
|
||||
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function run_test_erasure_sets() {
|
||||
start_minio_erasure_sets
|
||||
start_silo_erasure_sets
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
pkill silo
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
cat "$WORK_DIR/erasure-minio-sets.log"
|
||||
cat "$WORK_DIR/erasure-silo-sets.log"
|
||||
fi
|
||||
rm -f "$WORK_DIR/erasure-minio-sets.log"
|
||||
rm -f "$WORK_DIR/erasure-silo-sets.log"
|
||||
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function run_test_pool_erasure_sets() {
|
||||
start_minio_pool_erasure_sets
|
||||
start_silo_pool_erasure_sets
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
pkill silo
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
for i in $(seq 0 1); do
|
||||
echo "server$i log:"
|
||||
cat "$WORK_DIR/pool-minio-900$i.log"
|
||||
cat "$WORK_DIR/pool-silo-900$i.log"
|
||||
done
|
||||
fi
|
||||
|
||||
for i in $(seq 0 1); do
|
||||
rm -f "$WORK_DIR/pool-minio-900$i.log"
|
||||
rm -f "$WORK_DIR/pool-silo-900$i.log"
|
||||
done
|
||||
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function run_test_pool_erasure_sets_ipv6() {
|
||||
start_minio_pool_erasure_sets_ipv6
|
||||
start_silo_pool_erasure_sets_ipv6
|
||||
|
||||
export SERVER_ENDPOINT="[::1]:9000"
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
pkill silo
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
for i in $(seq 0 1); do
|
||||
echo "server$i log:"
|
||||
cat "$WORK_DIR/pool-minio-ipv6-900$i.log"
|
||||
cat "$WORK_DIR/pool-silo-ipv6-900$i.log"
|
||||
done
|
||||
fi
|
||||
|
||||
for i in $(seq 0 1); do
|
||||
rm -f "$WORK_DIR/pool-minio-ipv6-900$i.log"
|
||||
rm -f "$WORK_DIR/pool-silo-ipv6-900$i.log"
|
||||
done
|
||||
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function run_test_erasure() {
|
||||
start_minio_erasure
|
||||
start_silo_erasure
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
pkill silo
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
cat "$WORK_DIR/erasure-minio.log"
|
||||
cat "$WORK_DIR/erasure-silo.log"
|
||||
fi
|
||||
rm -f "$WORK_DIR/erasure-minio.log"
|
||||
rm -f "$WORK_DIR/erasure-silo.log"
|
||||
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function run_test_dist_erasure() {
|
||||
start_minio_dist_erasure
|
||||
start_silo_dist_erasure
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
pkill silo
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
echo "server1 log:"
|
||||
cat "$WORK_DIR/dist-minio-9000.log"
|
||||
cat "$WORK_DIR/dist-silo-9000.log"
|
||||
echo "server2 log:"
|
||||
cat "$WORK_DIR/dist-minio-9001.log"
|
||||
cat "$WORK_DIR/dist-silo-9001.log"
|
||||
echo "server3 log:"
|
||||
cat "$WORK_DIR/dist-minio-9002.log"
|
||||
cat "$WORK_DIR/dist-silo-9002.log"
|
||||
echo "server4 log:"
|
||||
cat "$WORK_DIR/dist-minio-9003.log"
|
||||
cat "$WORK_DIR/dist-silo-9003.log"
|
||||
fi
|
||||
|
||||
rm -f "$WORK_DIR/dist-minio-9000.log" "$WORK_DIR/dist-minio-9001.log" "$WORK_DIR/dist-minio-9002.log" "$WORK_DIR/dist-minio-9003.log"
|
||||
rm -f "$WORK_DIR/dist-silo-9000.log" "$WORK_DIR/dist-silo-9001.log" "$WORK_DIR/dist-silo-9002.log" "$WORK_DIR/dist-silo-9003.log"
|
||||
|
||||
return "$rv"
|
||||
}
|
||||
@@ -216,31 +216,21 @@ function purge() {
|
||||
function __init__() {
|
||||
echo "Initializing environment"
|
||||
mkdir -p "$WORK_DIR"
|
||||
mkdir -p "$MINIO_CONFIG_DIR"
|
||||
mkdir -p "$SILO_CONFIG_DIR"
|
||||
mkdir -p "$MINT_DATA_DIR"
|
||||
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "${WORK_DIR}/mc")
|
||||
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-mcli.sh" "${WORK_DIR}/mc"
|
||||
|
||||
shred -n 1 -s 1M - 1>"$FILE_1_MB" 2>/dev/null
|
||||
shred -n 1 -s 65M - 1>"$FILE_65_MB" 2>/dev/null
|
||||
|
||||
## version is purposefully set to '3' for minio to migrate configuration file
|
||||
echo '{"version": "3", "credential": {"accessKey": "minio", "secretKey": "minio123"}, "region": "us-east-1"}' >"$MINIO_CONFIG_DIR/config.json"
|
||||
echo '{"version": "3", "credential": {"accessKey": "silo", "secretKey": "silo123"}, "region": "us-east-1"}' >"$SILO_CONFIG_DIR/config.json"
|
||||
|
||||
if ! wget -q -O "$FUNCTIONAL_TESTS" https://raw.githubusercontent.com/minio/mc/master/functional-tests.sh; then
|
||||
echo "failed to download https://raw.githubusercontent.com/minio/mc/master/functional-tests.sh"
|
||||
exit 1
|
||||
fi
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-verified-fixture.sh" \
|
||||
https://raw.githubusercontent.com/pgsty/mc/4c4dcc4b55baf238cd0c81030d77945b3828f157/functional-tests.sh \
|
||||
9b98c8152b294d567b9bc732869226dd4f65d0f4d84092dc066a900a66a9e22c \
|
||||
"$FUNCTIONAL_TESTS"
|
||||
|
||||
sed -i 's|-sS|-sSg|g' "$FUNCTIONAL_TESTS"
|
||||
chmod a+x "$FUNCTIONAL_TESTS"
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
set -E
|
||||
set -o pipefail
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
if [ ! -x "$PWD/silo" ]; then
|
||||
echo "Silo executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
SILO_CONFIG_DIR="$WORK_DIR/.silo"
|
||||
SILO=("$PWD/silo" --config-dir "$SILO_CONFIG_DIR" server)
|
||||
|
||||
function start_minio_3_node() {
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
function start_silo_3_node() {
|
||||
export MINIO_ROOT_USER=silo
|
||||
export MINIO_ROOT_PASSWORD=silo123
|
||||
export MINIO_ERASURE_SET_DRIVE_COUNT=6
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
@@ -25,26 +25,26 @@ function start_minio_3_node() {
|
||||
args="$args http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/1/ http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/2/ http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/3/ http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/4/ http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/5/ http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/6/"
|
||||
done
|
||||
|
||||
"${MINIO[@]}" --address ":$((start_port + 1))" $args >"${WORK_DIR}/dist-minio-server1.log" 2>&1 &
|
||||
"${SILO[@]}" --address ":$((start_port + 1))" $args >"${WORK_DIR}/dist-silo-server1.log" 2>&1 &
|
||||
pid1=$!
|
||||
disown ${pid1}
|
||||
|
||||
"${MINIO[@]}" --address ":$((start_port + 2))" $args >"${WORK_DIR}/dist-minio-server2.log" 2>&1 &
|
||||
"${SILO[@]}" --address ":$((start_port + 2))" $args >"${WORK_DIR}/dist-silo-server2.log" 2>&1 &
|
||||
pid2=$!
|
||||
disown $pid2
|
||||
|
||||
"${MINIO[@]}" --address ":$((start_port + 3))" $args >"${WORK_DIR}/dist-minio-server3.log" 2>&1 &
|
||||
"${SILO[@]}" --address ":$((start_port + 3))" $args >"${WORK_DIR}/dist-silo-server3.log" 2>&1 &
|
||||
pid3=$!
|
||||
disown $pid3
|
||||
|
||||
export MC_HOST_myminio="http://minio:minio123@127.0.0.1:$((start_port + 1))"
|
||||
export MC_HOST_mysilo="http://silo:silo123@127.0.0.1:$((start_port + 1))"
|
||||
|
||||
timeout 15m /tmp/mc ready myminio || fail
|
||||
timeout 15m /tmp/mc ready mysilo || fail
|
||||
|
||||
# Wait for all drives to be online and formatted
|
||||
while [ $(/tmp/mc admin info --json myminio | jq '.info.servers[].drives[].state | select(. != "ok")' | wc -l) -gt 0 ]; do sleep 1; done
|
||||
while [ $(/tmp/mc admin info --json mysilo | jq '.info.servers[].drives[].state | select(. != "ok")' | wc -l) -gt 0 ]; do sleep 1; done
|
||||
# Wait for all drives to be healed
|
||||
while [ $(/tmp/mc admin info --json myminio | jq '.info.servers[].drives[].healing | select(. != null) | select(. == true)' | wc -l) -gt 0 ]; do sleep 1; done
|
||||
while [ $(/tmp/mc admin info --json mysilo | jq '.info.servers[].drives[].healing | select(. != null) | select(. == true)' | wc -l) -gt 0 ]; do sleep 1; done
|
||||
|
||||
# Wait for Status: in MinIO output
|
||||
while true; do
|
||||
@@ -66,26 +66,26 @@ function start_minio_3_node() {
|
||||
done
|
||||
|
||||
if ! ps -p $pid1 1>&2 >/dev/null; then
|
||||
echo "minio-server-1 is not running." && fail
|
||||
echo "silo-server-1 is not running." && fail
|
||||
fi
|
||||
|
||||
if ! ps -p $pid2 1>&2 >/dev/null; then
|
||||
echo "minio-server-2 is not running." && fail
|
||||
echo "silo-server-2 is not running." && fail
|
||||
fi
|
||||
|
||||
if ! ps -p $pid3 1>&2 >/dev/null; then
|
||||
echo "minio-server-3 is not running." && fail
|
||||
echo "silo-server-3 is not running." && fail
|
||||
fi
|
||||
|
||||
if ! pkill minio; then
|
||||
if ! pkill silo; then
|
||||
fail
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
if pgrep minio; then
|
||||
if pgrep silo; then
|
||||
# forcibly killing, to proceed further properly.
|
||||
if ! pkill -9 minio; then
|
||||
echo "no minio process running anymore, proceed."
|
||||
if ! pkill -9 silo; then
|
||||
echo "no Silo process running anymore, proceed."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -93,7 +93,7 @@ function start_minio_3_node() {
|
||||
function fail() {
|
||||
for i in $(seq 1 3); do
|
||||
echo "server$i log:"
|
||||
cat "${WORK_DIR}/dist-minio-server$i.log"
|
||||
cat "${WORK_DIR}/dist-silo-server$i.log"
|
||||
done
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
@@ -101,7 +101,7 @@ function fail() {
|
||||
}
|
||||
|
||||
function check_online() {
|
||||
if ! grep -q 'API:' ${WORK_DIR}/dist-minio-*.log; then
|
||||
if ! grep -q 'API:' ${WORK_DIR}/dist-silo-*.log; then
|
||||
echo "1"
|
||||
fi
|
||||
}
|
||||
@@ -113,19 +113,18 @@ function purge() {
|
||||
function __init__() {
|
||||
echo "Initializing environment"
|
||||
mkdir -p "$WORK_DIR"
|
||||
mkdir -p "$MINIO_CONFIG_DIR"
|
||||
mkdir -p "$SILO_CONFIG_DIR"
|
||||
|
||||
## version is purposefully set to '3' for minio to migrate configuration file
|
||||
echo '{"version": "3", "credential": {"accessKey": "minio", "secretKey": "minio123"}, "region": "us-east-1"}' >"$MINIO_CONFIG_DIR/config.json"
|
||||
echo '{"version": "3", "credential": {"accessKey": "silo", "secretKey": "silo123"}, "region": "us-east-1"}' >"$SILO_CONFIG_DIR/config.json"
|
||||
|
||||
if [ ! -f /tmp/mc ]; then
|
||||
wget --quiet -O /tmp/mc https://dl.minio.io/client/mc/release/linux-amd64/mc &&
|
||||
chmod +x /tmp/mc
|
||||
"$(git rev-parse --show-toplevel)/buildscripts/install-mcli.sh" /tmp/mc
|
||||
fi
|
||||
}
|
||||
|
||||
function perform_test() {
|
||||
start_minio_3_node $2
|
||||
start_silo_3_node $2
|
||||
|
||||
echo "Testing Distributed Erasure setup healing of drives"
|
||||
echo "Remove the contents of the disks belonging to '${1}' erasure set"
|
||||
@@ -133,7 +132,7 @@ function perform_test() {
|
||||
rm -rf ${WORK_DIR}/${1}/*/
|
||||
|
||||
set -x
|
||||
start_minio_3_node $2
|
||||
start_silo_3_node $2
|
||||
}
|
||||
|
||||
function main() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user