mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-28 00:58:59 +00:00
Merge branch 'main' of github.com:rustfs/s3-rustfs into feature/logger
# Conflicts: # Cargo.toml
This commit is contained in:
@@ -9,6 +9,9 @@ inputs:
|
||||
cache-shared-key:
|
||||
required: true
|
||||
default: ""
|
||||
cache-save-if:
|
||||
required: true
|
||||
default: true
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
@@ -39,7 +42,9 @@ runs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
cache-all-crates: true
|
||||
shared-key: ${{ inputs.cache-shared-key }}
|
||||
save-if: ${{ inputs.cache-save-if }}
|
||||
|
||||
- uses: mlugg/setup-zig@v1
|
||||
- uses: taiki-e/install-action@cargo-zigbuild
|
||||
|
||||
+130
-7
@@ -33,20 +33,143 @@ jobs:
|
||||
develop:
|
||||
needs: skip-check
|
||||
if: needs.skip-check.outputs.should_skip != 'true'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/setup
|
||||
with:
|
||||
cache-shared-key: "develop"
|
||||
|
||||
- name: Install s3s-e2e
|
||||
run: |
|
||||
cargo install s3s-e2e --git https://github.com/Nugine/s3s.git
|
||||
s3s-e2e --version
|
||||
|
||||
- name: Format
|
||||
run: cargo fmt --all --check
|
||||
|
||||
- name: Lint
|
||||
run: cargo check
|
||||
# run: cargo clippy
|
||||
# run: cargo clippy -- -D warnings
|
||||
run: cargo check --all-targets
|
||||
# TODO: cargo clippy
|
||||
|
||||
- name: Test
|
||||
run: cargo test --all --exclude e2e_test
|
||||
- name: Build debug
|
||||
run: |
|
||||
touch rustfs/build.rs
|
||||
cargo build -p rustfs --bins
|
||||
|
||||
# - name: Build release
|
||||
# run: |
|
||||
# touch rustfs/build.rs
|
||||
# cargo build -p rustfs --bins --release
|
||||
|
||||
- run: |
|
||||
mkdir -p ./target/artifacts
|
||||
cp target/debug/rustfs ./target/artifacts/rustfs-debug
|
||||
# cp target/release/rustfs ./target/artifacts/rustfs-release
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rustfs
|
||||
path: ./target/artifacts/*
|
||||
|
||||
test:
|
||||
name: Test
|
||||
needs:
|
||||
- skip-check
|
||||
- develop
|
||||
if: needs.skip-check.outputs.should_skip != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/setup
|
||||
with:
|
||||
cache-shared-key: "develop"
|
||||
cache-save-if: "false"
|
||||
- run: cargo test --all --exclude e2e_test
|
||||
|
||||
s3s-e2e:
|
||||
name: E2E (s3s-e2e)
|
||||
needs:
|
||||
- skip-check
|
||||
- develop
|
||||
if: needs.skip-check.outputs.should_skip != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/setup
|
||||
with:
|
||||
cache-shared-key: "develop"
|
||||
cache-save-if: false
|
||||
|
||||
- name: Install s3s-e2e
|
||||
run: |
|
||||
cargo install s3s-e2e --git https://github.com/Nugine/s3s.git
|
||||
s3s-e2e --version
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rustfs
|
||||
path: ./target/artifacts
|
||||
|
||||
- name: Run rustfs
|
||||
run: |
|
||||
./scripts/e2e-run.sh ./target/artifacts/rustfs-debug /data/rustfs
|
||||
sleep 10
|
||||
|
||||
- name: Run s3s-e2e
|
||||
timeout-minutes: 10
|
||||
run: |
|
||||
export AWS_ACCESS_KEY_ID=rustfsadmin
|
||||
export AWS_SECRET_ACCESS_KEY=rustfsadmin
|
||||
export AWS_REGION=us-east-1
|
||||
export AWS_ENDPOINT_URL=http://localhost:9000
|
||||
export RUST_LOG="s3s_e2e=debug,s3s_test=info,s3s=debug"
|
||||
export RUST_BACKTRACE=full
|
||||
s3s-e2e
|
||||
sudo killall rustfs-debug
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: s3s-e2e.logs
|
||||
path: /tmp/rustfs.log
|
||||
|
||||
# mint:
|
||||
# name: E2E (mint)
|
||||
# needs:
|
||||
# - skip-check
|
||||
# - develop
|
||||
# if: needs.skip-check.outputs.should_skip != 'true'
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# - uses: ./.github/actions/setup
|
||||
# with:
|
||||
# cache-shared-key: "develop"
|
||||
# cache-save-if: false
|
||||
|
||||
# - uses: actions/download-artifact@v4
|
||||
# with:
|
||||
# name: rustfs
|
||||
# path: ./target/artifacts
|
||||
|
||||
# - name: Run rustfs
|
||||
# run: |
|
||||
# ./scripts/e2e-run.sh ./target/artifacts/rustfs-release /data/rustfs
|
||||
# sleep 10
|
||||
|
||||
# - name: Run mint
|
||||
# timeout-minutes: 10
|
||||
# run: |
|
||||
# docker run \
|
||||
# -e "SERVER_ENDPOINT=localhost:9000" \
|
||||
# -e "ACCESS_KEY=rustfsadmin" \
|
||||
# -e "SECRET_KEY=rustfsadmin" \
|
||||
# -e "ENABLE_HTTPS=0" \
|
||||
# --network host \
|
||||
# minio/mint:edge
|
||||
# sudo killall rustfs-release
|
||||
|
||||
# - uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: mint.logs
|
||||
# path: /tmp/rustfs.log
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
name: E2E
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # at midnight of each sunday
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
skip-check:
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
concurrent_skipping: 'same_content_newer'
|
||||
cancel_others: true
|
||||
paths_ignore: '["*.md"]'
|
||||
|
||||
s3s-e2e:
|
||||
needs: skip-check
|
||||
if: needs.skip-check.outputs.should_skip != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/setup
|
||||
|
||||
- name: Build binaries
|
||||
run: |
|
||||
touch rustfs/build.rs
|
||||
cargo build -p rustfs --bins
|
||||
|
||||
- name: Install s3s-e2e
|
||||
run: |
|
||||
mkdir -p target/s3s-e2e
|
||||
cargo install s3s-e2e \
|
||||
--git https://github.com/Nugine/s3s.git \
|
||||
--target-dir target/s3s-e2e \
|
||||
--force
|
||||
s3s-e2e --version
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
export SKIP_BUILD=1
|
||||
nohup ./scripts/run.sh &
|
||||
sleep 3
|
||||
|
||||
export AWS_ACCESS_KEY_ID=rustfsadmin
|
||||
export AWS_SECRET_ACCESS_KEY=rustfsadmin
|
||||
export AWS_REGION=us-east-1
|
||||
export AWS_ENDPOINT_URL=http://localhost:9000
|
||||
export RUST_LOG="s3s_e2e=debug,s3s_test=info,s3s=debug"
|
||||
export RUST_BACKTRACE=full
|
||||
s3s-e2e
|
||||
killall rustfs
|
||||
mint:
|
||||
needs: skip-check
|
||||
if: needs.skip-check.outputs.should_skip != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/setup
|
||||
|
||||
- name: Build binaries
|
||||
run: |
|
||||
touch rustfs/build.rs
|
||||
cargo build -p rustfs --bins --release
|
||||
|
||||
- name: run this rustfs server
|
||||
run: |
|
||||
sudo mkdir -p /data/rustfs
|
||||
sudo nohup ./target/release/rustfs /data/rustfs &
|
||||
|
||||
- name: run mint test task
|
||||
run: |
|
||||
ps aux | grep rustfs
|
||||
SERVER_ADDRESS=$(ifconfig eth0 | awk 'NR==2 { print $2 }')
|
||||
docker run -e SERVER_ENDPOINT=${SERVER_ADDRESS}:9000 \
|
||||
-e ACCESS_KEY=rustfsadmin \
|
||||
-e SECRET_KEY=rustfsadmin \
|
||||
-e ENABLE_HTTPS=0 \
|
||||
minio/mint
|
||||
|
||||
### Temporarily disabled because it is not working
|
||||
# e2e:
|
||||
# # See this url if required matrix jobs are hanging
|
||||
# # https://github.com/fkirc/skip-duplicate-actions#how-to-use-skip-check-with-required-matrix-jobs
|
||||
# needs: skip-check
|
||||
# if: needs.skip-check.outputs.should_skip != 'true'
|
||||
|
||||
# timeout-minutes: 30
|
||||
# runs-on: ubuntu-latest
|
||||
# strategy:
|
||||
# matrix:
|
||||
# rust:
|
||||
# - stable
|
||||
# # - nightly
|
||||
|
||||
# steps:
|
||||
# - uses: arduino/setup-protoc@v3
|
||||
# with:
|
||||
# version: "27.0"
|
||||
|
||||
# - uses: Nugine/setup-flatc@v1
|
||||
# with:
|
||||
# version: "24.3.25"
|
||||
|
||||
# - uses: actions/checkout@v4
|
||||
|
||||
# - name: toolchain
|
||||
# uses: dtolnay/rust-toolchain@master
|
||||
# with:
|
||||
# toolchain: ${{ matrix.rust }}
|
||||
# # components: rustfmt, clippy
|
||||
|
||||
# - uses: Swatinem/rust-cache@v2
|
||||
# with:
|
||||
# cache-on-failure: true
|
||||
|
||||
# - name: run fs start
|
||||
# working-directory: .
|
||||
# run: |
|
||||
# nohup make e2e-server &
|
||||
|
||||
# - name: run fs test
|
||||
# working-directory: .
|
||||
# run: |
|
||||
# make probe-e2e
|
||||
|
||||
# - name: e2e test
|
||||
# run: cargo test -p e2e_test --lib
|
||||
@@ -5,3 +5,4 @@
|
||||
/test
|
||||
/logs
|
||||
.devcontainer
|
||||
rustfs/static/*
|
||||
|
||||
Generated
+86
-28
@@ -1829,13 +1829,34 @@ dependencies = [
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
dependencies = [
|
||||
"dirs-sys 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
"dirs-sys 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.4.6",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1846,7 +1867,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"redox_users 0.5.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -3185,25 +3206,6 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
|
||||
dependencies = [
|
||||
"include_dir_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir_macros"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@@ -4047,9 +4049,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "numeric_cast"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6699ec124a5322dd30184abe24b2b9a92237ed0fe8e038f1f410bf7cccfe8b33"
|
||||
checksum = "a3c00a0c9600379bd32f8972de90676a7672cba3bf4886986bc05902afc1e093"
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
@@ -5244,6 +5246,17 @@ dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"libredox",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.0"
|
||||
@@ -5447,6 +5460,41 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"shellexpand",
|
||||
"syn 2.0.98",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
|
||||
dependencies = [
|
||||
"sha2 0.10.8",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
@@ -5497,7 +5545,6 @@ dependencies = [
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"iam",
|
||||
"include_dir",
|
||||
"jsonwebtoken",
|
||||
"lazy_static",
|
||||
"lock",
|
||||
@@ -5505,6 +5552,7 @@ dependencies = [
|
||||
"madmin",
|
||||
"matchit 0.8.6",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"netif",
|
||||
"pin-project-lite",
|
||||
"prost",
|
||||
@@ -5513,6 +5561,7 @@ dependencies = [
|
||||
"protobuf",
|
||||
"protos",
|
||||
"rmp-serde",
|
||||
"rust-embed",
|
||||
"s3s",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5541,7 +5590,7 @@ version = "0.0.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dioxus",
|
||||
"dirs",
|
||||
"dirs 6.0.0",
|
||||
"futures-util",
|
||||
"keyring",
|
||||
"reqwest",
|
||||
@@ -5647,7 +5696,7 @@ checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||
[[package]]
|
||||
name = "s3s"
|
||||
version = "0.11.0-dev"
|
||||
source = "git+https://github.com/Nugine/s3s.git?rev=529c8933a11528c506d5fbf7c4c2ab155db37dfe#529c8933a11528c506d5fbf7c4c2ab155db37dfe"
|
||||
source = "git+https://github.com/Nugine/s3s.git?rev=ab139f72fe768fb9d8cecfe36269451da1ca9779#ab139f72fe768fb9d8cecfe36269451da1ca9779"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"async-trait",
|
||||
@@ -5694,7 +5743,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "s3s-policy"
|
||||
version = "0.11.0-dev"
|
||||
source = "git+https://github.com/Nugine/s3s.git?rev=529c8933a11528c506d5fbf7c4c2ab155db37dfe#529c8933a11528c506d5fbf7c4c2ab155db37dfe"
|
||||
source = "git+https://github.com/Nugine/s3s.git?rev=ab139f72fe768fb9d8cecfe36269451da1ca9779#ab139f72fe768fb9d8cecfe36269451da1ca9779"
|
||||
dependencies = [
|
||||
"indexmap 2.7.1",
|
||||
"serde",
|
||||
@@ -6017,6 +6066,15 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shellexpand"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b"
|
||||
dependencies = [
|
||||
"dirs 5.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
@@ -6946,7 +7004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eadd75f5002e2513eaa19b2365f533090cc3e93abd38788452d9ea85cff7b48a"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"dirs",
|
||||
"dirs 6.0.0",
|
||||
"libappindicator",
|
||||
"muda 0.15.3",
|
||||
"objc2 0.6.0",
|
||||
|
||||
+2
-2
@@ -80,10 +80,10 @@ rfd = { version = "0.15.2", default-features = false, features = ["xdg-portal",
|
||||
rmp = "0.8.14"
|
||||
rmp-serde = "1.3.0"
|
||||
rustfs-logging = { path = "packages/logging", version = "0.0.1" }
|
||||
s3s = { git = "https://github.com/Nugine/s3s.git", rev = "529c8933a11528c506d5fbf7c4c2ab155db37dfe", default-features = true, features = [
|
||||
s3s = { git = "https://github.com/Nugine/s3s.git", rev = "ab139f72fe768fb9d8cecfe36269451da1ca9779", default-features = true, features = [
|
||||
"tower",
|
||||
] }
|
||||
s3s-policy = { git = "https://github.com/Nugine/s3s.git", rev = "529c8933a11528c506d5fbf7c4c2ab155db37dfe" }
|
||||
s3s-policy = { git = "https://github.com/Nugine/s3s.git", rev = "ab139f72fe768fb9d8cecfe36269451da1ca9779" }
|
||||
shadow-rs = { version = "0.38.0", default-features = false }
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0.138"
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::disk::error::{
|
||||
use crate::disk::os::{check_path_length, is_empty_dir};
|
||||
use crate::disk::{LocalFileReader, LocalFileWriter, STORAGE_FORMAT_FILE};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::file_meta::read_xl_meta_no_data;
|
||||
use crate::file_meta::{get_file_info, read_xl_meta_no_data, FileInfoOpts};
|
||||
use crate::global::{GLOBAL_IsErasureSD, GLOBAL_RootDiskThreshold};
|
||||
use crate::heal::data_scanner::{has_active_rules, scan_data_folder, ScannerItem, ShouldSleepFn, SizeSummary};
|
||||
use crate::heal::data_scanner_metric::{ScannerMetric, ScannerMetrics};
|
||||
@@ -1995,10 +1995,8 @@ impl DiskAPI for LocalDisk {
|
||||
|
||||
let (data, _) = self.read_raw(volume, file_dir, file_path, read_data).await?;
|
||||
|
||||
let mut meta = FileMeta::default();
|
||||
meta.unmarshal_msg(&data)?;
|
||||
let fi = get_file_info(&data, volume, path, version_id, FileInfoOpts { data: read_data }).await?;
|
||||
|
||||
let fi = meta.into_fileinfo(volume, path, version_id, read_data, true)?;
|
||||
Ok(fi)
|
||||
}
|
||||
async fn read_xl(&self, volume: &str, path: &str, read_data: bool) -> Result<RawFileInfo> {
|
||||
|
||||
@@ -1208,8 +1208,9 @@ pub struct VolumeInfo {
|
||||
pub created: Option<OffsetDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||
pub struct ReadOptions {
|
||||
pub incl_free_versions: bool,
|
||||
pub read_data: bool,
|
||||
pub healing: bool,
|
||||
}
|
||||
|
||||
@@ -504,7 +504,7 @@ impl EndpointServerPools {
|
||||
self.0
|
||||
.first()
|
||||
.and_then(|v| v.endpoints.as_ref().first())
|
||||
.map_or(false, |v| v.is_local)
|
||||
.is_some_and(|v| v.is_local)
|
||||
}
|
||||
|
||||
/// returns a sorted list of nodes in this cluster
|
||||
|
||||
@@ -554,7 +554,7 @@ impl FileMeta {
|
||||
let header = &ver.header;
|
||||
|
||||
if let Some(vid) = has_vid {
|
||||
if header.version_id == Some(vid) {
|
||||
if header.version_id != Some(vid) {
|
||||
is_latest = false;
|
||||
succ_mod_time = header.mod_time;
|
||||
continue;
|
||||
@@ -2045,7 +2045,7 @@ pub struct FileInfoOpts {
|
||||
pub data: bool,
|
||||
}
|
||||
|
||||
async fn get_file_info(buf: &[u8], volume: &str, path: &str, version_id: &str, opts: FileInfoOpts) -> Result<FileInfo> {
|
||||
pub async fn get_file_info(buf: &[u8], volume: &str, path: &str, version_id: &str, opts: FileInfoOpts) -> Result<FileInfo> {
|
||||
let vid = {
|
||||
if version_id.is_empty() {
|
||||
None
|
||||
@@ -2068,7 +2068,6 @@ async fn get_file_info(buf: &[u8], volume: &str, path: &str, version_id: &str, o
|
||||
}
|
||||
|
||||
let fi = meta.into_fileinfo(volume, path, version_id, opts.data, true)?;
|
||||
|
||||
Ok(fi)
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -1088,7 +1088,11 @@ impl SetDisks {
|
||||
let mut errors = Vec::with_capacity(disks.len());
|
||||
|
||||
for disk in disks.iter() {
|
||||
let opts = ReadOptions { read_data, healing };
|
||||
let opts = ReadOptions {
|
||||
read_data,
|
||||
healing,
|
||||
..Default::default()
|
||||
};
|
||||
futures.push(async move {
|
||||
if let Some(disk) = disk {
|
||||
if version_id.is_empty() {
|
||||
@@ -3634,7 +3638,7 @@ impl ObjectIO for SetDisks {
|
||||
Ok(reader)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, data))]
|
||||
#[tracing::instrument(level = "debug", skip(self, data,))]
|
||||
async fn put_object(&self, bucket: &str, object: &str, data: &mut PutObjReader, opts: &ObjectOptions) -> Result<ObjectInfo> {
|
||||
let disks = self.disks.read().await;
|
||||
|
||||
@@ -5463,7 +5467,11 @@ fn get_complete_multipart_md5(parts: &[CompletePart]) -> String {
|
||||
|
||||
for part in parts.iter() {
|
||||
if let Some(etag) = &part.e_tag {
|
||||
buf.extend(etag.bytes());
|
||||
if let Ok(etag_bytes) = hex_simd::decode_to_vec(etag.as_bytes()) {
|
||||
buf.extend(etag_bytes);
|
||||
} else {
|
||||
buf.extend(etag.bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ use tokio::select;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tokio::sync::{broadcast, mpsc, RwLock};
|
||||
use tokio::time::{interval, sleep};
|
||||
use tracing::{debug, info, warn};
|
||||
use tracing::{debug, info};
|
||||
use uuid::Uuid;
|
||||
|
||||
const MAX_UPLOADS_LIST: usize = 10000;
|
||||
@@ -1235,10 +1235,8 @@ impl StorageAPI for ECStore {
|
||||
let mut buckets = self.peer_sys.list_bucket(opts).await?;
|
||||
|
||||
if !opts.no_metadata {
|
||||
warn!("list_bucket meta");
|
||||
for bucket in buckets.iter_mut() {
|
||||
if let Ok(created) = metadata_sys::created_at(&bucket.name).await {
|
||||
warn!("list_bucket created {:?}", &created);
|
||||
bucket.created = Some(created);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ pub fn get_format_erasure_in_quorum(formats: &[Option<FormatV3>]) -> Result<Form
|
||||
|
||||
let format = formats
|
||||
.iter()
|
||||
.find(|f| f.as_ref().map_or(false, |v| v.drives().eq(max_drives)))
|
||||
.find(|f| f.as_ref().is_some_and(|v| v.drives().eq(max_drives)))
|
||||
.ok_or(Error::new(ErasureError::ErasureReadQuorum))?;
|
||||
|
||||
let mut format = format.as_ref().unwrap().clone();
|
||||
|
||||
@@ -8,6 +8,7 @@ use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use std::collections::HashMap;
|
||||
use time::macros::offset;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
const ACCESS_KEY_MIN_LEN: usize = 3;
|
||||
@@ -212,10 +213,19 @@ pub fn create_new_credentials_with_metadata(
|
||||
return Err(Error::new(IamError::InvalidAccessKeyLength));
|
||||
}
|
||||
|
||||
if token_secret.is_empty() {
|
||||
return Ok(Credentials {
|
||||
access_key: ak.to_owned(),
|
||||
secret_key: sk.to_owned(),
|
||||
status: ACCOUNT_OFF.to_owned(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
let expiration = {
|
||||
if let Some(v) = claims.get("exp") {
|
||||
if let Some(expiry) = v.as_i64() {
|
||||
Some(OffsetDateTime::from_unix_timestamp(expiry)?.to_offset(OffsetDateTime::now_utc().offset()))
|
||||
Some(OffsetDateTime::from_unix_timestamp(expiry)?.to_offset(offset!(+8)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -224,7 +234,7 @@ pub fn create_new_credentials_with_metadata(
|
||||
}
|
||||
};
|
||||
|
||||
let token = utils::generate_jwt(claims, token_secret)?;
|
||||
let token = utils::generate_jwt(&claims, token_secret)?;
|
||||
|
||||
Ok(Credentials {
|
||||
access_key: ak.to_owned(),
|
||||
|
||||
+21
-1
@@ -4,6 +4,7 @@ use crate::{
|
||||
cache::{Cache, CacheEntity},
|
||||
error::{is_err_no_such_group, is_err_no_such_policy, is_err_no_such_user, Error as IamError},
|
||||
format::Format,
|
||||
get_global_action_cred,
|
||||
policy::{Policy, PolicyDoc, DEFAULT_POLICIES},
|
||||
store::{object::IAM_CONFIG_PREFIX, GroupInfo, MappedPolicy, Store, UserType},
|
||||
sys::{
|
||||
@@ -1577,8 +1578,27 @@ fn set_default_canned_policies(policies: &mut HashMap<String, PolicyDoc>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_token_signing_key() -> Option<String> {
|
||||
if let Some(s) = get_global_action_cred() {
|
||||
Some(s.secret_key.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_jwt_claims(u: &UserIdentity) -> Result<HashMap<String, Value>> {
|
||||
get_claims_from_token_with_secret(&u.credentials.session_token, &u.credentials.secret_key)
|
||||
let Some(sys_key) = get_token_signing_key() else {
|
||||
return Err(Error::msg("global active sk not init"));
|
||||
};
|
||||
|
||||
let keys = vec![&sys_key, &u.credentials.secret_key];
|
||||
|
||||
for key in keys {
|
||||
if let Ok(claims) = get_claims_from_token_with_secret(&u.credentials.session_token, key) {
|
||||
return Ok(claims);
|
||||
}
|
||||
}
|
||||
Err(Error::msg("unable to extract claims"))
|
||||
}
|
||||
|
||||
fn filter_policies(cache: &Cache, policy_name: &str, bucket_name: &str) -> (String, Policy) {
|
||||
|
||||
@@ -131,9 +131,9 @@ impl<'de> Deserialize<'de> for Functions {
|
||||
}
|
||||
}
|
||||
|
||||
// if inner_data.is_empty() {
|
||||
// return Err(Error::custom("has no condition element"));
|
||||
// }
|
||||
if inner_data.is_empty() {
|
||||
return Err(Error::custom("has no condition element"));
|
||||
}
|
||||
|
||||
Ok(inner_data)
|
||||
}
|
||||
|
||||
+20
-6
@@ -4,7 +4,7 @@ use crate::{
|
||||
cache::{Cache, CacheEntity},
|
||||
error::{is_err_no_such_policy, is_err_no_such_user},
|
||||
get_global_action_cred,
|
||||
manager::get_default_policyes,
|
||||
manager::{extract_jwt_claims, get_default_policyes},
|
||||
policy::PolicyDoc,
|
||||
};
|
||||
use ecstore::{
|
||||
@@ -420,8 +420,20 @@ impl Store for ObjectStore {
|
||||
u.credentials.access_key = name.to_owned();
|
||||
}
|
||||
|
||||
if u.credentials.session_token.is_empty() {
|
||||
// TODO: 解析sts
|
||||
if !u.credentials.session_token.is_empty() {
|
||||
match extract_jwt_claims(&u) {
|
||||
Ok(claims) => {
|
||||
u.credentials.claims = Some(claims);
|
||||
}
|
||||
Err(err) => {
|
||||
if u.credentials.is_temp() {
|
||||
let _ = self.delete_iam_config(get_user_identity_path(name, user_type)).await;
|
||||
let _ = self.delete_iam_config(get_mapped_policy_path(name, user_type, false)).await;
|
||||
}
|
||||
warn!("extract_jwt_claims failed: {}", err);
|
||||
return Err(Error::new(crate::error::Error::NoSuchUser(name.to_owned())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(u)
|
||||
@@ -807,7 +819,7 @@ impl Store for ObjectStore {
|
||||
}
|
||||
|
||||
// group policy
|
||||
if let Some(item_name_list) = listed_config_items.get(SVC_ACC_LIST_KEY) {
|
||||
if let Some(item_name_list) = listed_config_items.get(POLICY_DB_GROUPS_LIST_KEY) {
|
||||
let mut items_cache = CacheEntity::default();
|
||||
|
||||
for item in item_name_list.iter() {
|
||||
@@ -815,7 +827,9 @@ impl Store for ObjectStore {
|
||||
|
||||
info!("load group policy: {}", name);
|
||||
if let Err(err) = self.load_mapped_policy(name, UserType::Reg, true, &mut items_cache).await {
|
||||
return Err(Error::msg(std::format!("load group failed: {}", err)));
|
||||
if !is_err_no_such_policy(&err) {
|
||||
return Err(Error::msg(std::format!("load group policy failed: {}", err)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -825,7 +839,7 @@ impl Store for ObjectStore {
|
||||
let mut sts_policies_cache = CacheEntity::default();
|
||||
|
||||
// svc users
|
||||
if let Some(item_name_list) = listed_config_items.get(POLICY_DB_GROUPS_LIST_KEY) {
|
||||
if let Some(item_name_list) = listed_config_items.get(SVC_ACC_LIST_KEY) {
|
||||
let mut items_cache = HashMap::default();
|
||||
|
||||
for item in item_name_list.iter() {
|
||||
|
||||
@@ -229,6 +229,11 @@ impl<T: Store> IamSys<T> {
|
||||
}
|
||||
}
|
||||
|
||||
m.insert(
|
||||
"exp".to_string(),
|
||||
serde_json::Value::Number(serde_json::Number::from(opts.expiration.map_or(0, |t| t.unix_timestamp()))),
|
||||
);
|
||||
|
||||
let (access_key, secret_key) = if !opts.access_key.is_empty() || !opts.secret_key.is_empty() {
|
||||
(opts.access_key, opts.secret_key)
|
||||
} else {
|
||||
|
||||
@@ -71,29 +71,29 @@ impl ServiceTraceOpts {
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.s3 = query_pairs.get("s3").map_or(false, |v| v == "true");
|
||||
self.os = query_pairs.get("os").map_or(false, |v| v == "true");
|
||||
self.scanner = query_pairs.get("scanner").map_or(false, |v| v == "true");
|
||||
self.decommission = query_pairs.get("decommission").map_or(false, |v| v == "true");
|
||||
self.healing = query_pairs.get("healing").map_or(false, |v| v == "true");
|
||||
self.batch_replication = query_pairs.get("batch-replication").map_or(false, |v| v == "true");
|
||||
self.batch_key_rotation = query_pairs.get("batch-keyrotation").map_or(false, |v| v == "true");
|
||||
self.batch_expire = query_pairs.get("batch-expire").map_or(false, |v| v == "true");
|
||||
if query_pairs.get("all").map_or(false, |v| v == "true") {
|
||||
self.s3 = query_pairs.get("s3").is_some_and(|v| v == "true");
|
||||
self.os = query_pairs.get("os").is_some_and(|v| v == "true");
|
||||
self.scanner = query_pairs.get("scanner").is_some_and(|v| v == "true");
|
||||
self.decommission = query_pairs.get("decommission").is_some_and(|v| v == "true");
|
||||
self.healing = query_pairs.get("healing").is_some_and(|v| v == "true");
|
||||
self.batch_replication = query_pairs.get("batch-replication").is_some_and(|v| v == "true");
|
||||
self.batch_key_rotation = query_pairs.get("batch-keyrotation").is_some_and(|v| v == "true");
|
||||
self.batch_expire = query_pairs.get("batch-expire").is_some_and(|v| v == "true");
|
||||
if query_pairs.get("all").is_some_and(|v| v == "true") {
|
||||
self.s3 = true;
|
||||
self.internal = true;
|
||||
self.storage = true;
|
||||
self.os = true;
|
||||
}
|
||||
|
||||
self.rebalance = query_pairs.get("rebalance").map_or(false, |v| v == "true");
|
||||
self.storage = query_pairs.get("storage").map_or(false, |v| v == "true");
|
||||
self.internal = query_pairs.get("internal").map_or(false, |v| v == "true");
|
||||
self.only_errors = query_pairs.get("err").map_or(false, |v| v == "true");
|
||||
self.replication_resync = query_pairs.get("replication-resync").map_or(false, |v| v == "true");
|
||||
self.bootstrap = query_pairs.get("bootstrap").map_or(false, |v| v == "true");
|
||||
self.ftp = query_pairs.get("ftp").map_or(false, |v| v == "true");
|
||||
self.ilm = query_pairs.get("ilm").map_or(false, |v| v == "true");
|
||||
self.rebalance = query_pairs.get("rebalance").is_some_and(|v| v == "true");
|
||||
self.storage = query_pairs.get("storage").is_some_and(|v| v == "true");
|
||||
self.internal = query_pairs.get("internal").is_some_and(|v| v == "true");
|
||||
self.only_errors = query_pairs.get("err").is_some_and(|v| v == "true");
|
||||
self.replication_resync = query_pairs.get("replication-resync").is_some_and(|v| v == "true");
|
||||
self.bootstrap = query_pairs.get("bootstrap").is_some_and(|v| v == "true");
|
||||
self.ftp = query_pairs.get("ftp").is_some_and(|v| v == "true");
|
||||
self.ilm = query_pairs.get("ilm").is_some_and(|v| v == "true");
|
||||
|
||||
if let Some(threshold) = query_pairs.get("threshold") {
|
||||
let duration = parse_duration(threshold)?;
|
||||
|
||||
+2
-2
@@ -104,7 +104,7 @@ pub struct ServiceAccountInfo {
|
||||
#[serde(rename = "description", skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
|
||||
#[serde(rename = "expiration", skip_serializing_if = "Option::is_none")]
|
||||
#[serde(rename = "expiration", with = "time::serde::rfc3339::option")]
|
||||
pub expiration: Option<OffsetDateTime>,
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ pub struct AddServiceAccountReq {
|
||||
#[serde(rename = "description", skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
|
||||
#[serde(rename = "expiration", skip_serializing_if = "Option::is_none")]
|
||||
#[serde(rename = "expiration", with = "time::serde::rfc3339::option")]
|
||||
pub expiration: Option<OffsetDateTime>,
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -7,6 +7,9 @@ repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[[bin]]
|
||||
name = "rustfs"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -69,7 +72,8 @@ crypto = { path = "../crypto" }
|
||||
iam = { path = "../iam" }
|
||||
jsonwebtoken = "9.3.0"
|
||||
tower-http = { version = "0.6.2", features = ["cors"] }
|
||||
include_dir = "0.7.4"
|
||||
mime_guess = "2.0.5"
|
||||
rust-embed = { version = "8.5.0", features = ["interpolate-folder-path"] }
|
||||
|
||||
[build-dependencies]
|
||||
prost-build.workspace = true
|
||||
|
||||
@@ -18,12 +18,11 @@ use ecstore::store::is_valid_object_prefix;
|
||||
use ecstore::store_api::StorageAPI;
|
||||
use ecstore::utils::crypto::base64_encode;
|
||||
use ecstore::utils::path::path_join;
|
||||
use ecstore::utils::xml;
|
||||
use ecstore::GLOBAL_Endpoints;
|
||||
use futures::{Stream, StreamExt};
|
||||
use http::{HeaderMap, Uri};
|
||||
use hyper::StatusCode;
|
||||
use iam::auth::{get_claims_from_token_with_secret, get_new_credentials_with_metadata};
|
||||
use iam::auth::get_claims_from_token_with_secret;
|
||||
use iam::error::Error as IamError;
|
||||
use iam::policy::Policy;
|
||||
use iam::sys::SESSION_POLICY_NAME;
|
||||
@@ -33,10 +32,7 @@ use madmin::utils::parse_duration;
|
||||
use matchit::Params;
|
||||
use s3s::header::CONTENT_TYPE;
|
||||
use s3s::stream::{ByteStream, DynByteStream};
|
||||
use s3s::{
|
||||
dto::{AssumeRoleOutput, Credentials, Timestamp},
|
||||
s3_error, Body, S3Error, S3Request, S3Response, S3Result,
|
||||
};
|
||||
use s3s::{s3_error, Body, S3Error, S3Request, S3Response, S3Result};
|
||||
use s3s::{S3ErrorCode, StdError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@@ -47,7 +43,6 @@ use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration as std_Duration;
|
||||
use time::{Duration, OffsetDateTime};
|
||||
use tokio::sync::mpsc::{self};
|
||||
use tokio::time::interval;
|
||||
use tokio::{select, spawn};
|
||||
@@ -57,32 +52,10 @@ use tracing::{error, info, warn};
|
||||
pub mod group;
|
||||
pub mod policy;
|
||||
pub mod service_account;
|
||||
pub mod sts;
|
||||
pub mod trace;
|
||||
pub mod user;
|
||||
|
||||
const ASSUME_ROLE_ACTION: &str = "AssumeRole";
|
||||
const ASSUME_ROLE_VERSION: &str = "2011-06-15";
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
#[serde(rename_all = "PascalCase", default)]
|
||||
pub struct AssumeRoleRequest {
|
||||
pub action: String,
|
||||
pub duration_seconds: usize,
|
||||
pub version: String,
|
||||
pub role_arn: String,
|
||||
pub role_session_name: String,
|
||||
pub policy: String,
|
||||
pub external_id: String,
|
||||
}
|
||||
|
||||
fn get_token_signing_key() -> Option<String> {
|
||||
if let Some(s) = get_global_action_cred() {
|
||||
Some(s.secret_key.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// check_key_valid get auth.cred
|
||||
pub async fn check_key_valid(security_token: Option<String>, ak: &str) -> S3Result<(auth::Credentials, bool)> {
|
||||
let Some(mut cred) = get_global_action_cred() else {
|
||||
@@ -216,114 +189,6 @@ pub fn populate_session_policy(claims: &mut HashMap<String, Value>, policy: &str
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct AssumeRoleHandle {}
|
||||
#[async_trait::async_trait]
|
||||
impl Operation for AssumeRoleHandle {
|
||||
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
|
||||
warn!("handle AssumeRoleHandle");
|
||||
|
||||
let Some(user) = req.credentials else { return Err(s3_error!(InvalidRequest, "get cred failed")) };
|
||||
|
||||
let session_token = get_session_token(&req.headers);
|
||||
if session_token.is_some() {
|
||||
return Err(s3_error!(InvalidRequest, "AccessDenied1"));
|
||||
}
|
||||
|
||||
let (cred, _owner) = check_key_valid(session_token, &user.access_key).await?;
|
||||
|
||||
// // TODO: 判断权限, 不允许sts访问
|
||||
if cred.is_temp() || cred.is_service_account() {
|
||||
return Err(s3_error!(InvalidRequest, "AccessDenied"));
|
||||
}
|
||||
|
||||
let mut input = req.input;
|
||||
|
||||
let bytes = match input.store_all_unlimited().await {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
warn!("get body failed, e: {:?}", e);
|
||||
return Err(s3_error!(InvalidRequest, "get body failed"));
|
||||
}
|
||||
};
|
||||
|
||||
let body: AssumeRoleRequest = from_bytes(&bytes).map_err(|_e| s3_error!(InvalidRequest, "get body failed"))?;
|
||||
|
||||
if body.action.as_str() != ASSUME_ROLE_ACTION {
|
||||
return Err(s3_error!(InvalidArgument, "not suport action"));
|
||||
}
|
||||
|
||||
if body.version.as_str() != ASSUME_ROLE_VERSION {
|
||||
return Err(s3_error!(InvalidArgument, "not suport version"));
|
||||
}
|
||||
|
||||
let mut claims = cred.claims.unwrap_or_default();
|
||||
|
||||
populate_session_policy(&mut claims, &body.policy)?;
|
||||
|
||||
let exp = {
|
||||
if body.duration_seconds > 0 {
|
||||
body.duration_seconds
|
||||
} else {
|
||||
3600
|
||||
}
|
||||
};
|
||||
|
||||
claims.insert(
|
||||
"exp".to_string(),
|
||||
serde_json::Value::Number(serde_json::Number::from(OffsetDateTime::now_utc().unix_timestamp() + exp as i64)),
|
||||
);
|
||||
|
||||
claims.insert("parent".to_string(), serde_json::Value::String(cred.access_key.clone()));
|
||||
|
||||
// warn!("AssumeRole get cred {:?}", &user);
|
||||
// warn!("AssumeRole get body {:?}", &body);
|
||||
|
||||
let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
|
||||
|
||||
if let Err(_err) = iam_store.policy_db_get(&cred.access_key, &cred.groups).await {
|
||||
return Err(s3_error!(InvalidArgument, "invalid policy arg"));
|
||||
}
|
||||
|
||||
let Some(secret) = get_token_signing_key() else {
|
||||
return Err(s3_error!(InvalidArgument, "global active sk not init"));
|
||||
};
|
||||
|
||||
info!("AssumeRole get claims {:?}", &claims);
|
||||
|
||||
let mut new_cred = get_new_credentials_with_metadata(&claims, &secret)
|
||||
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("get new cred failed {}", e)))?;
|
||||
|
||||
new_cred.parent_user = cred.access_key.clone();
|
||||
|
||||
info!("AssumeRole get new_cred {:?}", &new_cred);
|
||||
|
||||
if let Err(_err) = iam_store.set_temp_user(&new_cred.access_key, &new_cred, None).await {
|
||||
return Err(s3_error!(InternalError, "set_temp_user failed"));
|
||||
}
|
||||
|
||||
// TODO: globalSiteReplicationSys
|
||||
|
||||
let resp = AssumeRoleOutput {
|
||||
credentials: Some(Credentials {
|
||||
access_key_id: new_cred.access_key,
|
||||
expiration: Timestamp::from(
|
||||
new_cred
|
||||
.expiration
|
||||
.unwrap_or(OffsetDateTime::now_utc().saturating_add(Duration::seconds(3600))),
|
||||
),
|
||||
secret_access_key: new_cred.secret_key,
|
||||
session_token: new_cred.session_token,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// getAssumeRoleCredentials
|
||||
let output = xml::serialize::<AssumeRoleOutput>(&resp).unwrap();
|
||||
|
||||
Ok(S3Response::new((StatusCode::OK, Body::from(output))))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Default)]
|
||||
#[serde(rename_all = "PascalCase", default)]
|
||||
pub struct AccountInfo {
|
||||
|
||||
@@ -40,9 +40,10 @@ impl Operation for AddServiceAccount {
|
||||
}
|
||||
};
|
||||
|
||||
let mut create_req: AddServiceAccountReq =
|
||||
let create_req: AddServiceAccountReq =
|
||||
serde_json::from_slice(&body[..]).map_err(|e| s3_error!(InvalidRequest, "unmarshal body failed, e: {:?}", e))?;
|
||||
create_req.expiration = create_req.expiration.and_then(|expire| expire.replace_millisecond(0).ok());
|
||||
|
||||
// create_req.expiration = create_req.expiration.and_then(|expire| expire.replace_millisecond(0).ok());
|
||||
|
||||
if has_space_be(&create_req.access_key) {
|
||||
return Err(s3_error!(InvalidRequest, "access key has spaces"));
|
||||
@@ -71,7 +72,7 @@ impl Operation for AddServiceAccount {
|
||||
let req_groups = cred.groups.clone();
|
||||
let mut req_is_derived_cred = false;
|
||||
|
||||
if cred.is_owner() || cred.is_service_account() {
|
||||
if cred.is_service_account() || cred.is_temp() {
|
||||
req_parent_user = cred.parent_user.clone();
|
||||
req_is_derived_cred = true;
|
||||
}
|
||||
@@ -128,7 +129,7 @@ impl Operation for AddServiceAccount {
|
||||
.await
|
||||
.map_err(|e| {
|
||||
debug!("create service account failed, e: {:?}", e);
|
||||
s3_error!(InternalError, "create service account failed")
|
||||
s3_error!(InternalError, "create service account failed, e: {:?}", e)
|
||||
})?;
|
||||
|
||||
let resp = AddServiceAccountResp {
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
use crate::admin::{
|
||||
handlers::{check_key_valid, get_session_token, populate_session_policy},
|
||||
router::Operation,
|
||||
};
|
||||
use ecstore::utils::xml;
|
||||
use http::StatusCode;
|
||||
use iam::{auth::get_new_credentials_with_metadata, manager::get_token_signing_key};
|
||||
use matchit::Params;
|
||||
use s3s::{
|
||||
dto::{AssumeRoleOutput, Credentials, Timestamp},
|
||||
s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_urlencoded::from_bytes;
|
||||
use time::{Duration, OffsetDateTime};
|
||||
use tracing::{info, warn};
|
||||
|
||||
const ASSUME_ROLE_ACTION: &str = "AssumeRole";
|
||||
const ASSUME_ROLE_VERSION: &str = "2011-06-15";
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
#[serde(rename_all = "PascalCase", default)]
|
||||
pub struct AssumeRoleRequest {
|
||||
pub action: String,
|
||||
pub duration_seconds: usize,
|
||||
pub version: String,
|
||||
pub role_arn: String,
|
||||
pub role_session_name: String,
|
||||
pub policy: String,
|
||||
pub external_id: String,
|
||||
}
|
||||
|
||||
pub struct AssumeRoleHandle {}
|
||||
#[async_trait::async_trait]
|
||||
impl Operation for AssumeRoleHandle {
|
||||
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
|
||||
warn!("handle AssumeRoleHandle");
|
||||
|
||||
let Some(user) = req.credentials else { return Err(s3_error!(InvalidRequest, "get cred failed")) };
|
||||
|
||||
let session_token = get_session_token(&req.headers);
|
||||
if session_token.is_some() {
|
||||
return Err(s3_error!(InvalidRequest, "AccessDenied1"));
|
||||
}
|
||||
|
||||
let (cred, _owner) = check_key_valid(session_token, &user.access_key).await?;
|
||||
|
||||
// // TODO: 判断权限, 不允许sts访问
|
||||
if cred.is_temp() || cred.is_service_account() {
|
||||
return Err(s3_error!(InvalidRequest, "AccessDenied"));
|
||||
}
|
||||
|
||||
let mut input = req.input;
|
||||
|
||||
let bytes = match input.store_all_unlimited().await {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
warn!("get body failed, e: {:?}", e);
|
||||
return Err(s3_error!(InvalidRequest, "get body failed"));
|
||||
}
|
||||
};
|
||||
|
||||
let body: AssumeRoleRequest = from_bytes(&bytes).map_err(|_e| s3_error!(InvalidRequest, "get body failed"))?;
|
||||
|
||||
if body.action.as_str() != ASSUME_ROLE_ACTION {
|
||||
return Err(s3_error!(InvalidArgument, "not suport action"));
|
||||
}
|
||||
|
||||
if body.version.as_str() != ASSUME_ROLE_VERSION {
|
||||
return Err(s3_error!(InvalidArgument, "not suport version"));
|
||||
}
|
||||
|
||||
let mut claims = cred.claims.unwrap_or_default();
|
||||
|
||||
populate_session_policy(&mut claims, &body.policy)?;
|
||||
|
||||
let exp = {
|
||||
if body.duration_seconds > 0 {
|
||||
body.duration_seconds
|
||||
} else {
|
||||
3600
|
||||
}
|
||||
};
|
||||
|
||||
claims.insert(
|
||||
"exp".to_string(),
|
||||
serde_json::Value::Number(serde_json::Number::from(OffsetDateTime::now_utc().unix_timestamp() + exp as i64)),
|
||||
);
|
||||
|
||||
claims.insert("parent".to_string(), serde_json::Value::String(cred.access_key.clone()));
|
||||
|
||||
// warn!("AssumeRole get cred {:?}", &user);
|
||||
// warn!("AssumeRole get body {:?}", &body);
|
||||
|
||||
let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
|
||||
|
||||
if let Err(_err) = iam_store.policy_db_get(&cred.access_key, &cred.groups).await {
|
||||
return Err(s3_error!(InvalidArgument, "invalid policy arg"));
|
||||
}
|
||||
|
||||
let Some(secret) = get_token_signing_key() else {
|
||||
return Err(s3_error!(InvalidArgument, "global active sk not init"));
|
||||
};
|
||||
|
||||
info!("AssumeRole get claims {:?}", &claims);
|
||||
|
||||
let mut new_cred = get_new_credentials_with_metadata(&claims, &secret)
|
||||
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("get new cred failed {}", e)))?;
|
||||
|
||||
new_cred.parent_user = cred.access_key.clone();
|
||||
|
||||
info!("AssumeRole get new_cred {:?}", &new_cred);
|
||||
|
||||
if let Err(_err) = iam_store.set_temp_user(&new_cred.access_key, &new_cred, None).await {
|
||||
return Err(s3_error!(InternalError, "set_temp_user failed"));
|
||||
}
|
||||
|
||||
// TODO: globalSiteReplicationSys
|
||||
|
||||
let resp = AssumeRoleOutput {
|
||||
credentials: Some(Credentials {
|
||||
access_key_id: new_cred.access_key,
|
||||
expiration: Timestamp::from(
|
||||
new_cred
|
||||
.expiration
|
||||
.unwrap_or(OffsetDateTime::now_utc().saturating_add(Duration::seconds(3600))),
|
||||
),
|
||||
secret_access_key: new_cred.secret_key,
|
||||
session_token: new_cred.session_token,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// getAssumeRoleCredentials
|
||||
let output = xml::serialize::<AssumeRoleOutput>(&resp).unwrap();
|
||||
|
||||
Ok(S3Response::new((StatusCode::OK, Body::from(output))))
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ use common::error::Result;
|
||||
use handlers::{
|
||||
group, policy,
|
||||
service_account::{AddServiceAccount, DeleteServiceAccount, InfoServiceAccount, ListServiceAccount, UpdateServiceAccount},
|
||||
user,
|
||||
sts, user,
|
||||
};
|
||||
use hyper::Method;
|
||||
use router::{AdminOperation, S3Router};
|
||||
@@ -19,7 +19,7 @@ pub fn make_admin_route() -> Result<impl S3Route> {
|
||||
let mut r: S3Router<AdminOperation> = S3Router::new();
|
||||
|
||||
// 1
|
||||
r.insert(Method::POST, "/", AdminOperation(&handlers::AssumeRoleHandle {}))?;
|
||||
r.insert(Method::POST, "/", AdminOperation(&sts::AssumeRoleHandle {}))?;
|
||||
|
||||
regist_user_route(&mut r)?;
|
||||
|
||||
|
||||
+2
-6
@@ -1,4 +1,3 @@
|
||||
use log::warn;
|
||||
use s3s::auth::S3Auth;
|
||||
use s3s::auth::SecretKey;
|
||||
use s3s::auth::SimpleAuth;
|
||||
@@ -20,22 +19,19 @@ impl IAMAuth {
|
||||
impl S3Auth for IAMAuth {
|
||||
async fn get_secret_key(&self, access_key: &str) -> S3Result<SecretKey> {
|
||||
if access_key.is_empty() {
|
||||
return Err(s3_error!(NotSignedUp, "Your account is not signed up"));
|
||||
return Err(s3_error!(UnauthorizedAccess, "Your account is not signed up"));
|
||||
}
|
||||
|
||||
if let Ok(key) = self.simple_auth.get_secret_key(access_key).await {
|
||||
return Ok(key);
|
||||
}
|
||||
|
||||
warn!("Failed to get secret key from simple auth");
|
||||
|
||||
if let Ok(iam_store) = iam::get() {
|
||||
if let Some(id) = iam_store.get_user(access_key).await {
|
||||
warn!("get cred {:?}", id.credentials);
|
||||
return Ok(SecretKey::from(id.credentials.secret_key.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Err(s3_error!(NotSignedUp, "Your account is not signed up2"))
|
||||
Err(s3_error!(UnauthorizedAccess, "Your account is not signed up2"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,13 +35,16 @@ const LONG_VERSION: &str = concat!(
|
||||
#[command(version = SHORT_VERSION, long_version = LONG_VERSION)]
|
||||
pub struct Opt {
|
||||
/// DIR points to a directory on a filesystem.
|
||||
#[arg(required = true)]
|
||||
#[arg(required = true, env = "RUSTFS_VOLUMES")]
|
||||
pub volumes: Vec<String>,
|
||||
|
||||
/// bind to a specific ADDRESS:PORT, ADDRESS can be an IP or hostname
|
||||
#[arg(long, default_value_t = format!("0.0.0.0:{}", DEFAULT_PORT), env = "RUSTFS_ADDRESS")]
|
||||
pub address: String,
|
||||
|
||||
#[arg(long, env = "RUSTFS_SERVER_DOMAINS")]
|
||||
pub server_domains: Vec<String>,
|
||||
|
||||
/// Access key used for authentication.
|
||||
#[arg(long, default_value_t = DEFAULT_ACCESS_KEY.to_string(), env = "RUSTFS_ACCESS_KEY")]
|
||||
pub access_key: String,
|
||||
|
||||
+94
-8
@@ -6,16 +6,32 @@ use axum::{
|
||||
Router,
|
||||
};
|
||||
|
||||
use include_dir::{include_dir, Dir};
|
||||
use mime_guess::from_path;
|
||||
use rust_embed::RustEmbed;
|
||||
use serde::Serialize;
|
||||
|
||||
static STATIC_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/static");
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "$CARGO_MANIFEST_DIR/static"]
|
||||
struct StaticFiles;
|
||||
|
||||
async fn static_handler(uri: axum::http::Uri) -> impl IntoResponse {
|
||||
let path = uri.path().trim_start_matches('/');
|
||||
if let Some(file) = STATIC_DIR.get_file(path) {
|
||||
let mut path = uri.path().trim_start_matches('/');
|
||||
if path.is_empty() {
|
||||
path = "index.html"
|
||||
}
|
||||
if let Some(file) = StaticFiles::get(path) {
|
||||
let mime_type = from_path(path).first_or_octet_stream();
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.body(Body::from(file.contents()))
|
||||
.header("Content-Type", mime_type.to_string())
|
||||
.body(Body::from(file.data))
|
||||
.unwrap()
|
||||
} else if let Some(file) = StaticFiles::get("index.html") {
|
||||
let mime_type = from_path("index.html").first_or_octet_stream();
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header("Content-Type", mime_type.to_string())
|
||||
.body(Body::from(file.data))
|
||||
.unwrap()
|
||||
} else {
|
||||
Response::builder()
|
||||
@@ -25,13 +41,83 @@ async fn static_handler(uri: axum::http::Uri) -> impl IntoResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_static_file_server(addrs: &str) {
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Config {
|
||||
api: Api,
|
||||
s3: S3,
|
||||
release: Release,
|
||||
license: License,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn new(url: &str, version: &str, date: &str) -> Self {
|
||||
Config {
|
||||
api: Api {
|
||||
base_url: format!("{}/rustfs/admin/v3", url),
|
||||
},
|
||||
s3: S3 {
|
||||
endpoint: url.to_owned(),
|
||||
region: "cn-east-1".to_owned(),
|
||||
},
|
||||
release: Release {
|
||||
version: version.to_string(),
|
||||
date: date.to_string(),
|
||||
},
|
||||
license: License {
|
||||
name: "Apache-2.0".to_string(),
|
||||
url: "https://www.apache.org/licenses/LICENSE-2.0".to_string(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn to_json(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Api {
|
||||
#[serde(rename = "baseURL")]
|
||||
base_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct S3 {
|
||||
endpoint: String,
|
||||
region: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Release {
|
||||
version: String,
|
||||
date: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct License {
|
||||
name: String,
|
||||
url: String,
|
||||
}
|
||||
|
||||
async fn config_handler(axum::extract::Extension(fs_addr): axum::extract::Extension<String>) -> impl IntoResponse {
|
||||
let cfg = Config::new(&fs_addr, "v0.0.1", "2025-01-01").to_json();
|
||||
|
||||
Response::builder()
|
||||
.header("content-type", "application/json")
|
||||
.status(StatusCode::OK)
|
||||
.body(Body::from(cfg))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn start_static_file_server(addrs: &str, fs_addr: &str) {
|
||||
// 创建路由
|
||||
let app = Router::new().route("/*file", get(static_handler));
|
||||
let app = Router::new()
|
||||
.route("/config.json", get(config_handler).layer(axum::extract::Extension(fs_addr.to_owned())))
|
||||
.nest_service("/", get(static_handler));
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(addrs).await.unwrap();
|
||||
|
||||
println!("console listening on: {}", listener.local_addr().unwrap());
|
||||
println!("console running on: http://{} with s3 api {}", listener.local_addr().unwrap(), fs_addr);
|
||||
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
|
||||
+1
-4
@@ -67,10 +67,7 @@ fn match_for_io_error(err_status: &Status) -> Option<&std::io::Error> {
|
||||
}
|
||||
}
|
||||
|
||||
err = match err.source() {
|
||||
Some(err) => err,
|
||||
None => return None,
|
||||
};
|
||||
err = err.source()?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-2
@@ -31,7 +31,7 @@ use hyper_util::{
|
||||
};
|
||||
use iam::init_iam_sys;
|
||||
use protos::proto_gen::node_service::node_service_server::NodeServiceServer;
|
||||
use s3s::service::S3ServiceBuilder;
|
||||
use s3s::{host::MultiDomain, service::S3ServiceBuilder};
|
||||
use service::hybrid;
|
||||
use std::{io::IsTerminal, net::SocketAddr};
|
||||
use tokio::net::TcpListener;
|
||||
@@ -139,6 +139,11 @@ async fn run(opt: config::Opt) -> Result<()> {
|
||||
|
||||
b.set_route(admin::make_admin_route()?);
|
||||
|
||||
if !opt.server_domains.is_empty() {
|
||||
info!("virtual-hosted-style requests are enabled use domain_name {:?}", &opt.server_domains);
|
||||
b.set_host(MultiDomain::new(&opt.server_domains)?);
|
||||
}
|
||||
|
||||
// // Enable parsing virtual-hosted-style requests
|
||||
// if let Some(dm) = opt.domain_name {
|
||||
// info!("virtual-hosted-style requests are enabled use domain_name {}", &dm);
|
||||
@@ -236,7 +241,13 @@ async fn run(opt: config::Opt) -> Result<()> {
|
||||
if opt.console_enable {
|
||||
info!("console is enabled");
|
||||
tokio::spawn(async move {
|
||||
console::start_static_file_server(&opt.console_address).await;
|
||||
let ep = if !opt.server_domains.is_empty() {
|
||||
format!("http://{}", opt.server_domains[0].clone())
|
||||
} else {
|
||||
format!("http://127.0.0.1:{}", server_port)
|
||||
};
|
||||
|
||||
console::start_static_file_server(&opt.console_address, &ep).await;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ impl S3Access for FS {
|
||||
);
|
||||
|
||||
if cx.credentials().is_none() {
|
||||
return Err(s3_error!(AccessDenied, "Signature is required"));
|
||||
return Err(s3_error!(UnauthorizedAccess, "Signature is required"));
|
||||
};
|
||||
|
||||
// TODO: FIXME: check auth
|
||||
|
||||
@@ -47,6 +47,7 @@ use s3s::S3;
|
||||
use s3s::{S3Request, S3Response};
|
||||
use std::fmt::Debug;
|
||||
use std::str::FromStr;
|
||||
use tracing::debug;
|
||||
use tracing::error;
|
||||
use tracing::info;
|
||||
use transform_stream::AsyncTryStream;
|
||||
@@ -549,6 +550,7 @@ impl S3 for FS {
|
||||
.map(|v| Bucket {
|
||||
creation_date: v.created.map(Timestamp::from),
|
||||
name: Some(v.name.clone()),
|
||||
..Default::default()
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -751,6 +753,7 @@ impl S3 for FS {
|
||||
content_length,
|
||||
tagging,
|
||||
metadata,
|
||||
version_id,
|
||||
..
|
||||
} = input;
|
||||
|
||||
@@ -784,10 +787,12 @@ impl S3 for FS {
|
||||
metadata.insert(xhttp::AMZ_OBJECT_TAGGING.to_owned(), tags);
|
||||
}
|
||||
|
||||
let opts: ObjectOptions = put_opts(&bucket, &key, None, &req.headers, Some(metadata))
|
||||
let opts: ObjectOptions = put_opts(&bucket, &key, version_id, &req.headers, Some(metadata))
|
||||
.await
|
||||
.map_err(to_s3_error)?;
|
||||
|
||||
debug!("put_object opts {:?}", &opts);
|
||||
|
||||
let obj_info = store
|
||||
.put_object(&bucket, &key, &mut reader, &opts)
|
||||
.await
|
||||
@@ -810,7 +815,11 @@ impl S3 for FS {
|
||||
req: S3Request<CreateMultipartUploadInput>,
|
||||
) -> S3Result<S3Response<CreateMultipartUploadOutput>> {
|
||||
let CreateMultipartUploadInput {
|
||||
bucket, key, tagging, ..
|
||||
bucket,
|
||||
key,
|
||||
tagging,
|
||||
version_id,
|
||||
..
|
||||
} = req.input;
|
||||
|
||||
// mc cp step 3
|
||||
@@ -827,7 +836,7 @@ impl S3 for FS {
|
||||
metadata.insert(xhttp::AMZ_OBJECT_TAGGING.to_owned(), tags);
|
||||
}
|
||||
|
||||
let opts: ObjectOptions = put_opts(&bucket, &key, None, &req.headers, Some(metadata))
|
||||
let opts: ObjectOptions = put_opts(&bucket, &key, version_id, &req.headers, Some(metadata))
|
||||
.await
|
||||
.map_err(to_s3_error)?;
|
||||
|
||||
@@ -952,7 +961,7 @@ impl S3 for FS {
|
||||
return Err(S3Error::with_message(S3ErrorCode::InternalError, "Not init".to_string()));
|
||||
};
|
||||
|
||||
store
|
||||
let oi = store
|
||||
.complete_multipart_upload(&bucket, &key, &upload_id, uploaded_parts, opts)
|
||||
.await
|
||||
.map_err(to_s3_error)?;
|
||||
@@ -960,6 +969,7 @@ impl S3 for FS {
|
||||
let output = CompleteMultipartUploadOutput {
|
||||
bucket: Some(bucket),
|
||||
key: Some(key),
|
||||
e_tag: oi.etag,
|
||||
..Default::default()
|
||||
};
|
||||
Ok(S3Response::new(output))
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
static index
|
||||
@@ -0,0 +1 @@
|
||||
console static path, do not delete
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash -ex
|
||||
BIN=$1
|
||||
VOLUME=$2
|
||||
|
||||
chmod +x $BIN
|
||||
sudo mkdir -p $VOLUME
|
||||
|
||||
export RUST_LOG="rustfs=debug,ecstore=debug,s3s=debug,iam=debug"
|
||||
export RUST_BACKTRACE=full
|
||||
|
||||
sudo nohup $BIN $VOLUME > /tmp/rustfs.log 2>&1 &
|
||||
+10
-14
@@ -7,10 +7,11 @@ fi
|
||||
current_dir=$(pwd)
|
||||
|
||||
mkdir -p ./target/volume/test
|
||||
mkdir -p ./target/volume/test{0..4}
|
||||
# mkdir -p ./target/volume/test{0..4}
|
||||
|
||||
|
||||
if [ -z "$RUST_LOG" ]; then
|
||||
export RUST_BACKTRACE=1
|
||||
export RUST_LOG="rustfs=debug,ecstore=debug,s3s=debug,iam=debug"
|
||||
fi
|
||||
|
||||
@@ -18,21 +19,16 @@ fi
|
||||
|
||||
# export RUSTFS_STORAGE_CLASS_INLINE_BLOCK="512 KB"
|
||||
|
||||
|
||||
# DATA_DIR_ARG="./target/volume/test{0...4}"
|
||||
DATA_DIR_ARG="./target/volume/test"
|
||||
# RUSTFS_VOLUMES="./target/volume/test{0...4}"
|
||||
export RUSTFS_VOLUMES="./target/volume/test"
|
||||
export RUSTFS_ADDRESS="0.0.0.0:9000"
|
||||
export RUSTFS_CONSOLE_ENABLE=true
|
||||
export RUSTFS_CONSOLE_ADDRESS="0.0.0.0:9002"
|
||||
# export RUSTFS_SERVER_DOMAINS="localhost:9000"
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
DATA_DIR_ARG="$1"
|
||||
export RUSTFS_VOLUMES="$1"
|
||||
fi
|
||||
|
||||
|
||||
# cargo run "$DATA_DIR_ARG"
|
||||
# -- --access-key AKEXAMPLERUSTFS \
|
||||
# --secret-key SKEXAMPLERUSTFS \
|
||||
# --address 0.0.0.0:9010 \
|
||||
# --domain-name 127.0.0.1:9010 \
|
||||
# "$DATA_DIR_ARG"
|
||||
|
||||
./target/debug/rustfs "$DATA_DIR_ARG"
|
||||
# cargo run ./target/volume/test
|
||||
cargo run --bin rustfs
|
||||
Reference in New Issue
Block a user