diff --git a/.docker/Dockerfile.devenv b/.docker/Dockerfile.devenv index de2fcb499..1e3916af8 100644 --- a/.docker/Dockerfile.devenv +++ b/.docker/Dockerfile.devenv @@ -7,12 +7,12 @@ RUN sed -i s@http://.*archive.ubuntu.com@http://repo.huaweicloud.com@g /etc/apt/ RUN apt-get clean && apt-get update && apt-get install wget git curl unzip gcc pkg-config libssl-dev lld libdbus-1-dev libwayland-dev libwebkit2gtk-4.1-dev libxdo-dev -y # install protoc -RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v27.0/protoc-27.0-linux-x86_64.zip \ - && unzip protoc-27.0-linux-x86_64.zip -d protoc3 \ - && mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc && mv protoc3/include/* /usr/local/include/ && rm -rf protoc-27.0-linux-x86_64.zip protoc3 +RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protoc-30.2-linux-x86_64.zip \ + && unzip protoc-30.2-linux-x86_64.zip -d protoc3 \ + && mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc && mv protoc3/include/* /usr/local/include/ && rm -rf protoc-30.2-linux-x86_64.zip protoc3 # install flatc -RUN wget https://github.com/google/flatbuffers/releases/download/v24.3.25/Linux.flatc.binary.g++-13.zip \ +RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \ && unzip Linux.flatc.binary.g++-13.zip \ && mv flatc /usr/local/bin/ && chmod +x /usr/local/bin/flatc && rm -rf Linux.flatc.binary.g++-13.zip diff --git a/.docker/Dockerfile.rockylinux9.3 b/.docker/Dockerfile.rockylinux9.3 index 340b84191..eb3a25d72 100644 --- a/.docker/Dockerfile.rockylinux9.3 +++ b/.docker/Dockerfile.rockylinux9.3 @@ -13,13 +13,13 @@ RUN dnf makecache RUN yum install wget git unzip gcc openssl-devel pkgconf-pkg-config -y # install protoc -RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v27.0/protoc-27.0-linux-x86_64.zip \ - && unzip protoc-27.0-linux-x86_64.zip -d protoc3 \ +RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protoc-30.2-linux-x86_64.zip \ + && unzip protoc-30.2-linux-x86_64.zip -d protoc3 \ && mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc \ - && rm -rf protoc-27.0-linux-x86_64.zip protoc3 + && rm -rf protoc-30.2-linux-x86_64.zip protoc3 # install flatc -RUN wget https://github.com/google/flatbuffers/releases/download/v24.3.25/Linux.flatc.binary.g++-13.zip \ +RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \ && unzip Linux.flatc.binary.g++-13.zip \ && mv flatc /usr/local/bin/ && chmod +x /usr/local/bin/flatc \ && rm -rf Linux.flatc.binary.g++-13.zip diff --git a/.docker/Dockerfile.ubuntu22.04 b/.docker/Dockerfile.ubuntu22.04 index b955de8e8..e8f715209 100644 --- a/.docker/Dockerfile.ubuntu22.04 +++ b/.docker/Dockerfile.ubuntu22.04 @@ -7,12 +7,12 @@ RUN sed -i s@http://.*archive.ubuntu.com@http://repo.huaweicloud.com@g /etc/apt/ RUN apt-get clean && apt-get update && apt-get install wget git curl unzip gcc pkg-config libssl-dev lld libdbus-1-dev libwayland-dev libwebkit2gtk-4.1-dev libxdo-dev -y # install protoc -RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v27.0/protoc-27.0-linux-x86_64.zip \ - && unzip protoc-27.0-linux-x86_64.zip -d protoc3 \ - && mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc && mv protoc3/include/* /usr/local/include/ && rm -rf protoc-27.0-linux-x86_64.zip protoc3 +RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protoc-30.2-linux-x86_64.zip \ + && unzip protoc-30.2-linux-x86_64.zip -d protoc3 \ + && mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc && mv protoc3/include/* /usr/local/include/ && rm -rf protoc-30.2-linux-x86_64.zip protoc3 # install flatc -RUN wget https://github.com/google/flatbuffers/releases/download/v24.3.25/Linux.flatc.binary.g++-13.zip \ +RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \ && unzip Linux.flatc.binary.g++-13.zip \ && mv flatc /usr/local/bin/ && chmod +x /usr/local/bin/flatc && rm -rf Linux.flatc.binary.g++-13.zip diff --git a/.docker/observability/README.md b/.docker/observability/README.md new file mode 100644 index 000000000..a84f7592d --- /dev/null +++ b/.docker/observability/README.md @@ -0,0 +1,101 @@ +# Observability + +This directory contains the observability stack for the application. The stack is composed of the following components: + +- Prometheus v3.2.1 +- Grafana 11.6.0 +- Loki 3.4.2 +- Jaeger 2.4.0 +- Otel Collector 0.120.0 #0.121.0 remove loki + +## Prometheus + +Prometheus is a monitoring and alerting toolkit. It scrapes metrics from instrumented jobs, either directly or via an +intermediary push gateway for short-lived jobs. It stores all scraped samples locally and runs rules over this data to +either aggregate and record new time series from existing data or generate alerts. Grafana or other API consumers can be +used to visualize the collected data. + +## Grafana + +Grafana is a multi-platform open-source analytics and interactive visualization web application. It provides charts, +graphs, and alerts for the web when connected to supported data sources. + +## Loki + +Loki is a horizontally-scalable, highly-available, multi-tenant log aggregation system inspired by Prometheus. It is +designed to be very cost-effective and easy to operate. It does not index the contents of the logs, but rather a set of +labels for each log stream. + +## Jaeger + +Jaeger is a distributed tracing system released as open source by Uber Technologies. It is used for monitoring and +troubleshooting microservices-based distributed systems, including: + +- Distributed context propagation +- Distributed transaction monitoring +- Root cause analysis +- Service dependency analysis +- Performance / latency optimization + +## Otel Collector + +The OpenTelemetry Collector offers a vendor-agnostic implementation on how to receive, process, and export telemetry +data. It removes the need to run, operate, and maintain multiple agents/collectors in order to support open-source +observability data formats (e.g. Jaeger, Prometheus, etc.) sending to one or more open-source or commercial back-ends. + +## How to use + +To deploy the observability stack, run the following command: + +```bash +docker-compose -f docker-compose.yml -f docker-compose.override.yml up -d +``` + +To access the Grafana dashboard, navigate to `http://localhost:3000` in your browser. The default username and password +are `admin` and `admin`, respectively. + +To access the Jaeger dashboard, navigate to `http://localhost:16686` in your browser. + +To access the Prometheus dashboard, navigate to `http://localhost:9090` in your browser. + +## How to stop + +To stop the observability stack, run the following command: + +```bash +docker-compose -f docker-compose.yml -f docker-compose.override.yml down +``` + +## How to remove data + +To remove the data generated by the observability stack, run the following command: + +```bash +docker-compose -f docker-compose.yml -f docker-compose.override.yml down -v +``` + +## How to configure + +To configure the observability stack, modify the `docker-compose.override.yml` file. The file contains the following + +```yaml +services: + prometheus: + environment: + - PROMETHEUS_CONFIG_FILE=/etc/prometheus/prometheus.yml + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + + grafana: + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - ./grafana/provisioning:/etc/grafana/provisioning +``` + +The `prometheus` service mounts the `prometheus.yml` file to `/etc/prometheus/prometheus.yml`. The `grafana` service +mounts the `grafana/provisioning` directory to `/etc/grafana/provisioning`. You can modify these files to configure the +observability stack. + + + diff --git a/.docker/observability/config/obs.toml b/.docker/observability/config/obs.toml new file mode 100644 index 000000000..ecb340061 --- /dev/null +++ b/.docker/observability/config/obs.toml @@ -0,0 +1,33 @@ +[observability] +endpoint = "http://localhost:4317" # Default is "http://localhost:4317" if not specified +use_stdout = false # Output with stdout, true output, false no output +sample_ratio = 2.0 +meter_interval = 30 +service_name = "rustfs" +service_version = "0.1.0" +environments = "production" +logger_level = "info" + +[sinks] +[sinks.kafka] # Kafka sink is disabled by default +enabled = false +bootstrap_servers = "localhost:9092" +topic = "logs" +batch_size = 100 # Default is 100 if not specified +batch_timeout_ms = 1000 # Default is 1000ms if not specified + +[sinks.webhook] +enabled = false +endpoint = "http://localhost:8080/webhook" +auth_token = "" +batch_size = 100 # Default is 3 if not specified +batch_timeout_ms = 1000 # Default is 100ms if not specified + +[sinks.file] +enabled = true +path = "/root/data/logs/app.log" +batch_size = 10 +batch_timeout_ms = 1000 # Default is 8192 bytes if not specified + +[logger] +queue_capacity = 10 \ No newline at end of file diff --git a/.docker/observability/docker-compose.yml b/.docker/observability/docker-compose.yml new file mode 100644 index 000000000..f6714bb26 --- /dev/null +++ b/.docker/observability/docker-compose.yml @@ -0,0 +1,65 @@ +services: + otel-collector: + image: otel/opentelemetry-collector-contrib:0.120.0 + environment: + - TZ=Asia/Shanghai + volumes: + - ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml + ports: + - 1888:1888 + - 8888:8888 + - 8889:8889 + - 13133:13133 + - 4317:4317 + - 4318:4318 + - 55679:55679 + networks: + - otel-network + jaeger: + image: jaegertracing/jaeger:2.4.0 + environment: + - TZ=Asia/Shanghai + ports: + - "16686:16686" + - "14317:4317" + - "14318:4318" + networks: + - otel-network + prometheus: + image: prom/prometheus:v3.2.1 + environment: + - TZ=Asia/Shanghai + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + networks: + - otel-network + loki: + image: grafana/loki:3.4.2 + environment: + - TZ=Asia/Shanghai + volumes: + - ./loki-config.yaml:/etc/loki/local-config.yaml + ports: + - "3100:3100" + command: -config.file=/etc/loki/local-config.yaml + networks: + - otel-network + grafana: + image: grafana/grafana:11.6.0 + ports: + - "3000:3000" # Web UI + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + - TZ=Asia/Shanghai + networks: + - otel-network + + +networks: + otel-network: + driver: bridge + name: "network_otel_config" + driver_opts: + com.docker.network.enable_ipv6: "true" diff --git a/.docker/observability/jaeger-config.yaml b/.docker/observability/jaeger-config.yaml new file mode 100644 index 000000000..0df35f836 --- /dev/null +++ b/.docker/observability/jaeger-config.yaml @@ -0,0 +1,87 @@ +service: + extensions: [ jaeger_storage, jaeger_query, remote_sampling, healthcheckv2 ] + pipelines: + traces: + receivers: [ otlp, jaeger, zipkin ] + processors: [ batch, adaptive_sampling ] + exporters: [ jaeger_storage_exporter ] + telemetry: + resource: + service.name: jaeger + metrics: + level: detailed + readers: + - pull: + exporter: + prometheus: + host: 0.0.0.0 + port: 8888 + logs: + level: debug + # TODO Initialize telemetry tracer once OTEL released new feature. + # https://github.com/open-telemetry/opentelemetry-collector/issues/10663 + +extensions: + healthcheckv2: + use_v2: true + http: + + # pprof: + # endpoint: 0.0.0.0:1777 + # zpages: + # endpoint: 0.0.0.0:55679 + + jaeger_query: + storage: + traces: some_store + traces_archive: another_store + ui: + config_file: ./cmd/jaeger/config-ui.json + # The maximum duration that is considered for clock skew adjustments. + # Defaults to 0 seconds, which means it's disabled. + max_clock_skew_adjust: 0s + + jaeger_storage: + backends: + some_store: + memory: + max_traces: 100000 + another_store: + memory: + max_traces: 100000 + + remote_sampling: + # You can either use file or adaptive sampling strategy in remote_sampling + # file: + # path: ./cmd/jaeger/sampling-strategies.json + adaptive: + sampling_store: some_store + initial_sampling_probability: 0.1 + http: + grpc: + +receivers: + otlp: + protocols: + grpc: + http: + + jaeger: + protocols: + grpc: + thrift_binary: + thrift_compact: + thrift_http: + + zipkin: + +processors: + batch: + # Adaptive Sampling Processor is required to support adaptive sampling. + # It expects remote_sampling extension with `adaptive:` config to be enabled. + adaptive_sampling: + +exporters: + jaeger_storage_exporter: + trace_storage: some_store + diff --git a/.docker/observability/loki-config.yaml b/.docker/observability/loki-config.yaml new file mode 100644 index 000000000..4aff8772a --- /dev/null +++ b/.docker/observability/loki-config.yaml @@ -0,0 +1,63 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + grpc_listen_port: 9096 + log_level: debug + grpc_server_max_concurrent_streams: 1000 + +common: + instance_addr: 127.0.0.1 + path_prefix: /tmp/loki + storage: + filesystem: + chunks_directory: /tmp/loki/chunks + rules_directory: /tmp/loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +limits_config: + metric_aggregation_enabled: true + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +pattern_ingester: + enabled: true + metric_aggregation: + loki_address: localhost:3100 + +ruler: + alertmanager_url: http://localhost:9093 + +frontend: + encoding: protobuf + +# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration +# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ +# +# Statistics help us better understand how Loki is used, and they show us performance +# levels for most users. This helps us prioritize features and documentation. +# For more information on what's sent, look at +# https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go +# Refer to the buildReport method to see what goes into a report. +# +# If you would like to disable reporting, uncomment the following lines: +#analytics: +# reporting_enabled: false diff --git a/.docker/observability/otel-collector-config.yaml b/.docker/observability/otel-collector-config.yaml new file mode 100644 index 000000000..24d67dc3c --- /dev/null +++ b/.docker/observability/otel-collector-config.yaml @@ -0,0 +1,57 @@ +receivers: + otlp: + protocols: + grpc: # OTLP gRPC 接收器 + endpoint: 0.0.0.0:4317 + http: # OTLP HTTP 接收器 + endpoint: 0.0.0.0:4318 + +processors: + batch: # 批处理处理器,提升吞吐量 + timeout: 5s + send_batch_size: 1000 + memory_limiter: + check_interval: 1s + limit_mib: 512 + +exporters: + otlp/traces: # OTLP 导出器,用于跟踪数据 + endpoint: "jaeger:4317" # Jaeger 的 OTLP gRPC 端点 + tls: + insecure: true # 开发环境禁用 TLS,生产环境需配置证书 + prometheus: # Prometheus 导出器,用于指标数据 + endpoint: "0.0.0.0:8889" # Prometheus 刮取端点 + namespace: "rustfs" # 指标前缀 + send_timestamps: true # 发送时间戳 + # enable_open_metrics: true + loki: # Loki 导出器,用于日志数据 + # endpoint: "http://loki:3100/otlp/v1/logs" + endpoint: "http://loki:3100/loki/api/v1/push" + tls: + insecure: true +extensions: + health_check: + pprof: + zpages: +service: + extensions: [ health_check, pprof, zpages ] # 启用扩展 + pipelines: + traces: + receivers: [ otlp ] + processors: [ memory_limiter,batch ] + exporters: [ otlp/traces ] + metrics: + receivers: [ otlp ] + processors: [ batch ] + exporters: [ prometheus ] + logs: + receivers: [ otlp ] + processors: [ batch ] + exporters: [ loki ] + telemetry: + logs: + level: "info" # Collector 日志级别 + metrics: + address: "0.0.0.0:8888" # Collector 自身指标暴露 + + diff --git a/.docker/observability/prometheus.yml b/.docker/observability/prometheus.yml new file mode 100644 index 000000000..8d3a031ef --- /dev/null +++ b/.docker/observability/prometheus.yml @@ -0,0 +1,11 @@ +global: + scrape_interval: 5s # 刮取间隔 + +scrape_configs: + - job_name: 'otel-collector' + static_configs: + - targets: ['otel-collector:8888'] # 从 Collector 刮取指标 + - job_name: 'otel-metrics' + static_configs: + - targets: ['otel-collector:8889'] # 应用指标 + diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 4c7afb30e..f74dc6fbe 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -20,16 +20,11 @@ runs: shell: bash run: | sudo apt update - sudo apt install -y \ - lld \ - libdbus-1-dev \ - libwayland-dev \ - libwebkit2gtk-4.1-dev \ - libxdo-dev + sudo apt install -y musl-tools build-essential lld libdbus-1-dev libwayland-dev libwebkit2gtk-4.1-dev libxdo-dev - uses: arduino/setup-protoc@v3 with: - version: "27.0" + version: "30.2" - uses: Nugine/setup-flatc@v1 with: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e11da3cf5..01e49f6c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,11 +3,11 @@ name: Build on: workflow_dispatch: schedule: - - cron: '0 0 * * 0' # at midnight of each sunday + - cron: "0 0 * * 0" # at midnight of each sunday push: branches: - main - tags: [ 'v*', '*' ] + tags: [ "v*", "*" ] jobs: build-rustfs: @@ -16,9 +16,10 @@ jobs: strategy: matrix: variant: - - { profile: dev, target: x86_64-unknown-linux-gnu, glibc: "default" } + # - { profile: dev, target: x86_64-unknown-linux-musl, glibc: "default" } + - { profile: release, target: x86_64-unknown-linux-musl, glibc: "default" } - { profile: release, target: x86_64-unknown-linux-gnu, glibc: "default" } - - { profile: release, target: x86_64-unknown-linux-gnu, glibc: "2.31" } + # - { profile: release, target: x86_64-unknown-linux-gnu, glibc: "2.31" } steps: - uses: actions/checkout@v4 @@ -51,13 +52,13 @@ jobs: ARTIFACT_NAME="${ARTIFACT_NAME}-glibc${{ matrix.variant.glibc }}" fi echo "artifact_name=${ARTIFACT_NAME}" >> $GITHUB_OUTPUT - + # Determine binary path bin_path="target/artifacts/rustfs.${{ matrix.variant.profile }}.${{ matrix.variant.target }}.bin" if [ -f "target/artifacts/rustfs.${{ matrix.variant.profile }}.${{ matrix.variant.target }}.glibc${{ matrix.variant.glibc }}.bin" ]; then bin_path="target/artifacts/rustfs.${{ matrix.variant.profile }}.${{ matrix.variant.target }}.glibc${{ matrix.variant.glibc }}.bin" fi - + # Create package mkdir -p ${ARTIFACT_NAME} cp "$bin_path" ${ARTIFACT_NAME}/rustfs @@ -69,6 +70,16 @@ jobs: name: ${{ steps.package.outputs.artifact_name }} path: ${{ steps.package.outputs.artifact_name }}.zip retention-days: 7 + - name: Upload to Aliyun OSS + uses: JohnGuan/oss-upload-action@main + with: + key-id: ${{ secrets.ALICLOUDOSS_KEY_ID }} + key-secret: ${{ secrets.ALICLOUDOSS_KEY_SECRET }} + region: oss-cn-beijing + bucket: rustfs-artifacts + assets: | + ${{ steps.package.outputs.artifact_name }}.zip:/artifacts/rustfs/${{ steps.package.outputs.artifact_name }}.zip + ${{ steps.package.outputs.artifact_name }}.zip:/artifacts/rustfs/${{ steps.package.outputs.artifact_name }}.latest.zip build-rustfs-gui: runs-on: ubuntu-latest @@ -88,32 +99,34 @@ jobs: name: "rustfs-${{ matrix.variant.profile }}-${{ matrix.variant.target }}" - name: Display structure of downloaded files run: | - ls -R + ls -R unzip -o -j "rustfs-${{ matrix.variant.profile }}-${{ matrix.variant.target }}.zip" -d ./cli/rustfs-gui/embedded-rustfs/ ls -la cli/rustfs-gui/embedded-rustfs - - name: Cache dioxus-cli - uses: actions/cache@v4 + # - name: Cache dioxus-cli + # uses: actions/cache@v4 + # with: + # path: ~/.cargo/bin/dx + # key: ${{ runner.os }}-dioxus-cli-${{ hashFiles('**/Cargo.lock') }} + # restore-keys: | + # ${{ runner.os }}-dioxus-cli- + # + # - name: Install dioxus-cli + # run: | + # if [ ! -f ~/.cargo/bin/dx ]; then + # cargo install dioxus-cli + # fi + - uses: taiki-e/cache-cargo-install-action@v2 with: - path: ~/.cargo/bin/dx - key: ${{ runner.os }}-dioxus-cli-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-dioxus-cli- - - - name: Install dioxus-cli - run: | - if [ ! -f ~/.cargo/bin/dx ]; then - cargo install dioxus-cli - fi - + tool: dioxus-cli - name: Build and Bundle rustfs-gui run: | ls -la - + release_path="target/${{ matrix.variant.target }}" mkdir -p ${release_path} cd cli/rustfs-gui ls -la embedded-rustfs - + # Configure the linker based on the target case "${{ matrix.target }}" in "x86_64-unknown-linux-gnu") @@ -140,7 +153,7 @@ jobs: # Validating Environment Variables (for Debugging) echo "CC for ${{ matrix.target }}: $CC_${{ matrix.target }}" echo "Linker for ${{ matrix.target }}: $CARGO_TARGET_${{ matrix.target }}_LINKER" - + if [[ "${{ matrix.variant.target }}" == *"apple-darwin"* ]]; then dx bundle --platform macos --package-types "macos" --package-types "dmg" --package-types "ios" --release --profile release --out-dir ../../${release_path} elif [[ "${{ matrix.variant.target }}" == *"windows-msvc"* ]]; then @@ -159,6 +172,16 @@ jobs: name: ${{ steps.package.outputs.gui_artifact_name }} path: ${{ steps.package.outputs.gui_artifact_name }}.zip retention-days: 7 + - name: Upload to Aliyun OSS + uses: JohnGuan/oss-upload-action@main + with: + key-id: ${{ secrets.ALICLOUDOSS_KEY_ID }} + key-secret: ${{ secrets.ALICLOUDOSS_KEY_SECRET }} + region: oss-cn-beijing + bucket: rustfs-artifacts + assets: | + ${{ steps.package.outputs.gui_artifact_name }}.zip:/artifacts/rustfs/${{ steps.package.outputs.gui_artifact_name }}.zip + ${{ steps.package.outputs.gui_artifact_name }}.zip:/artifacts/rustfs/${{ steps.package.outputs.gui_artifact_name }}.latest.zip merge: runs-on: ubuntu-latest @@ -167,5 +190,5 @@ jobs: - uses: actions/upload-artifact/merge@v4 with: name: rustfs-packages - pattern: 'rustfs-*' + pattern: "rustfs-*" delete-merged: true diff --git a/.gitignore b/.gitignore index 83b9ef43c..9b8266092 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,7 @@ .devcontainer rustfs/static/* vendor -cli/rustfs-gui/embedded-rustfs/rustfs \ No newline at end of file +cli/rustfs-gui/embedded-rustfs/rustfs +deploy/config/obs.toml +*.log +deploy/certs/* diff --git a/Cargo.lock b/Cargo.lock index 37c39134b..2d5492c48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,6 +63,20 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "const-random", + "getrandom 0.2.15", + "once_cell", + "version_check", + "zerocopy 0.7.35", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -72,6 +86,27 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -139,9 +174,44 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + +[[package]] +name = "api" +version = "0.0.1" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "datafusion", + "ecstore", + "futures", + "futures-core", + "http", + "object_store", + "s3s", + "snafu", + "tokio", + "tokio-util", + "tracing", + "transform-stream", + "url", +] + +[[package]] +name = "appauth" +version = "0.0.1" +dependencies = [ + "base64-simd", + "common", + "hex-simd", + "rand 0.8.5", + "rsa", + "serde", + "serde_json", +] [[package]] name = "arc-swap" @@ -161,12 +231,235 @@ dependencies = [ "password-hash", ] +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "arrow" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5ec52ba94edeed950e4a41f75d35376df196e8cb04437f7280a5aa49f20f796" +dependencies = [ + "arrow-arith", + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-csv", + "arrow-data", + "arrow-ipc", + "arrow-json", + "arrow-ord", + "arrow-row", + "arrow-schema", + "arrow-select", + "arrow-string", +] + +[[package]] +name = "arrow-arith" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc766fdacaf804cb10c7c70580254fcdb5d55cdfda2bc57b02baf5223a3af9e" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "num", +] + +[[package]] +name = "arrow-array" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12fcdb3f1d03f69d3ec26ac67645a8fe3f878d77b5ebb0b15d64a116c212985" +dependencies = [ + "ahash 0.8.11", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "chrono-tz", + "half", + "hashbrown 0.15.2", + "num", +] + +[[package]] +name = "arrow-buffer" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "263f4801ff1839ef53ebd06f99a56cecd1dbaf314ec893d93168e2e860e0291c" +dependencies = [ + "bytes", + "half", + "num", +] + +[[package]] +name = "arrow-cast" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede6175fbc039dfc946a61c1b6d42fd682fcecf5ab5d148fbe7667705798cac9" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", + "atoi", + "base64 0.22.1", + "chrono", + "comfy-table", + "half", + "lexical-core", + "num", + "ryu", +] + +[[package]] +name = "arrow-csv" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1644877d8bc9a0ef022d9153dc29375c2bda244c39aec05a91d0e87ccf77995f" +dependencies = [ + "arrow-array", + "arrow-cast", + "arrow-schema", + "chrono", + "csv", + "csv-core", + "lazy_static", + "regex", +] + +[[package]] +name = "arrow-data" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61cfdd7d99b4ff618f167e548b2411e5dd2c98c0ddebedd7df433d34c20a4429" +dependencies = [ + "arrow-buffer", + "arrow-schema", + "half", + "num", +] + +[[package]] +name = "arrow-ipc" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ff528658b521e33905334723b795ee56b393dbe9cf76c8b1f64b648c65a60c" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "flatbuffers", + "lz4_flex", +] + +[[package]] +name = "arrow-json" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee5b4ca98a7fb2efb9ab3309a5d1c88b5116997ff93f3147efdc1062a6158e9" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-data", + "arrow-schema", + "chrono", + "half", + "indexmap 2.9.0", + "lexical-core", + "memchr", + "num", + "serde", + "serde_json", + "simdutf8", +] + +[[package]] +name = "arrow-ord" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a3334a743bd2a1479dbc635540617a3923b4b2f6870f37357339e6b5363c21" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", +] + +[[package]] +name = "arrow-row" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d1d7a7291d2c5107e92140f75257a99343956871f3d3ab33a7b41532f79cb68" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "half", +] + +[[package]] +name = "arrow-schema" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cfaf5e440be44db5413b75b72c2a87c1f8f0627117d110264048f2969b99e9" + +[[package]] +name = "arrow-select" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69efcd706420e52cd44f5c4358d279801993846d1c2a8e52111853d61d55a619" +dependencies = [ + "ahash 0.8.11", + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "num", +] + +[[package]] +name = "arrow-string" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21546b337ab304a32cfc0770f671db7411787586b45b78b4593ae78e64e2b03" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", + "memchr", + "num", + "regex", + "regex-syntax 0.8.5", +] + [[package]] name = "ashpd" version = "0.8.1" @@ -186,22 +479,19 @@ dependencies = [ [[package]] name = "ashpd" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c39d707614dbcc6bed00015539f488d8e3fe3e66ed60961efc0c90f4b380b3" +checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.8.5", + "rand 0.9.0", "raw-window-handle 0.6.2", "serde", "serde_repr", "tokio", "url", - "wayland-backend", - "wayland-client", - "wayland-protocols", "zbus 5.5.0", ] @@ -229,6 +519,23 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-compression" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06575e6a9673580f52661c92107baabffbf41e2141373441cbcdc47cb733003c" +dependencies = [ + "bzip2", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "xz2", + "zstd", + "zstd-safe", +] + [[package]] name = "async-io" version = "2.4.0" @@ -242,7 +549,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix", + "rustix 0.38.44", "slab", "tracing", "windows-sys 0.59.0", @@ -274,7 +581,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix", + "rustix 0.38.44", "tracing", ] @@ -286,7 +593,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -301,34 +608,12 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix", + "rustix 0.38.44", "signal-hook-registry", "slab", "windows-sys 0.59.0", ] -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - [[package]] name = "async-task" version = "4.7.1" @@ -337,13 +622,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.86" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -384,6 +669,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atomic_enum" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e1aca718ea7b89985790c94aad72d77533063fe00bc497bb79a7c2dae6a661" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -391,22 +687,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] -name = "axum" -version = "0.7.9" +name = "aws-lc-rs" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "19b756939cb2f8dc900aa6dcd505e6e2428e9cae7ff7b028c49e3946efa70878" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f7720b74ed28ca77f90769a71fd8c637a0137f6fae4ae947e1050229cff57f" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "axum" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de45108900e1f9b9242f7f2e254aa3e2c029c921c258fe9e6b4217eeebd54288" dependencies = [ - "async-trait", "axum-core", "bytes", + "form_urlencoded", "futures-util", "http", "http-body", "http-body-util", "hyper", "hyper-util", - "itoa 1.0.14", - "matchit 0.7.3", + "itoa 1.0.15", + "matchit", "memchr", "mime", "percent-encoding", @@ -426,13 +745,12 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ - "async-trait", "bytes", - "futures-util", + "futures-core", "http", "http-body", "http-body-util", @@ -446,10 +764,54 @@ dependencies = [ ] [[package]] -name = "backon" -version = "1.4.0" +name = "axum-extra" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fef586913a57ff189f25c9b3d034356a5bf6b3fa9a7f067588fe1698ba1f5d" +checksum = "45bf463831f5131b7d3c756525b305d40f1185b688565648a92e1392ca35713d" +dependencies = [ + "axum", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "serde", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-server" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "495c05f60d6df0093e8fb6e74aa5846a0ad06abaf96d76166283720bf740f8ab" +dependencies = [ + "arc-swap", + "bytes", + "fs-err", + "http", + "http-body", + "hyper", + "hyper-util", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "backon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0b50b1b78dbadd44ab18b3c794e496f3a139abb9fbc27d9c94c4eebbb96496" dependencies = [ "fastrand", "gloo-timers", @@ -471,6 +833,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -489,9 +857,45 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" + +[[package]] +name = "bigdecimal" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a22f228ab7a1b23027ccc6c350b72868017af7ea8356fbdf19f8d991c690013" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.9.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.100", + "which", +] [[package]] name = "bitflags" @@ -517,6 +921,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "blake3" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389a099b34312839e16420d499a9cad9650541715937ffbdd40d36f49e77eeb3" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block" version = "0.1.6" @@ -534,9 +951,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.11.0-rc.3" +version = "0.11.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd016a0ddc7cb13661bf5576073ce07330a693f8608a1320b4e20561cc12cdc" +checksum = "a229bfd78e4827c91b9b95784f69492c1b77c1ab75a45a8a037b139215086f94" dependencies = [ "hybrid-array", ] @@ -572,6 +989,27 @@ dependencies = [ "piper", ] +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bumpalo" version = "3.17.0" @@ -586,15 +1024,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bytesize" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2c12f985c78475a6b8d629afd0c360260ef34cfef52efccdcfd31972f81c2e" +checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" [[package]] name = "bytestring" @@ -605,6 +1043,25 @@ dependencies = [ "bytes", ] +[[package]] +name = "bzip2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" +dependencies = [ + "bzip2-sys", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "cairo-rs" version = "0.18.5" @@ -632,10 +1089,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.16" +version = "1.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -645,6 +1104,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfb" version = "0.7.3" @@ -717,6 +1185,27 @@ dependencies = [ "windows-link", ] +[[package]] +name = "chrono-tz" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf 0.11.3", +] + +[[package]] +name = "chrono-tz-build" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f10f8c9340e31fc120ff885fcdb54a0b48e474bbd77cab557f0c30a3e569402" +dependencies = [ + "parse-zoneinfo", + "phf_codegen 0.11.3", +] + [[package]] name = "ciborium" version = "0.2.2" @@ -756,10 +1245,21 @@ dependencies = [ ] [[package]] -name = "clap" -version = "4.5.31" +name = "clang-sys" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.6", +] + +[[package]] +name = "clap" +version = "4.5.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -767,9 +1267,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.31" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -779,14 +1279,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -795,6 +1295,15 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "cocoa" version = "0.25.0" @@ -871,6 +1380,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "comfy-table" +version = "7.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a" +dependencies = [ + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "common" version = "0.0.1" @@ -879,7 +1398,7 @@ dependencies = [ "lazy_static", "scopeguard", "tokio", - "tonic", + "tonic 0.13.0", "tracing-error", ] @@ -892,6 +1411,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "config" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595aae20e65c3be792d05818e8c63025294ac3cb7e200f11459063a352a6ef80" +dependencies = [ + "async-trait", + "convert_case 0.6.0", + "json5", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "winnow 0.7.6", + "yaml-rust2", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -904,9 +1442,35 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.10.0" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb3c4a0d3776f7535c32793be81d6d5fec0d48ac70955d9834e643aa249a52f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-oid" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "tiny-keccak", +] [[package]] name = "const-serialize" @@ -926,7 +1490,7 @@ checksum = "04382d0d9df7434af6b1b49ea1a026ef39df1b0738b1cc373368cf175354f6eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -946,7 +1510,7 @@ checksum = "f0d1c4c3cb85e5856b34e829af0035d7154f8c2889b15bbf43c8a6c6786dcab5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -969,6 +1533,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "convert_case" version = "0.4.0" @@ -1067,6 +1637,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32c" version = "0.6.8" @@ -1086,10 +1671,19 @@ dependencies = [ ] [[package]] -name = "crossbeam-channel" -version = "0.5.14" +name = "crc64fast-nvme" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "4955638f00a809894c947f85a024020a20815b65a5eea633798ea7924edab2b3" +dependencies = [ + "crc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -1120,7 +1714,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "test-case", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", ] @@ -1137,13 +1731,11 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.0-rc.1" +version = "0.2.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0b8ce8218c97789f16356e7896b3714f26c2ee1079b79c0b7ae7064bb9089fa" +checksum = "170d71b5b14dec99db7739f6fc7d6ec2db80b78c3acb77db48392ccc3d8a9ea0" dependencies = [ - "getrandom 0.2.15", "hybrid-array", - "rand_core 0.6.4", ] [[package]] @@ -1170,7 +1762,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.98", + "syn 2.0.100", +] + +[[package]] +name = "csv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +dependencies = [ + "csv-core", + "itoa 1.0.15", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +dependencies = [ + "memchr", ] [[package]] @@ -1184,9 +1797,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -1194,26 +1807,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 2.0.98", + "strsim", + "syn 2.0.100", ] [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -1229,12 +1843,505 @@ dependencies = [ "parking_lot_core 0.9.10", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core 0.9.10", +] + [[package]] name = "data-encoding" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +[[package]] +name = "datafusion" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914e6f9525599579abbd90b0f7a55afcaaaa40350b9e9ed52563f126dfe45fd3" +dependencies = [ + "arrow", + "arrow-ipc", + "arrow-schema", + "async-trait", + "bytes", + "bzip2", + "chrono", + "datafusion-catalog", + "datafusion-catalog-listing", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-functions", + "datafusion-functions-aggregate", + "datafusion-functions-nested", + "datafusion-functions-table", + "datafusion-functions-window", + "datafusion-macros", + "datafusion-optimizer", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-optimizer", + "datafusion-physical-plan", + "datafusion-sql", + "flate2", + "futures", + "itertools 0.14.0", + "log", + "object_store", + "parking_lot 0.12.3", + "parquet", + "rand 0.8.5", + "regex", + "sqlparser", + "tempfile", + "tokio", + "url", + "uuid", + "xz2", + "zstd", +] + +[[package]] +name = "datafusion-catalog" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "998a6549e6ee4ee3980e05590b2960446a56b343ea30199ef38acd0e0b9036e2" +dependencies = [ + "arrow", + "async-trait", + "dashmap 6.1.0", + "datafusion-common", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-plan", + "datafusion-sql", + "futures", + "itertools 0.14.0", + "log", + "parking_lot 0.12.3", +] + +[[package]] +name = "datafusion-catalog-listing" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ac10096a5b3c0d8a227176c0e543606860842e943594ccddb45cf42a526e43" +dependencies = [ + "arrow", + "async-trait", + "datafusion-catalog", + "datafusion-common", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "futures", + "log", + "object_store", + "tokio", +] + +[[package]] +name = "datafusion-common" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f53d7ec508e1b3f68bd301cee3f649834fad51eff9240d898a4b2614cfd0a7a" +dependencies = [ + "ahash 0.8.11", + "arrow", + "arrow-ipc", + "base64 0.22.1", + "half", + "hashbrown 0.14.5", + "indexmap 2.9.0", + "libc", + "log", + "object_store", + "parquet", + "paste", + "recursive", + "sqlparser", + "tokio", + "web-time", +] + +[[package]] +name = "datafusion-common-runtime" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0fcf41523b22e14cc349b01526e8b9f59206653037f2949a4adbfde5f8cb668" +dependencies = [ + "log", + "tokio", +] + +[[package]] +name = "datafusion-datasource" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf7f37ad8b6e88b46c7eeab3236147d32ea64b823544f498455a8d9042839c92" +dependencies = [ + "arrow", + "async-compression", + "async-trait", + "bytes", + "bzip2", + "chrono", + "datafusion-catalog", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "flate2", + "futures", + "glob", + "itertools 0.14.0", + "log", + "object_store", + "rand 0.8.5", + "tokio", + "tokio-util", + "url", + "xz2", + "zstd", +] + +[[package]] +name = "datafusion-doc" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7db7a0239fd060f359dc56c6e7db726abaa92babaed2fb2e91c3a8b2fff8b256" + +[[package]] +name = "datafusion-execution" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0938f9e5b6bc5782be4111cdfb70c02b7b5451bf34fd57e4de062a7f7c4e31f1" +dependencies = [ + "arrow", + "dashmap 6.1.0", + "datafusion-common", + "datafusion-expr", + "futures", + "log", + "object_store", + "parking_lot 0.12.3", + "rand 0.8.5", + "tempfile", + "url", +] + +[[package]] +name = "datafusion-expr" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b36c28b00b00019a8695ad7f1a53ee1673487b90322ecbd604e2cf32894eb14f" +dependencies = [ + "arrow", + "chrono", + "datafusion-common", + "datafusion-doc", + "datafusion-expr-common", + "datafusion-functions-aggregate-common", + "datafusion-functions-window-common", + "datafusion-physical-expr-common", + "indexmap 2.9.0", + "paste", + "recursive", + "serde_json", + "sqlparser", +] + +[[package]] +name = "datafusion-expr-common" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f0a851a436c5a2139189eb4617a54e6a9ccb9edc96c4b3c83b3bb7c58b950e" +dependencies = [ + "arrow", + "datafusion-common", + "indexmap 2.9.0", + "itertools 0.14.0", + "paste", +] + +[[package]] +name = "datafusion-functions" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3196e37d7b65469fb79fee4f05e5bb58a456831035f9a38aa5919aeb3298d40" +dependencies = [ + "arrow", + "arrow-buffer", + "base64 0.22.1", + "blake2", + "blake3", + "chrono", + "datafusion-common", + "datafusion-doc", + "datafusion-execution", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-macros", + "hex", + "itertools 0.14.0", + "log", + "md-5", + "rand 0.8.5", + "regex", + "sha2 0.10.8", + "unicode-segmentation", + "uuid", +] + +[[package]] +name = "datafusion-functions-aggregate" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfc2d074d5ee4d9354fdcc9283d5b2b9037849237ddecb8942a29144b77ca05" +dependencies = [ + "ahash 0.8.11", + "arrow", + "datafusion-common", + "datafusion-doc", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions-aggregate-common", + "datafusion-macros", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "half", + "log", + "paste", +] + +[[package]] +name = "datafusion-functions-aggregate-common" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cbceba0f98d921309a9121b702bcd49289d383684cccabf9a92cda1602f3bbb" +dependencies = [ + "ahash 0.8.11", + "arrow", + "datafusion-common", + "datafusion-expr-common", + "datafusion-physical-expr-common", +] + +[[package]] +name = "datafusion-functions-nested" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170e27ce4baa27113ddf5f77f1a7ec484b0dbeda0c7abbd4bad3fc609c8ab71a" +dependencies = [ + "arrow", + "arrow-ord", + "datafusion-common", + "datafusion-doc", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions", + "datafusion-functions-aggregate", + "datafusion-macros", + "datafusion-physical-expr-common", + "itertools 0.14.0", + "log", + "paste", +] + +[[package]] +name = "datafusion-functions-table" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3a06a7f0817ded87b026a437e7e51de7f59d48173b0a4e803aa896a7bd6bb5" +dependencies = [ + "arrow", + "async-trait", + "datafusion-catalog", + "datafusion-common", + "datafusion-expr", + "datafusion-physical-plan", + "parking_lot 0.12.3", + "paste", +] + +[[package]] +name = "datafusion-functions-window" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c608b66496a1e05e3d196131eb9bebea579eed1f59e88d962baf3dda853bc6" +dependencies = [ + "datafusion-common", + "datafusion-doc", + "datafusion-expr", + "datafusion-functions-window-common", + "datafusion-macros", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "log", + "paste", +] + +[[package]] +name = "datafusion-functions-window-common" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2f9d83348957b4ad0cd87b5cb9445f2651863a36592fe5484d43b49a5f8d82" +dependencies = [ + "datafusion-common", + "datafusion-physical-expr-common", +] + +[[package]] +name = "datafusion-macros" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4800e1ff7ecf8f310887e9b54c9c444b8e215ccbc7b21c2f244cfae373b1ece7" +dependencies = [ + "datafusion-expr", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "datafusion-optimizer" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "971c51c54cd309001376fae752fb15a6b41750b6d1552345c46afbfb6458801b" +dependencies = [ + "arrow", + "chrono", + "datafusion-common", + "datafusion-expr", + "datafusion-physical-expr", + "indexmap 2.9.0", + "itertools 0.14.0", + "log", + "recursive", + "regex", + "regex-syntax 0.8.5", +] + +[[package]] +name = "datafusion-physical-expr" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1447c2c6bc8674a16be4786b4abf528c302803fafa186aa6275692570e64d85" +dependencies = [ + "ahash 0.8.11", + "arrow", + "datafusion-common", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-functions-aggregate-common", + "datafusion-physical-expr-common", + "half", + "hashbrown 0.14.5", + "indexmap 2.9.0", + "itertools 0.14.0", + "log", + "paste", + "petgraph", +] + +[[package]] +name = "datafusion-physical-expr-common" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f8c25dcd069073a75b3d2840a79d0f81e64bdd2c05f2d3d18939afb36a7dcb" +dependencies = [ + "ahash 0.8.11", + "arrow", + "datafusion-common", + "datafusion-expr-common", + "hashbrown 0.14.5", + "itertools 0.14.0", +] + +[[package]] +name = "datafusion-physical-optimizer" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68da5266b5b9847c11d1b3404ee96b1d423814e1973e1ad3789131e5ec912763" +dependencies = [ + "arrow", + "datafusion-common", + "datafusion-execution", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "itertools 0.14.0", + "log", + "recursive", +] + +[[package]] +name = "datafusion-physical-plan" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88cc160df00e413e370b3b259c8ea7bfbebc134d32de16325950e9e923846b7f" +dependencies = [ + "ahash 0.8.11", + "arrow", + "arrow-ord", + "arrow-schema", + "async-trait", + "chrono", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions-window-common", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "futures", + "half", + "hashbrown 0.14.5", + "indexmap 2.9.0", + "itertools 0.14.0", + "log", + "parking_lot 0.12.3", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "datafusion-sql" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325a212b67b677c0eb91447bf9a11b630f9fc4f62d8e5d145bf859f5a6b29e64" +dependencies = [ + "arrow", + "bigdecimal", + "datafusion-common", + "datafusion-expr", + "indexmap 2.9.0", + "log", + "recursive", + "regex", + "sqlparser", +] + [[package]] name = "dbus" version = "0.9.7" @@ -1260,15 +2367,57 @@ dependencies = [ ] [[package]] -name = "deranged" -version = "0.3.11" +name = "der" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.100", +] + [[package]] name = "derive_more" version = "0.99.19" @@ -1279,7 +2428,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -1289,19 +2438,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid 0.9.6", "crypto-common 0.1.6", "subtle", ] [[package]] name = "digest" -version = "0.11.0-pre.9" +version = "0.11.0-pre.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2e3d6615d99707295a9673e889bf363a04b2a466bd320c65a72536f7577379" +checksum = "6c478574b20020306f98d61c8ca3322d762e1ff08117422ac6106438605ea516" dependencies = [ - "block-buffer 0.11.0-rc.3", - "const-oid", - "crypto-common 0.2.0-rc.1", + "block-buffer 0.11.0-rc.4", + "const-oid 0.10.1", + "crypto-common 0.2.0-rc.2", "subtle", ] @@ -1381,7 +2531,7 @@ dependencies = [ "dioxus-rsx", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -1400,7 +2550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b0cca3e7a10a4a3df37ea52c4cc7a53e5c9233489e03ee3f2829471fc3099a" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "cocoa 0.25.0", "core-foundation 0.9.4", "dioxus-cli-config", @@ -1494,7 +2644,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe99b48a1348eec385b5c4bd3e80fd863b0d3b47257d34e2ddc58754dec5d128" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "ciborium", "dioxus-desktop", @@ -1575,7 +2725,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -1668,7 +2818,7 @@ dependencies = [ "proc-macro2", "quote", "slab", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -1680,7 +2830,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -1741,7 +2891,7 @@ dependencies = [ "proc-macro2", "quote", "server_fn_macro", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -1792,6 +2942,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1800,16 +2962,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", -] - -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading 0.8.6", + "syn 2.0.100", ] [[package]] @@ -1832,14 +2985,17 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] -name = "downcast-rs" -version = "1.2.1" +name = "dlv-list" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] [[package]] name = "dpi" @@ -1849,9 +3005,9 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" [[package]] name = "dtoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" +checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" [[package]] name = "dtoa-short" @@ -1884,7 +3040,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "tonic", + "tonic 0.13.0", "tower 0.5.2", "url", ] @@ -1914,7 +3070,7 @@ dependencies = [ "madmin", "md-5", "netif", - "nix", + "nix 0.29.0", "num", "num_cpus", "path-absolutize", @@ -1932,15 +3088,15 @@ dependencies = [ "s3s-policy", "serde", "serde_json", - "sha2 0.11.0-pre.4", + "sha2 0.11.0-pre.5", "siphasher 1.0.1", "tempfile", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "tokio", "tokio-stream", "tokio-util", - "tonic", + "tonic 0.13.0", "tower 0.5.2", "tracing", "tracing-error", @@ -1955,9 +3111,9 @@ dependencies = [ [[package]] name = "either" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encoding_rs" @@ -1992,7 +3148,7 @@ checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -2013,7 +3169,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -2024,9 +3180,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -2055,9 +3211,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ "event-listener", "pin-project-lite", @@ -2106,9 +3262,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -2120,6 +3276,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.5.0" @@ -2138,7 +3300,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -2156,6 +3318,22 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-err" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f89bda4c2a21204059a977ed3bfe746677dfd137b83c339e702b0ac91d482aa" +dependencies = [ + "autocfg", + "tokio", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futf" version = "0.1.5" @@ -2235,7 +3413,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -2422,14 +3600,16 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -2514,7 +3694,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -2656,7 +3836,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -2671,7 +3851,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.7.1", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -2680,12 +3860,13 @@ dependencies = [ [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", + "num-traits", ] [[package]] @@ -2694,7 +3875,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.8", ] [[package]] @@ -2702,12 +3883,28 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.2", +] [[package]] name = "heck" @@ -2766,11 +3963,11 @@ dependencies = [ [[package]] name = "hmac" -version = "0.13.0-pre.4" +version = "0.13.0-pre.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b1fb14e4df79f9406b434b60acef9f45c26c50062cccf1346c6103b8c47d58" +checksum = "62c11fc82c6b89c906b4d26b7b5a305d0b3aebd4b458dd1bd0a7ed98c548a28e" dependencies = [ - "digest 0.11.0-pre.9", + "digest 0.11.0-pre.10", ] [[package]] @@ -2798,13 +3995,13 @@ dependencies = [ [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa 1.0.14", + "itoa 1.0.15", ] [[package]] @@ -2819,12 +4016,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", + "futures-core", "http", "http-body", "pin-project-lite", @@ -2832,9 +4029,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -2844,15 +4041,15 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "hybrid-array" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d35805454dc9f8662a98d6d61886ffe26bd465f5960e0e55345c70d5c0d2a9" +checksum = "4dab50e193aebe510fe0e40230145820e02f48dae0cf339ea4204e6e708ff7bd" dependencies = [ "typenum", ] @@ -2871,7 +4068,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.14", + "itoa 1.0.15", "pin-project-lite", "smallvec", "tokio", @@ -2911,9 +4108,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -2921,6 +4118,7 @@ dependencies = [ "http", "http-body", "hyper", + "libc", "pin-project-lite", "socket2", "tokio", @@ -2940,10 +4138,9 @@ dependencies = [ "ecstore", "futures", "ipnetwork", - "itertools", + "itertools 0.14.0", "jsonwebtoken", "lazy_static", - "log", "madmin", "policy", "rand 0.8.5", @@ -2952,7 +4149,7 @@ dependencies = [ "serde_json", "strum", "test-case", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -2960,16 +4157,17 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core 0.61.0", ] [[package]] @@ -3022,9 +4220,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -3046,9 +4244,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -3067,9 +4265,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -3096,7 +4294,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -3138,9 +4336,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -3174,6 +4372,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + [[package]] name = "ipnet" version = "2.11.0" @@ -3201,6 +4405,24 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -3218,9 +4440,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "javascriptcore-rs" @@ -3267,6 +4489,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.2", + "libc", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -3277,13 +4509,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "jsonwebtoken" version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ - "base64", + "base64 0.22.1", "js-sys", "pem", "ring", @@ -3341,6 +4584,79 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "lexical-core" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b765c31809609075565a70b4b71402281283aeda7ecaf4818ac14a7b2ade8958" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2" +dependencies = [ + "lexical-parse-integer", + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-parse-integer" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-util" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "lexical-write-float" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5afc668a27f460fb45a81a757b6bf2f43c2d7e30cb5a2dcd3abf294c78d62bd" +dependencies = [ + "lexical-util", + "lexical-write-integer", + "static_assertions", +] + +[[package]] +name = "lexical-write-integer" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629ddff1a914a836fb245616a7888b62903aae58fa771e1d83943035efa0f978" +dependencies = [ + "lexical-util", + "static_assertions", +] [[package]] name = "libappindicator" @@ -3368,9 +4684,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.170" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libdbus-sys" @@ -3417,6 +4733,24 @@ dependencies = [ "libc", ] +[[package]] +name = "libsystemd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c592dc396b464005f78a5853555b9f240bc5378bf5221acc4e129910b2678869" +dependencies = [ + "hmac 0.12.1", + "libc", + "log", + "nix 0.27.1", + "nom", + "once_cell", + "serde", + "sha2 0.10.8", + "thiserror 1.0.69", + "uuid", +] + [[package]] name = "libxdo" version = "0.6.0" @@ -3436,12 +4770,30 @@ dependencies = [ "x11", ] +[[package]] +name = "libz-sys" +version = "1.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "litemap" version = "0.7.5" @@ -3473,7 +4825,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "tonic", + "tonic 0.13.0", "tracing", "tracing-error", "url", @@ -3492,9 +4844,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "longest-increasing-subsequence" @@ -3511,6 +4863,26 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "lz4_flex" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "mac" version = "0.1.1" @@ -3525,6 +4897,7 @@ dependencies = [ "common", "humantime", "hyper", + "s3s", "serde", "serde_json", "time", @@ -3573,7 +4946,7 @@ dependencies = [ "manganis-core", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -3607,15 +4980,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "matchit" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f926ade0c4e170215ae43342bf13b9310a437609c81f29f86c5df6657582ef9" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "md-5" @@ -3666,9 +5033,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -3800,6 +5167,18 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "nix" version = "0.29.0" @@ -3872,6 +5251,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-complex" version = "0.4.6" @@ -3925,6 +5321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -3952,10 +5349,10 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -4042,6 +5439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" dependencies = [ "bitflags 2.9.0", + "block2 0.6.0", "objc2 0.6.0", "objc2-core-foundation", "objc2-foundation 0.3.0", @@ -4105,7 +5503,6 @@ checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.9.0", "block2 0.5.1", - "dispatch", "libc", "objc2 0.5.2", ] @@ -4175,10 +5572,31 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.20.3" +name = "object_store" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "3cfccb68961a56facde1163f9319e0d15743352344e7808a11795fb99698dcaf" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "futures", + "humantime", + "itertools 0.13.0", + "parking_lot 0.12.3", + "percent-encoding", + "snafu", + "tokio", + "tracing", + "url", + "walkdir", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "opaque-debug" @@ -4186,12 +5604,156 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "opentelemetry" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e87237e2775f74896f9ad219d26a2081751187eb7c9f5c58dde20a23b95d16c" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "opentelemetry-appender-tracing" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e716f864eb23007bdd9dc4aec381e188a1cee28eecf22066772b5fd822b9727d" +dependencies = [ + "opentelemetry", + "tracing", + "tracing-core", + "tracing-log", + "tracing-opentelemetry", + "tracing-subscriber", +] + +[[package]] +name = "opentelemetry-http" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46d7ab32b827b5b495bd90fa95a6cb65ccc293555dcc3199ae2937d2d237c8ed" +dependencies = [ + "async-trait", + "bytes", + "http", + "opentelemetry", + "reqwest", + "tracing", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d899720fe06916ccba71c01d04ecd77312734e2de3467fd30d9d580c8ce85656" +dependencies = [ + "futures-core", + "http", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "reqwest", + "thiserror 2.0.12", + "tokio", + "tonic 0.12.3", + "tracing", +] + +[[package]] +name = "opentelemetry-prometheus" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "098a71a4430bb712be6130ed777335d2e5b19bc8566de5f2edddfce906def6ab" +dependencies = [ + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "prometheus", + "tracing", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c40da242381435e18570d5b9d50aca2a4f4f4d8e146231adb4e7768023309b3" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic 0.12.3", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b29a9f89f1a954936d5aa92f19b2feec3c8f3971d3e96206640db7f9706ae3" + +[[package]] +name = "opentelemetry-stdout" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e27d446dabd68610ef0b77d07b102ecde827a4596ea9c01a4d3811e945b286" +dependencies = [ + "chrono", + "futures-util", + "opentelemetry", + "opentelemetry_sdk", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afdefb21d1d47394abc1ba6c57363ab141be19e27cc70d0e422b7f303e4d290b" +dependencies = [ + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "opentelemetry", + "percent-encoding", + "rand 0.9.0", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -4288,11 +5850,56 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.9", + "redox_syscall 0.5.11", "smallvec", "windows-targets 0.52.6", ] +[[package]] +name = "parquet" +version = "54.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb15796ac6f56b429fd99e33ba133783ad75b27c36b4b5ce06f1f82cc97754e" +dependencies = [ + "ahash 0.8.11", + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-data", + "arrow-ipc", + "arrow-schema", + "arrow-select", + "base64 0.22.1", + "brotli", + "bytes", + "chrono", + "flate2", + "futures", + "half", + "hashbrown 0.15.2", + "lz4_flex", + "num", + "num-bigint", + "object_store", + "paste", + "seq-macro", + "simdutf8", + "snap", + "thrift", + "tokio", + "twox-hash", + "zstd", +] + +[[package]] +name = "parse-zoneinfo" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" +dependencies = [ + "regex", +] + [[package]] name = "password-hash" version = "0.5.0" @@ -4334,6 +5941,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "pbkdf2" version = "0.12.2" @@ -4350,16 +5963,70 @@ version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ - "base64", + "base64 0.22.1", "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +dependencies = [ + "memchr", + "thiserror 2.0.12", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "pest_meta" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +dependencies = [ + "once_cell", + "pest", + "sha2 0.10.8", +] + [[package]] name = "petgraph" version = "0.7.1" @@ -4367,7 +6034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.7.1", + "indexmap 2.9.0", ] [[package]] @@ -4390,6 +6057,15 @@ dependencies = [ "phf_shared 0.10.0", ] +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared 0.11.3", +] + [[package]] name = "phf_codegen" version = "0.8.0" @@ -4410,6 +6086,16 @@ dependencies = [ "phf_shared 0.10.0", ] +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + [[package]] name = "phf_generator" version = "0.8.0" @@ -4483,22 +6169,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -4525,10 +6211,31 @@ dependencies = [ ] [[package]] -name = "pkg-config" -version = "0.3.31" +name = "pkcs1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "png" @@ -4554,10 +6261,9 @@ dependencies = [ "crypto", "futures", "ipnetwork", - "itertools", + "itertools 0.14.0", "jsonwebtoken", "lazy_static", - "log", "madmin", "rand 0.8.5", "regex", @@ -4565,7 +6271,7 @@ dependencies = [ "serde_json", "strum", "test-case", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -4581,7 +6287,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix", + "rustix 0.38.44", "tracing", "windows-sys 0.59.0", ] @@ -4629,11 +6335,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy 0.8.24", ] [[package]] @@ -4644,12 +6350,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.29" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -4673,9 +6379,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit 0.22.24", ] @@ -4712,9 +6418,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -4727,10 +6433,25 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "version_check", ] +[[package]] +name = "prometheus" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ca5326d8d0b950a9acd87e6a3f94745394f62e4dae1b1ee22b2bc0c394af43a" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot 0.12.3", + "protobuf", + "thiserror 2.0.12", +] + [[package]] name = "prost" version = "0.13.5" @@ -4748,7 +6469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck 0.5.0", - "itertools", + "itertools 0.14.0", "log", "multimap", "once_cell", @@ -4757,7 +6478,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.98", + "syn 2.0.100", "tempfile", ] @@ -4768,10 +6489,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -4785,9 +6506,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.7.1" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" dependencies = [ "once_cell", "protobuf-support", @@ -4796,9 +6517,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.7.1" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" dependencies = [ "thiserror 1.0.69", ] @@ -4813,16 +6534,42 @@ dependencies = [ "prost-build", "protobuf", "tokio", - "tonic", + "tonic 0.13.0", "tonic-build", "tower 0.5.2", ] [[package]] -name = "quick-xml" -version = "0.37.2" +name = "psm" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" +checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" +dependencies = [ + "cc", +] + +[[package]] +name = "query" +version = "0.0.1" +dependencies = [ + "api", + "async-recursion", + "async-trait", + "datafusion", + "derive_builder", + "futures", + "parking_lot 0.12.3", + "s3s", + "snafu", + "tokio", + "tracing", +] + +[[package]] +name = "quick-xml" +version = "0.37.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369" dependencies = [ "memchr", "serde", @@ -4830,37 +6577,39 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" dependencies = [ "bytes", + "cfg_aliases", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", "rustls", "socket2", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tracing", + "web-time", ] [[package]] name = "quinn-proto" -version = "0.11.9" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" dependencies = [ "bytes", - "getrandom 0.2.15", - "rand 0.8.5", + "getrandom 0.3.2", + "rand 0.9.0", "ring", "rustc-hash 2.1.1", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.11", + "thiserror 2.0.12", "tinyvec", "tracing", "web-time", @@ -4868,27 +6617,33 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944" +checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" dependencies = [ "cfg_aliases", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.7.3" @@ -4922,7 +6677,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.21", + "zerocopy 0.8.24", ] [[package]] @@ -4979,7 +6734,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.2", ] [[package]] @@ -5012,6 +6767,56 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "rdkafka" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b52c81ac3cac39c9639b95c20452076e74b8d9a71bc6fc4d83407af2ea6fff" +dependencies = [ + "futures-channel", + "futures-util", + "libc", + "log", + "rdkafka-sys", + "serde", + "serde_derive", + "serde_json", + "slab", + "tokio", +] + +[[package]] +name = "rdkafka-sys" +version = "4.8.0+2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced38182dc436b3d9df0c77976f37a67134df26b050df1f0006688e46fc4c8be" +dependencies = [ + "libc", + "libz-sys", + "num_enum", + "pkg-config", +] + +[[package]] +name = "recursive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" +dependencies = [ + "recursive-proc-macro-impl", + "stacker", +] + +[[package]] +name = "recursive-proc-macro-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" +dependencies = [ + "quote", + "syn 2.0.100", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -5023,9 +6828,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ "bitflags 2.9.0", ] @@ -5049,7 +6854,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -5113,11 +6918,11 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.12" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "encoding_rs", "futures-channel", @@ -5186,19 +6991,19 @@ dependencies = [ [[package]] name = "rfd" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a24763657bff09769a8ccf12c8b8a50416fb035fe199263b4c5071e4e3f006f" +checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" dependencies = [ - "ashpd 0.10.2", - "block2 0.5.1", - "core-foundation 0.10.0", - "core-foundation-sys", + "ashpd 0.11.0", + "block2 0.6.0", + "dispatch2", "js-sys", "log", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", "pollster 0.4.0", "raw-window-handle 0.6.2", "urlencoding", @@ -5210,9 +7015,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.11" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", @@ -5245,10 +7050,42 @@ dependencies = [ ] [[package]] -name = "rust-embed" -version = "8.6.0" +name = "ron" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b3aba5104622db5c9fc61098de54708feb732e7763d7faa2fa625899f00bf6f" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.7", + "bitflags 2.9.0", + "serde", + "serde_derive", +] + +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid 0.9.6", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rust-embed" +version = "8.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fbc0ee50fcb99af7cebb442e5df7b5b45e9460ffa3f8f549cd26b862bec49d" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -5257,28 +7094,39 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.6.0" +version = "8.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f198c73be048d2c5aa8e12f7960ad08443e56fd39cc26336719fdb4ea0ebaae" +checksum = "6bf418c9a2e3f6663ca38b8a7134cc2c2167c9d69688860e8961e3faa731702e" dependencies = [ "proc-macro2", "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.98", + "syn 2.0.100", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "8.6.0" +version = "8.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a2fcdc9f40c8dc2922842ca9add611ad19f332227fc651d015881ad1552bd9a" +checksum = "08d55b95147fe01265d06b3955db798bdaed52e60e2211c41137701b3aba8e21" dependencies = [ "sha2 0.10.8", "walkdir", ] +[[package]] +name = "rust-ini" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" +dependencies = [ + "cfg-if", + "ordered-multimap", + "trim-in-place", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -5310,44 +7158,50 @@ dependencies = [ name = "rustfs" version = "0.1.0" dependencies = [ + "api", + "appauth", "async-trait", "atoi", + "atomic_enum", "axum", + "axum-extra", + "axum-server", "bytes", "chrono", "clap", "common", "const-str", "crypto", + "datafusion", "ecstore", "flatbuffers", "futures", "futures-util", - "h2", "http", "http-body", "hyper", "hyper-util", "iam", - "jsonwebtoken", "lazy_static", + "libsystemd", "local-ip-address", "lock", - "log", "madmin", - "matchit 0.8.6", + "matchit", "mime", "mime_guess", "netif", "pin-project-lite", "policy", - "prost", "prost-build", - "prost-types", - "protobuf", "protos", + "query", "rmp-serde", "rust-embed", + "rustfs-obs", + "rustls", + "rustls-pemfile", + "rustls-pki-types", "s3s", "serde", "serde_json", @@ -5355,18 +7209,15 @@ dependencies = [ "shadow-rs", "time", "tokio", + "tokio-rustls", "tokio-stream", "tokio-util", - "tonic", + "tonic 0.13.0", "tonic-build", - "tonic-reflection", "tower 0.5.2", "tower-http", "tracing", - "tracing-error", - "tracing-subscriber", "transform-stream", - "url", "uuid", ] @@ -5377,11 +7228,10 @@ dependencies = [ "chrono", "dioxus", "dirs 6.0.0", - "futures-util", "hex", "keyring", "lazy_static", - "rfd 0.15.2", + "rfd 0.15.3", "rust-embed", "serde", "serde_json", @@ -5391,6 +7241,35 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "rustfs-obs" +version = "0.0.1" +dependencies = [ + "async-trait", + "chrono", + "config", + "local-ip-address", + "opentelemetry", + "opentelemetry-appender-tracing", + "opentelemetry-otlp", + "opentelemetry-prometheus", + "opentelemetry-semantic-conventions", + "opentelemetry-stdout", + "opentelemetry_sdk", + "prometheus", + "rdkafka", + "reqwest", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tracing", + "tracing-core", + "tracing-error", + "tracing-opentelemetry", + "tracing-subscriber", +] + [[package]] name = "rustix" version = "0.38.44" @@ -5400,16 +7279,30 @@ dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.23" +version = "0.23.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" +checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -5439,10 +7332,11 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.8" +version = "0.103.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -5450,20 +7344,20 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "s3s" -version = "0.11.0-dev" -source = "git+https://github.com/Nugine/s3s.git?rev=ab139f72fe768fb9d8cecfe36269451da1ca9779#ab139f72fe768fb9d8cecfe36269451da1ca9779" +version = "0.12.0-dev" +source = "git+https://github.com/Nugine/s3s.git?rev=3ad13ace7af703c3c8afc99cf19f4c18c82603a3#3ad13ace7af703c3c8afc99cf19f4c18c82603a3" dependencies = [ "arrayvec", "async-trait", @@ -5472,17 +7366,20 @@ dependencies = [ "bytes", "bytestring", "chrono", + "const-str", "crc32c", "crc32fast", - "digest 0.11.0-pre.9", + "crc64fast-nvme", "futures", "hex-simd", - "hmac 0.13.0-pre.4", + "hmac 0.13.0-pre.5", + "http", "http-body", "http-body-util", "httparse", "hyper", - "itoa 1.0.14", + "itoa 1.0.15", + "md-5", "memchr", "mime", "nom", @@ -5492,12 +7389,12 @@ dependencies = [ "quick-xml", "serde", "serde_urlencoded", - "sha1 0.11.0-pre.4", - "sha2 0.11.0-pre.4", + "sha1 0.11.0-pre.5", + "sha2 0.11.0-pre.5", "smallvec", "std-next", "sync_wrapper", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "tokio", "tower 0.5.2", @@ -5509,13 +7406,13 @@ dependencies = [ [[package]] name = "s3s-policy" -version = "0.11.0-dev" -source = "git+https://github.com/Nugine/s3s.git?rev=ab139f72fe768fb9d8cecfe36269451da1ca9779#ab139f72fe768fb9d8cecfe36269451da1ca9779" +version = "0.12.0-dev" +source = "git+https://github.com/Nugine/s3s.git?rev=3ad13ace7af703c3c8afc99cf19f4c18c82603a3#3ad13ace7af703c3c8afc99cf19f4c18c82603a3" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -5527,12 +7424,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -5597,9 +7488,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "send_wrapper" @@ -5611,10 +7502,16 @@ dependencies = [ ] [[package]] -name = "serde" -version = "1.0.218" +name = "seq-macro" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -5632,22 +7529,22 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] name = "serde_json" -version = "1.0.139" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "itoa 1.0.14", + "itoa 1.0.15", "memchr", "ryu", "serde", @@ -5655,11 +7552,11 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" dependencies = [ - "itoa 1.0.14", + "itoa 1.0.15", "serde", ] @@ -5676,13 +7573,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -5701,7 +7598,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.14", + "itoa 1.0.15", "ryu", "serde", ] @@ -5714,7 +7611,7 @@ checksum = "4fae7a3038a32e5a34ba32c6c45eb4852f8affaf8b794ebfcd4b1099e2d62ebe" dependencies = [ "bytes", "const_format", - "dashmap", + "dashmap 5.5.3", "futures", "gloo-net", "http", @@ -5745,7 +7642,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "xxhash-rust", ] @@ -5756,7 +7653,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f2aa8119b558a17992e0ac1fd07f080099564f24532858811ce04f742542440" dependencies = [ "server_fn_macro", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -5782,13 +7679,13 @@ dependencies = [ [[package]] name = "sha1" -version = "0.11.0-pre.4" +version = "0.11.0-pre.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9540978cef7a8498211c1b1c14e5ce920fe5bd524ea84f4a3d72d4602515ae93" +checksum = "55f44e40722caefdd99383c25d3ae52a1094a1951215ae76f68837ece4e7f566" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.11.0-pre.9", + "digest 0.11.0-pre.10", ] [[package]] @@ -5804,13 +7701,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.11.0-pre.4" +version = "0.11.0-pre.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "540c0893cce56cdbcfebcec191ec8e0f470dd1889b6e7a0b503e310a94a168f5" +checksum = "19b4241d1a56954dce82cecda5c8e9c794eef6f53abe5e5216bac0a0ea71ffa7" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.11.0-pre.9", + "digest 0.11.0-pre.10", ] [[package]] @@ -5867,6 +7764,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -5887,7 +7794,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", ] @@ -5929,7 +7836,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33a1b4f13e2bbf2f5b29d09dfebc9de69229ffee245aed80e3b70f9b5fd28c06" dependencies = [ "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -5953,15 +7860,43 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "snafu" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +dependencies = [ + "backtrace", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -5999,12 +7934,57 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlparser" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66e3b7374ad4a6af849b08b3e7a6eda0edbd82f0fd59b57e22671bf16979899" +dependencies = [ + "log", + "recursive", + "sqlparser_derive", +] + +[[package]] +name = "sqlparser_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da5fc6819faabb412da764b99d3b713bb55083c11e7e0c00144d386cd6a1939c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -6013,19 +7993,19 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "std-next" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafdb55260d9b29c04fa52351e0db2a4aaeadc462cd884ccd7771c5a31aaf1aa" +checksum = "1258af3e6890fc6369f89e9d5c052c3406707aef3f8e836697155fb6e07153bf" dependencies = [ "simdutf8", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] name = "string_cache" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", "parking_lot 0.12.3", @@ -6071,7 +8051,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -6093,9 +8073,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.98" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -6119,7 +8099,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -6204,7 +8184,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -6215,15 +8195,14 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.17.1" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", - "getrandom 0.3.1", + "getrandom 0.3.2", "once_cell", - "rustix", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -6256,7 +8235,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -6267,7 +8246,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "test-case-core", ] @@ -6288,11 +8267,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] @@ -6303,18 +8282,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -6328,13 +8307,24 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.3.37" +name = "thrift" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" +dependencies = [ + "byteorder", + "integer-encoding", + "ordered-float", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", - "itoa 1.0.14", + "itoa 1.0.15", "libc", "num-conv", "num_threads", @@ -6346,20 +8336,29 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.7.6" @@ -6387,14 +8386,15 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.43.0" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", "libc", "mio", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", "socket2", @@ -6411,7 +8411,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -6437,9 +8437,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ "bytes", "futures-core", @@ -6476,7 +8476,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "toml_datetime", "winnow 0.5.40", ] @@ -6487,7 +8487,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "toml_datetime", "winnow 0.5.40", ] @@ -6498,11 +8498,11 @@ version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.7.3", + "winnow 0.7.6", ] [[package]] @@ -6511,10 +8511,36 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ - "async-stream", + "async-trait", + "base64 0.22.1", + "bytes", + "flate2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85839f0b32fd242bb3209262371d07feda6d780d16ee9d2bc88581b89da1549b" +dependencies = [ "async-trait", "axum", - "base64", + "base64 0.22.1", "bytes", "flate2", "h2", @@ -6527,12 +8553,10 @@ dependencies = [ "percent-encoding", "pin-project", "prost", - "rustls-pemfile", "socket2", "tokio", - "tokio-rustls", "tokio-stream", - "tower 0.4.13", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -6540,29 +8564,16 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +checksum = "d85f0383fadd15609306383a90e85eaed44169f931a5d2be1b42c76ceff1825e" dependencies = [ "prettyplease", "proc-macro2", "prost-build", "prost-types", "quote", - "syn 2.0.98", -] - -[[package]] -name = "tonic-reflection" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "878d81f52e7fcfd80026b7fdb6a9b578b3c3653ba987f87f0dce4b64043cba27" -dependencies = [ - "prost", - "prost-types", - "tokio", - "tokio-stream", - "tonic", + "syn 2.0.100", ] [[package]] @@ -6593,9 +8604,12 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", + "indexmap 2.9.0", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -6659,7 +8673,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -6693,6 +8707,24 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd8e764bd6f5813fd8bebc3117875190c5b0415be8f7f8059bffb6ecd979c444" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", + "web-time", +] + [[package]] name = "tracing-serde" version = "0.2.0" @@ -6762,10 +8794,16 @@ dependencies = [ "objc2-foundation 0.3.0", "once_cell", "png", - "thiserror 2.0.11", + "thiserror 2.0.12", "windows-sys 0.59.0", ] +[[package]] +name = "trim-in-place" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" + [[package]] name = "try-lock" version = "0.2.5" @@ -6790,12 +8828,28 @@ dependencies = [ "utf-8", ] +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "uds_windows" version = "1.1.0" @@ -6815,9 +8869,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-segmentation" @@ -6825,6 +8879,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -6891,25 +8951,27 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.15.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.2", + "js-sys", "rand 0.9.0", "serde", "uuid-macro-internal", + "wasm-bindgen", ] [[package]] name = "uuid-macro-internal" -version = "1.15.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9521621447c21497fac206ffe6e9f642f977c4f82eeba9201055f64884d9cb01" +checksum = "72dcd78c4f979627a754f5522cea6e6a25e55139056535fe6e69c506cd64a862" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -6918,6 +8980,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version-compare" version = "0.2.0" @@ -6974,7 +9042,7 @@ checksum = "59195a1db0e95b920366d949ba5e0d3fc0e70b67c09be15ce5abb790106b0571" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -6991,9 +9059,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -7020,7 +9088,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "wasm-bindgen-shared", ] @@ -7055,7 +9123,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7082,66 +9150,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wayland-backend" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" -dependencies = [ - "cc", - "downcast-rs", - "rustix", - "scoped-tls", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-client" -version = "0.31.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" -dependencies = [ - "bitflags 2.9.0", - "rustix", - "wayland-backend", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols" -version = "0.32.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" -dependencies = [ - "bitflags 2.9.0", - "wayland-backend", - "wayland-client", - "wayland-scanner", -] - -[[package]] -name = "wayland-scanner" -version = "0.31.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" -dependencies = [ - "proc-macro2", - "quick-xml", - "quote", -] - -[[package]] -name = "wayland-sys" -version = "0.31.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" -dependencies = [ - "dlib", - "log", - "pkg-config", -] - [[package]] name = "web-sys" version = "0.3.77" @@ -7242,8 +9250,8 @@ dependencies = [ "webview2-com-sys", "windows", "windows-core 0.58.0", - "windows-implement", - "windows-interface", + "windows-implement 0.58.0", + "windows-interface 0.58.0", ] [[package]] @@ -7254,7 +9262,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -7268,6 +9276,18 @@ dependencies = [ "windows-core 0.58.0", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "winapi" version = "0.3.9" @@ -7311,24 +9331,28 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.52.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] [[package]] name = "windows-core" -version = "0.58.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", - "windows-targets 0.52.6", + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.2", + "windows-strings 0.4.0", ] [[package]] @@ -7339,7 +9363,18 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] @@ -7350,24 +9385,35 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] name = "windows-link" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] name = "windows-registry" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ - "windows-result", - "windows-strings", - "windows-targets 0.52.6", + "windows-result 0.3.2", + "windows-strings 0.3.1", + "windows-targets 0.53.0", ] [[package]] @@ -7379,16 +9425,43 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -7464,7 +9537,7 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", @@ -7472,10 +9545,26 @@ dependencies = [ ] [[package]] -name = "windows-version" -version = "0.1.3" +name = "windows-targets" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bfbcc4996dd183ff1376a20ade1242da0d2dcaff83cc76710a588d24fd4c5db" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows-version" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" dependencies = [ "windows-link", ] @@ -7498,6 +9587,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -7516,6 +9611,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -7534,12 +9635,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -7558,6 +9671,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -7576,6 +9695,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -7594,6 +9719,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -7612,6 +9743,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -7623,18 +9760,18 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags 2.9.0", ] @@ -7666,7 +9803,7 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac0099a336829fbf54c26b5f620c68980ebbe37196772aeaf6118df4931b5cb0" dependencies = [ - "base64", + "base64 0.22.1", "block", "cocoa 0.26.0", "core-graphics 0.24.0", @@ -7737,6 +9874,26 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "yaml-rust2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818913695e83ece1f8d2a1c52d54484b7b46d0f9c06beeb2649b9da50d9b512d" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] + [[package]] name = "yoke" version = "0.7.5" @@ -7757,7 +9914,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "synstructure", ] @@ -7777,7 +9934,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix", + "nix 0.29.0", "ordered-stream", "rand 0.8.5", "serde", @@ -7808,7 +9965,7 @@ dependencies = [ "futures-core", "futures-lite", "hex", - "nix", + "nix 0.29.0", "ordered-stream", "serde", "serde_repr", @@ -7817,7 +9974,7 @@ dependencies = [ "tracing", "uds_windows", "windows-sys 0.59.0", - "winnow 0.7.3", + "winnow 0.7.6", "xdg-home", "zbus_macros 5.5.0", "zbus_names 4.2.0", @@ -7830,10 +9987,10 @@ version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "zvariant_utils 2.1.0", ] @@ -7843,10 +10000,10 @@ version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "zbus_names 4.2.0", "zvariant 5.4.0", "zvariant_utils 3.2.0", @@ -7871,7 +10028,7 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.3", + "winnow 0.7.6", "zvariant 5.4.0", ] @@ -7881,17 +10038,16 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive 0.7.35", ] [[package]] name = "zerocopy" -version = "0.8.21" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" dependencies = [ - "zerocopy-derive 0.8.21", + "zerocopy-derive 0.8.24", ] [[package]] @@ -7902,18 +10058,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] name = "zerocopy-derive" -version = "0.8.21" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -7933,7 +10089,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "synstructure", ] @@ -7962,7 +10118,35 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.15+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +dependencies = [ + "cc", + "pkg-config", ] [[package]] @@ -7990,7 +10174,7 @@ dependencies = [ "serde", "static_assertions", "url", - "winnow 0.7.3", + "winnow 0.7.6", "zvariant_derive 5.4.0", "zvariant_utils 3.2.0", ] @@ -8001,10 +10185,10 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "zvariant_utils 2.1.0", ] @@ -8014,10 +10198,10 @@ version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", "zvariant_utils 3.2.0", ] @@ -8029,7 +10213,7 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.100", ] [[package]] @@ -8042,6 +10226,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.98", - "winnow 0.7.3", + "syn 2.0.100", + "winnow 0.7.6", ] diff --git a/Cargo.toml b/Cargo.toml index 9e3e80616..0dc72ad96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,10 @@ members = [ "iam", # Identity and Access Management "crypto", # Cryptography and security features "cli/rustfs-gui", # Graphical user interface client + "crates/obs", # Observability utilities + "s3select/api", + "s3select/query", + "appauth", ] resolver = "2" @@ -29,12 +33,21 @@ all = "warn" [workspace.dependencies] madmin = { path = "./madmin" } -async-trait = "0.1.86" -backon = "1.3.0" -bytes = "1.9.0" -bytesize = "1.3.0" +atoi = "2.0.0" +async-recursion = "1.1.1" +async-trait = "0.1.88" +atomic_enum = "0.3.0" +axum = "0.8.3" +axum-extra = "0.10.1" +axum-server = { version = "0.7.2", features = ["tls-rustls"] } +backon = "1.5.0" +bytes = "1.10.1" +bytesize = "1.3.3" chrono = { version = "0.4.40", features = ["serde"] } -clap = { version = "4.5.31", features = ["derive", "env"] } +clap = { version = "4.5.35", features = ["derive", "env"] } +config = "0.15.11" +datafusion = "46.0.0" +derive_builder = "0.20.2" dioxus = { version = "0.6.3", features = ["router"] } dirs = "6.0.0" ecstore = { path = "./ecstore" } @@ -42,78 +55,93 @@ flatbuffers = "24.12.23" futures = "0.3.31" futures-util = "0.3.31" common = { path = "./common/common" } -policy = {path = "./policy"} +policy = { path = "./policy" } hex = "0.4.3" hyper = "1.6.0" -hyper-util = { version = "0.1.10", features = [ +hyper-util = { version = "0.1.11", features = [ "tokio", "server-auto", "server-graceful", ] } -http = "1.2.0" +http = "1.3.1" http-body = "1.0.1" -humantime = "2.1.0" -keyring = { version = "3.6.1", features = ["apple-native", "windows-native", "sync-secret-service"] } +humantime = "2.2.0" +jsonwebtoken = "9.3.1" +keyring = { version = "3.6.2", features = ["apple-native", "windows-native", "sync-secret-service"] } lock = { path = "./common/lock" } lazy_static = "1.5.0" +libsystemd = { version = "0.7" } local-ip-address = "0.6.3" +matchit = "0.8.4" +md-5 = "0.10.6" mime = "0.3.17" netif = "0.1.6" +opentelemetry = { version = "0.29.1" } +opentelemetry-appender-tracing = { version = "0.29.1", features = ["experimental_use_tracing_span_context", "experimental_metadata_attributes"] } +opentelemetry_sdk = { version = "0.29" } +opentelemetry-stdout = { version = "0.29.0" } +opentelemetry-otlp = { version = "0.29" } +opentelemetry-prometheus = { version = "0.29.1" } +opentelemetry-semantic-conventions = { version = "0.29.0", features = ["semconv_experimental"] } pin-project-lite = "0.2" +prometheus = "0.14.0" # pin-utils = "0.1.0" -prost = "0.13.4" -prost-build = "0.13.4" -prost-types = "0.13.4" +prost = "0.13.5" +prost-build = "0.13.5" +prost-types = "0.13.5" protobuf = "3.7" protos = { path = "./common/protos" } rand = "0.8.5" -reqwest = { version = "0.12.12", default-features = false, features = ["rustls-tls", "charset", "http2", "macos-system-configuration", "stream","blocking"] } -rfd = { version = "0.15.2", default-features = false, features = ["xdg-portal", "tokio"] } +rdkafka = { version = "0.37", features = ["tokio"] } +reqwest = { version = "0.12.15", default-features = false, features = ["rustls-tls", "charset", "http2", "macos-system-configuration", "stream", "json", "blocking"] } +rfd = { version = "0.15.3", default-features = false, features = ["xdg-portal", "tokio"] } rmp = "0.8.14" rmp-serde = "1.3.0" -rust-embed = "8.6.0" -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 = "ab139f72fe768fb9d8cecfe36269451da1ca9779" } -shadow-rs = { version = "0.38.0", default-features = false } -serde = { version = "1.0.217", features = ["derive"] } -serde_json = "1.0.138" +rustfs-obs = { path = "crates/obs", version = "0.0.1" } +rust-embed = "8.7.0" +rustls = { version = "0.23.26" } +rustls-pki-types = "1.11.0" +rustls-pemfile = "2.2.0" +s3s = { git = "https://github.com/Nugine/s3s.git", rev = "3ad13ace7af703c3c8afc99cf19f4c18c82603a3" } +s3s-policy = { git = "https://github.com/Nugine/s3s.git", rev = "3ad13ace7af703c3c8afc99cf19f4c18c82603a3" } +shadow-rs = { version = "0.38.1", default-features = false } +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" +serde_urlencoded = "0.7.1" sha2 = "0.10.8" -tempfile = "3.16.0" -thiserror = "2.0.11" -time = { version = "0.3.37", features = [ +snafu = "0.8.5" +tempfile = "3.19.1" +test-case = "3.3.1" +thiserror = "2.0.12" +time = { version = "0.3.41", features = [ "std", "parsing", "formatting", "macros", "serde", ] } -tokio = { version = "1.43.0", features = ["fs", "rt-multi-thread"] } -tonic = { version = "0.12.3", features = ["gzip"] } -tonic-build = "0.12.3" -tonic-reflection = "0.12" +tokio = { version = "1.44.2", features = ["fs", "rt-multi-thread"] } +tonic = { version = "0.13.0", features = ["gzip"] } +tonic-build = "0.13.0" +tokio-rustls = { version = "0.26", default-features = false } tokio-stream = "0.1.17" +tokio-util = { version = "0.7.14", features = ["io", "compat"] } tower = { version = "0.5.2", features = ["timeout"] } +tower-http = { version = "0.6.2", features = ["cors"] } tracing = "0.1.41" +tracing-core = "0.1.33" tracing-error = "0.2.1" tracing-subscriber = { version = "0.3.19", features = ["env-filter", "time"] } tracing-appender = "0.2.3" +tracing-opentelemetry = "0.30" transform-stream = "0.3.1" url = "2.5.4" -uuid = { version = "1.15.1", features = [ +uuid = { version = "1.16.0", features = [ "v4", "fast-rng", "macro-diagnostics", ] } -log = "0.4.25" -axum = "0.7.9" -md-5 = "0.10.6" workers = { path = "./common/workers" } -test-case = "3.3.1" -zip = "2.2.3" - - [profile.wasm-dev] inherits = "dev" diff --git a/Dockerfile.obs b/Dockerfile.obs new file mode 100644 index 000000000..fdcfcc3fe --- /dev/null +++ b/Dockerfile.obs @@ -0,0 +1,17 @@ +FROM alpine:latest + +# RUN apk add --no-cache +# 如果 rustfs 有依赖,可以在这里添加,例如: +# RUN apk add --no-cache openssl +# RUN apk add --no-cache bash # 安装 Bash + +WORKDIR /app + +COPY ./target/x86_64-unknown-linux-musl/release/rustfs /app/rustfs + +RUN chmod +x /app/rustfs + +EXPOSE 9000 +EXPOSE 9002 + +CMD ["/app/rustfs"] \ No newline at end of file diff --git a/README.md b/README.md index dbb7e9f6e..3e1de2cb0 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,32 @@ # How to compile RustFS -| Must package | Version | -|--------------|---------| -| Rust | 1.8.5 | -| protoc | 27.0 | -| flatc | 24.0+ | +| Must package | Version | download link | +|--------------|---------|----------------------------------------------------------------------------------------------------------------------------------| +| Rust | 1.8.5 | https://www.rust-lang.org/tools/install | +| protoc | 30.2 | [protoc-30.2-linux-x86_64.zip](https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protoc-30.2-linux-x86_64.zip) | +| flatc | 24.0+ | [Linux.flatc.binary.g++-13.zip](https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip) | Download Links: -https://github.com/google/flatbuffers/releases/download/v24.3.25/Linux.flatc.binary.g++-13.zip +https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip -https://github.com/protocolbuffers/protobuf/releases/download/v27.0/protoc-27.0-linux-x86_64.zip +https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protoc-30.2-linux-x86_64.zip Or use Docker: +```yml - uses: arduino/setup-protoc@v3 with: - version: "27.0" + version: "30.2" - uses: Nugine/setup-flatc@v1 with: - version: "24.3.25" + version: "25.2.10" +``` # How to add Console web -1. wget [http://dl.rustfs.com/console/console.latest.tar.gz](https://dl.rustfs.com/console/rustfs-console-latest.zip) +1. `wget https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip` 2. mkdir in this repos folder `./rustfs/static` @@ -32,14 +34,34 @@ Or use Docker: # Star RustFS -Add Env infomation: +Add Env Information: ``` -export RUST_LOG="rustfs=debug,ecstore=debug,s3s=debug,iam=debug" 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:9001" +# 具体路径修改为配置文件真实路径,obs.example.toml 仅供参考 其中`RUSTFS_OBS_CONFIG` 和下面变量二选一 +export RUSTFS_OBS_CONFIG="./deploy/config/obs.example.toml" + +# 如下变量需要必须参数都有值才可以,以及会覆盖配置文件`obs.example.toml`中的值 +export RUSTFS__OBSERVABILITY__ENDPOINT=http://localhost:4317 +export RUSTFS__OBSERVABILITY__USE_STDOUT=true +export RUSTFS__OBSERVABILITY__SAMPLE_RATIO=2.0 +export RUSTFS__OBSERVABILITY__METER_INTERVAL=30 +export RUSTFS__OBSERVABILITY__SERVICE_NAME=rustfs +export RUSTFS__OBSERVABILITY__SERVICE_VERSION=0.1.0 +export RUSTFS__OBSERVABILITY__ENVIRONMENT=develop +export RUSTFS__OBSERVABILITY__LOGGER_LEVEL=info +export RUSTFS__SINKS__FILE__ENABLED=true +export RUSTFS__SINKS__FILE__PATH="./deploy/logs/rustfs.log" +export RUSTFS__SINKS__WEBHOOK__ENABLED=false +export RUSTFS__SINKS__WEBHOOK__ENDPOINT="" +export RUSTFS__SINKS__WEBHOOK__AUTH_TOKEN="" +export RUSTFS__SINKS__KAFKA__ENABLED=false +export RUSTFS__SINKS__KAFKA__BOOTSTRAP_SERVERS="" +export RUSTFS__SINKS__KAFKA__TOPIC="" +export RUSTFS__LOGGER__QUEUE_CAPACITY=10 ``` You need replace your real data folder: @@ -47,3 +69,49 @@ You need replace your real data folder: ``` ./rustfs /data/rustfs ``` + +## How to deploy the observability stack + +The OpenTelemetry Collector offers a vendor-agnostic implementation on how to receive, process, and export telemetry +data. It removes the need to run, operate, and maintain multiple agents/collectors in order to support open-source +observability data formats (e.g. Jaeger, Prometheus, etc.) sending to one or more open-source or commercial back-ends. + +1. Enter the `.docker/observability` directory, +2. Run the following command: + +```bash +docker-compose -f docker-compose.yml up -d +``` + +3. Access the Grafana dashboard by navigating to `http://localhost:3000` in your browser. The default username and + password are `admin` and `admin`, respectively. + +4. Access the Jaeger dashboard by navigating to `http://localhost:16686` in your browser. + +5. Access the Prometheus dashboard by navigating to `http://localhost:9090` in your browser. + +## Create a new Observability configuration file + +#### 1. Enter the `deploy/config` directory, + +#### 2. Copy `obs.toml.example` to `obs.toml` + +#### 3. Modify the `obs.toml` configuration file + +##### 3.1. Modify the `endpoint` value to the address of the OpenTelemetry Collector + +##### 3.2. Modify the `service_name` value to the name of the service + +##### 3.3. Modify the `service_version` value to the version of the service + +##### 3.4. Modify the `environment` value to the environment of the service + +##### 3.5. Modify the `meter_interval` value to export interval + +##### 3.6. Modify the `sample_ratio` value to the sample ratio + +##### 3.7. Modify the `use_stdout` value to export to stdout + +##### 3.8. Modify the `logger_level` value to the logger level + + diff --git a/appauth/Cargo.toml b/appauth/Cargo.toml new file mode 100644 index 000000000..b638313e1 --- /dev/null +++ b/appauth/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "appauth" +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[dependencies] +base64-simd = "0.8.0" +common.workspace = true +hex-simd = "0.8.0" +rand.workspace = true +rsa = "0.9.8" +serde.workspace = true +serde_json.workspace = true + +[lints] +workspace = true diff --git a/appauth/src/lib.rs b/appauth/src/lib.rs new file mode 100644 index 000000000..79c66ba63 --- /dev/null +++ b/appauth/src/lib.rs @@ -0,0 +1 @@ +pub mod token; diff --git a/appauth/src/token.rs b/appauth/src/token.rs new file mode 100644 index 000000000..f18ae57f7 --- /dev/null +++ b/appauth/src/token.rs @@ -0,0 +1,110 @@ +use common::error::Result; +use rsa::Pkcs1v15Encrypt; +use rsa::{ + pkcs8::{DecodePrivateKey, DecodePublicKey}, + RsaPrivateKey, RsaPublicKey, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct Token { + pub name: String, // 应用ID + pub expired: u64, // 到期时间 (UNIX时间戳) +} + +// 公钥生成Token +// [token] Token对象 +// [key] 公钥字符串 +// 返回base64处理的加密字符串 +pub fn gencode(token: &Token, key: &str) -> Result { + let data = serde_json::to_vec(token)?; + let public_key = RsaPublicKey::from_public_key_pem(key)?; + let encrypted_data = public_key.encrypt(&mut rand::thread_rng(), Pkcs1v15Encrypt, &data)?; + Ok(base64_simd::URL_SAFE_NO_PAD.encode_to_string(&encrypted_data)) +} + +// 私钥解析Token +// [token] base64处理的加密字符串 +// [key] 私钥字符串 +// 返回Token对象 +pub fn parse(token: &str, key: &str) -> Result { + let encrypted_data = base64_simd::URL_SAFE_NO_PAD.decode_to_vec(token.as_bytes())?; + let private_key = RsaPrivateKey::from_pkcs8_pem(key)?; + let decrypted_data = private_key.decrypt(Pkcs1v15Encrypt, &encrypted_data)?; + let res: Token = serde_json::from_slice(&decrypted_data)?; + Ok(res) +} + +pub fn parse_license(license: &str) -> Result { + parse(license, TEST_PRIVATE_KEY) + // match parse(license, TEST_PRIVATE_KEY) { + // Ok(token) => { + // if token.expired > SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() { + // Ok(token) + // } else { + // Err("Token expired".into()) + // } + // } + // Err(e) => Err(e), + // } +} + +static TEST_PRIVATE_KEY:&str ="-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCj86SrJIuxSxR6\nBJ/dlJEUIj6NeBRnhLQlCDdovuz61+7kJXVcxaR66w4m8W7SLEUP+IlPtnn6vmiG\n7XMhGNHIr7r1JsEVVLhZmL3tKI66DEZl786ZhG81BWqUlmcooIPS8UEPZNqJXLuz\nVGhxNyVGbj/tV7QC2pSISnKaixc+nrhxvo7w56p5qrm9tik0PjTgfZsUePkoBsSN\npoRkAauS14MAzK6HGB75CzG3dZqXUNWSWVocoWtQbZUwFGXyzU01ammsHQDvc2xu\nK1RQpd1qYH5bOWZ0N0aPFwT0r59HztFXg9sbjsnuhO1A7OiUOkc6iGVuJ0wm/9nA\nwZIBqzgjAgMBAAECggEAPMpeSEbotPhNw2BrllE76ec4omPfzPJbiU+em+wPGoNu\nRJHPDnMKJbl6Kd5jZPKdOOrCnxfd6qcnQsBQa/kz7+GYxMV12l7ra+1Cnujm4v0i\nLTHZvPpp8ZLsjeOmpF3AAzsJEJgon74OqtOlVjVIUPEYKvzV9ijt4gsYq0zfdYv0\nhrTMzyrGM4/UvKLsFIBROAfCeWfA7sXLGH8JhrRAyDrtCPzGtyyAmzoHKHtHafcB\nuyPFw/IP8otAgpDk5iiQPNkH0WwzAQIm12oHuNUa66NwUK4WEjXTnDg8KeWLHHNv\nIfN8vdbZchMUpMIvvkr7is315d8f2cHCB5gEO+GWAQKBgQDR/0xNll+FYaiUKCPZ\nvkOCAd3l5mRhsqnjPQ/6Ul1lAyYWpoJSFMrGGn/WKTa/FVFJRTGbBjwP+Mx10bfb\ngUg2GILDTISUh54fp4zngvTi9w4MWGKXrb7I1jPkM3vbJfC/v2fraQ/r7qHPpO2L\nf6ZbGxasIlSvr37KeGoelwcAQQKBgQDH3hmOTS2Hl6D4EXdq5meHKrfeoicGN7m8\noQK7u8iwn1R9zK5nh6IXxBhKYNXNwdCQtBZVRvFjjZ56SZJb7lKqa1BcTsgJfZCy\nnI3Uu4UykrECAH8AVCVqBXUDJmeA2yE+gDAtYEjvhSDHpUfWxoGHr0B/Oqk2Lxc/\npRy1qV5fYwKBgBWSL/hYVf+RhIuTg/s9/BlCr9SJ0g3nGGRrRVTlWQqjRCpXeFOO\nJzYqSq9pFGKUggEQxoOyJEFPwVDo9gXqRcyov+Xn2kaXl7qQr3yoixc1YZALFDWY\nd1ySBEqQr0xXnV9U/gvEgwotPRnjSzNlLWV2ZuHPtPtG/7M0o1H5GZMBAoGAKr3N\nW0gX53o+my4pCnxRQW+aOIsWq1a5aqRIEFudFGBOUkS2Oz+fI1P1GdrRfhnnfzpz\n2DK+plp/vIkFOpGhrf4bBlJ2psjqa7fdANRFLMaAAfyXLDvScHTQTCcnVUAHQPVq\n2BlSH56pnugyj7SNuLV6pnql+wdhAmRN2m9o1h8CgYAbX2juSr4ioXwnYjOUdrIY\n4+ERvHcXdjoJmmPcAm4y5NbSqLXyU0FQmplNMt2A5LlniWVJ9KNdjAQUt60FZw/+\nr76LdxXaHNZghyx0BOs7mtq5unSQXamZ8KixasfhE9uz3ij1jXjG6hafWkS8/68I\nuWbaZqgvy7a9oPHYlKH7Jg==\n-----END PRIVATE KEY-----\n"; + +#[cfg(test)] +mod tests { + use super::*; + use rsa::{ + pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding}, + RsaPrivateKey, + }; + use std::time::{SystemTime, UNIX_EPOCH}; + #[test] + fn test_gencode_and_parse() { + let mut rng = rand::thread_rng(); + let bits = 2048; + let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to generate private key"); + let public_key = RsaPublicKey::from(&private_key); + + let private_key_pem = private_key.to_pkcs8_pem(LineEnding::LF).unwrap(); + let public_key_pem = public_key.to_public_key_pem(LineEnding::LF).unwrap(); + + let token = Token { + name: "test_app".to_string(), + expired: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 3600, // 1 hour from now + }; + + let encoded = gencode(&token, &public_key_pem).expect("Failed to encode token"); + + let decoded = parse(&encoded, &private_key_pem).expect("Failed to decode token"); + + assert_eq!(token.name, decoded.name); + assert_eq!(token.expired, decoded.expired); + } + + #[test] + fn test_parse_invalid_token() { + let private_key_pem = RsaPrivateKey::new(&mut rand::thread_rng(), 2048) + .expect("Failed to generate private key") + .to_pkcs8_pem(LineEnding::LF) + .unwrap(); + + let invalid_token = "invalid_base64_token"; + let result = parse(invalid_token, &private_key_pem); + + assert!(result.is_err()); + } + + #[test] + fn test_gencode_with_invalid_key() { + let token = Token { + name: "test_app".to_string(), + expired: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 3600, // 1 hour from now + }; + + let invalid_key = "invalid_public_key"; + let result = gencode(&token, invalid_key); + + assert!(result.is_err()); + } +} diff --git a/cli/rustfs-gui/Cargo.toml b/cli/rustfs-gui/Cargo.toml index c9af2fc37..faca71ee5 100644 --- a/cli/rustfs-gui/Cargo.toml +++ b/cli/rustfs-gui/Cargo.toml @@ -10,7 +10,6 @@ version.workspace = true chrono = { workspace = true } dioxus = { workspace = true, features = ["router"] } dirs = { workspace = true } -futures-util = { workspace = true } hex = { workspace = true } keyring = { workspace = true } lazy_static = { workspace = true } diff --git a/common/common/Cargo.toml b/common/common/Cargo.toml index 78eba0acd..b2a34d3ac 100644 --- a/common/common/Cargo.toml +++ b/common/common/Cargo.toml @@ -11,5 +11,5 @@ async-trait.workspace = true lazy_static.workspace = true scopeguard = "1.2.0" tokio.workspace = true -tonic.workspace = true +tonic = { workspace = true } tracing-error.workspace = true diff --git a/common/protos/Cargo.toml b/common/protos/Cargo.toml index e9197762e..581fb2d02 100644 --- a/common/protos/Cargo.toml +++ b/common/protos/Cargo.toml @@ -13,7 +13,7 @@ flatbuffers = { workspace = true } prost = { workspace = true } protobuf = { workspace = true } tokio = { workspace = true } -tonic = { workspace = true, features = ["transport", "tls"] } +tonic = { workspace = true, features = ["transport"] } tower = { workspace = true } [build-dependencies] diff --git a/common/protos/build.rs b/common/protos/build.rs index 766011af1..fc55ec7a6 100644 --- a/common/protos/build.rs +++ b/common/protos/build.rs @@ -8,7 +8,7 @@ use std::{ type AnyError = Box; const ENV_OUT_DIR: &str = "OUT_DIR"; -const VERSION_PROTOBUF: Version = Version(27, 0, 0); // 27.0 +const VERSION_PROTOBUF: Version = Version(30, 2, 0); // 30.2.0 const VERSION_FLATBUFFERS: Version = Version(24, 3, 25); // 24.3.25 /// Build protos if the major version of `flatc` or `protoc` is greater /// or lesser than the expected version. diff --git a/common/protos/src/generated/flatbuffers_generated/models.rs b/common/protos/src/generated/flatbuffers_generated/models.rs index aa1f6ae2f..e4949fdcf 100644 --- a/common/protos/src/generated/flatbuffers_generated/models.rs +++ b/common/protos/src/generated/flatbuffers_generated/models.rs @@ -1,10 +1,9 @@ // automatically generated by the FlatBuffers compiler, do not modify - // @generated -use core::mem; use core::cmp::Ordering; +use core::mem; extern crate flatbuffers; use self::flatbuffers::{EndianScalar, Follow}; @@ -12,112 +11,114 @@ use self::flatbuffers::{EndianScalar, Follow}; #[allow(unused_imports, dead_code)] pub mod models { - use core::mem; - use core::cmp::Ordering; + use core::cmp::Ordering; + use core::mem; - extern crate flatbuffers; - use self::flatbuffers::{EndianScalar, Follow}; + extern crate flatbuffers; + use self::flatbuffers::{EndianScalar, Follow}; -pub enum PingBodyOffset {} -#[derive(Copy, Clone, PartialEq)] + pub enum PingBodyOffset {} + #[derive(Copy, Clone, PartialEq)] -pub struct PingBody<'a> { - pub _tab: flatbuffers::Table<'a>, -} - -impl<'a> flatbuffers::Follow<'a> for PingBody<'a> { - type Inner = PingBody<'a>; - #[inline] - unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table::new(buf, loc) } - } -} - -impl<'a> PingBody<'a> { - pub const VT_PAYLOAD: flatbuffers::VOffsetT = 4; - - pub const fn get_fully_qualified_name() -> &'static str { - "models.PingBody" - } - - #[inline] - pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - PingBody { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, - args: &'args PingBodyArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = PingBodyBuilder::new(_fbb); - if let Some(x) = args.payload { builder.add_payload(x); } - builder.finish() - } - - - #[inline] - pub fn payload(&self) -> Option> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>(PingBody::VT_PAYLOAD, None)} - } -} - -impl flatbuffers::Verifiable for PingBody<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>>("payload", Self::VT_PAYLOAD, false)? - .finish(); - Ok(()) - } -} -pub struct PingBodyArgs<'a> { - pub payload: Option>>, -} -impl<'a> Default for PingBodyArgs<'a> { - #[inline] - fn default() -> Self { - PingBodyArgs { - payload: None, + pub struct PingBody<'a> { + pub _tab: flatbuffers::Table<'a>, } - } -} -pub struct PingBodyBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> PingBodyBuilder<'a, 'b, A> { - #[inline] - pub fn add_payload(&mut self, payload: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(PingBody::VT_PAYLOAD, payload); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> PingBodyBuilder<'a, 'b, A> { - let start = _fbb.start_table(); - PingBodyBuilder { - fbb_: _fbb, - start_: start, + impl<'a> flatbuffers::Follow<'a> for PingBody<'a> { + type Inner = PingBody<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } -} -impl core::fmt::Debug for PingBody<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("PingBody"); - ds.field("payload", &self.payload()); - ds.finish() - } -} -} // pub mod models + impl<'a> PingBody<'a> { + pub const VT_PAYLOAD: flatbuffers::VOffsetT = 4; + pub const fn get_fully_qualified_name() -> &'static str { + "models.PingBody" + } + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + PingBody { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args PingBodyArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = PingBodyBuilder::new(_fbb); + if let Some(x) = args.payload { + builder.add_payload(x); + } + builder.finish() + } + + #[inline] + pub fn payload(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>(PingBody::VT_PAYLOAD, None) + } + } + } + + impl flatbuffers::Verifiable for PingBody<'_> { + #[inline] + fn run_verifier(v: &mut flatbuffers::Verifier, pos: usize) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>>("payload", Self::VT_PAYLOAD, false)? + .finish(); + Ok(()) + } + } + pub struct PingBodyArgs<'a> { + pub payload: Option>>, + } + impl<'a> Default for PingBodyArgs<'a> { + #[inline] + fn default() -> Self { + PingBodyArgs { payload: None } + } + } + + pub struct PingBodyBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> PingBodyBuilder<'a, 'b, A> { + #[inline] + pub fn add_payload(&mut self, payload: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(PingBody::VT_PAYLOAD, payload); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> PingBodyBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + PingBodyBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for PingBody<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("PingBody"); + ds.field("payload", &self.payload()); + ds.finish() + } + } +} // pub mod models diff --git a/common/protos/src/generated/proto_gen/node_service.rs b/common/protos/src/generated/proto_gen/node_service.rs index 88f7b3ab0..8a0c4b825 100644 --- a/common/protos/src/generated/proto_gen/node_service.rs +++ b/common/protos/src/generated/proto_gen/node_service.rs @@ -622,10 +622,7 @@ pub struct GenerallyLockResponse { #[derive(Clone, PartialEq, ::prost::Message)] pub struct Mss { #[prost(map = "string, string", tag = "1")] - pub value: ::std::collections::HashMap< - ::prost::alloc::string::String, - ::prost::alloc::string::String, - >, + pub value: ::std::collections::HashMap<::prost::alloc::string::String, ::prost::alloc::string::String>, } #[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct LocalStorageInfoRequest { @@ -789,10 +786,7 @@ pub struct DownloadProfileDataResponse { #[prost(bool, tag = "1")] pub success: bool, #[prost(map = "string, bytes", tag = "2")] - pub data: ::std::collections::HashMap< - ::prost::alloc::string::String, - ::prost::alloc::vec::Vec, - >, + pub data: ::std::collections::HashMap<::prost::alloc::string::String, ::prost::alloc::vec::Vec>, #[prost(string, optional, tag = "3")] pub error_info: ::core::option::Option<::prost::alloc::string::String>, } @@ -1059,15 +1053,9 @@ pub struct LoadTransitionTierConfigResponse { } /// Generated client implementations. pub mod node_service_client { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; + #![allow(unused_variables, dead_code, missing_docs, clippy::wildcard_imports, clippy::let_unit_value)] use tonic::codegen::http::Uri; + use tonic::codegen::*; #[derive(Debug, Clone)] pub struct NodeServiceClient { inner: tonic::client::Grpc, @@ -1085,7 +1073,7 @@ pub mod node_service_client { } impl NodeServiceClient where - T: tonic::client::GrpcService, + T: tonic::client::GrpcService, T::Error: Into, T::ResponseBody: Body + std::marker::Send + 'static, ::Error: Into + std::marker::Send, @@ -1098,22 +1086,16 @@ pub mod node_service_client { let inner = tonic::client::Grpc::with_origin(inner, origin); Self { inner } } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> NodeServiceClient> + pub fn with_interceptor(inner: T, interceptor: F) -> NodeServiceClient> where F: tonic::service::Interceptor, T::ResponseBody: Default, T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, + http::Request, + Response = http::Response<>::ResponseBody>, >, - , - >>::Error: Into + std::marker::Send + std::marker::Sync, + >>::Error: + Into + std::marker::Send + std::marker::Sync, { NodeServiceClient::new(InterceptedService::new(inner, interceptor)) } @@ -1156,15 +1138,9 @@ pub mod node_service_client { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/Ping", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/Ping"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "Ping")); @@ -1173,22 +1149,13 @@ pub mod node_service_client { pub async fn heal_bucket( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/HealBucket", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/HealBucket"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "HealBucket")); @@ -1197,22 +1164,13 @@ pub mod node_service_client { pub async fn list_bucket( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ListBucket", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ListBucket"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ListBucket")); @@ -1221,22 +1179,13 @@ pub mod node_service_client { pub async fn make_bucket( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/MakeBucket", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/MakeBucket"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "MakeBucket")); @@ -1245,22 +1194,13 @@ pub mod node_service_client { pub async fn get_bucket_info( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetBucketInfo", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetBucketInfo"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetBucketInfo")); @@ -1269,22 +1209,13 @@ pub mod node_service_client { pub async fn delete_bucket( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DeleteBucket", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DeleteBucket"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "DeleteBucket")); @@ -1293,22 +1224,13 @@ pub mod node_service_client { pub async fn read_all( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ReadAll", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ReadAll"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ReadAll")); @@ -1317,22 +1239,13 @@ pub mod node_service_client { pub async fn write_all( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/WriteAll", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/WriteAll"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "WriteAll")); @@ -1345,15 +1258,9 @@ pub mod node_service_client { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/Delete", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/Delete"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "Delete")); @@ -1362,22 +1269,13 @@ pub mod node_service_client { pub async fn verify_file( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/VerifyFile", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/VerifyFile"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "VerifyFile")); @@ -1386,22 +1284,13 @@ pub mod node_service_client { pub async fn check_parts( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/CheckParts", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/CheckParts"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "CheckParts")); @@ -1410,22 +1299,13 @@ pub mod node_service_client { pub async fn rename_part( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/RenamePart", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/RenamePart"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "RenamePart")); @@ -1434,22 +1314,13 @@ pub mod node_service_client { pub async fn rename_file( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/RenameFile", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/RenameFile"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "RenameFile")); @@ -1462,15 +1333,9 @@ pub mod node_service_client { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/Write", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/Write"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "Write")); @@ -1479,22 +1344,13 @@ pub mod node_service_client { pub async fn write_stream( &mut self, request: impl tonic::IntoStreamingRequest, - ) -> std::result::Result< - tonic::Response>, - tonic::Status, - > { + ) -> std::result::Result>, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/WriteStream", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/WriteStream"); let mut req = request.into_streaming_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "WriteStream")); @@ -1504,22 +1360,13 @@ pub mod node_service_client { pub async fn read_at( &mut self, request: impl tonic::IntoStreamingRequest, - ) -> std::result::Result< - tonic::Response>, - tonic::Status, - > { + ) -> std::result::Result>, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ReadAt", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ReadAt"); let mut req = request.into_streaming_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ReadAt")); @@ -1528,22 +1375,13 @@ pub mod node_service_client { pub async fn list_dir( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ListDir", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ListDir"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ListDir")); @@ -1552,22 +1390,13 @@ pub mod node_service_client { pub async fn walk_dir( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response>, - tonic::Status, - > { + ) -> std::result::Result>, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/WalkDir", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/WalkDir"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "WalkDir")); @@ -1576,22 +1405,13 @@ pub mod node_service_client { pub async fn rename_data( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/RenameData", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/RenameData"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "RenameData")); @@ -1600,22 +1420,13 @@ pub mod node_service_client { pub async fn make_volumes( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/MakeVolumes", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/MakeVolumes"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "MakeVolumes")); @@ -1624,22 +1435,13 @@ pub mod node_service_client { pub async fn make_volume( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/MakeVolume", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/MakeVolume"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "MakeVolume")); @@ -1648,22 +1450,13 @@ pub mod node_service_client { pub async fn list_volumes( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ListVolumes", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ListVolumes"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ListVolumes")); @@ -1672,22 +1465,13 @@ pub mod node_service_client { pub async fn stat_volume( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/StatVolume", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/StatVolume"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "StatVolume")); @@ -1696,22 +1480,13 @@ pub mod node_service_client { pub async fn delete_paths( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DeletePaths", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DeletePaths"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "DeletePaths")); @@ -1720,22 +1495,13 @@ pub mod node_service_client { pub async fn update_metadata( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/UpdateMetadata", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/UpdateMetadata"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "UpdateMetadata")); @@ -1744,22 +1510,13 @@ pub mod node_service_client { pub async fn write_metadata( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/WriteMetadata", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/WriteMetadata"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "WriteMetadata")); @@ -1768,22 +1525,13 @@ pub mod node_service_client { pub async fn read_version( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ReadVersion", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ReadVersion"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ReadVersion")); @@ -1796,15 +1544,9 @@ pub mod node_service_client { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ReadXL", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ReadXL"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ReadXL")); @@ -1813,22 +1555,13 @@ pub mod node_service_client { pub async fn delete_version( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DeleteVersion", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DeleteVersion"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "DeleteVersion")); @@ -1837,22 +1570,13 @@ pub mod node_service_client { pub async fn delete_versions( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DeleteVersions", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DeleteVersions"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "DeleteVersions")); @@ -1861,22 +1585,13 @@ pub mod node_service_client { pub async fn read_multiple( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ReadMultiple", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ReadMultiple"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ReadMultiple")); @@ -1885,22 +1600,13 @@ pub mod node_service_client { pub async fn delete_volume( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DeleteVolume", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DeleteVolume"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "DeleteVolume")); @@ -1909,22 +1615,13 @@ pub mod node_service_client { pub async fn disk_info( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DiskInfo", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DiskInfo"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "DiskInfo")); @@ -1933,22 +1630,13 @@ pub mod node_service_client { pub async fn ns_scanner( &mut self, request: impl tonic::IntoStreamingRequest, - ) -> std::result::Result< - tonic::Response>, - tonic::Status, - > { + ) -> std::result::Result>, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/NsScanner", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/NsScanner"); let mut req = request.into_streaming_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "NsScanner")); @@ -1957,22 +1645,13 @@ pub mod node_service_client { pub async fn lock( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/Lock", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/Lock"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "Lock")); @@ -1981,22 +1660,13 @@ pub mod node_service_client { pub async fn un_lock( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/UnLock", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/UnLock"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "UnLock")); @@ -2005,22 +1675,13 @@ pub mod node_service_client { pub async fn r_lock( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/RLock", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/RLock"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "RLock")); @@ -2029,22 +1690,13 @@ pub mod node_service_client { pub async fn r_un_lock( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/RUnLock", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/RUnLock"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "RUnLock")); @@ -2053,22 +1705,13 @@ pub mod node_service_client { pub async fn force_un_lock( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ForceUnLock", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ForceUnLock"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ForceUnLock")); @@ -2077,22 +1720,13 @@ pub mod node_service_client { pub async fn refresh( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/Refresh", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/Refresh"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "Refresh")); @@ -2101,22 +1735,13 @@ pub mod node_service_client { pub async fn local_storage_info( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/LocalStorageInfo", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/LocalStorageInfo"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "LocalStorageInfo")); @@ -2125,22 +1750,13 @@ pub mod node_service_client { pub async fn server_info( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ServerInfo", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ServerInfo"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ServerInfo")); @@ -2149,22 +1765,13 @@ pub mod node_service_client { pub async fn get_cpus( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetCpus", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetCpus"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetCpus")); @@ -2173,22 +1780,13 @@ pub mod node_service_client { pub async fn get_net_info( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetNetInfo", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetNetInfo"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetNetInfo")); @@ -2197,22 +1795,13 @@ pub mod node_service_client { pub async fn get_partitions( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetPartitions", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetPartitions"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetPartitions")); @@ -2221,22 +1810,13 @@ pub mod node_service_client { pub async fn get_os_info( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetOsInfo", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetOsInfo"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetOsInfo")); @@ -2245,22 +1825,13 @@ pub mod node_service_client { pub async fn get_se_linux_info( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetSELinuxInfo", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetSELinuxInfo"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetSELinuxInfo")); @@ -2269,22 +1840,13 @@ pub mod node_service_client { pub async fn get_sys_config( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetSysConfig", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetSysConfig"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetSysConfig")); @@ -2293,22 +1855,13 @@ pub mod node_service_client { pub async fn get_sys_errors( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetSysErrors", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetSysErrors"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetSysErrors")); @@ -2317,22 +1870,13 @@ pub mod node_service_client { pub async fn get_mem_info( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetMemInfo", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetMemInfo"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetMemInfo")); @@ -2341,22 +1885,13 @@ pub mod node_service_client { pub async fn get_metrics( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetMetrics", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetMetrics"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetMetrics")); @@ -2365,22 +1900,13 @@ pub mod node_service_client { pub async fn get_proc_info( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetProcInfo", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetProcInfo"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetProcInfo")); @@ -2389,22 +1915,13 @@ pub mod node_service_client { pub async fn start_profiling( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/StartProfiling", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/StartProfiling"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "StartProfiling")); @@ -2413,48 +1930,28 @@ pub mod node_service_client { pub async fn download_profile_data( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DownloadProfileData", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DownloadProfileData"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "DownloadProfileData"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "DownloadProfileData")); self.inner.unary(req, path, codec).await } pub async fn get_bucket_stats( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetBucketStats", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetBucketStats"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetBucketStats")); @@ -2463,22 +1960,13 @@ pub mod node_service_client { pub async fn get_sr_metrics( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetSRMetrics", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetSRMetrics"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "GetSRMetrics")); @@ -2487,100 +1975,58 @@ pub mod node_service_client { pub async fn get_all_bucket_stats( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetAllBucketStats", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetAllBucketStats"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "GetAllBucketStats"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "GetAllBucketStats")); self.inner.unary(req, path, codec).await } pub async fn load_bucket_metadata( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/LoadBucketMetadata", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/LoadBucketMetadata"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "LoadBucketMetadata"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "LoadBucketMetadata")); self.inner.unary(req, path, codec).await } pub async fn delete_bucket_metadata( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DeleteBucketMetadata", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DeleteBucketMetadata"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "DeleteBucketMetadata"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "DeleteBucketMetadata")); self.inner.unary(req, path, codec).await } pub async fn delete_policy( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DeletePolicy", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DeletePolicy"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "DeletePolicy")); @@ -2589,22 +2035,13 @@ pub mod node_service_client { pub async fn load_policy( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/LoadPolicy", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/LoadPolicy"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "LoadPolicy")); @@ -2613,48 +2050,28 @@ pub mod node_service_client { pub async fn load_policy_mapping( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/LoadPolicyMapping", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/LoadPolicyMapping"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "LoadPolicyMapping"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "LoadPolicyMapping")); self.inner.unary(req, path, codec).await } pub async fn delete_user( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DeleteUser", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DeleteUser"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "DeleteUser")); @@ -2663,48 +2080,28 @@ pub mod node_service_client { pub async fn delete_service_account( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/DeleteServiceAccount", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/DeleteServiceAccount"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "DeleteServiceAccount"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "DeleteServiceAccount")); self.inner.unary(req, path, codec).await } pub async fn load_user( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/LoadUser", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/LoadUser"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "LoadUser")); @@ -2713,48 +2110,28 @@ pub mod node_service_client { pub async fn load_service_account( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/LoadServiceAccount", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/LoadServiceAccount"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "LoadServiceAccount"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "LoadServiceAccount")); self.inner.unary(req, path, codec).await } pub async fn load_group( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/LoadGroup", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/LoadGroup"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "LoadGroup")); @@ -2763,30 +2140,16 @@ pub mod node_service_client { pub async fn reload_site_replication_config( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ReloadSiteReplicationConfig", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ReloadSiteReplicationConfig"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "node_service.NodeService", - "ReloadSiteReplicationConfig", - ), - ); + .insert(GrpcMethod::new("node_service.NodeService", "ReloadSiteReplicationConfig")); self.inner.unary(req, path, codec).await } /// rpc VerifyBinary() returns () {}; @@ -2794,22 +2157,13 @@ pub mod node_service_client { pub async fn signal_service( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/SignalService", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/SignalService"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "SignalService")); @@ -2818,100 +2172,58 @@ pub mod node_service_client { pub async fn background_heal_status( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/BackgroundHealStatus", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/BackgroundHealStatus"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "BackgroundHealStatus"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "BackgroundHealStatus")); self.inner.unary(req, path, codec).await } pub async fn get_metacache_listing( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/GetMetacacheListing", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/GetMetacacheListing"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "GetMetacacheListing"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "GetMetacacheListing")); self.inner.unary(req, path, codec).await } pub async fn update_metacache_listing( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/UpdateMetacacheListing", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/UpdateMetacacheListing"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "UpdateMetacacheListing"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "UpdateMetacacheListing")); self.inner.unary(req, path, codec).await } pub async fn reload_pool_meta( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/ReloadPoolMeta", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/ReloadPoolMeta"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "ReloadPoolMeta")); @@ -2920,22 +2232,13 @@ pub mod node_service_client { pub async fn stop_rebalance( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/StopRebalance", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/StopRebalance"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("node_service.NodeService", "StopRebalance")); @@ -2944,69 +2247,38 @@ pub mod node_service_client { pub async fn load_rebalance_meta( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/LoadRebalanceMeta", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/LoadRebalanceMeta"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new("node_service.NodeService", "LoadRebalanceMeta"), - ); + .insert(GrpcMethod::new("node_service.NodeService", "LoadRebalanceMeta")); self.inner.unary(req, path, codec).await } pub async fn load_transition_tier_config( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; + .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/node_service.NodeService/LoadTransitionTierConfig", - ); + let path = http::uri::PathAndQuery::from_static("/node_service.NodeService/LoadTransitionTierConfig"); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "node_service.NodeService", - "LoadTransitionTierConfig", - ), - ); + .insert(GrpcMethod::new("node_service.NodeService", "LoadTransitionTierConfig")); self.inner.unary(req, path, codec).await } } } /// Generated server implementations. pub mod node_service_server { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] + #![allow(unused_variables, dead_code, missing_docs, clippy::wildcard_imports, clippy::let_unit_value)] use tonic::codegen::*; /// Generated trait containing gRPC methods that should be implemented for use with NodeServiceServer. #[async_trait] @@ -3019,38 +2291,23 @@ pub mod node_service_server { async fn heal_bucket( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn list_bucket( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn make_bucket( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_bucket_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn delete_bucket( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn read_all( &self, request: tonic::Request, @@ -3058,10 +2315,7 @@ pub mod node_service_server { async fn write_all( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn delete( &self, request: tonic::Request, @@ -3069,52 +2323,33 @@ pub mod node_service_server { async fn verify_file( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn check_parts( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn rename_part( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn rename_file( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn write( &self, request: tonic::Request, ) -> std::result::Result, tonic::Status>; /// Server streaming response type for the WriteStream method. - type WriteStreamStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result, - > + type WriteStreamStream: tonic::codegen::tokio_stream::Stream> + std::marker::Send + 'static; async fn write_stream( &self, request: tonic::Request>, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// Server streaming response type for the ReadAt method. - type ReadAtStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result, - > + type ReadAtStream: tonic::codegen::tokio_stream::Stream> + std::marker::Send + 'static; /// rpc Append(AppendRequest) returns (AppendResponse) {}; @@ -3127,9 +2362,7 @@ pub mod node_service_server { request: tonic::Request, ) -> std::result::Result, tonic::Status>; /// Server streaming response type for the WalkDir method. - type WalkDirStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result, - > + type WalkDirStream: tonic::codegen::tokio_stream::Stream> + std::marker::Send + 'static; async fn walk_dir( @@ -3139,66 +2372,39 @@ pub mod node_service_server { async fn rename_data( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn make_volumes( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn make_volume( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn list_volumes( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn stat_volume( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn delete_paths( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn update_metadata( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn write_metadata( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn read_version( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn read_xl( &self, request: tonic::Request, @@ -3206,42 +2412,25 @@ pub mod node_service_server { async fn delete_version( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn delete_versions( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn read_multiple( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn delete_volume( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn disk_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// Server streaming response type for the NsScanner method. - type NsScannerStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result, - > + type NsScannerStream: tonic::codegen::tokio_stream::Stream> + std::marker::Send + 'static; async fn ns_scanner( @@ -3251,59 +2440,35 @@ pub mod node_service_server { async fn lock( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn un_lock( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn r_lock( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn r_un_lock( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn force_un_lock( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn refresh( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn local_storage_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn server_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_cpus( &self, request: tonic::Request, @@ -3311,236 +2476,137 @@ pub mod node_service_server { async fn get_net_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_partitions( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_os_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_se_linux_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_sys_config( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_sys_errors( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_mem_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_metrics( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_proc_info( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn start_profiling( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn download_profile_data( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_bucket_stats( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_sr_metrics( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_all_bucket_stats( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn load_bucket_metadata( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn delete_bucket_metadata( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn delete_policy( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn load_policy( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn load_policy_mapping( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn delete_user( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn delete_service_account( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn load_user( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn load_service_account( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn load_group( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn reload_site_replication_config( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; /// rpc VerifyBinary() returns () {}; /// rpc CommitBinary() returns () {}; async fn signal_service( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn background_heal_status( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn get_metacache_listing( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn update_metacache_listing( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn reload_pool_meta( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn stop_rebalance( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn load_rebalance_meta( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn load_transition_tier_config( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; } #[derive(Debug)] pub struct NodeServiceServer { @@ -3563,10 +2629,7 @@ pub mod node_service_server { max_encoding_message_size: None, } } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService + pub fn with_interceptor(inner: T, interceptor: F) -> InterceptedService where F: tonic::service::Interceptor, { @@ -3607,13 +2670,10 @@ pub mod node_service_server { B: Body + std::marker::Send + 'static, B::Error: Into + std::marker::Send + 'static, { - type Response = http::Response; + type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -3621,21 +2681,12 @@ pub mod node_service_server { "/node_service.NodeService/Ping" => { #[allow(non_camel_case_types)] struct PingSvc(pub Arc); - impl tonic::server::UnaryService - for PingSvc { + impl tonic::server::UnaryService for PingSvc { type Response = super::PingResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::ping(&inner, request).await - }; + let fut = async move { ::ping(&inner, request).await }; Box::pin(fut) } } @@ -3648,14 +2699,8 @@ pub mod node_service_server { let method = PingSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -3664,23 +2709,12 @@ pub mod node_service_server { "/node_service.NodeService/HealBucket" => { #[allow(non_camel_case_types)] struct HealBucketSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for HealBucketSvc { + impl tonic::server::UnaryService for HealBucketSvc { type Response = super::HealBucketResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::heal_bucket(&inner, request).await - }; + let fut = async move { ::heal_bucket(&inner, request).await }; Box::pin(fut) } } @@ -3693,14 +2727,8 @@ pub mod node_service_server { let method = HealBucketSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -3709,23 +2737,12 @@ pub mod node_service_server { "/node_service.NodeService/ListBucket" => { #[allow(non_camel_case_types)] struct ListBucketSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for ListBucketSvc { + impl tonic::server::UnaryService for ListBucketSvc { type Response = super::ListBucketResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::list_bucket(&inner, request).await - }; + let fut = async move { ::list_bucket(&inner, request).await }; Box::pin(fut) } } @@ -3738,14 +2755,8 @@ pub mod node_service_server { let method = ListBucketSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -3754,23 +2765,12 @@ pub mod node_service_server { "/node_service.NodeService/MakeBucket" => { #[allow(non_camel_case_types)] struct MakeBucketSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for MakeBucketSvc { + impl tonic::server::UnaryService for MakeBucketSvc { type Response = super::MakeBucketResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::make_bucket(&inner, request).await - }; + let fut = async move { ::make_bucket(&inner, request).await }; Box::pin(fut) } } @@ -3783,14 +2783,8 @@ pub mod node_service_server { let method = MakeBucketSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -3799,23 +2793,12 @@ pub mod node_service_server { "/node_service.NodeService/GetBucketInfo" => { #[allow(non_camel_case_types)] struct GetBucketInfoSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetBucketInfoSvc { + impl tonic::server::UnaryService for GetBucketInfoSvc { type Response = super::GetBucketInfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_bucket_info(&inner, request).await - }; + let fut = async move { ::get_bucket_info(&inner, request).await }; Box::pin(fut) } } @@ -3828,14 +2811,8 @@ pub mod node_service_server { let method = GetBucketInfoSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -3844,23 +2821,12 @@ pub mod node_service_server { "/node_service.NodeService/DeleteBucket" => { #[allow(non_camel_case_types)] struct DeleteBucketSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DeleteBucketSvc { + impl tonic::server::UnaryService for DeleteBucketSvc { type Response = super::DeleteBucketResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::delete_bucket(&inner, request).await - }; + let fut = async move { ::delete_bucket(&inner, request).await }; Box::pin(fut) } } @@ -3873,14 +2839,8 @@ pub mod node_service_server { let method = DeleteBucketSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -3889,23 +2849,12 @@ pub mod node_service_server { "/node_service.NodeService/ReadAll" => { #[allow(non_camel_case_types)] struct ReadAllSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for ReadAllSvc { + impl tonic::server::UnaryService for ReadAllSvc { type Response = super::ReadAllResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::read_all(&inner, request).await - }; + let fut = async move { ::read_all(&inner, request).await }; Box::pin(fut) } } @@ -3918,14 +2867,8 @@ pub mod node_service_server { let method = ReadAllSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -3934,23 +2877,12 @@ pub mod node_service_server { "/node_service.NodeService/WriteAll" => { #[allow(non_camel_case_types)] struct WriteAllSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for WriteAllSvc { + impl tonic::server::UnaryService for WriteAllSvc { type Response = super::WriteAllResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::write_all(&inner, request).await - }; + let fut = async move { ::write_all(&inner, request).await }; Box::pin(fut) } } @@ -3963,14 +2895,8 @@ pub mod node_service_server { let method = WriteAllSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -3979,23 +2905,12 @@ pub mod node_service_server { "/node_service.NodeService/Delete" => { #[allow(non_camel_case_types)] struct DeleteSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DeleteSvc { + impl tonic::server::UnaryService for DeleteSvc { type Response = super::DeleteResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::delete(&inner, request).await - }; + let fut = async move { ::delete(&inner, request).await }; Box::pin(fut) } } @@ -4008,14 +2923,8 @@ pub mod node_service_server { let method = DeleteSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4024,23 +2933,12 @@ pub mod node_service_server { "/node_service.NodeService/VerifyFile" => { #[allow(non_camel_case_types)] struct VerifyFileSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for VerifyFileSvc { + impl tonic::server::UnaryService for VerifyFileSvc { type Response = super::VerifyFileResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::verify_file(&inner, request).await - }; + let fut = async move { ::verify_file(&inner, request).await }; Box::pin(fut) } } @@ -4053,14 +2951,8 @@ pub mod node_service_server { let method = VerifyFileSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4069,23 +2961,12 @@ pub mod node_service_server { "/node_service.NodeService/CheckParts" => { #[allow(non_camel_case_types)] struct CheckPartsSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for CheckPartsSvc { + impl tonic::server::UnaryService for CheckPartsSvc { type Response = super::CheckPartsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::check_parts(&inner, request).await - }; + let fut = async move { ::check_parts(&inner, request).await }; Box::pin(fut) } } @@ -4098,14 +2979,8 @@ pub mod node_service_server { let method = CheckPartsSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4114,23 +2989,12 @@ pub mod node_service_server { "/node_service.NodeService/RenamePart" => { #[allow(non_camel_case_types)] struct RenamePartSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for RenamePartSvc { + impl tonic::server::UnaryService for RenamePartSvc { type Response = super::RenamePartResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::rename_part(&inner, request).await - }; + let fut = async move { ::rename_part(&inner, request).await }; Box::pin(fut) } } @@ -4143,14 +3007,8 @@ pub mod node_service_server { let method = RenamePartSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4159,23 +3017,12 @@ pub mod node_service_server { "/node_service.NodeService/RenameFile" => { #[allow(non_camel_case_types)] struct RenameFileSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for RenameFileSvc { + impl tonic::server::UnaryService for RenameFileSvc { type Response = super::RenameFileResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::rename_file(&inner, request).await - }; + let fut = async move { ::rename_file(&inner, request).await }; Box::pin(fut) } } @@ -4188,14 +3035,8 @@ pub mod node_service_server { let method = RenameFileSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4204,21 +3045,12 @@ pub mod node_service_server { "/node_service.NodeService/Write" => { #[allow(non_camel_case_types)] struct WriteSvc(pub Arc); - impl tonic::server::UnaryService - for WriteSvc { + impl tonic::server::UnaryService for WriteSvc { type Response = super::WriteResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::write(&inner, request).await - }; + let fut = async move { ::write(&inner, request).await }; Box::pin(fut) } } @@ -4231,14 +3063,8 @@ pub mod node_service_server { let method = WriteSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4247,26 +3073,13 @@ pub mod node_service_server { "/node_service.NodeService/WriteStream" => { #[allow(non_camel_case_types)] struct WriteStreamSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::StreamingService - for WriteStreamSvc { + impl tonic::server::StreamingService for WriteStreamSvc { type Response = super::WriteResponse; type ResponseStream = T::WriteStreamStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - tonic::Streaming, - >, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request>) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::write_stream(&inner, request).await - }; + let fut = async move { ::write_stream(&inner, request).await }; Box::pin(fut) } } @@ -4279,14 +3092,8 @@ pub mod node_service_server { let method = WriteStreamSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.streaming(method, req).await; Ok(res) }; @@ -4295,26 +3102,13 @@ pub mod node_service_server { "/node_service.NodeService/ReadAt" => { #[allow(non_camel_case_types)] struct ReadAtSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::StreamingService - for ReadAtSvc { + impl tonic::server::StreamingService for ReadAtSvc { type Response = super::ReadAtResponse; type ResponseStream = T::ReadAtStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - tonic::Streaming, - >, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request>) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::read_at(&inner, request).await - }; + let fut = async move { ::read_at(&inner, request).await }; Box::pin(fut) } } @@ -4327,14 +3121,8 @@ pub mod node_service_server { let method = ReadAtSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.streaming(method, req).await; Ok(res) }; @@ -4343,23 +3131,12 @@ pub mod node_service_server { "/node_service.NodeService/ListDir" => { #[allow(non_camel_case_types)] struct ListDirSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for ListDirSvc { + impl tonic::server::UnaryService for ListDirSvc { type Response = super::ListDirResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::list_dir(&inner, request).await - }; + let fut = async move { ::list_dir(&inner, request).await }; Box::pin(fut) } } @@ -4372,14 +3149,8 @@ pub mod node_service_server { let method = ListDirSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4388,24 +3159,13 @@ pub mod node_service_server { "/node_service.NodeService/WalkDir" => { #[allow(non_camel_case_types)] struct WalkDirSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::ServerStreamingService - for WalkDirSvc { + impl tonic::server::ServerStreamingService for WalkDirSvc { type Response = super::WalkDirResponse; type ResponseStream = T::WalkDirStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::walk_dir(&inner, request).await - }; + let fut = async move { ::walk_dir(&inner, request).await }; Box::pin(fut) } } @@ -4418,14 +3178,8 @@ pub mod node_service_server { let method = WalkDirSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.server_streaming(method, req).await; Ok(res) }; @@ -4434,23 +3188,12 @@ pub mod node_service_server { "/node_service.NodeService/RenameData" => { #[allow(non_camel_case_types)] struct RenameDataSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for RenameDataSvc { + impl tonic::server::UnaryService for RenameDataSvc { type Response = super::RenameDataResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::rename_data(&inner, request).await - }; + let fut = async move { ::rename_data(&inner, request).await }; Box::pin(fut) } } @@ -4463,14 +3206,8 @@ pub mod node_service_server { let method = RenameDataSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4479,23 +3216,12 @@ pub mod node_service_server { "/node_service.NodeService/MakeVolumes" => { #[allow(non_camel_case_types)] struct MakeVolumesSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for MakeVolumesSvc { + impl tonic::server::UnaryService for MakeVolumesSvc { type Response = super::MakeVolumesResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::make_volumes(&inner, request).await - }; + let fut = async move { ::make_volumes(&inner, request).await }; Box::pin(fut) } } @@ -4508,14 +3234,8 @@ pub mod node_service_server { let method = MakeVolumesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4524,23 +3244,12 @@ pub mod node_service_server { "/node_service.NodeService/MakeVolume" => { #[allow(non_camel_case_types)] struct MakeVolumeSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for MakeVolumeSvc { + impl tonic::server::UnaryService for MakeVolumeSvc { type Response = super::MakeVolumeResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::make_volume(&inner, request).await - }; + let fut = async move { ::make_volume(&inner, request).await }; Box::pin(fut) } } @@ -4553,14 +3262,8 @@ pub mod node_service_server { let method = MakeVolumeSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4569,23 +3272,12 @@ pub mod node_service_server { "/node_service.NodeService/ListVolumes" => { #[allow(non_camel_case_types)] struct ListVolumesSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for ListVolumesSvc { + impl tonic::server::UnaryService for ListVolumesSvc { type Response = super::ListVolumesResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::list_volumes(&inner, request).await - }; + let fut = async move { ::list_volumes(&inner, request).await }; Box::pin(fut) } } @@ -4598,14 +3290,8 @@ pub mod node_service_server { let method = ListVolumesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4614,23 +3300,12 @@ pub mod node_service_server { "/node_service.NodeService/StatVolume" => { #[allow(non_camel_case_types)] struct StatVolumeSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for StatVolumeSvc { + impl tonic::server::UnaryService for StatVolumeSvc { type Response = super::StatVolumeResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::stat_volume(&inner, request).await - }; + let fut = async move { ::stat_volume(&inner, request).await }; Box::pin(fut) } } @@ -4643,14 +3318,8 @@ pub mod node_service_server { let method = StatVolumeSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4659,23 +3328,12 @@ pub mod node_service_server { "/node_service.NodeService/DeletePaths" => { #[allow(non_camel_case_types)] struct DeletePathsSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DeletePathsSvc { + impl tonic::server::UnaryService for DeletePathsSvc { type Response = super::DeletePathsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::delete_paths(&inner, request).await - }; + let fut = async move { ::delete_paths(&inner, request).await }; Box::pin(fut) } } @@ -4688,14 +3346,8 @@ pub mod node_service_server { let method = DeletePathsSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4704,23 +3356,12 @@ pub mod node_service_server { "/node_service.NodeService/UpdateMetadata" => { #[allow(non_camel_case_types)] struct UpdateMetadataSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for UpdateMetadataSvc { + impl tonic::server::UnaryService for UpdateMetadataSvc { type Response = super::UpdateMetadataResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::update_metadata(&inner, request).await - }; + let fut = async move { ::update_metadata(&inner, request).await }; Box::pin(fut) } } @@ -4733,14 +3374,8 @@ pub mod node_service_server { let method = UpdateMetadataSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4749,23 +3384,12 @@ pub mod node_service_server { "/node_service.NodeService/WriteMetadata" => { #[allow(non_camel_case_types)] struct WriteMetadataSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for WriteMetadataSvc { + impl tonic::server::UnaryService for WriteMetadataSvc { type Response = super::WriteMetadataResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::write_metadata(&inner, request).await - }; + let fut = async move { ::write_metadata(&inner, request).await }; Box::pin(fut) } } @@ -4778,14 +3402,8 @@ pub mod node_service_server { let method = WriteMetadataSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4794,23 +3412,12 @@ pub mod node_service_server { "/node_service.NodeService/ReadVersion" => { #[allow(non_camel_case_types)] struct ReadVersionSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for ReadVersionSvc { + impl tonic::server::UnaryService for ReadVersionSvc { type Response = super::ReadVersionResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::read_version(&inner, request).await - }; + let fut = async move { ::read_version(&inner, request).await }; Box::pin(fut) } } @@ -4823,14 +3430,8 @@ pub mod node_service_server { let method = ReadVersionSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4839,23 +3440,12 @@ pub mod node_service_server { "/node_service.NodeService/ReadXL" => { #[allow(non_camel_case_types)] struct ReadXLSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for ReadXLSvc { + impl tonic::server::UnaryService for ReadXLSvc { type Response = super::ReadXlResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::read_xl(&inner, request).await - }; + let fut = async move { ::read_xl(&inner, request).await }; Box::pin(fut) } } @@ -4868,14 +3458,8 @@ pub mod node_service_server { let method = ReadXLSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4884,23 +3468,12 @@ pub mod node_service_server { "/node_service.NodeService/DeleteVersion" => { #[allow(non_camel_case_types)] struct DeleteVersionSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DeleteVersionSvc { + impl tonic::server::UnaryService for DeleteVersionSvc { type Response = super::DeleteVersionResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::delete_version(&inner, request).await - }; + let fut = async move { ::delete_version(&inner, request).await }; Box::pin(fut) } } @@ -4913,14 +3486,8 @@ pub mod node_service_server { let method = DeleteVersionSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4929,23 +3496,12 @@ pub mod node_service_server { "/node_service.NodeService/DeleteVersions" => { #[allow(non_camel_case_types)] struct DeleteVersionsSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DeleteVersionsSvc { + impl tonic::server::UnaryService for DeleteVersionsSvc { type Response = super::DeleteVersionsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::delete_versions(&inner, request).await - }; + let fut = async move { ::delete_versions(&inner, request).await }; Box::pin(fut) } } @@ -4958,14 +3514,8 @@ pub mod node_service_server { let method = DeleteVersionsSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -4974,23 +3524,12 @@ pub mod node_service_server { "/node_service.NodeService/ReadMultiple" => { #[allow(non_camel_case_types)] struct ReadMultipleSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for ReadMultipleSvc { + impl tonic::server::UnaryService for ReadMultipleSvc { type Response = super::ReadMultipleResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::read_multiple(&inner, request).await - }; + let fut = async move { ::read_multiple(&inner, request).await }; Box::pin(fut) } } @@ -5003,14 +3542,8 @@ pub mod node_service_server { let method = ReadMultipleSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5019,23 +3552,12 @@ pub mod node_service_server { "/node_service.NodeService/DeleteVolume" => { #[allow(non_camel_case_types)] struct DeleteVolumeSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DeleteVolumeSvc { + impl tonic::server::UnaryService for DeleteVolumeSvc { type Response = super::DeleteVolumeResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::delete_volume(&inner, request).await - }; + let fut = async move { ::delete_volume(&inner, request).await }; Box::pin(fut) } } @@ -5048,14 +3570,8 @@ pub mod node_service_server { let method = DeleteVolumeSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5064,23 +3580,12 @@ pub mod node_service_server { "/node_service.NodeService/DiskInfo" => { #[allow(non_camel_case_types)] struct DiskInfoSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DiskInfoSvc { + impl tonic::server::UnaryService for DiskInfoSvc { type Response = super::DiskInfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::disk_info(&inner, request).await - }; + let fut = async move { ::disk_info(&inner, request).await }; Box::pin(fut) } } @@ -5093,14 +3598,8 @@ pub mod node_service_server { let method = DiskInfoSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5109,26 +3608,13 @@ pub mod node_service_server { "/node_service.NodeService/NsScanner" => { #[allow(non_camel_case_types)] struct NsScannerSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::StreamingService - for NsScannerSvc { + impl tonic::server::StreamingService for NsScannerSvc { type Response = super::NsScannerResponse; type ResponseStream = T::NsScannerStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - tonic::Streaming, - >, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request>) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::ns_scanner(&inner, request).await - }; + let fut = async move { ::ns_scanner(&inner, request).await }; Box::pin(fut) } } @@ -5141,14 +3627,8 @@ pub mod node_service_server { let method = NsScannerSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.streaming(method, req).await; Ok(res) }; @@ -5157,23 +3637,12 @@ pub mod node_service_server { "/node_service.NodeService/Lock" => { #[allow(non_camel_case_types)] struct LockSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for LockSvc { + impl tonic::server::UnaryService for LockSvc { type Response = super::GenerallyLockResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::lock(&inner, request).await - }; + let fut = async move { ::lock(&inner, request).await }; Box::pin(fut) } } @@ -5186,14 +3655,8 @@ pub mod node_service_server { let method = LockSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5202,23 +3665,12 @@ pub mod node_service_server { "/node_service.NodeService/UnLock" => { #[allow(non_camel_case_types)] struct UnLockSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for UnLockSvc { + impl tonic::server::UnaryService for UnLockSvc { type Response = super::GenerallyLockResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::un_lock(&inner, request).await - }; + let fut = async move { ::un_lock(&inner, request).await }; Box::pin(fut) } } @@ -5231,14 +3683,8 @@ pub mod node_service_server { let method = UnLockSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5247,23 +3693,12 @@ pub mod node_service_server { "/node_service.NodeService/RLock" => { #[allow(non_camel_case_types)] struct RLockSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for RLockSvc { + impl tonic::server::UnaryService for RLockSvc { type Response = super::GenerallyLockResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::r_lock(&inner, request).await - }; + let fut = async move { ::r_lock(&inner, request).await }; Box::pin(fut) } } @@ -5276,14 +3711,8 @@ pub mod node_service_server { let method = RLockSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5292,23 +3721,12 @@ pub mod node_service_server { "/node_service.NodeService/RUnLock" => { #[allow(non_camel_case_types)] struct RUnLockSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for RUnLockSvc { + impl tonic::server::UnaryService for RUnLockSvc { type Response = super::GenerallyLockResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::r_un_lock(&inner, request).await - }; + let fut = async move { ::r_un_lock(&inner, request).await }; Box::pin(fut) } } @@ -5321,14 +3739,8 @@ pub mod node_service_server { let method = RUnLockSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5337,23 +3749,12 @@ pub mod node_service_server { "/node_service.NodeService/ForceUnLock" => { #[allow(non_camel_case_types)] struct ForceUnLockSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for ForceUnLockSvc { + impl tonic::server::UnaryService for ForceUnLockSvc { type Response = super::GenerallyLockResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::force_un_lock(&inner, request).await - }; + let fut = async move { ::force_un_lock(&inner, request).await }; Box::pin(fut) } } @@ -5366,14 +3767,8 @@ pub mod node_service_server { let method = ForceUnLockSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5382,23 +3777,12 @@ pub mod node_service_server { "/node_service.NodeService/Refresh" => { #[allow(non_camel_case_types)] struct RefreshSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for RefreshSvc { + impl tonic::server::UnaryService for RefreshSvc { type Response = super::GenerallyLockResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::refresh(&inner, request).await - }; + let fut = async move { ::refresh(&inner, request).await }; Box::pin(fut) } } @@ -5411,14 +3795,8 @@ pub mod node_service_server { let method = RefreshSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5427,24 +3805,12 @@ pub mod node_service_server { "/node_service.NodeService/LocalStorageInfo" => { #[allow(non_camel_case_types)] struct LocalStorageInfoSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for LocalStorageInfoSvc { + impl tonic::server::UnaryService for LocalStorageInfoSvc { type Response = super::LocalStorageInfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::local_storage_info(&inner, request) - .await - }; + let fut = async move { ::local_storage_info(&inner, request).await }; Box::pin(fut) } } @@ -5457,14 +3823,8 @@ pub mod node_service_server { let method = LocalStorageInfoSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5473,23 +3833,12 @@ pub mod node_service_server { "/node_service.NodeService/ServerInfo" => { #[allow(non_camel_case_types)] struct ServerInfoSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for ServerInfoSvc { + impl tonic::server::UnaryService for ServerInfoSvc { type Response = super::ServerInfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::server_info(&inner, request).await - }; + let fut = async move { ::server_info(&inner, request).await }; Box::pin(fut) } } @@ -5502,14 +3851,8 @@ pub mod node_service_server { let method = ServerInfoSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5518,23 +3861,12 @@ pub mod node_service_server { "/node_service.NodeService/GetCpus" => { #[allow(non_camel_case_types)] struct GetCpusSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetCpusSvc { + impl tonic::server::UnaryService for GetCpusSvc { type Response = super::GetCpusResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_cpus(&inner, request).await - }; + let fut = async move { ::get_cpus(&inner, request).await }; Box::pin(fut) } } @@ -5547,14 +3879,8 @@ pub mod node_service_server { let method = GetCpusSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5563,23 +3889,12 @@ pub mod node_service_server { "/node_service.NodeService/GetNetInfo" => { #[allow(non_camel_case_types)] struct GetNetInfoSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetNetInfoSvc { + impl tonic::server::UnaryService for GetNetInfoSvc { type Response = super::GetNetInfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_net_info(&inner, request).await - }; + let fut = async move { ::get_net_info(&inner, request).await }; Box::pin(fut) } } @@ -5592,14 +3907,8 @@ pub mod node_service_server { let method = GetNetInfoSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5608,23 +3917,12 @@ pub mod node_service_server { "/node_service.NodeService/GetPartitions" => { #[allow(non_camel_case_types)] struct GetPartitionsSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetPartitionsSvc { + impl tonic::server::UnaryService for GetPartitionsSvc { type Response = super::GetPartitionsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_partitions(&inner, request).await - }; + let fut = async move { ::get_partitions(&inner, request).await }; Box::pin(fut) } } @@ -5637,14 +3935,8 @@ pub mod node_service_server { let method = GetPartitionsSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5653,23 +3945,12 @@ pub mod node_service_server { "/node_service.NodeService/GetOsInfo" => { #[allow(non_camel_case_types)] struct GetOsInfoSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetOsInfoSvc { + impl tonic::server::UnaryService for GetOsInfoSvc { type Response = super::GetOsInfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_os_info(&inner, request).await - }; + let fut = async move { ::get_os_info(&inner, request).await }; Box::pin(fut) } } @@ -5682,14 +3963,8 @@ pub mod node_service_server { let method = GetOsInfoSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5698,23 +3973,12 @@ pub mod node_service_server { "/node_service.NodeService/GetSELinuxInfo" => { #[allow(non_camel_case_types)] struct GetSELinuxInfoSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetSELinuxInfoSvc { + impl tonic::server::UnaryService for GetSELinuxInfoSvc { type Response = super::GetSeLinuxInfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_se_linux_info(&inner, request).await - }; + let fut = async move { ::get_se_linux_info(&inner, request).await }; Box::pin(fut) } } @@ -5727,14 +3991,8 @@ pub mod node_service_server { let method = GetSELinuxInfoSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5743,23 +4001,12 @@ pub mod node_service_server { "/node_service.NodeService/GetSysConfig" => { #[allow(non_camel_case_types)] struct GetSysConfigSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetSysConfigSvc { + impl tonic::server::UnaryService for GetSysConfigSvc { type Response = super::GetSysConfigResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_sys_config(&inner, request).await - }; + let fut = async move { ::get_sys_config(&inner, request).await }; Box::pin(fut) } } @@ -5772,14 +4019,8 @@ pub mod node_service_server { let method = GetSysConfigSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5788,23 +4029,12 @@ pub mod node_service_server { "/node_service.NodeService/GetSysErrors" => { #[allow(non_camel_case_types)] struct GetSysErrorsSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetSysErrorsSvc { + impl tonic::server::UnaryService for GetSysErrorsSvc { type Response = super::GetSysErrorsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_sys_errors(&inner, request).await - }; + let fut = async move { ::get_sys_errors(&inner, request).await }; Box::pin(fut) } } @@ -5817,14 +4047,8 @@ pub mod node_service_server { let method = GetSysErrorsSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5833,23 +4057,12 @@ pub mod node_service_server { "/node_service.NodeService/GetMemInfo" => { #[allow(non_camel_case_types)] struct GetMemInfoSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetMemInfoSvc { + impl tonic::server::UnaryService for GetMemInfoSvc { type Response = super::GetMemInfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_mem_info(&inner, request).await - }; + let fut = async move { ::get_mem_info(&inner, request).await }; Box::pin(fut) } } @@ -5862,14 +4075,8 @@ pub mod node_service_server { let method = GetMemInfoSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5878,23 +4085,12 @@ pub mod node_service_server { "/node_service.NodeService/GetMetrics" => { #[allow(non_camel_case_types)] struct GetMetricsSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetMetricsSvc { + impl tonic::server::UnaryService for GetMetricsSvc { type Response = super::GetMetricsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_metrics(&inner, request).await - }; + let fut = async move { ::get_metrics(&inner, request).await }; Box::pin(fut) } } @@ -5907,14 +4103,8 @@ pub mod node_service_server { let method = GetMetricsSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5923,23 +4113,12 @@ pub mod node_service_server { "/node_service.NodeService/GetProcInfo" => { #[allow(non_camel_case_types)] struct GetProcInfoSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetProcInfoSvc { + impl tonic::server::UnaryService for GetProcInfoSvc { type Response = super::GetProcInfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_proc_info(&inner, request).await - }; + let fut = async move { ::get_proc_info(&inner, request).await }; Box::pin(fut) } } @@ -5952,14 +4131,8 @@ pub mod node_service_server { let method = GetProcInfoSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -5968,23 +4141,12 @@ pub mod node_service_server { "/node_service.NodeService/StartProfiling" => { #[allow(non_camel_case_types)] struct StartProfilingSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for StartProfilingSvc { + impl tonic::server::UnaryService for StartProfilingSvc { type Response = super::StartProfilingResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::start_profiling(&inner, request).await - }; + let fut = async move { ::start_profiling(&inner, request).await }; Box::pin(fut) } } @@ -5997,14 +4159,8 @@ pub mod node_service_server { let method = StartProfilingSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6013,24 +4169,12 @@ pub mod node_service_server { "/node_service.NodeService/DownloadProfileData" => { #[allow(non_camel_case_types)] struct DownloadProfileDataSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DownloadProfileDataSvc { + impl tonic::server::UnaryService for DownloadProfileDataSvc { type Response = super::DownloadProfileDataResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::download_profile_data(&inner, request) - .await - }; + let fut = async move { ::download_profile_data(&inner, request).await }; Box::pin(fut) } } @@ -6043,14 +4187,8 @@ pub mod node_service_server { let method = DownloadProfileDataSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6059,23 +4197,12 @@ pub mod node_service_server { "/node_service.NodeService/GetBucketStats" => { #[allow(non_camel_case_types)] struct GetBucketStatsSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetBucketStatsSvc { + impl tonic::server::UnaryService for GetBucketStatsSvc { type Response = super::GetBucketStatsDataResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_bucket_stats(&inner, request).await - }; + let fut = async move { ::get_bucket_stats(&inner, request).await }; Box::pin(fut) } } @@ -6088,14 +4215,8 @@ pub mod node_service_server { let method = GetBucketStatsSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6104,23 +4225,12 @@ pub mod node_service_server { "/node_service.NodeService/GetSRMetrics" => { #[allow(non_camel_case_types)] struct GetSRMetricsSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetSRMetricsSvc { + impl tonic::server::UnaryService for GetSRMetricsSvc { type Response = super::GetSrMetricsDataResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_sr_metrics(&inner, request).await - }; + let fut = async move { ::get_sr_metrics(&inner, request).await }; Box::pin(fut) } } @@ -6133,14 +4243,8 @@ pub mod node_service_server { let method = GetSRMetricsSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6149,24 +4253,12 @@ pub mod node_service_server { "/node_service.NodeService/GetAllBucketStats" => { #[allow(non_camel_case_types)] struct GetAllBucketStatsSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetAllBucketStatsSvc { + impl tonic::server::UnaryService for GetAllBucketStatsSvc { type Response = super::GetAllBucketStatsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_all_bucket_stats(&inner, request) - .await - }; + let fut = async move { ::get_all_bucket_stats(&inner, request).await }; Box::pin(fut) } } @@ -6179,14 +4271,8 @@ pub mod node_service_server { let method = GetAllBucketStatsSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6195,24 +4281,12 @@ pub mod node_service_server { "/node_service.NodeService/LoadBucketMetadata" => { #[allow(non_camel_case_types)] struct LoadBucketMetadataSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for LoadBucketMetadataSvc { + impl tonic::server::UnaryService for LoadBucketMetadataSvc { type Response = super::LoadBucketMetadataResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::load_bucket_metadata(&inner, request) - .await - }; + let fut = async move { ::load_bucket_metadata(&inner, request).await }; Box::pin(fut) } } @@ -6225,14 +4299,8 @@ pub mod node_service_server { let method = LoadBucketMetadataSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6241,24 +4309,12 @@ pub mod node_service_server { "/node_service.NodeService/DeleteBucketMetadata" => { #[allow(non_camel_case_types)] struct DeleteBucketMetadataSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DeleteBucketMetadataSvc { + impl tonic::server::UnaryService for DeleteBucketMetadataSvc { type Response = super::DeleteBucketMetadataResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::delete_bucket_metadata(&inner, request) - .await - }; + let fut = async move { ::delete_bucket_metadata(&inner, request).await }; Box::pin(fut) } } @@ -6271,14 +4327,8 @@ pub mod node_service_server { let method = DeleteBucketMetadataSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6287,23 +4337,12 @@ pub mod node_service_server { "/node_service.NodeService/DeletePolicy" => { #[allow(non_camel_case_types)] struct DeletePolicySvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DeletePolicySvc { + impl tonic::server::UnaryService for DeletePolicySvc { type Response = super::DeletePolicyResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::delete_policy(&inner, request).await - }; + let fut = async move { ::delete_policy(&inner, request).await }; Box::pin(fut) } } @@ -6316,14 +4355,8 @@ pub mod node_service_server { let method = DeletePolicySvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6332,23 +4365,12 @@ pub mod node_service_server { "/node_service.NodeService/LoadPolicy" => { #[allow(non_camel_case_types)] struct LoadPolicySvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for LoadPolicySvc { + impl tonic::server::UnaryService for LoadPolicySvc { type Response = super::LoadPolicyResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::load_policy(&inner, request).await - }; + let fut = async move { ::load_policy(&inner, request).await }; Box::pin(fut) } } @@ -6361,14 +4383,8 @@ pub mod node_service_server { let method = LoadPolicySvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6377,24 +4393,12 @@ pub mod node_service_server { "/node_service.NodeService/LoadPolicyMapping" => { #[allow(non_camel_case_types)] struct LoadPolicyMappingSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for LoadPolicyMappingSvc { + impl tonic::server::UnaryService for LoadPolicyMappingSvc { type Response = super::LoadPolicyMappingResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::load_policy_mapping(&inner, request) - .await - }; + let fut = async move { ::load_policy_mapping(&inner, request).await }; Box::pin(fut) } } @@ -6407,14 +4411,8 @@ pub mod node_service_server { let method = LoadPolicyMappingSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6423,23 +4421,12 @@ pub mod node_service_server { "/node_service.NodeService/DeleteUser" => { #[allow(non_camel_case_types)] struct DeleteUserSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DeleteUserSvc { + impl tonic::server::UnaryService for DeleteUserSvc { type Response = super::DeleteUserResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::delete_user(&inner, request).await - }; + let fut = async move { ::delete_user(&inner, request).await }; Box::pin(fut) } } @@ -6452,14 +4439,8 @@ pub mod node_service_server { let method = DeleteUserSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6468,24 +4449,12 @@ pub mod node_service_server { "/node_service.NodeService/DeleteServiceAccount" => { #[allow(non_camel_case_types)] struct DeleteServiceAccountSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for DeleteServiceAccountSvc { + impl tonic::server::UnaryService for DeleteServiceAccountSvc { type Response = super::DeleteServiceAccountResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::delete_service_account(&inner, request) - .await - }; + let fut = async move { ::delete_service_account(&inner, request).await }; Box::pin(fut) } } @@ -6498,14 +4467,8 @@ pub mod node_service_server { let method = DeleteServiceAccountSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6514,23 +4477,12 @@ pub mod node_service_server { "/node_service.NodeService/LoadUser" => { #[allow(non_camel_case_types)] struct LoadUserSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for LoadUserSvc { + impl tonic::server::UnaryService for LoadUserSvc { type Response = super::LoadUserResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::load_user(&inner, request).await - }; + let fut = async move { ::load_user(&inner, request).await }; Box::pin(fut) } } @@ -6543,14 +4495,8 @@ pub mod node_service_server { let method = LoadUserSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6559,24 +4505,12 @@ pub mod node_service_server { "/node_service.NodeService/LoadServiceAccount" => { #[allow(non_camel_case_types)] struct LoadServiceAccountSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for LoadServiceAccountSvc { + impl tonic::server::UnaryService for LoadServiceAccountSvc { type Response = super::LoadServiceAccountResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::load_service_account(&inner, request) - .await - }; + let fut = async move { ::load_service_account(&inner, request).await }; Box::pin(fut) } } @@ -6589,14 +4523,8 @@ pub mod node_service_server { let method = LoadServiceAccountSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6605,23 +4533,12 @@ pub mod node_service_server { "/node_service.NodeService/LoadGroup" => { #[allow(non_camel_case_types)] struct LoadGroupSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for LoadGroupSvc { + impl tonic::server::UnaryService for LoadGroupSvc { type Response = super::LoadGroupResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::load_group(&inner, request).await - }; + let fut = async move { ::load_group(&inner, request).await }; Box::pin(fut) } } @@ -6634,14 +4551,8 @@ pub mod node_service_server { let method = LoadGroupSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6650,30 +4561,14 @@ pub mod node_service_server { "/node_service.NodeService/ReloadSiteReplicationConfig" => { #[allow(non_camel_case_types)] struct ReloadSiteReplicationConfigSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService< - super::ReloadSiteReplicationConfigRequest, - > for ReloadSiteReplicationConfigSvc { + impl tonic::server::UnaryService + for ReloadSiteReplicationConfigSvc + { type Response = super::ReloadSiteReplicationConfigResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - super::ReloadSiteReplicationConfigRequest, - >, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::reload_site_replication_config( - &inner, - request, - ) - .await - }; + let fut = async move { ::reload_site_replication_config(&inner, request).await }; Box::pin(fut) } } @@ -6686,14 +4581,8 @@ pub mod node_service_server { let method = ReloadSiteReplicationConfigSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6702,23 +4591,12 @@ pub mod node_service_server { "/node_service.NodeService/SignalService" => { #[allow(non_camel_case_types)] struct SignalServiceSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for SignalServiceSvc { + impl tonic::server::UnaryService for SignalServiceSvc { type Response = super::SignalServiceResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::signal_service(&inner, request).await - }; + let fut = async move { ::signal_service(&inner, request).await }; Box::pin(fut) } } @@ -6731,14 +4609,8 @@ pub mod node_service_server { let method = SignalServiceSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6747,24 +4619,12 @@ pub mod node_service_server { "/node_service.NodeService/BackgroundHealStatus" => { #[allow(non_camel_case_types)] struct BackgroundHealStatusSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for BackgroundHealStatusSvc { + impl tonic::server::UnaryService for BackgroundHealStatusSvc { type Response = super::BackgroundHealStatusResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::background_heal_status(&inner, request) - .await - }; + let fut = async move { ::background_heal_status(&inner, request).await }; Box::pin(fut) } } @@ -6777,14 +4637,8 @@ pub mod node_service_server { let method = BackgroundHealStatusSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6793,24 +4647,12 @@ pub mod node_service_server { "/node_service.NodeService/GetMetacacheListing" => { #[allow(non_camel_case_types)] struct GetMetacacheListingSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for GetMetacacheListingSvc { + impl tonic::server::UnaryService for GetMetacacheListingSvc { type Response = super::GetMetacacheListingResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::get_metacache_listing(&inner, request) - .await - }; + let fut = async move { ::get_metacache_listing(&inner, request).await }; Box::pin(fut) } } @@ -6823,14 +4665,8 @@ pub mod node_service_server { let method = GetMetacacheListingSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6839,27 +4675,12 @@ pub mod node_service_server { "/node_service.NodeService/UpdateMetacacheListing" => { #[allow(non_camel_case_types)] struct UpdateMetacacheListingSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for UpdateMetacacheListingSvc { + impl tonic::server::UnaryService for UpdateMetacacheListingSvc { type Response = super::UpdateMetacacheListingResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::update_metacache_listing( - &inner, - request, - ) - .await - }; + let fut = async move { ::update_metacache_listing(&inner, request).await }; Box::pin(fut) } } @@ -6872,14 +4693,8 @@ pub mod node_service_server { let method = UpdateMetacacheListingSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6888,23 +4703,12 @@ pub mod node_service_server { "/node_service.NodeService/ReloadPoolMeta" => { #[allow(non_camel_case_types)] struct ReloadPoolMetaSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for ReloadPoolMetaSvc { + impl tonic::server::UnaryService for ReloadPoolMetaSvc { type Response = super::ReloadPoolMetaResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::reload_pool_meta(&inner, request).await - }; + let fut = async move { ::reload_pool_meta(&inner, request).await }; Box::pin(fut) } } @@ -6917,14 +4721,8 @@ pub mod node_service_server { let method = ReloadPoolMetaSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6933,23 +4731,12 @@ pub mod node_service_server { "/node_service.NodeService/StopRebalance" => { #[allow(non_camel_case_types)] struct StopRebalanceSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for StopRebalanceSvc { + impl tonic::server::UnaryService for StopRebalanceSvc { type Response = super::StopRebalanceResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::stop_rebalance(&inner, request).await - }; + let fut = async move { ::stop_rebalance(&inner, request).await }; Box::pin(fut) } } @@ -6962,14 +4749,8 @@ pub mod node_service_server { let method = StopRebalanceSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -6978,24 +4759,12 @@ pub mod node_service_server { "/node_service.NodeService/LoadRebalanceMeta" => { #[allow(non_camel_case_types)] struct LoadRebalanceMetaSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for LoadRebalanceMetaSvc { + impl tonic::server::UnaryService for LoadRebalanceMetaSvc { type Response = super::LoadRebalanceMetaResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::load_rebalance_meta(&inner, request) - .await - }; + let fut = async move { ::load_rebalance_meta(&inner, request).await }; Box::pin(fut) } } @@ -7008,14 +4777,8 @@ pub mod node_service_server { let method = LoadRebalanceMetaSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; @@ -7024,29 +4787,12 @@ pub mod node_service_server { "/node_service.NodeService/LoadTransitionTierConfig" => { #[allow(non_camel_case_types)] struct LoadTransitionTierConfigSvc(pub Arc); - impl< - T: NodeService, - > tonic::server::UnaryService - for LoadTransitionTierConfigSvc { + impl tonic::server::UnaryService for LoadTransitionTierConfigSvc { type Response = super::LoadTransitionTierConfigResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - super::LoadTransitionTierConfigRequest, - >, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::load_transition_tier_config( - &inner, - request, - ) - .await - }; + let fut = async move { ::load_transition_tier_config(&inner, request).await }; Box::pin(fut) } } @@ -7059,36 +4805,20 @@ pub mod node_service_server { let method = LoadTransitionTierConfigSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); + .apply_compression_config(accept_compression_encodings, send_compression_encodings) + .apply_max_message_size_config(max_decoding_message_size, max_encoding_message_size); let res = grpc.unary(method, req).await; Ok(res) }; Box::pin(fut) } - _ => { - Box::pin(async move { - let mut response = http::Response::new(empty_body()); - let headers = response.headers_mut(); - headers - .insert( - tonic::Status::GRPC_STATUS, - (tonic::Code::Unimplemented as i32).into(), - ); - headers - .insert( - http::header::CONTENT_TYPE, - tonic::metadata::GRPC_CONTENT_TYPE, - ); - Ok(response) - }) - } + _ => Box::pin(async move { + let mut response = http::Response::new(tonic::body::Body::default()); + let headers = response.headers_mut(); + headers.insert(tonic::Status::GRPC_STATUS, (tonic::Code::Unimplemented as i32).into()); + headers.insert(http::header::CONTENT_TYPE, tonic::metadata::GRPC_CONTENT_TYPE); + Ok(response) + }), } } } diff --git a/crates/obs/Cargo.toml b/crates/obs/Cargo.toml new file mode 100644 index 000000000..4290b76b3 --- /dev/null +++ b/crates/obs/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "rustfs-obs" +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lints] +workspace = true + +[features] +default = ["file"] +kafka = ["dep:rdkafka"] +webhook = ["dep:reqwest"] +file = [] + +[dependencies] +async-trait = { workspace = true } +chrono = { workspace = true } +config = { workspace = true } +opentelemetry = { workspace = true } +opentelemetry-appender-tracing = { workspace = true, features = ["experimental_use_tracing_span_context", "experimental_metadata_attributes"] } +opentelemetry_sdk = { workspace = true, features = ["rt-tokio"] } +opentelemetry-stdout = { workspace = true } +opentelemetry-otlp = { workspace = true, features = ["grpc-tonic", "gzip-tonic"] } +opentelemetry-prometheus = { workspace = true } +opentelemetry-semantic-conventions = { workspace = true, features = ["semconv_experimental"] } +prometheus = { workspace = true } +serde = { workspace = true } +tracing = { workspace = true, features = ["std", "attributes"] } +tracing-core = { workspace = true } +tracing-error = { workspace = true } +tracing-opentelemetry = { workspace = true } +tracing-subscriber = { workspace = true, features = ["registry", "std", "fmt", "env-filter", "tracing-log", "time", "local-time", "json"] } +tokio = { workspace = true, features = ["sync", "fs", "rt-multi-thread"] } +rdkafka = { workspace = true, features = ["tokio"], optional = true } +reqwest = { workspace = true, optional = true, default-features = false } +serde_json = { workspace = true } +thiserror = { workspace = true } +local-ip-address = { workspace = true } + + +[dev-dependencies] +chrono = { workspace = true } +opentelemetry = { workspace = true } +opentelemetry_sdk = { workspace = true, features = ["rt-tokio"] } +opentelemetry-stdout = { workspace = true } +opentelemetry-otlp = { workspace = true, features = ["grpc-tonic"] } +opentelemetry-semantic-conventions = { workspace = true, features = ["semconv_experimental"] } +tokio = { workspace = true, features = ["full"] } +tracing = { workspace = true, features = ["std", "attributes"] } +tracing-subscriber = { workspace = true, features = ["registry", "std", "fmt"] } \ No newline at end of file diff --git a/crates/obs/examples/config.toml b/crates/obs/examples/config.toml new file mode 100644 index 000000000..929867f03 --- /dev/null +++ b/crates/obs/examples/config.toml @@ -0,0 +1,33 @@ +[observability] +endpoint = "http://localhost:4317" # Default is "http://localhost:4317" if not specified +use_stdout = false # Output with stdout, true output, false no output +sample_ratio = 1 +meter_interval = 30 +service_name = "rustfs_obs" +service_version = "0.1.0" +environments = "develop" +logger_level = "debug" + +[sinks] +[sinks.kafka] +enabled = false +bootstrap_servers = "localhost:9092" +topic = "logs" +batch_size = 100 # Default is 100 if not specified +batch_timeout_ms = 1000 # Default is 1000ms if not specified + +[sinks.webhook] +enabled = false +endpoint = "http://localhost:8080/webhook" +auth_token = "" +batch_size = 100 # Default is 3 if not specified +batch_timeout_ms = 1000 # Default is 100ms if not specified + +[sinks.file] +enabled = true +path = "logs/app.log" +batch_size = 100 +batch_timeout_ms = 1000 # Default is 8192 bytes if not specified + +[logger] +queue_capacity = 10000 \ No newline at end of file diff --git a/crates/obs/examples/server.rs b/crates/obs/examples/server.rs new file mode 100644 index 000000000..55977b10c --- /dev/null +++ b/crates/obs/examples/server.rs @@ -0,0 +1,88 @@ +use opentelemetry::global; +use rustfs_obs::{get_logger, init_obs, load_config, log_info, BaseLogEntry, ServerLogEntry}; +use std::collections::HashMap; +use std::time::{Duration, SystemTime}; +use tracing::{info, instrument}; +use tracing_core::Level; + +#[tokio::main] +async fn main() { + let obs_conf = Some("crates/obs/examples/config.toml".to_string()); + let config = load_config(obs_conf); + let (_logger, _guard) = init_obs(config.clone()).await; + let span = tracing::span!(Level::INFO, "main"); + let _enter = span.enter(); + info!("Program starts"); + // Simulate the operation + tokio::time::sleep(Duration::from_millis(100)).await; + run( + "service-demo".to_string(), + "object-demo".to_string(), + "user-demo".to_string(), + "service-demo".to_string(), + ) + .await; + info!("Program ends"); +} + +#[instrument(fields(bucket, object, user))] +async fn run(bucket: String, object: String, user: String, service_name: String) { + let start_time = SystemTime::now(); + info!("Log module initialization is completed service_name: {:?}", service_name); + + // Record Metrics + let meter = global::meter("rustfs"); + let request_duration = meter.f64_histogram("s3_request_duration_seconds").build(); + request_duration.record( + start_time.elapsed().unwrap().as_secs_f64(), + &[opentelemetry::KeyValue::new("operation", "run")], + ); + + let base_entry = BaseLogEntry::new() + .message(Some("run logger api_handler info".to_string())) + .request_id(Some("request_id".to_string())) + .timestamp(chrono::DateTime::from(start_time)) + .tags(Some(HashMap::default())); + + let server_entry = ServerLogEntry::new(Level::INFO, "api_handler".to_string()) + .with_base(base_entry) + .user_id(Some(user.clone())) + .add_field("operation".to_string(), "login".to_string()) + .add_field("bucket".to_string(), bucket.clone()) + .add_field("object".to_string(), object.clone()); + + let result = get_logger().lock().await.log_server_entry(server_entry).await; + info!("Logging is completed {:?}", result); + put_object("bucket".to_string(), "object".to_string(), "user".to_string()).await; + info!("Logging is completed"); + tokio::time::sleep(Duration::from_secs(2)).await; + info!("Program run ends"); +} + +#[instrument(fields(bucket, object, user))] +async fn put_object(bucket: String, object: String, user: String) { + let start_time = SystemTime::now(); + info!("Starting put_object operation time: {:?}", start_time); + + let meter = global::meter("rustfs"); + let request_duration = meter.f64_histogram("s3_request_duration_seconds").build(); + request_duration.record( + start_time.elapsed().unwrap().as_secs_f64(), + &[opentelemetry::KeyValue::new("operation", "put_object")], + ); + + info!( + "Starting PUT operation content: bucket = {}, object = {}, user = {},start_time = {}", + bucket, + object, + user, + start_time.elapsed().unwrap().as_secs_f64() + ); + + let result = log_info("put_object logger info", "put_object").await; + info!("put_object is completed {:?}", result); + // Simulate the operation + tokio::time::sleep(Duration::from_millis(100)).await; + + info!("PUT operation completed"); +} diff --git a/crates/obs/src/config.rs b/crates/obs/src/config.rs new file mode 100644 index 000000000..a9e620161 --- /dev/null +++ b/crates/obs/src/config.rs @@ -0,0 +1,271 @@ +use crate::global::{ENVIRONMENT, LOGGER_LEVEL, METER_INTERVAL, SAMPLE_RATIO, SERVICE_NAME, SERVICE_VERSION, USE_STDOUT}; +use config::{Config, Environment, File, FileFormat}; +use serde::Deserialize; +use std::env; + +/// OpenTelemetry Configuration +/// Add service name, service version, environment +/// Add interval time for metric collection +/// Add sample ratio for trace sampling +/// Add endpoint for metric collection +/// Add use_stdout for output to stdout +/// Add logger level for log level +#[derive(Debug, Deserialize, Clone)] +pub struct OtelConfig { + pub endpoint: String, + pub use_stdout: Option, + pub sample_ratio: Option, + pub meter_interval: Option, + pub service_name: Option, + pub service_version: Option, + pub environment: Option, + pub logger_level: Option, +} + +// 辅助函数:从环境变量中提取可观测性配置 +fn extract_otel_config_from_env() -> OtelConfig { + OtelConfig { + endpoint: env::var("RUSTFS_OBSERVABILITY_ENDPOINT").unwrap_or_else(|_| "".to_string()), + use_stdout: env::var("RUSTFS_OBSERVABILITY_USE_STDOUT") + .ok() + .and_then(|v| v.parse().ok()) + .or(Some(USE_STDOUT)), + sample_ratio: env::var("RUSTFS_OBSERVABILITY_SAMPLE_RATIO") + .ok() + .and_then(|v| v.parse().ok()) + .or(Some(SAMPLE_RATIO)), + meter_interval: env::var("RUSTFS_OBSERVABILITY_METER_INTERVAL") + .ok() + .and_then(|v| v.parse().ok()) + .or(Some(METER_INTERVAL)), + service_name: env::var("RUSTFS_OBSERVABILITY_SERVICE_NAME") + .ok() + .and_then(|v| v.parse().ok()) + .or(Some(SERVICE_NAME.to_string())), + service_version: env::var("RUSTFS_OBSERVABILITY_SERVICE_VERSION") + .ok() + .and_then(|v| v.parse().ok()) + .or(Some(SERVICE_VERSION.to_string())), + environment: env::var("RUSTFS_OBSERVABILITY_ENVIRONMENT") + .ok() + .and_then(|v| v.parse().ok()) + .or(Some(ENVIRONMENT.to_string())), + logger_level: env::var("RUSTFS_OBSERVABILITY_LOGGER_LEVEL") + .ok() + .and_then(|v| v.parse().ok()) + .or(Some(LOGGER_LEVEL.to_string())), + } +} + +impl Default for OtelConfig { + fn default() -> Self { + extract_otel_config_from_env() + } +} + +/// Kafka Sink Configuration - Add batch parameters +#[derive(Debug, Deserialize, Clone, Default)] +pub struct KafkaSinkConfig { + pub enabled: bool, + pub bootstrap_servers: String, + pub topic: String, + pub batch_size: Option, // Batch size, default 100 + pub batch_timeout_ms: Option, // Batch timeout time, default 1000ms +} + +/// Webhook Sink Configuration - Add Retry Parameters +#[derive(Debug, Deserialize, Clone, Default)] +pub struct WebhookSinkConfig { + pub enabled: bool, + pub endpoint: String, + pub auth_token: String, + pub max_retries: Option, // Maximum number of retry times, default 3 + pub retry_delay_ms: Option, // Retry the delay cardinality, default 100ms +} + +/// File Sink Configuration - Add buffering parameters +#[derive(Debug, Deserialize, Clone)] +pub struct FileSinkConfig { + pub enabled: bool, + pub path: String, + pub buffer_size: Option, // Write buffer size, default 8192 + pub flush_interval_ms: Option, // Refresh interval time, default 1000ms + pub flush_threshold: Option, // Refresh threshold, default 100 logs +} + +impl FileSinkConfig { + pub fn get_default_log_path() -> String { + let temp_dir = env::temp_dir().join("rustfs"); + + if let Err(e) = std::fs::create_dir_all(&temp_dir) { + eprintln!("Failed to create log directory: {}", e); + return "rustfs/rustfs.log".to_string(); + } + + temp_dir + .join("rustfs.log") + .to_str() + .unwrap_or("rustfs/rustfs.log") + .to_string() + } +} + +impl Default for FileSinkConfig { + fn default() -> Self { + FileSinkConfig { + enabled: true, + path: env::var("RUSTFS_SINKS_FILE_PATH") + .ok() + .filter(|s| !s.trim().is_empty()) + .unwrap_or_else(Self::get_default_log_path), + buffer_size: Some(8192), + flush_interval_ms: Some(1000), + flush_threshold: Some(100), + } + } +} + +/// Sink configuration collection +#[derive(Debug, Deserialize, Clone)] +pub struct SinkConfig { + pub kafka: Option, + pub webhook: Option, + pub file: Option, +} + +impl Default for SinkConfig { + fn default() -> Self { + SinkConfig { + kafka: None, + webhook: None, + file: Some(FileSinkConfig::default()), + } + } +} + +///Logger Configuration +#[derive(Debug, Deserialize, Clone)] +pub struct LoggerConfig { + pub queue_capacity: Option, +} + +impl Default for LoggerConfig { + fn default() -> Self { + LoggerConfig { + queue_capacity: Some(10000), + } + } +} + +/// Overall application configuration +/// Add observability, sinks, and logger configuration +/// +/// Observability: OpenTelemetry configuration +/// Sinks: Kafka, Webhook, File sink configuration +/// Logger: Logger configuration +/// +/// # Example +/// ``` +/// use rustfs_obs::AppConfig; +/// use rustfs_obs::load_config; +/// +/// let config = load_config(None); +/// ``` +#[derive(Debug, Deserialize, Clone)] +pub struct AppConfig { + pub observability: OtelConfig, + pub sinks: SinkConfig, + pub logger: Option, +} + +// 为 AppConfig 实现 Default +impl AppConfig { + pub fn new() -> Self { + Self { + observability: OtelConfig::default(), + sinks: SinkConfig::default(), + logger: Some(LoggerConfig::default()), + } + } +} + +impl Default for AppConfig { + fn default() -> Self { + Self::new() + } +} + +const DEFAULT_CONFIG_FILE: &str = "obs"; + +/// Loading the configuration file +/// Supports TOML, YAML and .env formats, read in order by priority +/// +/// # Parameters +/// - `config_dir`: Configuration file path +/// +/// # Returns +/// Configuration information +/// +/// # Example +/// ``` +/// use rustfs_obs::AppConfig; +/// use rustfs_obs::load_config; +/// +/// let config = load_config(None); +/// ``` +pub fn load_config(config_dir: Option) -> AppConfig { + let config_dir = if let Some(path) = config_dir { + // If a path is provided, check if it's empty + if path.is_empty() { + // If empty, use the default config file name + DEFAULT_CONFIG_FILE.to_string() + } else { + // Use the provided path + let path = std::path::Path::new(&path); + if path.extension().is_some() { + // If path has extension, use it as is (extension will be added by Config::builder) + path.with_extension("").to_string_lossy().into_owned() + } else { + // If path is a directory, append the default config file name + path.to_string_lossy().into_owned() + } + } + } else { + // If no path provided, use current directory + default config file + match env::current_dir() { + Ok(dir) => dir.join(DEFAULT_CONFIG_FILE).to_string_lossy().into_owned(), + Err(_) => { + eprintln!("Warning: Failed to get current directory, using default config file"); + DEFAULT_CONFIG_FILE.to_string() + } + } + }; + + // Log using proper logging instead of println when possible + println!("Using config file base: {}", config_dir); + + let app_config = Config::builder() + .add_source(File::with_name(config_dir.as_str()).format(FileFormat::Toml).required(false)) + .add_source(File::with_name(config_dir.as_str()).format(FileFormat::Yaml).required(false)) + .add_source( + Environment::default() + .prefix("RUSTFS") + .prefix_separator("__") + .separator("__") + .with_list_parse_key("volumes") + .try_parsing(true), + ) + .build() + .unwrap_or_default(); + + match app_config.try_deserialize::() { + Ok(app_config) => { + println!("Parsed AppConfig: {:?}", app_config); + app_config + } + Err(e) => { + println!("Failed to deserialize config: {}", e); + AppConfig::default() + } + } +} diff --git a/crates/obs/src/entry/args.rs b/crates/obs/src/entry/args.rs new file mode 100644 index 000000000..1099398ac --- /dev/null +++ b/crates/obs/src/entry/args.rs @@ -0,0 +1,74 @@ +use crate::entry::ObjectVersion; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// Args - defines the arguments for API operations +/// Args is used to define the arguments for API operations. +/// +/// # Example +/// ``` +/// use rustfs_obs::Args; +/// use std::collections::HashMap; +/// +/// let args = Args::new() +/// .set_bucket(Some("my-bucket".to_string())) +/// .set_object(Some("my-object".to_string())) +/// .set_version_id(Some("123".to_string())) +/// .set_metadata(Some(HashMap::new())); +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)] +pub struct Args { + #[serde(rename = "bucket", skip_serializing_if = "Option::is_none")] + pub bucket: Option, + #[serde(rename = "object", skip_serializing_if = "Option::is_none")] + pub object: Option, + #[serde(rename = "versionId", skip_serializing_if = "Option::is_none")] + pub version_id: Option, + #[serde(rename = "objects", skip_serializing_if = "Option::is_none")] + pub objects: Option>, + #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] + pub metadata: Option>, +} + +impl Args { + /// Create a new Args object + pub fn new() -> Self { + Args { + bucket: None, + object: None, + version_id: None, + objects: None, + metadata: None, + } + } + + /// Set the bucket + pub fn set_bucket(mut self, bucket: Option) -> Self { + self.bucket = bucket; + self + } + + /// Set the object + pub fn set_object(mut self, object: Option) -> Self { + self.object = object; + self + } + + /// Set the version ID + pub fn set_version_id(mut self, version_id: Option) -> Self { + self.version_id = version_id; + self + } + + /// Set the objects + pub fn set_objects(mut self, objects: Option>) -> Self { + self.objects = objects; + self + } + + /// Set the metadata + pub fn set_metadata(mut self, metadata: Option>) -> Self { + self.metadata = metadata; + self + } +} diff --git a/crates/obs/src/entry/audit.rs b/crates/obs/src/entry/audit.rs new file mode 100644 index 000000000..0aba2bac3 --- /dev/null +++ b/crates/obs/src/entry/audit.rs @@ -0,0 +1,453 @@ +use crate::{BaseLogEntry, LogRecord, ObjectVersion}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::collections::HashMap; + +/// API details structure +/// ApiDetails is used to define the details of an API operation +/// +/// The `ApiDetails` structure contains the following fields: +/// - `name` - the name of the API operation +/// - `bucket` - the bucket name +/// - `object` - the object name +/// - `objects` - the list of objects +/// - `status` - the status of the API operation +/// - `status_code` - the status code of the API operation +/// - `input_bytes` - the input bytes +/// - `output_bytes` - the output bytes +/// - `header_bytes` - the header bytes +/// - `time_to_first_byte` - the time to first byte +/// - `time_to_first_byte_in_ns` - the time to first byte in nanoseconds +/// - `time_to_response` - the time to response +/// - `time_to_response_in_ns` - the time to response in nanoseconds +/// +/// The `ApiDetails` structure contains the following methods: +/// - `new` - create a new `ApiDetails` with default values +/// - `set_name` - set the name +/// - `set_bucket` - set the bucket +/// - `set_object` - set the object +/// - `set_objects` - set the objects +/// - `set_status` - set the status +/// - `set_status_code` - set the status code +/// - `set_input_bytes` - set the input bytes +/// - `set_output_bytes` - set the output bytes +/// - `set_header_bytes` - set the header bytes +/// - `set_time_to_first_byte` - set the time to first byte +/// - `set_time_to_first_byte_in_ns` - set the time to first byte in nanoseconds +/// - `set_time_to_response` - set the time to response +/// - `set_time_to_response_in_ns` - set the time to response in nanoseconds +/// +/// # Example +/// ``` +/// use rustfs_obs::ApiDetails; +/// use rustfs_obs::ObjectVersion; +/// +/// let api = ApiDetails::new() +/// .set_name(Some("GET".to_string())) +/// .set_bucket(Some("my-bucket".to_string())) +/// .set_object(Some("my-object".to_string())) +/// .set_objects(vec![ObjectVersion::new_with_object_name("my-object".to_string())]) +/// .set_status(Some("OK".to_string())) +/// .set_status_code(Some(200)) +/// .set_input_bytes(100) +/// .set_output_bytes(200) +/// .set_header_bytes(Some(50)) +/// .set_time_to_first_byte(Some("100ms".to_string())) +/// .set_time_to_first_byte_in_ns(Some("100000000ns".to_string())) +/// .set_time_to_response(Some("200ms".to_string())) +/// .set_time_to_response_in_ns(Some("200000000ns".to_string())); +/// ``` +#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] +pub struct ApiDetails { + #[serde(rename = "name", skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(rename = "bucket", skip_serializing_if = "Option::is_none")] + pub bucket: Option, + #[serde(rename = "object", skip_serializing_if = "Option::is_none")] + pub object: Option, + #[serde(rename = "objects", skip_serializing_if = "Vec::is_empty", default)] + pub objects: Vec, + #[serde(rename = "status", skip_serializing_if = "Option::is_none")] + pub status: Option, + #[serde(rename = "statusCode", skip_serializing_if = "Option::is_none")] + pub status_code: Option, + #[serde(rename = "rx")] + pub input_bytes: i64, + #[serde(rename = "tx")] + pub output_bytes: i64, + #[serde(rename = "txHeaders", skip_serializing_if = "Option::is_none")] + pub header_bytes: Option, + #[serde(rename = "timeToFirstByte", skip_serializing_if = "Option::is_none")] + pub time_to_first_byte: Option, + #[serde(rename = "timeToFirstByteInNS", skip_serializing_if = "Option::is_none")] + pub time_to_first_byte_in_ns: Option, + #[serde(rename = "timeToResponse", skip_serializing_if = "Option::is_none")] + pub time_to_response: Option, + #[serde(rename = "timeToResponseInNS", skip_serializing_if = "Option::is_none")] + pub time_to_response_in_ns: Option, +} + +impl ApiDetails { + /// Create a new `ApiDetails` with default values + pub fn new() -> Self { + ApiDetails { + name: None, + bucket: None, + object: None, + objects: Vec::new(), + status: None, + status_code: None, + input_bytes: 0, + output_bytes: 0, + header_bytes: None, + time_to_first_byte: None, + time_to_first_byte_in_ns: None, + time_to_response: None, + time_to_response_in_ns: None, + } + } + + /// Set the name + pub fn set_name(mut self, name: Option) -> Self { + self.name = name; + self + } + + /// Set the bucket + pub fn set_bucket(mut self, bucket: Option) -> Self { + self.bucket = bucket; + self + } + + /// Set the object + pub fn set_object(mut self, object: Option) -> Self { + self.object = object; + self + } + + /// Set the objects + pub fn set_objects(mut self, objects: Vec) -> Self { + self.objects = objects; + self + } + + /// Set the status + pub fn set_status(mut self, status: Option) -> Self { + self.status = status; + self + } + + /// Set the status code + pub fn set_status_code(mut self, status_code: Option) -> Self { + self.status_code = status_code; + self + } + + /// Set the input bytes + pub fn set_input_bytes(mut self, input_bytes: i64) -> Self { + self.input_bytes = input_bytes; + self + } + + /// Set the output bytes + pub fn set_output_bytes(mut self, output_bytes: i64) -> Self { + self.output_bytes = output_bytes; + self + } + + /// Set the header bytes + pub fn set_header_bytes(mut self, header_bytes: Option) -> Self { + self.header_bytes = header_bytes; + self + } + + /// Set the time to first byte + pub fn set_time_to_first_byte(mut self, time_to_first_byte: Option) -> Self { + self.time_to_first_byte = time_to_first_byte; + self + } + + /// Set the time to first byte in nanoseconds + pub fn set_time_to_first_byte_in_ns(mut self, time_to_first_byte_in_ns: Option) -> Self { + self.time_to_first_byte_in_ns = time_to_first_byte_in_ns; + self + } + + /// Set the time to response + pub fn set_time_to_response(mut self, time_to_response: Option) -> Self { + self.time_to_response = time_to_response; + self + } + + /// Set the time to response in nanoseconds + pub fn set_time_to_response_in_ns(mut self, time_to_response_in_ns: Option) -> Self { + self.time_to_response_in_ns = time_to_response_in_ns; + self + } +} + +/// Entry - audit entry logs +/// AuditLogEntry is used to define the structure of an audit log entry +/// +/// The `AuditLogEntry` structure contains the following fields: +/// - `base` - the base log entry +/// - `version` - the version of the audit log entry +/// - `deployment_id` - the deployment ID +/// - `event` - the event +/// - `entry_type` - the type of audit message +/// - `api` - the API details +/// - `remote_host` - the remote host +/// - `user_agent` - the user agent +/// - `req_path` - the request path +/// - `req_host` - the request host +/// - `req_claims` - the request claims +/// - `req_query` - the request query +/// - `req_header` - the request header +/// - `resp_header` - the response header +/// - `access_key` - the access key +/// - `parent_user` - the parent user +/// - `error` - the error +/// +/// The `AuditLogEntry` structure contains the following methods: +/// - `new` - create a new `AuditEntry` with default values +/// - `new_with_values` - create a new `AuditEntry` with version, time, event and api details +/// - `with_base` - set the base log entry +/// - `set_version` - set the version +/// - `set_deployment_id` - set the deployment ID +/// - `set_event` - set the event +/// - `set_entry_type` - set the entry type +/// - `set_api` - set the API details +/// - `set_remote_host` - set the remote host +/// - `set_user_agent` - set the user agent +/// - `set_req_path` - set the request path +/// - `set_req_host` - set the request host +/// - `set_req_claims` - set the request claims +/// - `set_req_query` - set the request query +/// - `set_req_header` - set the request header +/// - `set_resp_header` - set the response header +/// - `set_access_key` - set the access key +/// - `set_parent_user` - set the parent user +/// - `set_error` - set the error +/// +/// # Example +/// ``` +/// use rustfs_obs::AuditLogEntry; +/// use rustfs_obs::ApiDetails; +/// use std::collections::HashMap; +/// +/// let entry = AuditLogEntry::new() +/// .set_version("1.0".to_string()) +/// .set_deployment_id(Some("123".to_string())) +/// .set_event("event".to_string()) +/// .set_entry_type(Some("type".to_string())) +/// .set_api(ApiDetails::new()) +/// .set_remote_host(Some("remote-host".to_string())) +/// .set_user_agent(Some("user-agent".to_string())) +/// .set_req_path(Some("req-path".to_string())) +/// .set_req_host(Some("req-host".to_string())) +/// .set_req_claims(Some(HashMap::new())) +/// .set_req_query(Some(HashMap::new())) +/// .set_req_header(Some(HashMap::new())) +/// .set_resp_header(Some(HashMap::new())) +/// .set_access_key(Some("access-key".to_string())) +/// .set_parent_user(Some("parent-user".to_string())) +/// .set_error(Some("error".to_string())); +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +pub struct AuditLogEntry { + #[serde(flatten)] + pub base: BaseLogEntry, + pub version: String, + #[serde(rename = "deploymentid", skip_serializing_if = "Option::is_none")] + pub deployment_id: Option, + pub event: String, + // Class of audit message - S3, admin ops, bucket management + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub entry_type: Option, + pub api: ApiDetails, + #[serde(rename = "remotehost", skip_serializing_if = "Option::is_none")] + pub remote_host: Option, + #[serde(rename = "userAgent", skip_serializing_if = "Option::is_none")] + pub user_agent: Option, + #[serde(rename = "requestPath", skip_serializing_if = "Option::is_none")] + pub req_path: Option, + #[serde(rename = "requestHost", skip_serializing_if = "Option::is_none")] + pub req_host: Option, + #[serde(rename = "requestClaims", skip_serializing_if = "Option::is_none")] + pub req_claims: Option>, + #[serde(rename = "requestQuery", skip_serializing_if = "Option::is_none")] + pub req_query: Option>, + #[serde(rename = "requestHeader", skip_serializing_if = "Option::is_none")] + pub req_header: Option>, + #[serde(rename = "responseHeader", skip_serializing_if = "Option::is_none")] + pub resp_header: Option>, + #[serde(rename = "accessKey", skip_serializing_if = "Option::is_none")] + pub access_key: Option, + #[serde(rename = "parentUser", skip_serializing_if = "Option::is_none")] + pub parent_user: Option, + #[serde(rename = "error", skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +impl AuditLogEntry { + /// Create a new `AuditEntry` with default values + pub fn new() -> Self { + AuditLogEntry { + base: BaseLogEntry::new(), + version: String::new(), + deployment_id: None, + event: String::new(), + entry_type: None, + api: ApiDetails::new(), + remote_host: None, + user_agent: None, + req_path: None, + req_host: None, + req_claims: None, + req_query: None, + req_header: None, + resp_header: None, + access_key: None, + parent_user: None, + error: None, + } + } + + /// Create a new `AuditEntry` with version, time, event and api details + pub fn new_with_values(version: String, time: DateTime, event: String, api: ApiDetails) -> Self { + let mut base = BaseLogEntry::new(); + base.timestamp = time; + + AuditLogEntry { + base, + version, + deployment_id: None, + event, + entry_type: None, + api, + remote_host: None, + user_agent: None, + req_path: None, + req_host: None, + req_claims: None, + req_query: None, + req_header: None, + resp_header: None, + access_key: None, + parent_user: None, + error: None, + } + } + + /// Set the base log entry + pub fn with_base(mut self, base: BaseLogEntry) -> Self { + self.base = base; + self + } + + /// Set the version + pub fn set_version(mut self, version: String) -> Self { + self.version = version; + self + } + + /// Set the deployment ID + pub fn set_deployment_id(mut self, deployment_id: Option) -> Self { + self.deployment_id = deployment_id; + self + } + + /// Set the event + pub fn set_event(mut self, event: String) -> Self { + self.event = event; + self + } + + /// Set the entry type + pub fn set_entry_type(mut self, entry_type: Option) -> Self { + self.entry_type = entry_type; + self + } + + /// Set the API details + pub fn set_api(mut self, api: ApiDetails) -> Self { + self.api = api; + self + } + + /// Set the remote host + pub fn set_remote_host(mut self, remote_host: Option) -> Self { + self.remote_host = remote_host; + self + } + + /// Set the user agent + pub fn set_user_agent(mut self, user_agent: Option) -> Self { + self.user_agent = user_agent; + self + } + + /// Set the request path + pub fn set_req_path(mut self, req_path: Option) -> Self { + self.req_path = req_path; + self + } + + /// Set the request host + pub fn set_req_host(mut self, req_host: Option) -> Self { + self.req_host = req_host; + self + } + + /// Set the request claims + pub fn set_req_claims(mut self, req_claims: Option>) -> Self { + self.req_claims = req_claims; + self + } + + /// Set the request query + pub fn set_req_query(mut self, req_query: Option>) -> Self { + self.req_query = req_query; + self + } + + /// Set the request header + pub fn set_req_header(mut self, req_header: Option>) -> Self { + self.req_header = req_header; + self + } + + /// Set the response header + pub fn set_resp_header(mut self, resp_header: Option>) -> Self { + self.resp_header = resp_header; + self + } + + /// Set the access key + pub fn set_access_key(mut self, access_key: Option) -> Self { + self.access_key = access_key; + self + } + + /// Set the parent user + pub fn set_parent_user(mut self, parent_user: Option) -> Self { + self.parent_user = parent_user; + self + } + + /// Set the error + pub fn set_error(mut self, error: Option) -> Self { + self.error = error; + self + } +} + +impl LogRecord for AuditLogEntry { + fn to_json(&self) -> String { + serde_json::to_string(self).unwrap_or_else(|_| String::from("{}")) + } + + fn get_timestamp(&self) -> DateTime { + self.base.timestamp + } +} diff --git a/crates/obs/src/entry/base.rs b/crates/obs/src/entry/base.rs new file mode 100644 index 000000000..422646e09 --- /dev/null +++ b/crates/obs/src/entry/base.rs @@ -0,0 +1,92 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::collections::HashMap; + +/// Base log entry structure shared by all log types +/// This structure is used to serialize log entries to JSON +/// and send them to the log sinks +/// This structure is also used to deserialize log entries from JSON +/// This structure is also used to store log entries in the database +/// This structure is also used to query log entries from the database +/// +/// The `BaseLogEntry` structure contains the following fields: +/// - `timestamp` - the timestamp of the log entry +/// - `request_id` - the request ID of the log entry +/// - `message` - the message of the log entry +/// - `tags` - the tags of the log entry +/// +/// The `BaseLogEntry` structure contains the following methods: +/// - `new` - create a new `BaseLogEntry` with default values +/// - `message` - set the message +/// - `request_id` - set the request ID +/// - `tags` - set the tags +/// - `timestamp` - set the timestamp +/// +/// # Example +/// ``` +/// use rustfs_obs::BaseLogEntry; +/// use chrono::{DateTime, Utc}; +/// use std::collections::HashMap; +/// +/// let timestamp = Utc::now(); +/// let request = Some("req-123".to_string()); +/// let message = Some("This is a log message".to_string()); +/// let tags = Some(HashMap::new()); +/// +/// let entry = BaseLogEntry::new() +/// .timestamp(timestamp) +/// .request_id(request) +/// .message(message) +/// .tags(tags); +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)] +pub struct BaseLogEntry { + #[serde(rename = "time")] + pub timestamp: DateTime, + + #[serde(rename = "requestID", skip_serializing_if = "Option::is_none")] + pub request_id: Option, + + #[serde(rename = "message", skip_serializing_if = "Option::is_none")] + pub message: Option, + + #[serde(rename = "tags", skip_serializing_if = "Option::is_none")] + pub tags: Option>, +} + +impl BaseLogEntry { + /// Create a new BaseLogEntry with default values + pub fn new() -> Self { + BaseLogEntry { + timestamp: Utc::now(), + request_id: None, + message: None, + tags: None, + } + } + + /// Set the message + pub fn message(mut self, message: Option) -> Self { + self.message = message; + self + } + + /// Set the request ID + pub fn request_id(mut self, request_id: Option) -> Self { + self.request_id = request_id; + self + } + + /// Set the tags + pub fn tags(mut self, tags: Option>) -> Self { + self.tags = tags; + self + } + + /// Set the timestamp + pub fn timestamp(mut self, timestamp: DateTime) -> Self { + self.timestamp = timestamp; + self + } +} diff --git a/crates/obs/src/entry/mod.rs b/crates/obs/src/entry/mod.rs new file mode 100644 index 000000000..6718e5050 --- /dev/null +++ b/crates/obs/src/entry/mod.rs @@ -0,0 +1,144 @@ +pub(crate) mod args; +pub(crate) mod audit; +pub(crate) mod base; +pub(crate) mod unified; + +use serde::de::Error; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use tracing_core::Level; + +/// ObjectVersion is used across multiple modules +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub struct ObjectVersion { + #[serde(rename = "name")] + pub object_name: String, + #[serde(rename = "versionId", skip_serializing_if = "Option::is_none")] + pub version_id: Option, +} + +impl ObjectVersion { + /// Create a new ObjectVersion object + pub fn new() -> Self { + ObjectVersion { + object_name: String::new(), + version_id: None, + } + } + + /// Create a new ObjectVersion with object name + pub fn new_with_object_name(object_name: String) -> Self { + ObjectVersion { + object_name, + version_id: None, + } + } + + /// Set the object name + pub fn set_object_name(mut self, object_name: String) -> Self { + self.object_name = object_name; + self + } + + /// Set the version ID + pub fn set_version_id(mut self, version_id: Option) -> Self { + self.version_id = version_id; + self + } +} + +impl Default for ObjectVersion { + fn default() -> Self { + Self::new() + } +} + +/// Log kind/level enum +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] +pub enum LogKind { + #[serde(rename = "INFO")] + #[default] + Info, + #[serde(rename = "WARNING")] + Warning, + #[serde(rename = "ERROR")] + Error, + #[serde(rename = "FATAL")] + Fatal, +} + +/// Trait for types that can be serialized to JSON and have a timestamp +/// This trait is used by `ServerLogEntry` to convert the log entry to JSON +/// and get the timestamp of the log entry +/// This trait is implemented by `ServerLogEntry` +/// +/// # Example +/// ``` +/// use rustfs_obs::LogRecord; +/// use chrono::{DateTime, Utc}; +/// use rustfs_obs::ServerLogEntry; +/// use tracing_core::Level; +/// +/// let log_entry = ServerLogEntry::new(Level::INFO, "api_handler".to_string()); +/// let json = log_entry.to_json(); +/// let timestamp = log_entry.get_timestamp(); +/// ``` +pub trait LogRecord { + fn to_json(&self) -> String; + fn get_timestamp(&self) -> chrono::DateTime; +} + +/// Wrapper for `tracing_core::Level` to implement `Serialize` and `Deserialize` +/// for `ServerLogEntry` +/// This is necessary because `tracing_core::Level` does not implement `Serialize` +/// and `Deserialize` +/// This is a workaround to allow `ServerLogEntry` to be serialized and deserialized +/// using `serde` +/// +/// # Example +/// ``` +/// use rustfs_obs::SerializableLevel; +/// use tracing_core::Level; +/// +/// let level = Level::INFO; +/// let serializable_level = SerializableLevel::from(level); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SerializableLevel(pub Level); + +impl From for SerializableLevel { + fn from(level: Level) -> Self { + SerializableLevel(level) + } +} + +impl From for Level { + fn from(serializable_level: SerializableLevel) -> Self { + serializable_level.0 + } +} + +impl Serialize for SerializableLevel { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(self.0.as_str()) + } +} + +impl<'de> Deserialize<'de> for SerializableLevel { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match s.as_str() { + "TRACE" => Ok(SerializableLevel(Level::TRACE)), + "DEBUG" => Ok(SerializableLevel(Level::DEBUG)), + "INFO" => Ok(SerializableLevel(Level::INFO)), + "WARN" => Ok(SerializableLevel(Level::WARN)), + "ERROR" => Ok(SerializableLevel(Level::ERROR)), + _ => Err(D::Error::custom("unknown log level")), + } + } +} diff --git a/crates/obs/src/entry/unified.rs b/crates/obs/src/entry/unified.rs new file mode 100644 index 000000000..877b2f8fc --- /dev/null +++ b/crates/obs/src/entry/unified.rs @@ -0,0 +1,287 @@ +use crate::{AuditLogEntry, BaseLogEntry, LogKind, LogRecord, SerializableLevel}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use tracing_core::Level; + +/// Server log entry with structured fields +/// ServerLogEntry is used to log structured log entries from the server +/// +/// The `ServerLogEntry` structure contains the following fields: +/// - `base` - the base log entry +/// - `level` - the log level +/// - `source` - the source of the log entry +/// - `user_id` - the user ID +/// - `fields` - the structured fields of the log entry +/// +/// The `ServerLogEntry` structure contains the following methods: +/// - `new` - create a new `ServerLogEntry` with specified level and source +/// - `with_base` - set the base log entry +/// - `user_id` - set the user ID +/// - `fields` - set the fields +/// - `add_field` - add a field +/// +/// # Example +/// ``` +/// use rustfs_obs::ServerLogEntry; +/// use tracing_core::Level; +/// +/// let entry = ServerLogEntry::new(Level::INFO, "test_module".to_string()) +/// .user_id(Some("user-456".to_string())) +/// .add_field("operation".to_string(), "login".to_string()); +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct ServerLogEntry { + #[serde(flatten)] + pub base: BaseLogEntry, + + pub level: SerializableLevel, + pub source: String, + + #[serde(rename = "userId", skip_serializing_if = "Option::is_none")] + pub user_id: Option, + + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub fields: Vec<(String, String)>, +} + +impl ServerLogEntry { + /// Create a new ServerLogEntry with specified level and source + pub fn new(level: Level, source: String) -> Self { + ServerLogEntry { + base: BaseLogEntry::new(), + level: SerializableLevel(level), + source, + user_id: None, + fields: Vec::new(), + } + } + + /// Set the base log entry + pub fn with_base(mut self, base: BaseLogEntry) -> Self { + self.base = base; + self + } + + /// Set the user ID + pub fn user_id(mut self, user_id: Option) -> Self { + self.user_id = user_id; + self + } + + /// Set fields + pub fn fields(mut self, fields: Vec<(String, String)>) -> Self { + self.fields = fields; + self + } + + /// Add a field + pub fn add_field(mut self, key: String, value: String) -> Self { + self.fields.push((key, value)); + self + } +} + +impl LogRecord for ServerLogEntry { + fn to_json(&self) -> String { + serde_json::to_string(self).unwrap_or_else(|_| String::from("{}")) + } + + fn get_timestamp(&self) -> DateTime { + self.base.timestamp + } +} + +/// Console log entry structure +/// ConsoleLogEntry is used to log console log entries +/// The `ConsoleLogEntry` structure contains the following fields: +/// - `base` - the base log entry +/// - `level` - the log level +/// - `console_msg` - the console message +/// - `node_name` - the node name +/// - `err` - the error message +/// +/// The `ConsoleLogEntry` structure contains the following methods: +/// - `new` - create a new `ConsoleLogEntry` +/// - `new_with_console_msg` - create a new `ConsoleLogEntry` with console message and node name +/// - `with_base` - set the base log entry +/// - `set_level` - set the log level +/// - `set_node_name` - set the node name +/// - `set_console_msg` - set the console message +/// - `set_err` - set the error message +/// +/// # Example +/// ``` +/// use rustfs_obs::ConsoleLogEntry; +/// +/// let entry = ConsoleLogEntry::new_with_console_msg("Test message".to_string(), "node-123".to_string()); +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ConsoleLogEntry { + #[serde(flatten)] + pub base: BaseLogEntry, + + pub level: LogKind, + pub console_msg: String, + pub node_name: String, + + #[serde(skip)] + pub err: Option, +} + +impl ConsoleLogEntry { + /// Create a new ConsoleLogEntry + pub fn new() -> Self { + ConsoleLogEntry { + base: BaseLogEntry::new(), + level: LogKind::Info, + console_msg: String::new(), + node_name: String::new(), + err: None, + } + } + + /// Create a new ConsoleLogEntry with console message and node name + pub fn new_with_console_msg(console_msg: String, node_name: String) -> Self { + ConsoleLogEntry { + base: BaseLogEntry::new(), + level: LogKind::Info, + console_msg, + node_name, + err: None, + } + } + + /// Set the base log entry + pub fn with_base(mut self, base: BaseLogEntry) -> Self { + self.base = base; + self + } + + /// Set the log level + pub fn set_level(mut self, level: LogKind) -> Self { + self.level = level; + self + } + + /// Set the node name + pub fn set_node_name(mut self, node_name: String) -> Self { + self.node_name = node_name; + self + } + + /// Set the console message + pub fn set_console_msg(mut self, console_msg: String) -> Self { + self.console_msg = console_msg; + self + } + + /// Set the error message + pub fn set_err(mut self, err: Option) -> Self { + self.err = err; + self + } +} + +impl Default for ConsoleLogEntry { + fn default() -> Self { + Self::new() + } +} + +impl LogRecord for ConsoleLogEntry { + fn to_json(&self) -> String { + serde_json::to_string(self).unwrap_or_else(|_| String::from("{}")) + } + + fn get_timestamp(&self) -> DateTime { + self.base.timestamp + } +} + +/// Unified log entry type +/// UnifiedLogEntry is used to log different types of log entries +/// +/// The `UnifiedLogEntry` enum contains the following variants: +/// - `Server` - a server log entry +/// - `Audit` - an audit log entry +/// - `Console` - a console log entry +/// +/// The `UnifiedLogEntry` enum contains the following methods: +/// - `to_json` - convert the log entry to JSON +/// - `get_timestamp` - get the timestamp of the log entry +/// +/// # Example +/// ``` +/// use rustfs_obs::{UnifiedLogEntry, ServerLogEntry}; +/// use tracing_core::Level; +/// +/// let server_entry = ServerLogEntry::new(Level::INFO, "test_module".to_string()); +/// let unified = UnifiedLogEntry::Server(server_entry); +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum UnifiedLogEntry { + #[serde(rename = "server")] + Server(ServerLogEntry), + + #[serde(rename = "audit")] + Audit(Box), + + #[serde(rename = "console")] + Console(ConsoleLogEntry), +} + +impl LogRecord for UnifiedLogEntry { + fn to_json(&self) -> String { + match self { + UnifiedLogEntry::Server(entry) => entry.to_json(), + UnifiedLogEntry::Audit(entry) => entry.to_json(), + UnifiedLogEntry::Console(entry) => entry.to_json(), + } + } + + fn get_timestamp(&self) -> DateTime { + match self { + UnifiedLogEntry::Server(entry) => entry.get_timestamp(), + UnifiedLogEntry::Audit(entry) => entry.get_timestamp(), + UnifiedLogEntry::Console(entry) => entry.get_timestamp(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_base_log_entry() { + let base = BaseLogEntry::new() + .request_id(Some("req-123".to_string())) + .message(Some("Test message".to_string())); + + assert_eq!(base.request_id, Some("req-123".to_string())); + assert_eq!(base.message, Some("Test message".to_string())); + } + + #[test] + fn test_server_log_entry() { + let entry = ServerLogEntry::new(Level::INFO, "test_module".to_string()) + .user_id(Some("user-456".to_string())) + .add_field("operation".to_string(), "login".to_string()); + + assert_eq!(entry.level.0, Level::INFO); + assert_eq!(entry.source, "test_module"); + assert_eq!(entry.user_id, Some("user-456".to_string())); + assert_eq!(entry.fields.len(), 1); + assert_eq!(entry.fields[0], ("operation".to_string(), "login".to_string())); + } + + #[test] + fn test_unified_log_entry_json() { + let server_entry = ServerLogEntry::new(Level::INFO, "test_source".to_string()); + let unified = UnifiedLogEntry::Server(server_entry); + + let json = unified.to_json(); + assert!(json.contains("test_source")); + } +} diff --git a/crates/obs/src/global.rs b/crates/obs/src/global.rs new file mode 100644 index 000000000..41c5e555e --- /dev/null +++ b/crates/obs/src/global.rs @@ -0,0 +1,89 @@ +use crate::telemetry::OtelGuard; +use std::sync::{Arc, Mutex}; +use tokio::sync::{OnceCell, SetError}; +use tracing::{error, info}; + +pub(crate) const USE_STDOUT: bool = true; +pub(crate) const SERVICE_NAME: &str = "RustFS"; +pub(crate) const SAMPLE_RATIO: f64 = 1.0; +pub(crate) const METER_INTERVAL: u64 = 60; +pub(crate) const SERVICE_VERSION: &str = "0.1.0"; +pub(crate) const ENVIRONMENT: &str = "production"; +pub(crate) const LOGGER_LEVEL: &str = "info"; + +/// Global guard for OpenTelemetry tracing +static GLOBAL_GUARD: OnceCell>> = OnceCell::const_new(); + +/// Error type for global guard operations +#[derive(Debug, thiserror::Error)] +pub enum GuardError { + #[error("Failed to set global guard: {0}")] + SetError(#[from] SetError>>), + #[error("Global guard not initialized")] + NotInitialized, +} + +/// Set the global guard for OpenTelemetry +/// +/// # Arguments +/// * `guard` - The OtelGuard instance to set globally +/// +/// # Returns +/// * `Ok(())` if successful +/// * `Err(GuardError)` if setting fails +/// +/// # Example +/// ```rust +/// use rustfs_obs::{init_telemetry, load_config, set_global_guard}; +/// +/// async fn init() -> Result<(), Box> { +/// let config = load_config(None); +/// let guard = init_telemetry(&config.observability).await?; +/// set_global_guard(guard)?; +/// Ok(()) +/// } +/// ``` +pub fn set_global_guard(guard: OtelGuard) -> Result<(), GuardError> { + info!("Initializing global OpenTelemetry guard"); + GLOBAL_GUARD.set(Arc::new(Mutex::new(guard))).map_err(GuardError::SetError) +} + +/// Get the global guard for OpenTelemetry +/// +/// # Returns +/// * `Ok(Arc>)` if guard exists +/// * `Err(GuardError)` if guard not initialized +/// +/// # Example +/// ```rust +/// use rustfs_obs::get_global_guard; +/// +/// async fn trace_operation() -> Result<(), Box> { +/// let guard = get_global_guard()?; +/// let _lock = guard.lock().unwrap(); +/// // Perform traced operation +/// Ok(()) +/// } +/// ``` +pub fn get_global_guard() -> Result>, GuardError> { + GLOBAL_GUARD.get().cloned().ok_or(GuardError::NotInitialized) +} + +/// Try to get the global guard for OpenTelemetry +/// +/// # Returns +/// * `Some(Arc>)` if guard exists +/// * `None` if guard not initialized +pub fn try_get_global_guard() -> Option>> { + GLOBAL_GUARD.get().cloned() +} + +#[cfg(test)] +mod tests { + use super::*; + #[tokio::test] + async fn test_get_uninitialized_guard() { + let result = get_global_guard(); + assert!(matches!(result, Err(GuardError::NotInitialized))); + } +} diff --git a/crates/obs/src/lib.rs b/crates/obs/src/lib.rs new file mode 100644 index 000000000..843c52367 --- /dev/null +++ b/crates/obs/src/lib.rs @@ -0,0 +1,94 @@ +/// # obs +/// +/// `obs` is a logging and observability library for Rust. +/// It provides a simple and easy-to-use interface for logging and observability. +/// It is built on top of the `log` crate and `opentelemetry` crate. +/// +/// ## Features +/// - Structured logging +/// - Distributed tracing +/// - Metrics collection +/// - Log processing worker +/// - Multiple sinks +/// - Configuration-based setup +/// - Telemetry guard +/// - Global logger +/// - Log levels +/// - Log entry types +/// - Log record +/// - Object version +/// - Local IP address +/// +/// ## Usage +/// +/// ```rust +/// use rustfs_obs::{AppConfig, init_obs}; +/// +/// let config = AppConfig::default(); +/// let (logger, guard) = init_obs(config); +/// ``` +mod config; +mod entry; +mod global; +mod logger; +mod sink; +mod telemetry; +mod utils; +mod worker; +pub use config::load_config; +pub use config::{AppConfig, OtelConfig}; +pub use entry::args::Args; +pub use entry::audit::{ApiDetails, AuditLogEntry}; +pub use entry::base::BaseLogEntry; +pub use entry::unified::{ConsoleLogEntry, ServerLogEntry, UnifiedLogEntry}; +pub use entry::{LogKind, LogRecord, ObjectVersion, SerializableLevel}; +pub use global::{get_global_guard, set_global_guard, try_get_global_guard, GuardError}; +pub use logger::{ensure_logger_initialized, log_debug, log_error, log_info, log_trace, log_warn, log_with_context}; +pub use logger::{get_global_logger, init_global_logger, locked_logger, start_logger}; +pub use logger::{log_init_state, InitLogStatus}; +pub use logger::{LogError, Logger}; +pub use sink::Sink; +use std::sync::Arc; +pub use telemetry::init_telemetry; +pub use telemetry::{get_global_registry, metrics}; +use tokio::sync::Mutex; +pub use utils::{get_local_ip, get_local_ip_with_default}; +pub use worker::start_worker; + +/// Initialize the observability module +/// +/// # Parameters +/// - `config`: Configuration information +/// +/// # Returns +/// A tuple containing the logger and the telemetry guard +/// +/// # Example +/// ``` +/// use rustfs_obs::{AppConfig, init_obs}; +/// +/// let config = AppConfig::default(); +/// let (logger, guard) = init_obs(config); +/// ``` +pub async fn init_obs(config: AppConfig) -> (Arc>, telemetry::OtelGuard) { + let guard = init_telemetry(&config.observability); + let sinks = sink::create_sinks(&config).await; + let logger = init_global_logger(&config, sinks).await; + (logger, guard) +} + +/// Get the global logger instance +/// This function returns a reference to the global logger instance. +/// +/// # Returns +/// A reference to the global logger instance +/// +/// # Example +/// ``` +/// use rustfs_obs::get_logger; +/// +/// let logger = get_logger(); +/// ``` +pub fn get_logger() -> &'static Arc> { + get_global_logger() +} diff --git a/crates/obs/src/logger.rs b/crates/obs/src/logger.rs new file mode 100644 index 000000000..1e62712cb --- /dev/null +++ b/crates/obs/src/logger.rs @@ -0,0 +1,535 @@ +use crate::global::{ENVIRONMENT, SERVICE_NAME, SERVICE_VERSION}; +use crate::{AppConfig, AuditLogEntry, BaseLogEntry, ConsoleLogEntry, OtelConfig, ServerLogEntry, Sink, UnifiedLogEntry}; +use std::sync::Arc; +use std::time::SystemTime; +use tokio::sync::mpsc::{self, Receiver, Sender}; +use tokio::sync::{Mutex, OnceCell}; +use tracing_core::Level; + +// Add the global instance at the module level +static GLOBAL_LOGGER: OnceCell>> = OnceCell::const_new(); + +/// Server log processor +#[derive(Debug)] +pub struct Logger { + sender: Sender, // Log sending channel + queue_capacity: usize, +} + +impl Logger { + /// Create a new Logger instance + /// Returns Logger and corresponding Receiver + pub fn new(config: &AppConfig) -> (Self, Receiver) { + // Get queue capacity from configuration, or use default values 10000 + let queue_capacity = config.logger.as_ref().and_then(|l| l.queue_capacity).unwrap_or(10000); + let (sender, receiver) = mpsc::channel(queue_capacity); + (Logger { sender, queue_capacity }, receiver) + } + + /// get the queue capacity + /// This function returns the queue capacity. + /// # Returns + /// The queue capacity + /// # Example + /// ``` + /// use rustfs_obs::Logger; + /// async fn example(logger: &Logger) { + /// let _ = logger.get_queue_capacity(); + /// } + /// ``` + pub fn get_queue_capacity(&self) -> usize { + self.queue_capacity + } + + /// Log a server entry + #[tracing::instrument(skip(self), fields(log_source = "logger_server"))] + pub async fn log_server_entry(&self, entry: ServerLogEntry) -> Result<(), LogError> { + self.log_entry(UnifiedLogEntry::Server(entry)).await + } + + /// Log an audit entry + #[tracing::instrument(skip(self), fields(log_source = "logger_audit"))] + pub async fn log_audit_entry(&self, entry: AuditLogEntry) -> Result<(), LogError> { + self.log_entry(UnifiedLogEntry::Audit(Box::new(entry))).await + } + + /// Log a console entry + #[tracing::instrument(skip(self), fields(log_source = "logger_console"))] + pub async fn log_console_entry(&self, entry: ConsoleLogEntry) -> Result<(), LogError> { + self.log_entry(UnifiedLogEntry::Console(entry)).await + } + + /// Asynchronous logging of unified log entries + #[tracing::instrument(skip(self), fields(log_source = "logger"))] + #[tracing::instrument(level = "error", skip_all)] + pub async fn log_entry(&self, entry: UnifiedLogEntry) -> Result<(), LogError> { + // Extract information for tracing based on entry type + match &entry { + UnifiedLogEntry::Server(server) => { + tracing::Span::current() + .record("log_level", server.level.0.as_str()) + .record("log_message", server.base.message.as_deref().unwrap_or("log message not set")) + .record("source", &server.source); + + // Generate tracing event based on log level + match server.level.0 { + Level::ERROR => { + tracing::error!(target: "server_logs", message = %server.base.message.as_deref().unwrap_or("")); + } + Level::WARN => { + tracing::warn!(target: "server_logs", message = %server.base.message.as_deref().unwrap_or("")); + } + Level::INFO => { + tracing::info!(target: "server_logs", message = %server.base.message.as_deref().unwrap_or("")); + } + Level::DEBUG => { + tracing::debug!(target: "server_logs", message = %server.base.message.as_deref().unwrap_or("")); + } + Level::TRACE => { + tracing::trace!(target: "server_logs", message = %server.base.message.as_deref().unwrap_or("")); + } + } + } + UnifiedLogEntry::Audit(audit) => { + tracing::info!( + target: "audit_logs", + event = %audit.event, + api = %audit.api.name.as_deref().unwrap_or("unknown"), + message = %audit.base.message.as_deref().unwrap_or("") + ); + } + UnifiedLogEntry::Console(console) => { + let level_str = match console.level { + crate::LogKind::Info => "INFO", + crate::LogKind::Warning => "WARN", + crate::LogKind::Error => "ERROR", + crate::LogKind::Fatal => "FATAL", + }; + + tracing::info!( + target: "console_logs", + level = %level_str, + node = %console.node_name, + message = %console.console_msg + ); + } + } + + // Send logs to async queue with improved error handling + match self.sender.try_send(entry) { + Ok(_) => Ok(()), + Err(mpsc::error::TrySendError::Full(entry)) => { + // Processing strategy when queue is full + tracing::warn!("Log queue full, applying backpressure"); + match tokio::time::timeout(std::time::Duration::from_millis(500), self.sender.send(entry)).await { + Ok(Ok(_)) => Ok(()), + Ok(Err(_)) => Err(LogError::SendFailed("Channel closed")), + Err(_) => Err(LogError::Timeout("Queue backpressure timeout")), + } + } + Err(mpsc::error::TrySendError::Closed(_)) => Err(LogError::SendFailed("Logger channel closed")), + } + } + + /// Write log with context information + /// This function writes log messages with context information. + /// + /// # Parameters + /// - `message`: Message to be logged + /// - `source`: Source of the log + /// - `request_id`: Request ID + /// - `user_id`: User ID + /// - `fields`: Additional fields + /// + /// # Returns + /// Result indicating whether the operation was successful + /// + /// # Example + /// ``` + /// use tracing_core::Level; + /// use rustfs_obs::Logger; + /// + /// async fn example(logger: &Logger) { + /// let _ = logger.write_with_context("This is an information message", "example",Level::INFO, Some("req-12345".to_string()), Some("user-6789".to_string()), vec![("endpoint".to_string(), "/api/v1/data".to_string())]).await; + /// } + pub async fn write_with_context( + &self, + message: &str, + source: &str, + level: Level, + request_id: Option, + user_id: Option, + fields: Vec<(String, String)>, + ) -> Result<(), LogError> { + let base = BaseLogEntry::new().message(Some(message.to_string())).request_id(request_id); + + let server_entry = ServerLogEntry::new(level, source.to_string()) + .user_id(user_id) + .fields(fields) + .with_base(base); + + self.log_server_entry(server_entry).await + } + + /// Write log + /// This function writes log messages. + /// # Parameters + /// - `message`: Message to be logged + /// - `source`: Source of the log + /// - `level`: Log level + /// + /// # Returns + /// Result indicating whether the operation was successful + /// + /// # Example + /// ``` + /// use rustfs_obs::Logger; + /// use tracing_core::Level; + /// + /// async fn example(logger: &Logger) { + /// let _ = logger.write("This is an information message", "example", Level::INFO).await; + /// } + /// ``` + pub async fn write(&self, message: &str, source: &str, level: Level) -> Result<(), LogError> { + self.write_with_context(message, source, level, None, None, Vec::new()).await + } + + /// Shutdown the logger + /// This function shuts down the logger. + /// + /// # Returns + /// Result indicating whether the operation was successful + /// + /// # Example + /// ``` + /// use rustfs_obs::Logger; + /// + /// async fn example(logger: Logger) { + /// let _ = logger.shutdown().await; + /// } + /// ``` + pub async fn shutdown(self) -> Result<(), LogError> { + drop(self.sender); //Close the sending end so that the receiver knows that there is no new message + Ok(()) + } +} + +/// Log error type +/// This enum defines the error types that can occur when logging. +/// It is used to provide more detailed error information. +/// # Example +/// ``` +/// use rustfs_obs::LogError; +/// use thiserror::Error; +/// +/// LogError::SendFailed("Failed to send log"); +/// LogError::Timeout("Operation timed out"); +/// ``` +#[derive(Debug, thiserror::Error)] +pub enum LogError { + #[error("Failed to send log: {0}")] + SendFailed(&'static str), + #[error("Operation timed out: {0}")] + Timeout(&'static str), +} + +/// Start the log module +/// This function starts the log module. +/// It initializes the logger and starts the worker to process logs. +/// # Parameters +/// - `config`: Configuration information +/// - `sinks`: A vector of Sink instances +/// # Returns +/// The global logger instance +/// # Example +/// ``` +/// use rustfs_obs::{AppConfig, start_logger}; +/// +/// let config = AppConfig::default(); +/// let sinks = vec![]; +/// let logger = start_logger(&config, sinks); +/// ``` +pub fn start_logger(config: &AppConfig, sinks: Vec>) -> Logger { + let (logger, receiver) = Logger::new(config); + tokio::spawn(crate::worker::start_worker(receiver, sinks)); + logger +} + +/// Initialize the global logger instance +/// This function initializes the global logger instance and returns a reference to it. +/// If the logger has been initialized before, it will return the existing logger instance. +/// +/// # Parameters +/// - `config`: Configuration information +/// - `sinks`: A vector of Sink instances +/// +/// # Returns +/// A reference to the global logger instance +/// +/// # Example +/// ``` +/// use rustfs_obs::{AppConfig,init_global_logger}; +/// +/// let config = AppConfig::default(); +/// let sinks = vec![]; +/// let logger = init_global_logger(&config, sinks); +/// ``` +pub async fn init_global_logger(config: &AppConfig, sinks: Vec>) -> Arc> { + let logger = Arc::new(Mutex::new(start_logger(config, sinks))); + GLOBAL_LOGGER.set(logger.clone()).expect("Logger already initialized"); + logger +} + +/// Get the global logger instance +/// +/// This function returns a reference to the global logger instance. +/// +/// # Returns +/// A reference to the global logger instance +/// +/// # Example +/// ``` +/// use rustfs_obs::get_global_logger; +/// +/// let logger = get_global_logger(); +/// ``` +pub fn get_global_logger() -> &'static Arc> { + GLOBAL_LOGGER.get().expect("Logger not initialized") +} + +/// Get the global logger instance with a lock +/// This function returns a reference to the global logger instance with a lock. +/// It is used to ensure that the logger is thread-safe. +/// +/// # Returns +/// A reference to the global logger instance with a lock +/// +/// # Example +/// ``` +/// use rustfs_obs::locked_logger; +/// +/// async fn example() { +/// let logger = locked_logger().await; +/// } +/// ``` +pub async fn locked_logger() -> tokio::sync::MutexGuard<'static, Logger> { + get_global_logger().lock().await +} + +/// Initialize with default empty logger if needed (optional) +/// This function initializes the logger with a default empty logger if needed. +/// It is used to ensure that the logger is initialized before logging. +/// +/// # Returns +/// A reference to the global logger instance +/// +/// # Example +/// ``` +/// use rustfs_obs::ensure_logger_initialized; +/// +/// let logger = ensure_logger_initialized(); +/// ``` +pub fn ensure_logger_initialized() -> &'static Arc> { + if GLOBAL_LOGGER.get().is_none() { + let config = AppConfig::default(); + let sinks = vec![]; + let logger = Arc::new(Mutex::new(start_logger(&config, sinks))); + let _ = GLOBAL_LOGGER.set(logger); + } + GLOBAL_LOGGER.get().unwrap() +} + +/// Log information +/// This function logs information messages. +/// +/// # Parameters +/// - `message`: Message to be logged +/// - `source`: Source of the log +/// +/// # Returns +/// Result indicating whether the operation was successful +/// +/// # Example +/// ``` +/// use rustfs_obs::log_info; +/// +/// async fn example() { +/// let _ = log_info("This is an information message", "example").await; +/// } +/// ``` +pub async fn log_info(message: &str, source: &str) -> Result<(), LogError> { + get_global_logger().lock().await.write(message, source, Level::INFO).await +} + +/// Log error +/// This function logs error messages. +/// # Parameters +/// - `message`: Message to be logged +/// - `source`: Source of the log +/// # Returns +/// Result indicating whether the operation was successful +/// # Example +/// ``` +/// use rustfs_obs::log_error; +/// +/// async fn example() { +/// let _ = log_error("This is an error message", "example").await; +/// } +pub async fn log_error(message: &str, source: &str) -> Result<(), LogError> { + get_global_logger().lock().await.write(message, source, Level::ERROR).await +} + +/// Log warning +/// This function logs warning messages. +/// # Parameters +/// - `message`: Message to be logged +/// - `source`: Source of the log +/// # Returns +/// Result indicating whether the operation was successful +/// +/// # Example +/// ``` +/// use rustfs_obs::log_warn; +/// +/// async fn example() { +/// let _ = log_warn("This is a warning message", "example").await; +/// } +/// ``` +pub async fn log_warn(message: &str, source: &str) -> Result<(), LogError> { + get_global_logger().lock().await.write(message, source, Level::WARN).await +} + +/// Log debug +/// This function logs debug messages. +/// # Parameters +/// - `message`: Message to be logged +/// - `source`: Source of the log +/// # Returns +/// Result indicating whether the operation was successful +/// +/// # Example +/// ``` +/// use rustfs_obs::log_debug; +/// +/// async fn example() { +/// let _ = log_debug("This is a debug message", "example").await; +/// } +/// ``` +pub async fn log_debug(message: &str, source: &str) -> Result<(), LogError> { + get_global_logger().lock().await.write(message, source, Level::DEBUG).await +} + +/// Log trace +/// This function logs trace messages. +/// # Parameters +/// - `message`: Message to be logged +/// - `source`: Source of the log +/// +/// # Returns +/// Result indicating whether the operation was successful +/// +/// # Example +/// ``` +/// use rustfs_obs::log_trace; +/// +/// async fn example() { +/// let _ = log_trace("This is a trace message", "example").await; +/// } +/// ``` +pub async fn log_trace(message: &str, source: &str) -> Result<(), LogError> { + get_global_logger().lock().await.write(message, source, Level::TRACE).await +} + +/// Log with context information +/// This function logs messages with context information. +/// # Parameters +/// - `message`: Message to be logged +/// - `source`: Source of the log +/// - `level`: Log level +/// - `request_id`: Request ID +/// - `user_id`: User ID +/// - `fields`: Additional fields +/// # Returns +/// Result indicating whether the operation was successful +/// # Example +/// ``` +/// use tracing_core::Level; +/// use rustfs_obs::log_with_context; +/// +/// async fn example() { +/// let _ = log_with_context("This is an information message", "example", Level::INFO, Some("req-12345".to_string()), Some("user-6789".to_string()), vec![("endpoint".to_string(), "/api/v1/data".to_string())]).await; +/// } +/// ``` +pub async fn log_with_context( + message: &str, + source: &str, + level: Level, + request_id: Option, + user_id: Option, + fields: Vec<(String, String)>, +) -> Result<(), LogError> { + get_global_logger() + .lock() + .await + .write_with_context(message, source, level, request_id, user_id, fields) + .await +} + +/// Log initialization status +#[derive(Debug)] +pub struct InitLogStatus { + pub timestamp: SystemTime, + pub service_name: String, + pub version: String, + pub environment: String, +} + +impl Default for InitLogStatus { + fn default() -> Self { + Self { + timestamp: SystemTime::now(), + service_name: String::from(SERVICE_NAME), + version: SERVICE_VERSION.to_string(), + environment: ENVIRONMENT.to_string(), + } + } +} + +impl InitLogStatus { + pub fn new_config(config: &OtelConfig) -> Self { + let config = config.clone(); + let environment = config.environment.unwrap_or(ENVIRONMENT.to_string()); + let version = config.service_version.unwrap_or(SERVICE_VERSION.to_string()); + Self { + timestamp: SystemTime::now(), + service_name: String::from(SERVICE_NAME), + version, + environment, + } + } + + pub async fn init_start_log(config: &OtelConfig) -> Result<(), LogError> { + let status = Self::new_config(config); + log_init_state(Some(status)).await + } +} + +/// Log initialization details during system startup +pub async fn log_init_state(status: Option) -> Result<(), LogError> { + let status = status.unwrap_or_default(); + + let base_entry = BaseLogEntry::new() + .timestamp(chrono::DateTime::from(status.timestamp)) + .message(Some(format!( + "Service initialization started - {} v{} in {}", + status.service_name, status.version, status.environment + ))) + .request_id(Some("system_init".to_string())); + + let server_entry = ServerLogEntry::new(Level::INFO, "system_initialization".to_string()) + .with_base(base_entry) + .user_id(Some("system".to_string())); + + get_global_logger().lock().await.log_server_entry(server_entry).await?; + Ok(()) +} diff --git a/crates/obs/src/sink.rs b/crates/obs/src/sink.rs new file mode 100644 index 000000000..4df212b31 --- /dev/null +++ b/crates/obs/src/sink.rs @@ -0,0 +1,497 @@ +use crate::{AppConfig, LogRecord, UnifiedLogEntry}; +use async_trait::async_trait; +use std::sync::Arc; +use tokio::fs::OpenOptions; +use tokio::io; +use tokio::io::AsyncWriteExt; + +/// Sink Trait definition, asynchronously write logs +#[async_trait] +pub trait Sink: Send + Sync { + async fn write(&self, entry: &UnifiedLogEntry); +} + +#[cfg(feature = "kafka")] +/// Kafka Sink Implementation +pub struct KafkaSink { + producer: rdkafka::producer::FutureProducer, + topic: String, + batch_size: usize, + batch_timeout_ms: u64, + entries: Arc>>, + last_flush: Arc, +} + +#[cfg(feature = "kafka")] +impl KafkaSink { + /// Create a new KafkaSink instance + pub fn new(producer: rdkafka::producer::FutureProducer, topic: String, batch_size: usize, batch_timeout_ms: u64) -> Self { + // Create Arc-wrapped values first + let entries = Arc::new(tokio::sync::Mutex::new(Vec::with_capacity(batch_size))); + let last_flush = Arc::new(std::sync::atomic::AtomicU64::new( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64, + )); + let sink = KafkaSink { + producer: producer.clone(), + topic: topic.clone(), + batch_size, + batch_timeout_ms, + entries: entries.clone(), + last_flush: last_flush.clone(), + }; + + // Start background flusher + tokio::spawn(Self::periodic_flush(producer, topic, entries, last_flush, batch_timeout_ms)); + + sink + } + + /// Add a getter method to read the batch_timeout_ms field + #[allow(dead_code)] + pub fn batch_timeout(&self) -> u64 { + self.batch_timeout_ms + } + + /// Add a method to dynamically adjust the timeout if needed + #[allow(dead_code)] + pub fn set_batch_timeout(&mut self, new_timeout_ms: u64) { + self.batch_timeout_ms = new_timeout_ms; + } + + async fn periodic_flush( + producer: rdkafka::producer::FutureProducer, + topic: String, + entries: Arc>>, + last_flush: Arc, + timeout_ms: u64, + ) { + loop { + tokio::time::sleep(tokio::time::Duration::from_millis(timeout_ms / 2)).await; + + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + + let last = last_flush.load(std::sync::atomic::Ordering::Relaxed); + + if now - last >= timeout_ms { + let mut batch = entries.lock().await; + if !batch.is_empty() { + Self::send_batch(&producer, &topic, batch.drain(..).collect()).await; + last_flush.store(now, std::sync::atomic::Ordering::Relaxed); + } + } + } + } + + async fn send_batch(producer: &rdkafka::producer::FutureProducer, topic: &str, entries: Vec) { + for entry in entries { + let payload = match serde_json::to_string(&entry) { + Ok(p) => p, + Err(e) => { + eprintln!("Failed to serialize log entry: {}", e); + continue; + } + }; + + let span_id = entry.get_timestamp().to_rfc3339(); + + let _ = producer + .send( + rdkafka::producer::FutureRecord::to(topic).payload(&payload).key(&span_id), + std::time::Duration::from_secs(5), + ) + .await; + } + } +} + +#[cfg(feature = "kafka")] +#[async_trait] +impl Sink for KafkaSink { + async fn write(&self, entry: &UnifiedLogEntry) { + let mut batch = self.entries.lock().await; + batch.push(entry.clone()); + + let should_flush_by_size = batch.len() >= self.batch_size; + let should_flush_by_time = { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + let last = self.last_flush.load(std::sync::atomic::Ordering::Relaxed); + now - last >= self.batch_timeout_ms + }; + + if should_flush_by_size || should_flush_by_time { + // Existing flush logic + let entries_to_send: Vec = batch.drain(..).collect(); + let producer = self.producer.clone(); + let topic = self.topic.clone(); + + self.last_flush.store( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64, + std::sync::atomic::Ordering::Relaxed, + ); + + tokio::spawn(async move { + KafkaSink::send_batch(&producer, &topic, entries_to_send).await; + }); + } + } +} + +#[cfg(feature = "kafka")] +impl Drop for KafkaSink { + fn drop(&mut self) { + // Perform any necessary cleanup here + // For example, you might want to flush any remaining entries + let producer = self.producer.clone(); + let topic = self.topic.clone(); + let entries = self.entries.clone(); + let last_flush = self.last_flush.clone(); + + tokio::spawn(async move { + let mut batch = entries.lock().await; + if !batch.is_empty() { + KafkaSink::send_batch(&producer, &topic, batch.drain(..).collect()).await; + last_flush.store( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64, + std::sync::atomic::Ordering::Relaxed, + ); + } + }); + + eprintln!("Dropping KafkaSink with topic: {}", self.topic); + } +} + +#[cfg(feature = "webhook")] +/// Webhook Sink Implementation +pub struct WebhookSink { + endpoint: String, + auth_token: String, + client: reqwest::Client, + max_retries: usize, + retry_delay_ms: u64, +} + +#[cfg(feature = "webhook")] +impl WebhookSink { + pub fn new(endpoint: String, auth_token: String, max_retries: usize, retry_delay_ms: u64) -> Self { + WebhookSink { + endpoint, + auth_token, + client: reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(10)) + .build() + .unwrap_or_else(|_| reqwest::Client::new()), + max_retries, + retry_delay_ms, + } + } +} + +#[cfg(feature = "webhook")] +#[async_trait] +impl Sink for WebhookSink { + async fn write(&self, entry: &UnifiedLogEntry) { + let mut retries = 0; + let url = self.endpoint.clone(); + let entry_clone = entry.clone(); + let auth_value = reqwest::header::HeaderValue::from_str(format!("Bearer {}", self.auth_token.clone()).as_str()).unwrap(); + while retries < self.max_retries { + match self + .client + .post(&url) + .header(reqwest::header::AUTHORIZATION, auth_value.clone()) + .json(&entry_clone) + .send() + .await + { + Ok(response) if response.status().is_success() => { + return; + } + _ => { + retries += 1; + if retries < self.max_retries { + tokio::time::sleep(tokio::time::Duration::from_millis( + self.retry_delay_ms * (1 << retries), // Exponential backoff + )) + .await; + } + } + } + } + + eprintln!("Failed to send log to webhook after {} retries", self.max_retries); + } +} + +#[cfg(feature = "webhook")] +impl Drop for WebhookSink { + fn drop(&mut self) { + // Perform any necessary cleanup here + // For example, you might want to log that the sink is being dropped + eprintln!("Dropping WebhookSink with URL: {}", self.endpoint); + } +} + +#[cfg(feature = "file")] +/// File Sink Implementation +pub struct FileSink { + path: String, + buffer_size: usize, + writer: Arc>>, + entry_count: std::sync::atomic::AtomicUsize, + last_flush: std::sync::atomic::AtomicU64, + flush_interval_ms: u64, // Time between flushes + flush_threshold: usize, // Number of entries before flush +} + +#[cfg(feature = "file")] +impl FileSink { + /// Create a new FileSink instance + pub async fn new( + path: String, + buffer_size: usize, + flush_interval_ms: u64, + flush_threshold: usize, + ) -> Result { + // check if the file exists + let file_exists = tokio::fs::metadata(&path).await.is_ok(); + // if the file not exists, create it + if !file_exists { + tokio::fs::create_dir_all(std::path::Path::new(&path).parent().unwrap()).await?; + tracing::debug!("File does not exist, creating it. Path: {:?}", path) + } + let file = if file_exists { + // If the file exists, open it in append mode + tracing::debug!("FileSink: File exists, opening in append mode."); + OpenOptions::new().append(true).create(true).open(&path).await? + } else { + // If the file does not exist, create it + tracing::debug!("FileSink: File does not exist, creating a new file."); + // Create the file and write a header or initial content if needed + OpenOptions::new().create(true).truncate(true).write(true).open(&path).await? + }; + let writer = io::BufWriter::with_capacity(buffer_size, file); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + Ok(FileSink { + path, + buffer_size, + writer: Arc::new(tokio::sync::Mutex::new(writer)), + entry_count: std::sync::atomic::AtomicUsize::new(0), + last_flush: std::sync::atomic::AtomicU64::new(now), + flush_interval_ms, + flush_threshold, + }) + } + + #[allow(dead_code)] + async fn initialize_writer(&mut self) -> io::Result<()> { + let file = tokio::fs::File::create(&self.path).await?; + + // Use buffer_size to create a buffer writer with a specified capacity + let buf_writer = io::BufWriter::with_capacity(self.buffer_size, file); + + // Replace the original writer with the new Mutex + self.writer = Arc::new(tokio::sync::Mutex::new(buf_writer)); + Ok(()) + } + + // Get the current buffer size + #[allow(dead_code)] + pub fn buffer_size(&self) -> usize { + self.buffer_size + } + + // How to dynamically adjust the buffer size + #[allow(dead_code)] + pub async fn set_buffer_size(&mut self, new_size: usize) -> io::Result<()> { + if self.buffer_size != new_size { + self.buffer_size = new_size; + // Reinitialize the writer directly, without checking is_some() + self.initialize_writer().await?; + } + Ok(()) + } + + // Check if flushing is needed based on count or time + fn should_flush(&self) -> bool { + // Check entry count threshold + if self.entry_count.load(std::sync::atomic::Ordering::Relaxed) >= self.flush_threshold { + return true; + } + + // Check time threshold + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + + let last = self.last_flush.load(std::sync::atomic::Ordering::Relaxed); + now - last >= self.flush_interval_ms + } +} + +#[cfg(feature = "file")] +#[async_trait] +impl Sink for FileSink { + async fn write(&self, entry: &UnifiedLogEntry) { + let line = format!("{:?}\n", entry); + let mut writer = self.writer.lock().await; + + if let Err(e) = writer.write_all(line.as_bytes()).await { + eprintln!( + "Failed to write log to file {}: {},entry timestamp:{:?}", + self.path, + e, + entry.get_timestamp() + ); + return; + } + + // Only flush periodically to improve performance + // Logic to determine when to flush could be added here + // Increment the entry count + self.entry_count.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + + // Check if we should flush + if self.should_flush() { + if let Err(e) = writer.flush().await { + eprintln!("Failed to flush log file {}: {}", self.path, e); + return; + } + + // Reset counters + self.entry_count.store(0, std::sync::atomic::Ordering::Relaxed); + + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + + self.last_flush.store(now, std::sync::atomic::Ordering::Relaxed); + } + } +} + +#[cfg(feature = "file")] +impl Drop for FileSink { + fn drop(&mut self) { + let writer = self.writer.clone(); + let path = self.path.clone(); + + tokio::task::spawn_blocking(move || { + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async { + let mut writer = writer.lock().await; + if let Err(e) = writer.flush().await { + eprintln!("Failed to flush log file {}: {}", path, e); + } + }); + }); + } +} + +/// Create a list of Sink instances +pub async fn create_sinks(config: &AppConfig) -> Vec> { + let mut sinks: Vec> = Vec::new(); + + #[cfg(feature = "kafka")] + { + match &config.sinks.kafka { + Some(sink_kafka) => { + if sink_kafka.enabled { + match rdkafka::config::ClientConfig::new() + .set("bootstrap.servers", &sink_kafka.bootstrap_servers) + .set("message.timeout.ms", "5000") + .create() + { + Ok(producer) => { + sinks.push(Arc::new(KafkaSink::new( + producer, + sink_kafka.topic.clone(), + sink_kafka.batch_size.unwrap_or(100), + sink_kafka.batch_timeout_ms.unwrap_or(1000), + ))); + } + Err(e) => { + tracing::error!("Failed to create Kafka producer: {}", e); + } + } + } else { + tracing::info!("Kafka sink is disabled in the configuration"); + } + } + _ => { + tracing::info!("Kafka sink is not configured or disabled"); + } + } + } + #[cfg(feature = "webhook")] + { + match &config.sinks.webhook { + Some(sink_webhook) => { + if sink_webhook.enabled { + sinks.push(Arc::new(WebhookSink::new( + sink_webhook.endpoint.clone(), + sink_webhook.auth_token.clone(), + sink_webhook.max_retries.unwrap_or(3), + sink_webhook.retry_delay_ms.unwrap_or(100), + ))); + } else { + tracing::info!("Webhook sink is disabled in the configuration"); + } + } + _ => { + tracing::info!("Webhook sink is not configured or disabled"); + } + } + } + + #[cfg(feature = "file")] + { + // let config = config.clone(); + match &config.sinks.file { + Some(sink_file) => { + tracing::info!("File sink is enabled in the configuration"); + let path = if sink_file.enabled { + sink_file.path.clone() + } else { + "rustfs.log".to_string() + }; + tracing::debug!("FileSink: Using path: {}", path); + sinks.push(Arc::new( + FileSink::new( + path.clone(), + sink_file.buffer_size.unwrap_or(8192), + sink_file.flush_interval_ms.unwrap_or(1000), + sink_file.flush_threshold.unwrap_or(100), + ) + .await + .unwrap(), + )); + } + _ => { + tracing::info!("File sink is not configured or disabled"); + } + } + } + + sinks +} diff --git a/crates/obs/src/telemetry.rs b/crates/obs/src/telemetry.rs new file mode 100644 index 000000000..82c93de73 --- /dev/null +++ b/crates/obs/src/telemetry.rs @@ -0,0 +1,319 @@ +use crate::global::{ENVIRONMENT, LOGGER_LEVEL, METER_INTERVAL, SAMPLE_RATIO, SERVICE_NAME, SERVICE_VERSION, USE_STDOUT}; +use crate::{get_local_ip_with_default, OtelConfig}; +use opentelemetry::trace::TracerProvider; +use opentelemetry::{global, KeyValue}; +use opentelemetry_appender_tracing::layer; +use opentelemetry_otlp::WithExportConfig; +use opentelemetry_sdk::logs::SdkLoggerProvider; +use opentelemetry_sdk::{ + metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider}, + trace::{RandomIdGenerator, Sampler, SdkTracerProvider}, + Resource, +}; +use opentelemetry_semantic_conventions::{ + attribute::{DEPLOYMENT_ENVIRONMENT_NAME, NETWORK_LOCAL_ADDRESS, SERVICE_VERSION as OTEL_SERVICE_VERSION}, + SCHEMA_URL, +}; +use prometheus::{Encoder, Registry, TextEncoder}; +use std::io::IsTerminal; +use std::sync::Arc; +use tokio::sync::{Mutex, OnceCell}; +use tracing::{info, warn}; +use tracing_error::ErrorLayer; +use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer}; + +/// A guard object that manages the lifecycle of OpenTelemetry components. +/// +/// This struct holds references to the created OpenTelemetry providers and ensures +/// they are properly shut down when the guard is dropped. It implements the RAII +/// (Resource Acquisition Is Initialization) pattern for managing telemetry resources. +/// +/// When this guard goes out of scope, it will automatically shut down: +/// - The tracer provider (for distributed tracing) +/// - The meter provider (for metrics collection) +/// - The logger provider (for structured logging) +/// +/// # Example +/// +/// ``` +/// use rustfs_obs::{init_telemetry, OtelConfig}; +/// +/// let config = OtelConfig::default(); +/// let otel_guard = init_telemetry(&config); +/// +/// // The guard is kept alive for the duration of the application +/// // When it's dropped, all telemetry components are properly shut down +/// drop(otel_guard); +/// ``` +#[derive(Debug)] +pub struct OtelGuard { + tracer_provider: SdkTracerProvider, + meter_provider: SdkMeterProvider, + logger_provider: SdkLoggerProvider, +} + +impl Drop for OtelGuard { + fn drop(&mut self) { + if let Err(err) = self.tracer_provider.shutdown() { + eprintln!("Tracer shutdown error: {:?}", err); + } + if let Err(err) = self.meter_provider.shutdown() { + eprintln!("Meter shutdown error: {:?}", err); + } + if let Err(err) = self.logger_provider.shutdown() { + eprintln!("Logger shutdown error: {:?}", err); + } + } +} + +/// Global registry for Prometheus metrics +static GLOBAL_REGISTRY: OnceCell>> = OnceCell::const_new(); + +/// Get the global registry instance +/// This function returns a reference to the global registry instance. +/// +/// # Returns +/// A reference to the global registry instance +/// +/// # Example +/// ``` +/// use rustfs_obs::get_global_registry; +/// +/// let registry = get_global_registry(); +/// ``` +pub fn get_global_registry() -> Arc> { + GLOBAL_REGISTRY.get().unwrap().clone() +} + +/// Prometheus metric endpoints +/// This function returns a string containing the Prometheus metrics. +/// The metrics are collected from the global registry. +/// The function is used to expose the metrics via an HTTP endpoint. +/// +/// # Returns +/// A string containing the Prometheus metrics +/// +/// # Example +/// ``` +/// use rustfs_obs::metrics; +/// +/// async fn main() { +/// let metrics = metrics().await; +/// println!("{}", metrics); +/// } +/// ``` +pub async fn metrics() -> String { + let encoder = TextEncoder::new(); + // Get a reference to the registry for reading metrics + let registry = get_global_registry().lock().await.to_owned(); + let metric_families = registry.gather(); + if metric_families.is_empty() { + warn!("No metrics available in Prometheus registry"); + } else { + info!("Metrics collected: {} families", metric_families.len()); + } + let mut buffer = Vec::new(); + encoder.encode(&metric_families, &mut buffer).unwrap(); + String::from_utf8(buffer).unwrap_or_else(|_| "Error encoding metrics".to_string()) +} + +/// create OpenTelemetry Resource +fn resource(config: &OtelConfig) -> Resource { + let config = config.clone(); + Resource::builder() + .with_service_name(config.service_name.unwrap_or(SERVICE_NAME.to_string())) + .with_schema_url( + [ + KeyValue::new(OTEL_SERVICE_VERSION, config.service_version.unwrap_or(SERVICE_VERSION.to_string())), + KeyValue::new(DEPLOYMENT_ENVIRONMENT_NAME, config.environment.unwrap_or(ENVIRONMENT.to_string())), + KeyValue::new(NETWORK_LOCAL_ADDRESS, get_local_ip_with_default()), + ], + SCHEMA_URL, + ) + .build() +} + +/// Creates a periodic reader for stdout metrics +fn create_periodic_reader(interval: u64) -> PeriodicReader { + PeriodicReader::builder(opentelemetry_stdout::MetricExporter::default()) + .with_interval(std::time::Duration::from_secs(interval)) + .build() +} + +/// Initialize Meter Provider +fn init_meter_provider(config: &OtelConfig) -> SdkMeterProvider { + let mut builder = MeterProviderBuilder::default().with_resource(resource(config)); + // If endpoint is empty, use stdout output + if config.endpoint.is_empty() { + builder = builder.with_reader(create_periodic_reader(config.meter_interval.unwrap_or(METER_INTERVAL))); + } else { + // If endpoint is not empty, use otlp output + let exporter = opentelemetry_otlp::MetricExporter::builder() + .with_tonic() + .with_endpoint(&config.endpoint) + .with_temporality(opentelemetry_sdk::metrics::Temporality::default()) + .build() + .unwrap(); + builder = builder.with_reader( + PeriodicReader::builder(exporter) + .with_interval(std::time::Duration::from_secs(config.meter_interval.unwrap_or(METER_INTERVAL))) + .build(), + ); + // If use_stdout is true, output to stdout at the same time + if config.use_stdout.unwrap_or(USE_STDOUT) { + builder = builder.with_reader(create_periodic_reader(config.meter_interval.unwrap_or(METER_INTERVAL))); + } + } + let registry = Registry::new(); + // Set global registry + GLOBAL_REGISTRY.set(Arc::new(Mutex::new(registry.clone()))).unwrap(); + // Create Prometheus exporter + let prometheus_exporter = opentelemetry_prometheus::exporter().with_registry(registry).build().unwrap(); + // Build meter provider + let meter_provider = builder.with_reader(prometheus_exporter).build(); + global::set_meter_provider(meter_provider.clone()); + meter_provider +} + +/// Initialize Tracer Provider +fn init_tracer_provider(config: &OtelConfig) -> SdkTracerProvider { + let sample_ratio = config.sample_ratio.unwrap_or(SAMPLE_RATIO); + let sampler = if sample_ratio > 0.0 && sample_ratio < 1.0 { + Sampler::TraceIdRatioBased(sample_ratio) + } else { + Sampler::AlwaysOn + }; + let builder = SdkTracerProvider::builder() + .with_sampler(sampler) + .with_id_generator(RandomIdGenerator::default()) + .with_resource(resource(config)); + + let tracer_provider = if config.endpoint.is_empty() { + builder + .with_batch_exporter(opentelemetry_stdout::SpanExporter::default()) + .build() + } else { + let exporter = opentelemetry_otlp::SpanExporter::builder() + .with_tonic() + .with_endpoint(&config.endpoint) + .build() + .unwrap(); + if config.use_stdout.unwrap_or(USE_STDOUT) { + builder + .with_batch_exporter(exporter) + .with_batch_exporter(opentelemetry_stdout::SpanExporter::default()) + } else { + builder.with_batch_exporter(exporter) + } + .build() + }; + + global::set_tracer_provider(tracer_provider.clone()); + tracer_provider +} + +/// Initialize Telemetry +pub fn init_telemetry(config: &OtelConfig) -> OtelGuard { + let tracer_provider = init_tracer_provider(config); + let meter_provider = init_meter_provider(config); + + // Initialize logger provider based on configuration + let logger_provider = { + let mut builder = SdkLoggerProvider::builder().with_resource(resource(config)); + + if config.endpoint.is_empty() { + // Use stdout exporter when no endpoint is configured + builder = builder.with_simple_exporter(opentelemetry_stdout::LogExporter::default()); + } else { + // Configure OTLP exporter when endpoint is provided + let exporter = opentelemetry_otlp::LogExporter::builder() + .with_tonic() + .with_endpoint(&config.endpoint) + .build() + .unwrap(); + + builder = builder.with_batch_exporter(exporter); + + // Add stdout exporter if requested + if config.use_stdout.unwrap_or(USE_STDOUT) { + builder = builder.with_batch_exporter(opentelemetry_stdout::LogExporter::default()); + } + } + + builder.build() + }; + let config = config.clone(); + let logger_level = config.logger_level.unwrap_or(LOGGER_LEVEL.to_string()); + let logger_level = logger_level.as_str(); + // Setup OpenTelemetryTracingBridge layer + let otel_layer = { + // Filter to prevent infinite telemetry loops + // This blocks events from OpenTelemetry and its dependent libraries (tonic, reqwest, etc.) + // from being sent back to OpenTelemetry itself + let filter_otel = match logger_level { + "trace" | "debug" => { + info!("OpenTelemetry tracing initialized with level: {}", logger_level); + EnvFilter::new(logger_level) + } + _ => { + let mut filter = EnvFilter::new(logger_level); + for directive in ["hyper", "tonic", "h2", "reqwest"] { + filter = filter.add_directive(format!("{}=off", directive).parse().unwrap()); + } + filter + } + }; + + layer::OpenTelemetryTracingBridge::new(&logger_provider).with_filter(filter_otel) + }; + + let tracer = tracer_provider.tracer(config.service_name.unwrap_or(SERVICE_NAME.to_string())); + let registry = tracing_subscriber::registry() + .with(switch_level(logger_level)) + .with(OpenTelemetryLayer::new(tracer)) + .with(MetricsLayer::new(meter_provider.clone())) + .with(otel_layer); + // Configure formatting layer + let enable_color = std::io::stdout().is_terminal(); + let fmt_layer = tracing_subscriber::fmt::layer() + .with_ansi(enable_color) + .with_thread_names(true) + .with_file(true) + .with_line_number(true); + + // Creating a formatting layer with explicit type to avoid type mismatches + let fmt_layer = fmt_layer.with_filter( + EnvFilter::new(logger_level).add_directive( + format!("opentelemetry={}", if config.endpoint.is_empty() { logger_level } else { "off" }) + .parse() + .unwrap(), + ), + ); + + registry.with(ErrorLayer::default()).with(fmt_layer).init(); + if !config.endpoint.is_empty() { + info!( + "OpenTelemetry telemetry initialized with OTLP endpoint: {}, logger_level: {}", + config.endpoint, logger_level + ); + } + + OtelGuard { + tracer_provider, + meter_provider, + logger_provider, + } +} + +/// Switch log level +fn switch_level(logger_level: &str) -> tracing_subscriber::filter::LevelFilter { + match logger_level { + "error" => tracing_subscriber::filter::LevelFilter::ERROR, + "warn" => tracing_subscriber::filter::LevelFilter::WARN, + "info" => tracing_subscriber::filter::LevelFilter::INFO, + "debug" => tracing_subscriber::filter::LevelFilter::DEBUG, + "trace" => tracing_subscriber::filter::LevelFilter::TRACE, + _ => tracing_subscriber::filter::LevelFilter::OFF, + } +} diff --git a/crates/obs/src/utils.rs b/crates/obs/src/utils.rs new file mode 100644 index 000000000..774594577 --- /dev/null +++ b/crates/obs/src/utils.rs @@ -0,0 +1,42 @@ +use local_ip_address::{local_ip, local_ipv6}; +use std::net::{IpAddr, Ipv4Addr}; + +/// Get the IP address of the machine +/// +/// Priority is given to trying to get the IPv4 address, and if it fails, try to get the IPv6 address. +/// If both fail to retrieve, None is returned. +/// +/// # Returns +/// +/// * `Some(IpAddr)` - Native IP address (IPv4 or IPv6) +/// * `None` - Unable to obtain any native IP address +pub fn get_local_ip() -> Option { + local_ip().ok().or_else(|| local_ipv6().ok()) +} + +/// Get the IP address of the machine as a string +/// +/// If the IP address cannot be obtained, returns "127.0.0.1" as the default value. +/// +/// # Returns +/// +/// * `String` - Native IP address (IPv4 or IPv6) as a string, or the default value +pub fn get_local_ip_with_default() -> String { + get_local_ip() + .unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))) // Provide a safe default value + .to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_local_ip() { + match get_local_ip() { + Some(ip) => println!("the ip address of this machine:{}", ip), + None => println!("Unable to obtain the IP address of the machine"), + } + assert!(get_local_ip().is_some()); + } +} diff --git a/crates/obs/src/worker.rs b/crates/obs/src/worker.rs new file mode 100644 index 000000000..2d7ee2e14 --- /dev/null +++ b/crates/obs/src/worker.rs @@ -0,0 +1,13 @@ +use crate::{sink::Sink, UnifiedLogEntry}; +use std::sync::Arc; +use tokio::sync::mpsc::Receiver; + +/// Start the log processing worker thread +pub async fn start_worker(receiver: Receiver, sinks: Vec>) { + let mut receiver = receiver; + while let Some(entry) = receiver.recv().await { + for sink in &sinks { + sink.write(&entry).await; + } + } +} diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index c28bf7006..5b0f58969 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -14,7 +14,7 @@ aes-gcm = { version = "0.10.3", features = ["std"], optional = true } argon2 = { version = "0.5.3", features = ["std"], optional = true } cfg-if = "1.0.0" chacha20poly1305 = { version = "0.10.1", optional = true } -jsonwebtoken = "9.3.0" +jsonwebtoken = { workspace = true } pbkdf2 = { version = "0.12.2", optional = true } rand = { workspace = true, optional = true } sha2 = { version = "0.10.8", optional = true } diff --git a/data/README.md b/data/README.md new file mode 100644 index 000000000..2e3d4df77 --- /dev/null +++ b/data/README.md @@ -0,0 +1 @@ +## Observability Docker Compose \ No newline at end of file diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 000000000..4d3d4476a --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,35 @@ +# RustFS Deploy + +This directory contains the deployment scripts and configurations for the project. +The deployment process is divided into two main parts: the RustFS binary and the RustFS console. The RustFS binary is +responsible for the core functionality of the system, while the RustFS console provides a web-based interface for +managing and monitoring the system. + +# Directory Structure + +```text +|--data // data directory +| |--vol1 // volume 1 not created +| |--vol2 // volume 2 not created +| |--vol3 // volume 3 not created +| |--vol4 // volume 4 not created +| |--README.md // data directory readme +|--logs // log directory +| |--rustfs.log // RustFS log +| |--README.md // logs directory readme +|--build +| |--rustfs.run.md // deployment script for RustFS +| |--rustfs.run-zh.md // deployment script for RustFS in Chinese +| |--rustfs.service // systemd service file +| |--rustfs-zh.service.md // systemd service file in Chinese +|--certs +| |--README.md // certs readme +| |--rustfs_tls_cert.pem // API cert.pem +| |--rustfs_tls_key.pem // API key.pem +| |--rustfs_console_tls_cert.pem // console cert.pem +| |--rustfs_console_tls_key.pem // console key.pem +|--config +| |--obs.example.yaml // example config +| |--rustfs.env // env config +| |--rustfs-zh.env // env config in Chinese +``` \ No newline at end of file diff --git a/deploy/build/rustfs-zh.service b/deploy/build/rustfs-zh.service new file mode 100644 index 000000000..a1cd0df05 --- /dev/null +++ b/deploy/build/rustfs-zh.service @@ -0,0 +1,102 @@ +[Unit] +Description=RustFS Object Storage Server +# 定义服务的描述,说明这是一个 RustFS 对象存储服务器,显示在 systemctl status 中。 +Documentation=https://rustfs.com/docs/ +# 提供服务的官方文档链接,方便管理员查阅,占位符需替换为实际 URL。 +After=network-online.target +# 指定服务在 network-online.target(网络就绪)之后启动,确保网络可用。 +Wants=network-online.target +# 表示服务希望依赖 network-online.target,但不是强依赖,即使网络未就绪也尝试启动。 +# If you're using a database, you'll need to add the corresponding dependencies +# 如果服务依赖数据库,可以添加数据库相关的依赖项(当前为注释,未启用)。 +# After=postgresql.service +# 示例:若依赖 PostgreSQL,则在 PostgreSQL 服务后启动(当前未启用)。 +# Requires=postgresql.service +# 示例:若强制依赖 PostgreSQL,则要求其启动成功(当前未启用)。 + +[Service] +Type=notify +# 服务类型为 notify,表示服务通过 sd_notify 通知 systemd 其状态(如就绪)。 +NotifyAccess=main +# 指定只有主进程可以发送通知给 systemd,避免子进程干扰。 +User=rustfs +# 以 rustfs 用户身份运行服务,需预先创建此用户,提升安全性。 +Group=rustfs +# 以 rustfs 组身份运行服务,与 User 配合使用。 + +# working directory +WorkingDirectory=/opt/rustfs +# 设置服务的工作目录为 /opt/rustfs,影响相对路径的解析。 + +# 定义环境变量配置,用于传递给服务程序。 +Environment=RUSTFS_ACCESS_KEY=rustfsadmin +# 设置访问密钥为 rustfsadmin,用于 RustFS 的认证。 +Environment=RUSTFS_SECRET_KEY=rustfsadmin +# 设置秘密密钥为 rustfsadmin,与访问密钥配套使用。 +ExecStart=/usr/local/bin/rustfs \ + --address 0.0.0.0:9000 \ + --volumes /data/rustfs/vol1,/data/rustfs/vol2 \ + --obs-config /etc/rustfs/obs.yaml \ + --console-enable \ + --console-address 0.0.0.0:9002 +# 定义启动命令,运行 /usr/local/bin/rustfs,带参数: +# --address 0.0.0.0:9000:服务监听所有接口的 9000 端口。 +# --volumes:指定存储卷路径为 /data/rustfs/vol1 和 /data/rustfs/vol2。 +# --obs-config:指定配置文件路径为 /etc/rustfs/obs.yaml。 +# --console-enable:启用控制台功能。 +# --console-address 0.0.0.0:9002:控制台监听所有接口的 9002 端口。 + +# 定义环境变量配置,用于传递给服务程序,推荐使用且简洁 +# rustfs 示例文件 详见: `../config/rustfs-zh.env` +EnvironmentFile=-/etc/default/rustfs +ExecStart=/usr/local/bin/rustfs $RUSTFS_VOLUMES $RUSTFS_OPTS + +# resource constraints +LimitNOFILE=1048576 +# 设置文件描述符上限为 1048576,支持高并发连接。 +LimitNPROC=32768 +# 设置进程数上限为 32768,限制子进程数量。 +TasksMax=infinity +# 允许服务创建无限数量的线程(谨慎使用,可能耗尽资源)。 + +# restart the policy +Restart=always +# 服务异常退出时总是重启,提高可用性。 +RestartSec=10s +# 重启前等待 10 秒,避免频繁重启导致资源浪费。 + +# graceful exit configuration +TimeoutStartSec=30s +# 启动超时时间为 30 秒,若超时则认为启动失败。 +TimeoutStopSec=30s +# 停止超时时间为 30 秒,若超时则强制停止。 + +# security settings +NoNewPrivileges=true +# 禁止服务提升权限,增强安全性。 +ProtectSystem=full +# 保护系统目录(如 /usr、/boot、/etc)为只读,防止服务修改。 +ProtectHome=true +# 保护用户主目录(如 /home、/root),禁止服务访问。 +PrivateTmp=true +# 为服务提供私有 /tmp 目录,隔离临时文件。 +PrivateDevices=true +# 禁止服务访问硬件设备(如 /dev),提升安全性。 +ProtectClock=true +# 保护系统时钟,禁止服务修改时间。 +ProtectKernelTunables=true +# 保护内核参数(/proc/sys),禁止服务修改。 +ProtectKernelModules=true +# 禁止服务加载或卸载内核模块。 +ProtectControlGroups=true +# 保护控制组(cgroups),禁止服务修改。 +RestrictSUIDSGID=true +# 禁止服务使用 SUID/SGID 文件,提升安全性。 +RestrictRealtime=true +# 禁止服务使用实时调度,防止资源滥用。 +ReadWritePaths=/data/rustfs +# 允许服务对 /data/rustfs 目录读写,限制其他路径访问。 + +[Install] +WantedBy=multi-user.target +# 服务在多用户模式下自动启动,配合 systemctl enable 使用。 \ No newline at end of file diff --git a/deploy/build/rustfs.run-zh.md b/deploy/build/rustfs.run-zh.md new file mode 100644 index 000000000..85def56e7 --- /dev/null +++ b/deploy/build/rustfs.run-zh.md @@ -0,0 +1,90 @@ +# RustFS 服务安装配置教程 + +## 1. 准备工作 + +### 1.1 创建系统用户 + +```bash +# 创建 rustfs 系统用户和用户组,禁止登录shell +sudo useradd -r -s /sbin/nologin rustfs +``` + +### 1.2 创建必要目录 + +```bash +# 创建程序目录 +sudo mkdir -p /opt/rustfs + +# 创建数据目录 +sudo mkdir -p /data/rustfs/{vol1,vol2} + +# 创建配置目录 +sudo mkdir -p /etc/rustfs + +# 设置目录权限 +sudo chown -R rustfs:rustfs /opt/rustfs /data/rustfs +sudo chmod 755 /opt/rustfs /data/rustfs +``` + +## 2. 安装 RustFS + +```bash +# 复制 RustFS 二进制文件 +sudo cp rustfs /usr/local/bin/ +sudo chmod +x /usr/local/bin/rustfs + +# 复制配置文件 +sudo cp obs.yaml /etc/rustfs/ +sudo chown -R rustfs:rustfs /etc/rustfs +``` + +## 3. 配置 Systemd 服务 + +```bash +# 复制服务单元文件 +sudo cp rustfs.service /etc/systemd/system/ + +# 重新加载 systemd 配置 +sudo systemctl daemon-reload +``` + +## 4. 服务管理 + +### 4.1 启动服务 + +```bash +sudo systemctl start rustfs +``` + +### 4.2 查看服务状态 + +```bash +sudo systemctl status rustfs +``` + +### 4.3 启用开机自启 + +```bash +sudo systemctl enable rustfs +``` + +### 4.4 查看服务日志 + +```bash +# 查看实时日志 +sudo journalctl -u rustfs -f + +# 查看今天的日志 +sudo journalctl -u rustfs --since today +``` + +## 5. 验证安装 + +```bash +# 检查服务端口 +ss -tunlp | grep 9000 +ss -tunlp | grep 9002 + +# 测试服务可用性 +curl -I http://localhost:9000 +``` diff --git a/deploy/build/rustfs.run.md b/deploy/build/rustfs.run.md new file mode 100644 index 000000000..2e26ea31a --- /dev/null +++ b/deploy/build/rustfs.run.md @@ -0,0 +1,90 @@ +# RustFS Service Installation Guide + +## 1. Prerequisites + +### 1.1 Create System User + +```bash +# Create rustfs system user and group without login shell +sudo useradd -r -s /sbin/nologin rustfs +``` + +### 1.2 Create Required Directories + +```bash +# Create program directory +sudo mkdir -p /opt/rustfs + +# Create data directories +sudo mkdir -p /data/rustfs/{vol1,vol2} + +# Create configuration directory +sudo mkdir -p /etc/rustfs + +# Set directory permissions +sudo chown -R rustfs:rustfs /opt/rustfs /data/rustfs +sudo chmod 755 /opt/rustfs /data/rustfs +``` + +## 2. Install RustFS + +```bash +# Copy RustFS binary +sudo cp rustfs /usr/local/bin/ +sudo chmod +x /usr/local/bin/rustfs + +# Copy configuration file +sudo cp obs.yaml /etc/rustfs/ +sudo chown -R rustfs:rustfs /etc/rustfs +``` + +## 3. Configure Systemd Service + +```bash +# Copy service unit file +sudo cp rustfs.service /etc/systemd/system/ + +# Reload systemd configuration +sudo systemctl daemon-reload +``` + +## 4. Service Management + +### 4.1 Start Service + +```bash +sudo systemctl start rustfs +``` + +### 4.2 Check Service Status + +```bash +sudo systemctl status rustfs +``` + +### 4.3 Enable Auto-start + +```bash +sudo systemctl enable rustfs +``` + +### 4.4 View Service Logs + +```bash +# View real-time logs +sudo journalctl -u rustfs -f + +# View today's logs +sudo journalctl -u rustfs --since today +``` + +## 5. Verify Installation + +```bash +# Check service ports +ss -tunlp | grep 9000 +ss -tunlp | grep 9002 + +# Test service availability +curl -I http://localhost:9000 +``` diff --git a/deploy/build/rustfs.service b/deploy/build/rustfs.service new file mode 100644 index 000000000..df6e4067b --- /dev/null +++ b/deploy/build/rustfs.service @@ -0,0 +1,62 @@ +[Unit] +Description=RustFS Object Storage Server +Documentation=https://rustfs.com/docs/ +After=network-online.target +Wants=network-online.target +# If you're using a database, you'll need to add the corresponding dependencies +# After=postgresql.service +# Requires=postgresql.service + +[Service] +Type=notify +NotifyAccess=main +User=rustfs +Group=rustfs + +# working directory +WorkingDirectory=/opt/rustfs + +# environment variable configuration and main program (Option 1: Directly specify arguments) +Environment=RUSTFS_ACCESS_KEY=rustfsadmin +Environment=RUSTFS_SECRET_KEY=rustfsadmin +ExecStart=/usr/local/bin/rustfs \ + --address 0.0.0.0:9000 \ + --volumes /data/rustfs/vol1,/data/rustfs/vol2 \ + --obs-config /etc/rustfs/obs.yaml \ + --console-enable \ + --console-address 0.0.0.0:9002 + +# environment variable configuration (Option 2: Use environment variables) +# rustfs example file see: `../config/rustfs.env` +EnvironmentFile=-/etc/default/rustfs +ExecStart=/usr/local/bin/rustfs $RUSTFS_VOLUMES $RUSTFS_OPTS + +# resource constraints +LimitNOFILE=1048576 +LimitNPROC=32768 +TasksMax=infinity + +# restart the policy +Restart=always +RestartSec=10s + +# graceful exit configuration +TimeoutStartSec=30s +TimeoutStopSec=30s + +# security settings +NoNewPrivileges=true +ProtectSystem=full +ProtectHome=true +PrivateTmp=true +PrivateDevices=true +ProtectClock=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true +RestrictSUIDSGID=true +RestrictRealtime=true +ReadWritePaths=/data/rustfs + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/deploy/certs/README.md b/deploy/certs/README.md new file mode 100644 index 000000000..84b733e79 --- /dev/null +++ b/deploy/certs/README.md @@ -0,0 +1,38 @@ +## Certs + +### Generate a self-signed certificate + +```bash +openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes +``` + +### Generate a self-signed certificate with a specific subject + +```bash +openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes \ + -subj "/C=US/ST=California/L=San Francisco/O=My Company/CN=mydomain.com" +``` + +### Generate a self-signed certificate with a specific subject and SAN + +```bash +openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes \ + -subj "/C=US/ST=California/L=San Francisco/O=My Company/CN=mydomain.com" \ + -addext "subjectAltName=DNS:mydomain.com,DNS:www.mydomain.com" +``` + +### Generate a self-signed certificate with a specific subject and SAN (multiple SANs) + +```bash +openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes \ + -subj "/C=US/ST=California/L=San Francisco/O=My Company/CN=mydomain.com" \ + -addext "subjectAltName=DNS:mydomain.com,DNS:www.mydomain.com,DNS:api.mydomain.com" +``` + +### TLS File + +```text + rustfs_public.crt #api cert.pem + + rustfs_private.key #api key.pem +``` \ No newline at end of file diff --git a/deploy/config/.example.env b/deploy/config/.example.env new file mode 100644 index 000000000..a1ea67b4f --- /dev/null +++ b/deploy/config/.example.env @@ -0,0 +1,27 @@ +OBSERVABILITY__ENDPOINT=http://localhost:4317 +OBSERVABILITY__USE_STDOUT=true +OBSERVABILITY__SAMPLE_RATIO=2.0 +OBSERVABILITY__METER_INTERVAL=30 +OBSERVABILITY__SERVICE_NAME=rustfs +OBSERVABILITY__SERVICE_VERSION=0.1.0 +OBSERVABILITY__ENVIRONMENT=develop +OBSERVABILITY__LOGGER_LEVEL=debug + +SINKS__KAFKA__ENABLED=false +SINKS__KAFKA__BOOTSTRAP_SERVERS=localhost:9092 +SINKS__KAFKA__TOPIC=logs +SINKS__KAFKA__BATCH_SIZE=100 +SINKS__KAFKA__BATCH_TIMEOUT_MS=1000 + +SINKS__WEBHOOK__ENABLED=false +SINKS__WEBHOOK__ENDPOINT=http://localhost:8080/webhook +SINKS__WEBHOOK__AUTH_TOKEN= +SINKS__WEBHOOK__BATCH_SIZE=100 +SINKS__WEBHOOK__BATCH_TIMEOUT_MS=1000 + +SINKS__FILE__ENABLED=true +SINKS__FILE__PATH=./deploy/logs/rustfs.log +SINKS__FILE__BATCH_SIZE=10 +SINKS__FILE__BATCH_TIMEOUT_MS=1000 + +LOGGER__QUEUE_CAPACITY=10 \ No newline at end of file diff --git a/deploy/config/obs-zh.example.toml b/deploy/config/obs-zh.example.toml new file mode 100644 index 000000000..2bf4260f8 --- /dev/null +++ b/deploy/config/obs-zh.example.toml @@ -0,0 +1,33 @@ +[observability] +endpoint = "http://localhost:4317" # 可观测性数据上报的终端地址,默认为"http://localhost:4317" +use_stdout = true # 是否将日志输出到标准输出 +sample_ratio = 2.0 # 采样率,表示每 2 条数据采样 1 条 +meter_interval = 30 # 指标收集间隔,单位为秒 +service_name = "rustfs" # 服务名称,用于标识当前服务 +service_version = "0.1.0" # 服务版本号 +environments = "develop" # 运行环境,如开发环境 (develop) +logger_level = "debug" # 日志级别,可选 debug/info/warn/error 等 + +[sinks] +[sinks.kafka] # Kafka 接收器配置 +enabled = false # 是否启用 Kafka 接收器,默认禁用 +bootstrap_servers = "localhost:9092" # Kafka 服务器地址 +topic = "logs" # Kafka 主题名称 +batch_size = 100 # 批处理大小,每次发送的消息数量 +batch_timeout_ms = 1000 # 批处理超时时间,单位为毫秒 + +[sinks.webhook] # Webhook 接收器配置 +enabled = false # 是否启用 Webhook 接收器 +endpoint = "http://localhost:8080/webhook" # Webhook 接收地址 +auth_token = "" # 认证令牌 +batch_size = 100 # 批处理大小 +batch_timeout_ms = 1000 # 批处理超时时间,单位为毫秒 + +[sinks.file] # 文件接收器配置 +enabled = true # 是否启用文件接收器 +path = "./deploy/logs/rustfs.log" # 日志文件路径 +batch_size = 10 # 批处理大小 +batch_timeout_ms = 1000 # 批处理超时时间,单位为毫秒 + +[logger] # 日志器配置 +queue_capacity = 10 # 日志队列容量,表示可以缓存的日志条数 \ No newline at end of file diff --git a/deploy/config/obs.example.toml b/deploy/config/obs.example.toml new file mode 100644 index 000000000..a3c3d0966 --- /dev/null +++ b/deploy/config/obs.example.toml @@ -0,0 +1,33 @@ +[observability] +endpoint = "http://localhost:4317" # Default is "http://localhost:4317" if not specified +use_stdout = false # Output with stdout, true output, false no output +sample_ratio = 2.0 +meter_interval = 30 +service_name = "rustfs" +service_version = "0.1.0" +environment = "develop" +logger_level = "info" + +[sinks] +[sinks.kafka] # Kafka sink is disabled by default +enabled = false +bootstrap_servers = "localhost:9092" +topic = "logs" +batch_size = 100 # Default is 100 if not specified +batch_timeout_ms = 1000 # Default is 1000ms if not specified + +[sinks.webhook] +enabled = false +endpoint = "http://localhost:8080/webhook" +auth_token = "" +batch_size = 100 # Default is 3 if not specified +batch_timeout_ms = 1000 # Default is 100ms if not specified + +[sinks.file] +enabled = true +path = "./deploy/logs/rustfs.log" +batch_size = 100 +batch_timeout_ms = 1000 # Default is 8192 bytes if not specified + +[logger] +queue_capacity = 10000 diff --git a/deploy/config/rustfs-zh.env b/deploy/config/rustfs-zh.env new file mode 100644 index 000000000..b9700adb8 --- /dev/null +++ b/deploy/config/rustfs-zh.env @@ -0,0 +1,26 @@ +# RustFS 管理员用户名 +RUSTFS_ROOT_USER=rustfsadmin +# RustFS 管理员密码 +RUSTFS_ROOT_PASSWORD=rustfsadmin + +# 数据卷配置示例路径:deploy/data/rustfs.env +# RustFS 数据卷存储路径,支持多卷配置,vol1 到 vol4 +RUSTFS_VOLUMES="./deploy/deploy/vol{1...4}" +# RustFS 服务启动参数,指定监听地址和端口 +RUSTFS_OPTS="--address 0.0.0.0:9000" +# RustFS 服务监听地址和端口 +RUSTFS_ADDRESS="0.0.0.0:9000" +# 是否启用 RustFS 控制台功能 +RUSTFS_CONSOLE_ENABLE=true +# RustFS 控制台监听地址和端口 +RUSTFS_CONSOLE_ADDRESS="0.0.0.0:9002" +# RustFS 服务端点地址,用于客户端访问 +RUSTFS_SERVER_ENDPOINT="http://127.0.0.1:9000" +# RustFS 服务域名配置 +RUSTFS_SERVER_DOMAINS=127.0.0.1:9002 +# RustFS 许可证内容 +RUSTFS_LICENSE="license content" +# 可观测性配置文件路径:deploy/config/obs.example.toml +RUSTFS_OBS_CONFIG=/etc/default/obs.toml +# TLS 证书目录路径:deploy/certs +RUSTFS_TLS_PATH=/etc/default/tls \ No newline at end of file diff --git a/deploy/config/rustfs.env b/deploy/config/rustfs.env new file mode 100644 index 000000000..ec7d31b48 --- /dev/null +++ b/deploy/config/rustfs.env @@ -0,0 +1,26 @@ +# RustFS administrator username +RUSTFS_ROOT_USER=rustfsadmin +# RustFS administrator password +RUSTFS_ROOT_PASSWORD=rustfsadmin + +# Data volume configuration example path: deploy/data/rustfs.env +# RustFS data volume storage paths, supports multiple volumes from vol1 to vol4 +RUSTFS_VOLUMES="./deploy/deploy/vol{1...4}" +# RustFS service startup parameters, specifying listen address and port +RUSTFS_OPTS="--address 0.0.0.0:9000" +# RustFS service listen address and port +RUSTFS_ADDRESS="0.0.0.0:9000" +# Enable RustFS console functionality +RUSTFS_CONSOLE_ENABLE=true +# RustFS console listen address and port +RUSTFS_CONSOLE_ADDRESS="0.0.0.0:9002" +# RustFS service endpoint for client access +RUSTFS_SERVER_ENDPOINT="http://127.0.0.1:9000" +# RustFS service domain configuration +RUSTFS_SERVER_DOMAINS=127.0.0.1:9002 +# RustFS license content +RUSTFS_LICENSE="license content" +# Observability configuration file path: deploy/config/obs.example.toml +RUSTFS_OBS_CONFIG=/etc/default/obs.toml +# TLS certificates directory path: deploy/certs +RUSTFS_TLS_PATH=/etc/default/tls \ No newline at end of file diff --git a/deploy/data/README.md b/deploy/data/README.md new file mode 100644 index 000000000..ac7023e57 --- /dev/null +++ b/deploy/data/README.md @@ -0,0 +1 @@ +# Data Volumes \ No newline at end of file diff --git a/deploy/logs/README.md b/deploy/logs/README.md new file mode 100644 index 000000000..b2c0294c2 --- /dev/null +++ b/deploy/logs/README.md @@ -0,0 +1 @@ +# RustFS Logs \ No newline at end of file diff --git a/docker-compose-obs.yaml b/docker-compose-obs.yaml new file mode 100644 index 000000000..9a01db4cb --- /dev/null +++ b/docker-compose-obs.yaml @@ -0,0 +1,148 @@ +services: + otel-collector: + image: otel/opentelemetry-collector-contrib:0.120.0 + environment: + - TZ=Asia/Shanghai + volumes: + - ./.docker/observability/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml + ports: + - 1888:1888 + - 8888:8888 + - 8889:8889 + - 13133:13133 + - 4317:4317 + - 4318:4318 + - 55679:55679 + networks: + - rustfs-network + jaeger: + image: jaegertracing/jaeger:2.4.0 + environment: + - TZ=Asia/Shanghai + ports: + - "16686:16686" + - "14317:4317" + - "14318:4318" + networks: + - rustfs-network + prometheus: + image: prom/prometheus:v3.2.1 + environment: + - TZ=Asia/Shanghai + volumes: + - ./.docker/observability/prometheus.yml:/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + networks: + - rustfs-network + loki: + image: grafana/loki:3.4.2 + environment: + - TZ=Asia/Shanghai + volumes: + - ./.docker/observability/loki-config.yaml:/etc/loki/local-config.yaml + ports: + - "3100:3100" + command: -config.file=/etc/loki/local-config.yaml + networks: + - rustfs-network + grafana: + image: grafana/grafana:11.6.0 + ports: + - "3000:3000" # Web UI + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + - TZ=Asia/Shanghai + networks: + - rustfs-network + + node1: + build: + context: . + dockerfile: Dockerfile.obs + container_name: node1 + environment: + - RUSTFS_VOLUMES=/root/data/target/volume/test{1...4} + - RUSTFS_ADDRESS=0.0.0.0:9000 + - RUSTFS_CONSOLE_ENABLE=true + - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9002 + - RUSTFS_OBS_CONFIG=/etc/observability/config.obs.toml + platform: linux/amd64 + ports: + - "9001:9000" # 映射宿主机的 9001 端口到容器的 9000 端口 + - "9101:9002" + volumes: + - ./data:/root/data # 将当前路径挂载到容器内的 /root/data + - ./.docker/observability/config/obs.toml:/etc/observability/config.obs.toml + networks: + - rustfs-network + + node2: + build: + context: . + dockerfile: Dockerfile.obs + container_name: node2 + environment: + - RUSTFS_VOLUMES=/root/data/target/volume/test{1...4} + - RUSTFS_ADDRESS=0.0.0.0:9000 + - RUSTFS_CONSOLE_ENABLE=true + - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9002 + - RUSTFS_OBS_CONFIG=/etc/observability/config.obs.toml + platform: linux/amd64 + ports: + - "9002:9000" # 映射宿主机的 9002 端口到容器的 9000 端口 + - "9102:9002" + volumes: + - ./data:/root/data + - ./.docker/observability/config/obs.toml:/etc/observability/config.obs.toml + networks: + - rustfs-network + + node3: + build: + context: . + dockerfile: Dockerfile.obs + container_name: node3 + environment: + - RUSTFS_VOLUMES=/root/data/target/volume/test{1...4} + - RUSTFS_ADDRESS=0.0.0.0:9000 + - RUSTFS_CONSOLE_ENABLE=true + - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9002 + - RUSTFS_OBS_CONFIG=/etc/observability/config.obs.toml + platform: linux/amd64 + ports: + - "9003:9000" # 映射宿主机的 9003 端口到容器的 9000 端口 + - "9103:9002" + volumes: + - ./data:/root/data + - ./.docker/observability/config/obs.toml:/etc/observability/config.obs.toml + networks: + - rustfs-network + + node4: + build: + context: . + dockerfile: Dockerfile.obs + container_name: node4 + environment: + - RUSTFS_VOLUMES=/root/data/target/volume/test{1...4} + - RUSTFS_ADDRESS=0.0.0.0:9000 + - RUSTFS_CONSOLE_ENABLE=true + - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9002 + - RUSTFS_OBS_CONFIG=/etc/observability/config.obs.toml + platform: linux/amd64 + ports: + - "9004:9000" # 映射宿主机的 9004 端口到容器的 9000 端口 + - "9104:9002" + volumes: + - ./data:/root/data + - ./.docker/observability/config/obs.toml:/etc/observability/config.obs.toml + networks: + - rustfs-network + +networks: + rustfs-network: + driver: bridge + name: "network_rustfs_config" + driver_opts: + com.docker.network.enable_ipv6: "true" diff --git a/e2e_test/Cargo.toml b/e2e_test/Cargo.toml index a519f647e..8374bdcf7 100644 --- a/e2e_test/Cargo.toml +++ b/e2e_test/Cargo.toml @@ -22,7 +22,7 @@ protos.workspace = true rmp-serde.workspace = true serde.workspace = true serde_json.workspace = true -tonic = { version = "0.12.3", features = ["gzip"] } +tonic = { workspace = true } tokio = { workspace = true } tower.workspace = true url.workspace = true diff --git a/ecstore/Cargo.toml b/ecstore/Cargo.toml index 4f822c038..a57bdcd50 100644 --- a/ecstore/Cargo.toml +++ b/ecstore/Cargo.toml @@ -32,7 +32,7 @@ s3s.workspace = true http.workspace = true highway = "1.3.0" url.workspace = true -uuid = { version = "1.15.1", features = ["v4", "fast-rng", "serde"] } +uuid = { workspace = true, features = ["v4", "fast-rng", "serde"] } reed-solomon-erasure = { version = "6.0.0", features = ["simd-accel"] } transform-stream = "0.3.1" lazy_static.workspace = true @@ -44,7 +44,7 @@ path-absolutize = "3.1.1" protos.workspace = true rmp.workspace = true rmp-serde.workspace = true -tokio-util = { version = "0.7.13", features = ["io", "compat"] } +tokio-util = { workspace = true, features = ["io", "compat"] } crc32fast = "1.4.2" siphasher = "1.0.1" base64-simd = "0.8.0" diff --git a/ecstore/src/config/storageclass.rs b/ecstore/src/config/storageclass.rs index 3859fcb5e..edb2095f9 100644 --- a/ecstore/src/config/storageclass.rs +++ b/ecstore/src/config/storageclass.rs @@ -189,13 +189,7 @@ pub fn lookup_config(kvs: &KVS, set_drive_count: usize) -> Result { validate_parity_inner(standard.parity, rrs.parity, set_drive_count)?; - let optimize = { - if let Ok(ev) = env::var(OPTIMIZE_ENV) { - Some(ev) - } else { - None - } - }; + let optimize = { env::var(OPTIMIZE_ENV).ok() }; let inline_block = { if let Ok(ev) = env::var(INLINE_BLOCK_ENV) { diff --git a/ecstore/src/disk/error.rs b/ecstore/src/disk/error.rs index a0773b5e5..d7ea2854a 100644 --- a/ecstore/src/disk/error.rs +++ b/ecstore/src/disk/error.rs @@ -1,4 +1,5 @@ use std::io::{self, ErrorKind}; +use std::path::PathBuf; use tracing::error; @@ -345,6 +346,19 @@ pub fn os_err_to_file_err(e: io::Error) -> Error { } } +#[derive(Debug, thiserror::Error)] +pub struct FileAccessDeniedWithContext { + pub path: PathBuf, + #[source] + pub source: std::io::Error, +} + +impl std::fmt::Display for FileAccessDeniedWithContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "访问文件 '{}' 被拒绝: {}", self.path.display(), self.source) + } +} + pub fn is_unformatted_disk(err: &Error) -> bool { matches!(err.downcast_ref::(), Some(DiskError::UnformattedDisk)) } diff --git a/ecstore/src/disk/local.rs b/ecstore/src/disk/local.rs index 76b5f3ac3..24e5782fe 100644 --- a/ecstore/src/disk/local.rs +++ b/ecstore/src/disk/local.rs @@ -11,16 +11,20 @@ use super::{ }; use crate::bitrot::bitrot_verify; use crate::bucket::metadata_sys::{self}; +use crate::bucket::versioning::VersioningApi; +use crate::bucket::versioning_sys::BucketVersioningSys; use crate::cache_value::cache::{Cache, Opts, UpdateFn}; use crate::disk::error::{ convert_access_error, is_err_os_not_exist, is_sys_err_handle_invalid, is_sys_err_invalid_arg, is_sys_err_is_dir, - is_sys_err_not_dir, map_err_not_exists, os_err_to_file_err, + is_sys_err_not_dir, map_err_not_exists, os_err_to_file_err, FileAccessDeniedWithContext, }; use crate::disk::os::{check_path_length, is_empty_dir}; use crate::disk::STORAGE_FORMAT_FILE; 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::{ + lc_has_active_rules, rep_has_active_rules, scan_data_folder, ScannerItem, ShouldSleepFn, SizeSummary, +}; use crate::heal::data_scanner_metric::{ScannerMetric, ScannerMetrics}; use crate::heal::data_usage_cache::{DataUsageCache, DataUsageEntry}; use crate::heal::error::{ERR_IGNORE_FILE_CONTRIB, ERR_SKIP_FILE}; @@ -51,7 +55,6 @@ use path_absolutize::Absolutize; use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::io::SeekFrom; -use std::os::unix::fs::MetadataExt; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use std::time::{Duration, SystemTime}; @@ -308,7 +311,19 @@ impl LocalDisk { // }) // } + #[allow(unreachable_code)] + #[allow(unused_variables)] pub async fn move_to_trash(&self, delete_path: &PathBuf, recursive: bool, immediate_purge: bool) -> Result<()> { + if recursive { + remove_all(delete_path).await?; + } else { + remove(delete_path).await?; + } + + return Ok(()); + + // TODO: 异步通知 检测硬盘空间 清空回收站 + let trash_path = self.get_object_path(super::RUSTFS_META_TMP_DELETED_BUCKET, Uuid::new_v4().to_string().as_str())?; if let Some(parent) = trash_path.parent() { if !parent.exists() { @@ -347,7 +362,6 @@ impl LocalDisk { return Ok(()); } - // TODO: 异步通知 检测硬盘空间 清空回收站 Ok(()) } @@ -383,7 +397,10 @@ impl LocalDisk { kind => { if kind.to_string() != "directory not empty" { warn!("delete_file remove_dir {:?} err {}", &delete_path, kind.to_string()); - return Err(Error::from(err)); + return Err(Error::new(FileAccessDeniedWithContext { + path: delete_path.clone(), + source: err, + })); } } } @@ -395,7 +412,10 @@ impl LocalDisk { ErrorKind::NotFound => (), _ => { warn!("delete_file remove_file {:?} err {:?}", &delete_path, &err); - return Err(Error::from(err)); + return Err(Error::new(FileAccessDeniedWithContext { + path: delete_path.clone(), + source: err, + })); } } } @@ -472,13 +492,14 @@ impl LocalDisk { let meta = f.metadata().await?; if meta.is_dir() { - return Err(Error::new(DiskError::FileNotFound)); + // fix use io::Error + return Err(std::io::Error::new(ErrorKind::NotFound, "is dir").into()); } let meta = f.metadata().await.map_err(os_err_to_file_err)?; if meta.is_dir() { - return Err(Error::new(DiskError::FileNotFound)); + return Err(std::io::Error::new(ErrorKind::NotFound, "is dir").into()); } let size = meta.len() as usize; @@ -743,12 +764,10 @@ impl LocalDisk { .await .map_err(os_err_to_file_err)?; - // let mut data = Vec::new(); - // let n = file.read_to_end(&mut data).await?; - let meta = file.metadata().await?; + let file_size = meta.len() as usize; - bitrot_verify(Box::new(file), meta.size() as usize, part_size, algo, sum.to_vec(), shard_size).await + bitrot_verify(Box::new(file), file_size, part_size, algo, sum.to_vec(), shard_size).await } async fn scan_dir( @@ -816,8 +835,6 @@ impl LocalDisk { // 第一层过滤 for item in entries.iter_mut() { - // warn!("walk_dir get entry {:?}", &entry); - let entry = item.clone(); // check limit if opts.limit > 0 && *objs_returned >= opts.limit { @@ -857,7 +874,10 @@ impl LocalDisk { let metadata = self .read_metadata(self.get_object_path(bucket, format!("{}/{}", ¤t, &entry).as_str())?) .await?; - let name = entry.trim_end_matches(STORAGE_FORMAT_FILE).trim_end_matches(SLASH_SEPARATOR); + + // 用strip_suffix只删除一次 + let entry = entry.strip_suffix(STORAGE_FORMAT_FILE).unwrap_or_default().to_owned(); + let name = entry.trim_end_matches(SLASH_SEPARATOR); let name = decode_dir_object(format!("{}/{}", ¤t, &name).as_str()); out.write_obj(&MetaCacheEntry { @@ -887,7 +907,6 @@ impl LocalDisk { let mut dir_stack: Vec = Vec::with_capacity(5); for entry in entries.iter() { - // if opts.limit > 0 && *objs_returned >= opts.limit { return Ok(()); } @@ -1217,7 +1236,7 @@ impl DiskAPI for LocalDisk { .join(path) .join(fi.data_dir.map_or("".to_string(), |dir| dir.to_string())) .join(format!("part.{}", part.number)); - let err = match self + let err = (self .bitrot_verify( &part_path, erasure.shard_file_size(part.size), @@ -1225,11 +1244,8 @@ impl DiskAPI for LocalDisk { &checksum_info.hash, erasure.shard_size(erasure.block_size), ) - .await - { - Ok(_) => None, - Err(err) => Some(err), - }; + .await) + .err(); resp.results[i] = conv_part_err_to_int(&err); if resp.results[i] == CHECK_PART_UNKNOWN { if let Some(err) = err { @@ -2283,6 +2299,7 @@ impl DiskAPI for LocalDisk { Ok(info) } + #[tracing::instrument(level = "info", skip_all)] async fn ns_scanner( &self, cache: &DataUsageCache, @@ -2296,18 +2313,30 @@ impl DiskAPI for LocalDisk { // must befor metadata_sys let Some(store) = new_object_layer_fn() else { return Err(Error::msg("errServerNotInitialized")) }; + let mut cache = cache.clone(); + // Check if the current bucket has a configured lifecycle policy + if let Ok((lc, _)) = metadata_sys::get_lifecycle_config(&cache.info.name).await { + if lc_has_active_rules(&lc, "") { + cache.info.life_cycle = Some(lc); + } + } + // Check if the current bucket has replication configuration if let Ok((rcfg, _)) = metadata_sys::get_replication_config(&cache.info.name).await { - if has_active_rules(&rcfg, "", true) { + if rep_has_active_rules(&rcfg, "", true) { // TODO: globalBucketTargetSys } } + let vcfg = match BucketVersioningSys::get(&cache.info.name).await { + Ok(vcfg) => Some(vcfg), + Err(_) => None, + }; + let loc = self.get_disk_location(); let disks = store.get_disks(loc.pool_idx.unwrap(), loc.disk_idx.unwrap()).await?; let disk = Arc::new(LocalDisk::new(&self.endpoint(), false).await?); let disk_clone = disk.clone(); - let mut cache = cache.clone(); cache.info.updates = Some(updates.clone()); let mut data_usage_info = scan_data_folder( &disks, @@ -2316,8 +2345,9 @@ impl DiskAPI for LocalDisk { Box::new(move |item: &ScannerItem| { let mut item = item.clone(); let disk = disk_clone.clone(); + let vcfg = vcfg.clone(); Box::pin(async move { - if item.path.ends_with(&format!("{}{}", SLASH_SEPARATOR, STORAGE_FORMAT_FILE)) { + if !item.path.ends_with(&format!("{}{}", SLASH_SEPARATOR, STORAGE_FORMAT_FILE)) { return Err(Error::from_string(ERR_SKIP_FILE)); } let stop_fn = ScannerMetrics::log(ScannerMetric::ScanObject); @@ -2358,7 +2388,12 @@ impl DiskAPI for LocalDisk { } }; - let versioned = false; + let versioned = if let Some(vcfg) = vcfg.as_ref() { + vcfg.versioned(item.object_path().to_str().unwrap_or_default()) + } else { + false + }; + let mut obj_deleted = false; for info in obj_infos.iter() { let done = ScannerMetrics::time(ScannerMetric::ApplyVersion); diff --git a/ecstore/src/endpoints.rs b/ecstore/src/endpoints.rs index 51a9c0b82..ea44edf42 100644 --- a/ecstore/src/endpoints.rs +++ b/ecstore/src/endpoints.rs @@ -1,4 +1,4 @@ -use tracing::warn; +use tracing::{instrument, warn}; use crate::{ disk::endpoint::{Endpoint, EndpointType}, @@ -407,7 +407,7 @@ pub struct PoolEndpoints { pub platform: String, } -/// list of list of endpoints +/// list of endpoints #[derive(Debug, Clone, Default)] pub struct EndpointServerPools(pub Vec); @@ -532,6 +532,8 @@ impl EndpointServerPools { nodes } + + #[instrument] pub fn hosts_sorted(&self) -> Vec> { let (mut peers, local) = self.peers(); @@ -604,7 +606,6 @@ impl EndpointServerPools { #[cfg(test)] mod test { - use super::*; use std::path::Path; diff --git a/ecstore/src/global.rs b/ecstore/src/global.rs index 8735249c9..f6fc4bdb8 100644 --- a/ecstore/src/global.rs +++ b/ecstore/src/global.rs @@ -61,7 +61,7 @@ pub fn get_global_deployment_id() -> Option { pub fn set_global_endpoints(eps: Vec) { GLOBAL_Endpoints .set(EndpointServerPools::from(eps)) - .expect("GLOBAL_Endpoints set faild") + .expect("GLOBAL_Endpoints set failed") } pub fn get_global_endpoints() -> EndpointServerPools { diff --git a/ecstore/src/heal/data_scanner.rs b/ecstore/src/heal/data_scanner.rs index 52ded12a2..315b21fef 100644 --- a/ecstore/src/heal/data_scanner.rs +++ b/ecstore/src/heal/data_scanner.rs @@ -18,7 +18,10 @@ use super::{ data_usage_cache::{DataUsageCache, DataUsageEntry, DataUsageHash}, heal_commands::{HealScanMode, HEAL_DEEP_SCAN, HEAL_NORMAL_SCAN}, }; -use crate::heal::data_usage::DATA_USAGE_ROOT; +use crate::{ + bucket::{versioning::VersioningApi, versioning_sys::BucketVersioningSys}, + heal::data_usage::DATA_USAGE_ROOT, +}; use crate::{ cache_value::metacache_set::{list_path_raw, ListPathRawOptions}, config::{ @@ -49,7 +52,7 @@ use common::error::{Error, Result}; use lazy_static::lazy_static; use rand::Rng; use rmp_serde::{Deserializer, Serializer}; -use s3s::dto::{ReplicationConfiguration, ReplicationRuleStatus}; +use s3s::dto::{BucketLifecycleConfiguration, ExpirationStatus, LifecycleRule, ReplicationConfiguration, ReplicationRuleStatus}; use serde::{Deserialize, Serialize}; use tokio::{ sync::{ @@ -352,7 +355,7 @@ impl CurrentScannerCycle { let str_len = rmp::decode::read_str_len(&mut cur)?; - // !!! Vec::with_capacity(str_len) 失败,vec!正常 + // !!!Vec::with_capacity(str_len) 失败,vec! 正常 let mut field_buff = vec![0u8; str_len as usize]; cur.read_exact(&mut field_buff)?; @@ -412,7 +415,7 @@ pub struct ScannerItem { pub prefix: String, pub object_name: String, pub replication: Option, - // todo: lifecycle + pub lifecycle: Option, // typ: fs::Permissions, pub heal: Heal, pub debug: bool, @@ -435,7 +438,7 @@ impl ScannerItem { pub async fn apply_versions_actions(&self, fivs: &[FileInfo]) -> Result> { let obj_infos = self.apply_newer_noncurrent_version_limit(fivs).await?; - if obj_infos.len() >= SCANNER_EXCESS_OBJECT_VERSIONS.load(Ordering::SeqCst).try_into().unwrap() { + if obj_infos.len() >= SCANNER_EXCESS_OBJECT_VERSIONS.load(Ordering::SeqCst) as usize { // todo } @@ -444,12 +447,7 @@ impl ScannerItem { cumulative_size += obj_info.size; } - if cumulative_size - >= SCANNER_EXCESS_OBJECT_VERSIONS_TOTAL_SIZE - .load(Ordering::SeqCst) - .try_into() - .unwrap() - { + if cumulative_size >= SCANNER_EXCESS_OBJECT_VERSIONS_TOTAL_SIZE.load(Ordering::SeqCst) as usize { //todo } @@ -457,22 +455,31 @@ impl ScannerItem { } pub async fn apply_newer_noncurrent_version_limit(&self, fivs: &[FileInfo]) -> Result> { - let done = ScannerMetrics::time(ScannerMetric::ApplyNonCurrent); - let mut object_infos = Vec::new(); - for info in fivs.iter() { - object_infos.push(info.to_object_info(&self.bucket, &self.object_path().to_string_lossy(), false)); + // let done = ScannerMetrics::time(ScannerMetric::ApplyNonCurrent); + let versioned = match BucketVersioningSys::get(&self.bucket).await { + Ok(vcfg) => vcfg.versioned(self.object_path().to_str().unwrap_or_default()), + Err(_) => false, + }; + let mut object_infos = Vec::with_capacity(fivs.len()); + + if self.lifecycle.is_none() { + for info in fivs.iter() { + object_infos.push(info.to_object_info(&self.bucket, &self.object_path().to_string_lossy(), versioned)); + } + return Ok(object_infos); } - done().await; + + // done().await; Ok(object_infos) } - pub async fn apply_actions(&self, _oi: &ObjectInfo, _size_s: &SizeSummary) -> (bool, usize) { + pub async fn apply_actions(&self, oi: &ObjectInfo, _size_s: &SizeSummary) -> (bool, usize) { let done = ScannerMetrics::time(ScannerMetric::Ilm); //todo: lifecycle done().await; - (false, 0) + (false, oi.size) } } @@ -551,6 +558,7 @@ impl FolderScanner { true } + #[tracing::instrument(level = "info", skip_all)] async fn scan_folder(&mut self, folder: &CachedFolder, into: &mut DataUsageEntry) -> Result<()> { let this_hash = hash_path(&folder.name); let was_compacted = into.compacted; @@ -564,8 +572,18 @@ impl FolderScanner { let (_, prefix) = path_to_bucket_object_with_base_path(&self.root, &folder.name); // Todo: lifeCycle + let active_life_cycle = if let Some(lc) = self.old_cache.info.life_cycle.as_ref() { + if lc_has_active_rules(lc, &prefix) { + self.old_cache.info.life_cycle.clone() + } else { + None + } + } else { + None + }; + let replication_cfg = if self.old_cache.info.replication.is_some() - && has_active_rules(self.old_cache.info.replication.as_ref().unwrap(), &prefix, true) + && rep_has_active_rules(self.old_cache.info.replication.as_ref().unwrap(), &prefix, true) { self.old_cache.info.replication.clone() } else { @@ -596,7 +614,7 @@ impl FolderScanner { continue; } - if !sub_path.is_dir() { + if sub_path.is_dir() { let h = hash_path(ent_name.to_str().unwrap()); if h == this_hash { continue; @@ -641,6 +659,7 @@ impl FolderScanner { .unwrap_or_default(), debug: self.data_usage_scanner_debug, replication: replication_cfg.clone(), + lifecycle: active_life_cycle.clone(), heal: Heal::default(), }; @@ -684,16 +703,11 @@ impl FolderScanner { } let should_compact = self.new_cache.info.name != folder.name - && existing_folders.len() + new_folders.len() >= DATA_SCANNER_COMPACT_AT_FOLDERS.try_into().unwrap() - || existing_folders.len() + new_folders.len() >= DATA_SCANNER_FORCE_COMPACT_AT_FOLDERS.try_into().unwrap(); + && (existing_folders.len() + new_folders.len() >= DATA_SCANNER_COMPACT_AT_FOLDERS as usize + || existing_folders.len() + new_folders.len() >= DATA_SCANNER_FORCE_COMPACT_AT_FOLDERS as usize); let total_folders = existing_folders.len() + new_folders.len(); - if total_folders - > SCANNER_EXCESS_FOLDERS - .load(std::sync::atomic::Ordering::SeqCst) - .try_into() - .unwrap() - { + if total_folders > SCANNER_EXCESS_FOLDERS.load(Ordering::SeqCst) as usize { let _prefix_name = format!("{}/", folder.name.trim_end_matches('/')); // todo: notification } @@ -941,7 +955,7 @@ impl FolderScanner { let _ = list_path_raw(rx, lopts).await; if *found_objs.read().await { - let this = CachedFolder { + let this: CachedFolder = CachedFolder { name: k.clone(), parent: this_hash.clone(), object_heal_prob_div: 1, @@ -957,7 +971,7 @@ impl FolderScanner { if !into.compacted && self.new_cache.info.name != folder.name { let mut flat = self.new_cache.size_recursive(&this_hash.key()).unwrap_or_default(); flat.compacted = true; - let compact = if flat.objects < DATA_SCANNER_COMPACT_LEAST_OBJECT.try_into().unwrap() { + let compact = if flat.objects < DATA_SCANNER_COMPACT_LEAST_OBJECT as usize { true } else { // Compact if we only have objects as children... @@ -1002,6 +1016,7 @@ impl FolderScanner { Ok(()) } + #[tracing::instrument(level = "info", skip_all)] async fn send_update(&mut self) { if SystemTime::now().duration_since(self.last_update).unwrap() < Duration::from_secs(60) { return; @@ -1013,8 +1028,9 @@ impl FolderScanner { } } +#[tracing::instrument(level = "info", skip(into, folder_scanner))] async fn scan(folder: &CachedFolder, into: &mut DataUsageEntry, folder_scanner: &mut FolderScanner) { - let mut dst = if !into.compacted { + let mut dst = if into.compacted { DataUsageEntry::default() } else { into.clone() @@ -1034,7 +1050,74 @@ async fn scan(folder: &CachedFolder, into: &mut DataUsageEntry, folder_scanner: } } -pub fn has_active_rules(config: &ReplicationConfiguration, prefix: &str, recursive: bool) -> bool { +fn lc_get_prefix(rule: &LifecycleRule) -> String { + if let Some(p) = &rule.prefix { + return p.to_string(); + } else if let Some(filter) = &rule.filter { + if let Some(p) = &filter.prefix { + return p.to_string(); + } else if let Some(and) = &filter.and { + if let Some(p) = &and.prefix { + return p.to_string(); + } + } + } + + "".into() +} + +pub fn lc_has_active_rules(config: &BucketLifecycleConfiguration, prefix: &str) -> bool { + if config.rules.is_empty() { + return false; + } + + for rule in config.rules.iter() { + if rule.status == ExpirationStatus::from_static(ExpirationStatus::DISABLED) { + continue; + } + let rule_prefix = lc_get_prefix(rule); + if !prefix.is_empty() && !rule_prefix.is_empty() { + if !prefix.starts_with(&rule_prefix) && !rule_prefix.starts_with(prefix) { + continue; + } + } + + if let Some(e) = &rule.noncurrent_version_expiration { + if let Some(true) = e.noncurrent_days.map(|d| d > 0) { + return true; + } + if let Some(true) = e.newer_noncurrent_versions.map(|d| d > 0) { + return true; + } + } + + if rule.noncurrent_version_transitions.is_some() { + return true; + } + if let Some(true) = rule.expiration.as_ref().map(|e| e.date.is_some()) { + return true; + } + + if let Some(true) = rule.expiration.as_ref().map(|e| e.days.is_some()) { + return true; + } + + if let Some(Some(true)) = rule.expiration.as_ref().map(|e| e.expired_object_delete_marker.map(|m| m)) { + return true; + } + + if let Some(true) = rule.transitions.as_ref().map(|t| !t.is_empty()) { + return true; + } + + if rule.transitions.is_some() { + return true; + } + } + false +} + +pub fn rep_has_active_rules(config: &ReplicationConfiguration, prefix: &str, recursive: bool) -> bool { if config.rules.is_empty() { return false; } @@ -1092,8 +1175,14 @@ pub async fn scan_data_folder( root: base_path, get_size: get_size_fn, old_cache: cache.clone(), - new_cache: DataUsageCache::default(), - update_cache: DataUsageCache::default(), + new_cache: DataUsageCache { + info: cache.info.clone(), + ..Default::default() + }, + update_cache: DataUsageCache { + info: cache.info.clone(), + ..Default::default() + }, data_usage_scanner_debug: false, heal_object_select: 0, scan_mode: heal_scan_mode, @@ -1121,8 +1210,7 @@ pub async fn scan_data_folder( if s.scan_folder(&folder, &mut root).await.is_err() { close_disk().await; } - s.new_cache - .force_compact(DATA_SCANNER_COMPACT_AT_CHILDREN.try_into().unwrap()); + s.new_cache.force_compact(DATA_SCANNER_COMPACT_AT_CHILDREN as usize); s.new_cache.info.last_update = Some(SystemTime::now()); s.new_cache.info.next_cycle = cache.info.next_cycle; close_disk().await; diff --git a/ecstore/src/heal/data_usage_cache.rs b/ecstore/src/heal/data_usage_cache.rs index 7bc33df81..de5b73734 100644 --- a/ecstore/src/heal/data_usage_cache.rs +++ b/ecstore/src/heal/data_usage_cache.rs @@ -10,7 +10,7 @@ use http::HeaderMap; use path_clean::PathClean; use rand::Rng; use rmp_serde::Serializer; -use s3s::dto::ReplicationConfiguration; +use s3s::dto::{BucketLifecycleConfiguration, ReplicationConfiguration}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::hash::{DefaultHasher, Hash, Hasher}; @@ -347,7 +347,8 @@ pub struct DataUsageCacheInfo { pub next_cycle: u32, pub last_update: Option, pub skip_healing: bool, - // todo: life_cycle + #[serde(skip)] + pub life_cycle: Option, // pub life_cycle: #[serde(skip)] pub updates: Option>, @@ -380,7 +381,7 @@ impl DataUsageCache { let mut retries = 0; while retries < 5 { let path = Path::new(BUCKET_META_PREFIX).join(name); - warn!("Loading data usage cache from backend: {}", path.display()); + // warn!("Loading data usage cache from backend: {}", path.display()); match store .get_object_reader( RUSTFS_META_BUCKET, @@ -588,7 +589,7 @@ impl DataUsageCache { Some(e) => e, None => return, }; - if top_e.children.len() > DATA_SCANNER_FORCE_COMPACT_AT_FOLDERS.try_into().unwrap() { + if top_e.children.len() > >::try_into(DATA_SCANNER_FORCE_COMPACT_AT_FOLDERS).unwrap() { self.reduce_children_of(&hash_path(&self.info.name), limit, true); } if self.cache.len() <= limit { diff --git a/ecstore/src/heal/heal_ops.rs b/ecstore/src/heal/heal_ops.rs index d5c4544f3..6f4614578 100644 --- a/ecstore/src/heal/heal_ops.rs +++ b/ecstore/src/heal/heal_ops.rs @@ -406,10 +406,7 @@ impl HealSequence { async fn traverse_and_heal(h: Arc) { let buckets_only = false; - let result = match Self::heal_items(h.clone(), buckets_only).await { - Ok(_) => None, - Err(err) => Some(err), - }; + let result = (Self::heal_items(h.clone(), buckets_only).await).err(); let _ = h.traverse_and_heal_done_tx.read().await.send(result).await; } diff --git a/ecstore/src/io.rs b/ecstore/src/io.rs index 764c8834d..3ca27fe0d 100644 --- a/ecstore/src/io.rs +++ b/ecstore/src/io.rs @@ -140,10 +140,14 @@ impl EtagReader { impl AsyncRead for EtagReader { fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll> { + let befor_size = buf.filled().len(); + match Pin::new(&mut self.inner).poll_read(cx, buf) { Poll::Ready(Ok(())) => { - let bytes = buf.filled(); - self.md5.update(bytes); + if buf.filled().len() > befor_size { + let bytes = &buf.filled()[befor_size..]; + self.md5.update(bytes); + } Poll::Ready(Ok(())) } diff --git a/ecstore/src/peer.rs b/ecstore/src/peer.rs index 5328cbc9b..f352121f9 100644 --- a/ecstore/src/peer.rs +++ b/ecstore/src/peer.rs @@ -80,12 +80,7 @@ impl S3PeerSys { let mut futures = Vec::with_capacity(self.clients.len()); for client in self.clients.iter() { // client_clon - futures.push(async move { - match client.get_bucket_info(bucket, &BucketOptions::default()).await { - Ok(_) => None, - Err(err) => Some(err), - } - }); + futures.push(async move { (client.get_bucket_info(bucket, &BucketOptions::default()).await).err() }); } let errs = join_all(futures).await; diff --git a/ecstore/src/set_disk.rs b/ecstore/src/set_disk.rs index e37b8b35c..0148935e8 100644 --- a/ecstore/src/set_disk.rs +++ b/ecstore/src/set_disk.rs @@ -1235,7 +1235,7 @@ impl SetDisks { let shallow_versions: Vec> = metadata_shallow_versions.iter().flatten().cloned().collect(); - let read_quorum = (fileinfos.len() + 1) / 2; + let read_quorum = fileinfos.len().div_ceil(2); let versions = merge_file_meta_versions(read_quorum, false, 1, &shallow_versions); let meta = FileMeta { versions, @@ -3020,7 +3020,7 @@ impl SetDisks { }); // Calc usage let before = cache.info.last_update; - let cache = match disk.clone().ns_scanner(&cache, tx, heal_scan_mode, None).await { + let mut cache = match disk.clone().ns_scanner(&cache, tx, heal_scan_mode, None).await { Ok(cache) => cache, Err(_) => { if cache.info.last_update > before { @@ -3030,6 +3030,9 @@ impl SetDisks { continue; } }; + + cache.info.updates = None; + let _ = task.await; let mut root = DataUsageEntry::default(); if let Some(r) = cache.root() { root = cache.flatten(&r); @@ -3046,7 +3049,6 @@ impl SetDisks { entry: root, }) .await; - let _ = task.await; let _ = cache.save(&cache_name.to_string_lossy()).await; } } @@ -5090,7 +5092,7 @@ fn is_object_dang_ling( }); if !valid_meta.is_valid() { - let data_blocks = (meta_arr.len() + 1) / 2; + let data_blocks = meta_arr.len().div_ceil(2); if not_found_parts_errs > data_blocks { return Ok(valid_meta); } @@ -5103,7 +5105,7 @@ fn is_object_dang_ling( } if valid_meta.deleted { - let data_blocks = (errs.len() + 1) / 2; + let data_blocks = errs.len().div_ceil(2); if not_found_meta_errs > data_blocks { return Ok(valid_meta); } @@ -5318,7 +5320,7 @@ async fn disks_with_all_parts( if let Some(data) = &meta.data { let checksum_info = meta.erasure.get_checksum_info(meta.parts[0].number); let data_len = data.len(); - let verify_err = match bitrot_verify( + let verify_err = (bitrot_verify( Box::new(Cursor::new(data.clone())), data_len, meta.erasure.shard_file_size(meta.size), @@ -5326,11 +5328,8 @@ async fn disks_with_all_parts( checksum_info.hash, meta.erasure.shard_size(meta.erasure.block_size), ) - .await - { - Ok(_) => None, - Err(err) => Some(err), - }; + .await) + .err(); if let Some(vec) = data_errs_by_part.get_mut(&0) { if index < vec.len() { diff --git a/ecstore/src/store.rs b/ecstore/src/store.rs index 84c6da013..793e31a26 100644 --- a/ecstore/src/store.rs +++ b/ecstore/src/store.rs @@ -608,7 +608,7 @@ impl ECStore { let mut ress = Vec::new(); - // join_all结果跟输入顺序一致 + // join_all 结果跟输入顺序一致 for (i, res) in results.into_iter().enumerate() { let index = i; @@ -740,7 +740,7 @@ impl ECStore { let cancel_clone = cancel.clone(); let all_buckets_clone = all_buckets.clone(); futures.push(async move { - let (tx, mut rx) = mpsc::channel(100); + let (tx, mut rx) = mpsc::channel(1); let task = tokio::spawn(async move { loop { match rx.recv().await { @@ -951,6 +951,7 @@ impl ECStore { } } +#[tracing::instrument(level = "info", skip(all_buckets, updates))] async fn update_scan( all_merged: Arc>, results: Arc>>, @@ -972,7 +973,7 @@ async fn update_scan( } w.merge(info); } - if w.info.last_update > *last_update && w.root().is_none() { + if (last_update.is_none() || w.info.last_update > *last_update) && w.root().is_some() { let _ = updates.send(w.dui(&w.info.name, &all_buckets)).await; *last_update = w.info.last_update; } @@ -1010,7 +1011,7 @@ pub async fn all_local_disk() -> Vec { .collect() } -// init_local_disks 初始化本地磁盘,server启动前必须初始化成功 +// init_local_disks 初始化本地磁盘,server 启动前必须初始化成功 pub async fn init_local_disks(endpoint_pools: EndpointServerPools) -> Result<()> { let opt = &DiskOption { cleanup: true, @@ -1279,7 +1280,7 @@ impl StorageAPI for ECStore { // TODO: replication opts.srdelete_op - // 删除meta + // 删除 meta self.delete_all(RUSTFS_META_BUCKET, format!("{}/{}", BUCKET_META_PREFIX, bucket).as_str()) .await?; Ok(()) @@ -1483,7 +1484,7 @@ impl StorageAPI for ECStore { // results.push(jh.await.unwrap()); // } - // 记录pool Index 对应的objects pool_idx -> objects idx + // 记录 pool Index 对应的 objects pool_idx -> objects idx let mut pool_obj_idx_map = HashMap::new(); let mut orig_index_map = HashMap::new(); @@ -1533,9 +1534,9 @@ impl StorageAPI for ECStore { if !pool_obj_idx_map.is_empty() { for (i, sets) in self.pools.iter().enumerate() { - // 取pool idx 对应的 objects index + // 取 pool idx 对应的 objects index if let Some(objs) = pool_obj_idx_map.get(&i) { - // 取对应obj,理论上不会none + // 取对应 obj,理论上不会 none // let objs: Vec = obj_idxs.iter().filter_map(|&idx| objects.get(idx).cloned()).collect(); if objs.is_empty() { @@ -1544,10 +1545,10 @@ impl StorageAPI for ECStore { let (pdel_objs, perrs) = sets.delete_objects(bucket, objs.clone(), opts.clone()).await?; - // 同时存入不可能为none + // 同时存入不可能为 none let org_indexes = orig_index_map.get(&i).unwrap(); - // perrs的顺序理论上跟obj_idxs顺序一致 + // perrs 的顺序理论上跟 obj_idxs 顺序一致 for (i, err) in perrs.into_iter().enumerate() { let obj_idx = org_indexes[i]; @@ -1580,7 +1581,7 @@ impl StorageAPI for ECStore { let object = utils::path::encode_dir_object(object); let object = object.as_str(); - // 查询在哪个pool + // 查询在哪个 pool let (mut pinfo, errs) = self .get_pool_info_existing_with_opts(bucket, object, &opts) .await diff --git a/ecstore/src/store_list_objects.rs b/ecstore/src/store_list_objects.rs index 4ebd42e4f..8b9118de3 100644 --- a/ecstore/src/store_list_objects.rs +++ b/ecstore/src/store_list_objects.rs @@ -25,7 +25,7 @@ use std::io::ErrorKind; use std::sync::Arc; use tokio::sync::broadcast::{self, Receiver as B_Receiver}; use tokio::sync::mpsc::{self, Receiver, Sender}; -use tracing::{error, warn}; +use tracing::error; use uuid::Uuid; const MAX_OBJECT_LIST: i32 = 1000; @@ -248,7 +248,7 @@ impl ECStore { ..Default::default() }; - warn!("list_objects_generic opts {:?}", &opts); + // warn!("list_objects_generic opts {:?}", &opts); // use get if !opts.prefix.is_empty() && opts.limit == 1 && opts.marker.is_none() { @@ -698,7 +698,7 @@ impl ECStore { futures.push(async move { let mut ask_disks = get_list_quorum(&opts.ask_disks, set.set_drive_count as i32); if ask_disks == -1 { - let new_disks = get_quorum_disks(&disks, &infos, (disks.len() + 1) / 2); + let new_disks = get_quorum_disks(&disks, &infos, disks.len().div_ceil(2)); if !new_disks.is_empty() { disks = new_disks; } else { @@ -1156,13 +1156,7 @@ async fn merge_entry_channels( if let Some(entry) = &best { let mut versions = Vec::with_capacity(to_merge.len() + 1); - let mut has_xl = { - if let Ok(meta) = entry.clone().xl_meta() { - Some(meta) - } else { - None - } - }; + let mut has_xl = { entry.clone().xl_meta().ok() }; if let Some(x) = &has_xl { versions.push(x.versions.clone()); @@ -1229,7 +1223,7 @@ impl SetDisks { let mut ask_disks = get_list_quorum(&opts.ask_disks, self.set_drive_count as i32); if ask_disks == -1 { - let new_disks = get_quorum_disks(&disks, &infos, (disks.len() + 1) / 2); + let new_disks = get_quorum_disks(&disks, &infos, disks.len().div_ceil(2)); if !new_disks.is_empty() { disks = new_disks; ask_disks = 1; diff --git a/ecstore/src/utils/os/linux.rs b/ecstore/src/utils/os/linux.rs index ca64f8186..064b74ae5 100644 --- a/ecstore/src/utils/os/linux.rs +++ b/ecstore/src/utils/os/linux.rs @@ -151,7 +151,7 @@ fn read_drive_stats(stats_file: &str) -> Result { fn read_stat(file_name: &str) -> Result> { // 打开文件 let path = Path::new(file_name); - let file = File::open(&path)?; + let file = File::open(path)?; // 创建一个 BufReader let reader = io::BufReader::new(file); @@ -161,7 +161,8 @@ fn read_stat(file_name: &str) -> Result> { if let Some(line) = reader.lines().next() { let line = line?; // 分割行并解析为 u64 - for token in line.trim().split_whitespace() { + // https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace + for token in line.split_whitespace() { let ui64: u64 = token.parse()?; stats.push(ui64); } diff --git a/ecstore/src/utils/os/windows.rs b/ecstore/src/utils/os/windows.rs index 432e90e66..a99d7001c 100644 --- a/ecstore/src/utils/os/windows.rs +++ b/ecstore/src/utils/os/windows.rs @@ -1,7 +1,8 @@ #![allow(unsafe_code)] // TODO: audit unsafe code use super::IOStats; -use crate::{disk::Info, error::Result}; +use crate::disk::Info; +use common::error::Result; use std::io::{Error, ErrorKind}; use std::mem; use std::os::windows::ffi::OsStrExt; diff --git a/iam/Cargo.toml b/iam/Cargo.toml index 5afc93c54..982df57e1 100644 --- a/iam/Cargo.toml +++ b/iam/Cargo.toml @@ -11,7 +11,6 @@ workspace = true [dependencies] tokio.workspace = true -log.workspace = true time = { workspace = true, features = ["serde-human-readable"] } serde = { workspace = true, features = ["derive", "rc"] } ecstore = { path = "../ecstore" } @@ -27,7 +26,7 @@ itertools = "0.14.0" futures.workspace = true rand.workspace = true base64-simd = "0.8.0" -jsonwebtoken = "9.3.0" +jsonwebtoken = { workspace = true } tracing.workspace = true madmin.workspace = true lazy_static.workspace = true diff --git a/iam/src/cache.rs b/iam/src/cache.rs index f16921162..b5dd20c3b 100644 --- a/iam/src/cache.rs +++ b/iam/src/cache.rs @@ -6,12 +6,12 @@ use std::{ }; use arc_swap::{ArcSwap, AsRaw, Guard}; -use log::warn; use policy::{ auth::UserIdentity, policy::{Args, PolicyDoc}, }; use time::OffsetDateTime; +use tracing::warn; use crate::store::{GroupInfo, MappedPolicy}; @@ -63,7 +63,7 @@ impl Cache { let mut new = CacheEntity::clone(&cur); op(&mut new); - // 使用cas原子替换内容 + // 使用 cas 原子替换内容 let prev = target.compare_and_swap(&*cur, Arc::new(new)); let swapped = Self::ptr_eq(&*cur, &*prev); if swapped { @@ -112,8 +112,8 @@ impl CacheInner { // todo!() // } - // /// 如果是临时用户,返回Ok(Some(partent_name))) - // /// 如果不是临时用户,返回Ok(None) + // /// 如果是临时用户,返回 Ok(Some(partent_name))) + // /// 如果不是临时用户,返回 Ok(None) // fn is_temp_user(&self, user_name: &str) -> crate::Result> { // let user = self // .get_user(user_name) @@ -126,8 +126,8 @@ impl CacheInner { // } // } - // /// 如果是临时用户,返回Ok(Some(partent_name))) - // /// 如果不是临时用户,返回Ok(None) + // /// 如果是临时用户,返回 Ok(Some(partent_name))) + // /// 如果不是临时用户,返回 Ok(None) // fn is_service_account(&self, user_name: &str) -> crate::Result> { // let user = self // .get_user(user_name) diff --git a/iam/src/lib.rs b/iam/src/lib.rs index 88392bb98..a37c0d916 100644 --- a/iam/src/lib.rs +++ b/iam/src/lib.rs @@ -1,12 +1,12 @@ use common::error::{Error, Result}; use ecstore::store::ECStore; use error::Error as IamError; -use log::debug; use manager::IamCache; use policy::auth::Credentials; use std::sync::{Arc, OnceLock}; use store::object::ObjectStore; use sys::IamSys; +use tracing::{debug, instrument}; pub mod cache; pub mod error; @@ -51,6 +51,7 @@ pub fn get_global_action_cred() -> Option { GLOBAL_ACTIVE_CRED.get().cloned() } +#[instrument(skip(ecstore))] pub async fn init_iam_sys(ecstore: Arc) -> Result<()> { debug!("init iam system"); let s = IamCache::new(ObjectStore::new(ecstore)).await; diff --git a/iam/src/manager.rs b/iam/src/manager.rs index f4fc076c2..2eb8a6f0e 100644 --- a/iam/src/manager.rs +++ b/iam/src/manager.rs @@ -11,7 +11,6 @@ use crate::{ use common::error::{Error, Result}; use ecstore::config::error::is_err_config_not_found; use ecstore::utils::{crypto::base64_encode, path::path_join_buf}; -use log::{debug, warn}; use madmin::{AccountStatus, AddOrUpdateUserReq, GroupDesc}; use policy::{ arn::ARN, @@ -40,6 +39,7 @@ use tokio::{ }, }; use tracing::error; +use tracing::warn; const IAM_FORMAT_FILE: &str = "format.json"; const IAM_FORMAT_VERSION_1: i32 = 1; @@ -135,7 +135,7 @@ where } async fn load(self: Arc) -> Result<()> { - debug!("load iam to cache"); + // debug!("load iam to cache"); self.api.load_all(&self.cache).await?; self.last_timestamp .store(OffsetDateTime::now_utc().unix_timestamp(), Ordering::Relaxed); diff --git a/iam/src/store/object.rs b/iam/src/store/object.rs index 20ab01289..94c56fb07 100644 --- a/iam/src/store/object.rs +++ b/iam/src/store/object.rs @@ -24,7 +24,7 @@ use serde::{de::DeserializeOwned, Serialize}; use std::{collections::HashMap, sync::Arc}; use tokio::sync::broadcast::{self, Receiver as B_Receiver}; use tokio::sync::mpsc::{self, Sender}; -use tracing::{debug, info, warn}; +use tracing::{info, warn}; lazy_static! { pub static ref IAM_CONFIG_PREFIX: String = format!("{}/iam", RUSTFS_CONFIG_PREFIX); @@ -133,7 +133,7 @@ impl ObjectStore { } async fn list_iam_config_items(&self, prefix: &str, ctx_rx: B_Receiver, sender: Sender) { - debug!("list iam config items, prefix: {}", &prefix); + // debug!("list iam config items, prefix: {}", &prefix); // todo, 实现walk,使用walk @@ -675,11 +675,11 @@ impl Store for ObjectStore { async fn load_all(&self, cache: &Cache) -> Result<()> { let listed_config_items = self.list_all_iamconfig_items().await?; + let mut policy_docs_cache = CacheEntity::new(get_default_policyes()); + if let Some(policies_list) = listed_config_items.get(POLICIES_LIST_KEY) { let mut policies_list = policies_list.clone(); - let mut policy_docs_cache = CacheEntity::new(get_default_policyes()); - loop { if policies_list.len() < 32 { let policy_docs = self.load_policy_doc_concurrent(&policies_list).await?; @@ -712,10 +712,10 @@ impl Store for ObjectStore { policies_list = policies_list.split_off(32); } - - cache.policy_docs.store(Arc::new(policy_docs_cache.update_load_time())); } + cache.policy_docs.store(Arc::new(policy_docs_cache.update_load_time())); + let mut user_items_cache = CacheEntity::default(); // users diff --git a/iam/src/sys.rs b/iam/src/sys.rs index ee2a26b61..175dae6bc 100644 --- a/iam/src/sys.rs +++ b/iam/src/sys.rs @@ -121,6 +121,18 @@ impl IamSys { // TODO: notification } + pub async fn get_role_policy(&self, arn_str: &str) -> Result<(ARN, String)> { + let Some(arn) = ARN::parse(arn_str).ok() else { + return Err(Error::msg("Invalid ARN")); + }; + + let Some(policy) = self.roles_map.get(&arn) else { + return Err(Error::msg("No such role")); + }; + + Ok((arn, policy.clone())) + } + pub async fn delete_user(&self, name: &str, _notify: bool) -> Result<()> { self.store.delete_user(name, UserType::Reg).await // TODO: notification diff --git a/madmin/Cargo.toml b/madmin/Cargo.toml index 8977a66ad..18bea2bae 100644 --- a/madmin/Cargo.toml +++ b/madmin/Cargo.toml @@ -18,3 +18,4 @@ serde.workspace = true serde_json.workspace = true time.workspace = true tracing.workspace = true +s3s.workspace = true diff --git a/madmin/src/user.rs b/madmin/src/user.rs index 3a398c51f..a13fa1710 100644 --- a/madmin/src/user.rs +++ b/madmin/src/user.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use time::OffsetDateTime; +use crate::BackendInfo; + #[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)] pub enum AccountStatus { #[serde(rename = "enabled")] @@ -221,3 +224,40 @@ impl UpdateServiceAccountReq { Ok(()) } } + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct AccountInfo { + pub account_name: String, + pub server: BackendInfo, + pub policy: serde_json::Value, // Use iam/policy::parse to parse the result, to be done by the caller. + pub buckets: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct BucketAccessInfo { + pub name: String, + pub size: u64, + pub objects: u64, + pub object_sizes_histogram: HashMap, + pub object_versions_histogram: HashMap, + pub details: Option, + pub prefix_usage: HashMap, + #[serde(rename = "expiration", with = "time::serde::rfc3339::option")] + pub created: Option, + pub access: AccountAccess, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct BucketDetails { + pub versioning: bool, + pub versioning_suspended: bool, + pub locking: bool, + pub replication: bool, + // pub tagging: Option, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct AccountAccess { + pub read: bool, + pub write: bool, +} diff --git a/policy/Cargo.toml b/policy/Cargo.toml index da2c4636c..87f11b2ce 100644 --- a/policy/Cargo.toml +++ b/policy/Cargo.toml @@ -11,7 +11,6 @@ workspace = true [dependencies] tokio.workspace = true -log.workspace = true time = { workspace = true, features = ["serde-human-readable"] } serde = { workspace = true, features = ["derive", "rc"] } serde_json.workspace = true @@ -25,7 +24,7 @@ itertools = "0.14.0" futures.workspace = true rand.workspace = true base64-simd = "0.8.0" -jsonwebtoken = "9.3.0" +jsonwebtoken = { workspace = true } tracing.workspace = true madmin.workspace = true lazy_static.workspace = true diff --git a/policy/src/policy/action.rs b/policy/src/policy/action.rs index e152e89b7..b9df63b6c 100644 --- a/policy/src/policy/action.rs +++ b/policy/src/policy/action.rs @@ -54,6 +54,7 @@ pub enum Action { AdminAction(AdminAction), StsAction(StsAction), KmsAction(KmsAction), + None, } impl Action { @@ -69,6 +70,7 @@ impl From<&Action> for &str { Action::AdminAction(s) => s.into(), Action::StsAction(s) => s.into(), Action::KmsAction(s) => s.into(), + Action::None => "", } } } @@ -232,35 +234,254 @@ pub enum S3Action { PutObjectFanOutAction, } +// #[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr, Debug, Copy)] +// #[serde(try_from = "&str", into = "&str")] +// pub enum AdminAction { +// #[strum(serialize = "admin:*")] +// AllActions, +// #[strum(serialize = "admin:Profiling")] +// ProfilingAdminAction, +// #[strum(serialize = "admin:ServerTrace")] +// TraceAdminAction, +// #[strum(serialize = "admin:ConsoleLog")] +// ConsoleLogAdminAction, +// #[strum(serialize = "admin:ServerInfo")] +// ServerInfoAdminAction, +// #[strum(serialize = "admin:OBDInfo")] +// HealthInfoAdminAction, +// #[strum(serialize = "admin:TopLocksInfo")] +// TopLocksAdminAction, +// #[strum(serialize = "admin:LicenseInfo")] +// LicenseInfoAdminAction, +// #[strum(serialize = "admin:BandwidthMonitor")] +// BandwidthMonitorAction, +// #[strum(serialize = "admin:InspectData")] +// InspectDataAction, +// #[strum(serialize = "admin:Prometheus")] +// PrometheusAdminAction, +// #[strum(serialize = "admin:ListServiceAccounts")] +// ListServiceAccountsAdminAction, +// #[strum(serialize = "admin:CreateServiceAccount")] +// CreateServiceAccountAdminAction, +// } + +// AdminAction - admin policy action. #[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr, Debug, Copy)] #[serde(try_from = "&str", into = "&str")] pub enum AdminAction { - #[strum(serialize = "admin:*")] - AllActions, + #[strum(serialize = "admin:Heal")] + HealAdminAction, + #[strum(serialize = "admin:Decommission")] + DecommissionAdminAction, + #[strum(serialize = "admin:Rebalance")] + RebalanceAdminAction, + #[strum(serialize = "admin:StorageInfo")] + StorageInfoAdminAction, + #[strum(serialize = "admin:Prometheus")] + PrometheusAdminAction, + #[strum(serialize = "admin:DataUsageInfo")] + DataUsageInfoAdminAction, + #[strum(serialize = "admin:ForceUnlock")] + ForceUnlockAdminAction, + #[strum(serialize = "admin:TopLocksInfo")] + TopLocksAdminAction, #[strum(serialize = "admin:Profiling")] ProfilingAdminAction, #[strum(serialize = "admin:ServerTrace")] TraceAdminAction, #[strum(serialize = "admin:ConsoleLog")] ConsoleLogAdminAction, + #[strum(serialize = "admin:KMSCreateKey")] + KMSCreateKeyAdminAction, + #[strum(serialize = "admin:KMSKeyStatus")] + KMSKeyStatusAdminAction, #[strum(serialize = "admin:ServerInfo")] ServerInfoAdminAction, #[strum(serialize = "admin:OBDInfo")] HealthInfoAdminAction, - #[strum(serialize = "admin:TopLocksInfo")] - TopLocksAdminAction, #[strum(serialize = "admin:LicenseInfo")] LicenseInfoAdminAction, #[strum(serialize = "admin:BandwidthMonitor")] BandwidthMonitorAction, #[strum(serialize = "admin:InspectData")] InspectDataAction, - #[strum(serialize = "admin:Prometheus")] - PrometheusAdminAction, - #[strum(serialize = "admin:ListServiceAccounts")] - ListServiceAccountsAdminAction, + #[strum(serialize = "admin:ServerUpdate")] + ServerUpdateAdminAction, + #[strum(serialize = "admin:ServiceRestart")] + ServiceRestartAdminAction, + #[strum(serialize = "admin:ServiceStop")] + ServiceStopAdminAction, + #[strum(serialize = "admin:ServiceFreeze")] + ServiceFreezeAdminAction, + #[strum(serialize = "admin:ConfigUpdate")] + ConfigUpdateAdminAction, + #[strum(serialize = "admin:CreateUser")] + CreateUserAdminAction, + #[strum(serialize = "admin:DeleteUser")] + DeleteUserAdminAction, + #[strum(serialize = "admin:ListUsers")] + ListUsersAdminAction, + #[strum(serialize = "admin:EnableUser")] + EnableUserAdminAction, + #[strum(serialize = "admin:DisableUser")] + DisableUserAdminAction, + #[strum(serialize = "admin:GetUser")] + GetUserAdminAction, + #[strum(serialize = "admin:SiteReplicationAdd")] + SiteReplicationAddAction, + #[strum(serialize = "admin:SiteReplicationDisable")] + SiteReplicationDisableAction, + #[strum(serialize = "admin:SiteReplicationRemove")] + SiteReplicationRemoveAction, + #[strum(serialize = "admin:SiteReplicationResync")] + SiteReplicationResyncAction, + #[strum(serialize = "admin:SiteReplicationInfo")] + SiteReplicationInfoAction, + #[strum(serialize = "admin:SiteReplicationOperation")] + SiteReplicationOperationAction, #[strum(serialize = "admin:CreateServiceAccount")] CreateServiceAccountAdminAction, + #[strum(serialize = "admin:UpdateServiceAccount")] + UpdateServiceAccountAdminAction, + #[strum(serialize = "admin:RemoveServiceAccount")] + RemoveServiceAccountAdminAction, + #[strum(serialize = "admin:ListServiceAccounts")] + ListServiceAccountsAdminAction, + #[strum(serialize = "admin:ListTemporaryAccounts")] + ListTemporaryAccountsAdminAction, + #[strum(serialize = "admin:AddUserToGroup")] + AddUserToGroupAdminAction, + #[strum(serialize = "admin:RemoveUserFromGroup")] + RemoveUserFromGroupAdminAction, + #[strum(serialize = "admin:GetGroup")] + GetGroupAdminAction, + #[strum(serialize = "admin:ListGroups")] + ListGroupsAdminAction, + #[strum(serialize = "admin:EnableGroup")] + EnableGroupAdminAction, + #[strum(serialize = "admin:DisableGroup")] + DisableGroupAdminAction, + #[strum(serialize = "admin:CreatePolicy")] + CreatePolicyAdminAction, + #[strum(serialize = "admin:DeletePolicy")] + DeletePolicyAdminAction, + #[strum(serialize = "admin:GetPolicy")] + GetPolicyAdminAction, + #[strum(serialize = "admin:AttachUserOrGroupPolicy")] + AttachPolicyAdminAction, + #[strum(serialize = "admin:UpdatePolicyAssociation")] + UpdatePolicyAssociationAction, + #[strum(serialize = "admin:ListUserPolicies")] + ListUserPoliciesAdminAction, + #[strum(serialize = "admin:SetBucketQuota")] + SetBucketQuotaAdminAction, + #[strum(serialize = "admin:GetBucketQuota")] + GetBucketQuotaAdminAction, + #[strum(serialize = "admin:SetBucketTarget")] + SetBucketTargetAction, + #[strum(serialize = "admin:GetBucketTarget")] + GetBucketTargetAction, + #[strum(serialize = "admin:ReplicationDiff")] + ReplicationDiff, + #[strum(serialize = "admin:ImportBucketMetadata")] + ImportBucketMetadataAction, + #[strum(serialize = "admin:ExportBucketMetadata")] + ExportBucketMetadataAction, + #[strum(serialize = "admin:SetTier")] + SetTierAction, + #[strum(serialize = "admin:ListTier")] + ListTierAction, + #[strum(serialize = "admin:ExportIAM")] + ExportIAMAction, + #[strum(serialize = "admin:ImportIAM")] + ImportIAMAction, + #[strum(serialize = "admin:ListBatchJobs")] + ListBatchJobsAction, + #[strum(serialize = "admin:DescribeBatchJob")] + DescribeBatchJobAction, + #[strum(serialize = "admin:StartBatchJob")] + StartBatchJobAction, + #[strum(serialize = "admin:CancelBatchJob")] + CancelBatchJobAction, + #[strum(serialize = "admin:*")] + AllAdminActions, +} + +impl AdminAction { + // IsValid - checks if action is valid or not. + pub fn is_valid(&self) -> bool { + matches!( + self, + AdminAction::HealAdminAction + | AdminAction::DecommissionAdminAction + | AdminAction::RebalanceAdminAction + | AdminAction::StorageInfoAdminAction + | AdminAction::PrometheusAdminAction + | AdminAction::DataUsageInfoAdminAction + | AdminAction::ForceUnlockAdminAction + | AdminAction::TopLocksAdminAction + | AdminAction::ProfilingAdminAction + | AdminAction::TraceAdminAction + | AdminAction::ConsoleLogAdminAction + | AdminAction::KMSCreateKeyAdminAction + | AdminAction::KMSKeyStatusAdminAction + | AdminAction::ServerInfoAdminAction + | AdminAction::HealthInfoAdminAction + | AdminAction::LicenseInfoAdminAction + | AdminAction::BandwidthMonitorAction + | AdminAction::InspectDataAction + | AdminAction::ServerUpdateAdminAction + | AdminAction::ServiceRestartAdminAction + | AdminAction::ServiceStopAdminAction + | AdminAction::ServiceFreezeAdminAction + | AdminAction::ConfigUpdateAdminAction + | AdminAction::CreateUserAdminAction + | AdminAction::DeleteUserAdminAction + | AdminAction::ListUsersAdminAction + | AdminAction::EnableUserAdminAction + | AdminAction::DisableUserAdminAction + | AdminAction::GetUserAdminAction + | AdminAction::SiteReplicationAddAction + | AdminAction::SiteReplicationDisableAction + | AdminAction::SiteReplicationRemoveAction + | AdminAction::SiteReplicationResyncAction + | AdminAction::SiteReplicationInfoAction + | AdminAction::SiteReplicationOperationAction + | AdminAction::CreateServiceAccountAdminAction + | AdminAction::UpdateServiceAccountAdminAction + | AdminAction::RemoveServiceAccountAdminAction + | AdminAction::ListServiceAccountsAdminAction + | AdminAction::ListTemporaryAccountsAdminAction + | AdminAction::AddUserToGroupAdminAction + | AdminAction::RemoveUserFromGroupAdminAction + | AdminAction::GetGroupAdminAction + | AdminAction::ListGroupsAdminAction + | AdminAction::EnableGroupAdminAction + | AdminAction::DisableGroupAdminAction + | AdminAction::CreatePolicyAdminAction + | AdminAction::DeletePolicyAdminAction + | AdminAction::GetPolicyAdminAction + | AdminAction::AttachPolicyAdminAction + | AdminAction::UpdatePolicyAssociationAction + | AdminAction::ListUserPoliciesAdminAction + | AdminAction::SetBucketQuotaAdminAction + | AdminAction::GetBucketQuotaAdminAction + | AdminAction::SetBucketTargetAction + | AdminAction::GetBucketTargetAction + | AdminAction::ReplicationDiff + | AdminAction::ImportBucketMetadataAction + | AdminAction::ExportBucketMetadataAction + | AdminAction::SetTierAction + | AdminAction::ListTierAction + | AdminAction::ExportIAMAction + | AdminAction::ImportIAMAction + | AdminAction::ListBatchJobsAction + | AdminAction::DescribeBatchJobAction + | AdminAction::StartBatchJobAction + | AdminAction::CancelBatchJobAction + | AdminAction::AllAdminActions + ) + } } #[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr, Debug, Copy)] diff --git a/policy/src/policy/policy.rs b/policy/src/policy/policy.rs index f1de3790c..b96f66e41 100644 --- a/policy/src/policy/policy.rs +++ b/policy/src/policy/policy.rs @@ -240,7 +240,7 @@ fn get_values_from_claims(claims: &HashMap, claim_name: &str) -> (s, false) } -fn get_policies_from_claims(claims: &HashMap, policy_claim_name: &str) -> (HashSet, bool) { +pub fn get_policies_from_claims(claims: &HashMap, policy_claim_name: &str) -> (HashSet, bool) { get_values_from_claims(claims, policy_claim_name) } @@ -401,7 +401,7 @@ pub mod default { effect: Effect::Allow, actions: ActionSet({ let mut hash_set = HashSet::new(); - hash_set.insert(Action::AdminAction(AdminAction::AllActions)); + hash_set.insert(Action::AdminAction(AdminAction::AllAdminActions)); hash_set }), not_actions: ActionSet(Default::default()), diff --git a/rustfs/Cargo.toml b/rustfs/Cargo.toml index f0c8d1185..cf59da3e4 100644 --- a/rustfs/Cargo.toml +++ b/rustfs/Cargo.toml @@ -16,66 +16,70 @@ workspace = true [dependencies] madmin.workspace = true -log.workspace = true +api = { path = "../s3select/api" } +appauth = { version = "0.0.1", path = "../appauth" } +atoi = { workspace = true } +atomic_enum = { workspace = true } +axum.workspace = true +axum-extra = { workspace = true } +axum-server = { workspace = true } async-trait.workspace = true bytes.workspace = true +chrono = { workspace = true } clap.workspace = true +crypto = { path = "../crypto" } +datafusion = { workspace = true } common.workspace = true +const-str = { version = "0.6.1", features = ["std", "proc"] } ecstore.workspace = true -policy.workspace =true +policy.workspace = true flatbuffers.workspace = true futures.workspace = true futures-util.workspace = true -h2 = "0.4.7" hyper.workspace = true hyper-util.workspace = true http.workspace = true http-body.workspace = true +iam = { path = "../iam" } lock.workspace = true +local-ip-address = { workspace = true } +matchit = { workspace = true } mime.workspace = true -netif.workspace = true +mime_guess = "2.0.5" pin-project-lite.workspace = true -prost.workspace = true -prost-types.workspace = true protos.workspace = true -protobuf.workspace = true +query = { path = "../s3select/query" } rmp-serde.workspace = true +rustfs-obs = { workspace = true } +rustls.workspace = true +rustls-pemfile.workspace = true +rustls-pki-types.workspace = true +rust-embed = { workspace = true, features = ["interpolate-folder-path"] } s3s.workspace = true serde.workspace = true serde_json.workspace = true +serde_urlencoded = { workspace = true } +shadow-rs.workspace = true tracing.workspace = true time = { workspace = true, features = ["parsing", "formatting", "serde"] } -tokio-util = { version = "0.7.13", features = ["io", "compat"] } +tokio-util.workspace = true tokio = { workspace = true, features = [ "rt-multi-thread", "macros", "net", "signal", ] } +tokio-rustls.workspace = true lazy_static.workspace = true tokio-stream.workspace = true -tonic = { version = "0.12.3", features = ["gzip"] } -tonic-reflection.workspace = true +tonic = { workspace = true } tower.workspace = true -tracing-error.workspace = true -tracing-subscriber.workspace = true transform-stream.workspace = true -uuid = "1.15.1" -url.workspace = true -axum.workspace = true -matchit = "0.8.6" -shadow-rs.workspace = true -const-str = { version = "0.6.1", features = ["std", "proc"] } -atoi = "2.0.0" -serde_urlencoded = "0.7.1" -crypto = { path = "../crypto" } -iam = { path = "../iam" } -jsonwebtoken = "9.3.0" -tower-http = { version = "0.6.2", features = ["cors"] } -mime_guess = "2.0.5" -rust-embed = { workspace = true, features = ["interpolate-folder-path"] } -local-ip-address = { workspace = true } -chrono = { workspace = true } +tower-http.workspace = true +uuid = { workspace = true } + +[target.'cfg(target_os = "linux")'.dependencies] +libsystemd.workspace = true [build-dependencies] prost-build.workspace = true @@ -87,14 +91,13 @@ futures-util.workspace = true # uuid = { version = "1.8.0", features = ["v4", "fast-rng", "serde"] } ecstore = { path = "../ecstore" } s3s.workspace = true -clap = { version = "4.5.31", features = ["derive", "env"] } -tracing-subscriber = { version = "0.3.19", features = ["env-filter", "time"] } -hyper-util = { version = "0.1.10", features = [ +clap = { workspace = true } +hyper-util = { workspace = true, features = [ "tokio", "server-auto", "server-graceful", ] } -transform-stream = "0.3.1" +transform-stream = { workspace = true } netif = "0.1.6" shadow-rs.workspace = true # pin-utils = "0.1.0" diff --git a/rustfs/README.md b/rustfs/README.md new file mode 100644 index 000000000..86b2ab767 --- /dev/null +++ b/rustfs/README.md @@ -0,0 +1,30 @@ +rustfs/ +├── Cargo.toml +├── src/ +│ ├── main.rs # 主入口 +│ ├── admin/ +│ │ └── mod.rs # 管理接口 +│ ├── auth/ +│ │ └── mod.rs # 认证模块 +│ ├── config/ +│ │ ├── mod.rs # 配置模块 +│ │ └── options.rs # 命令行参数 +│ ├── console/ +│ │ ├── mod.rs # 控制台模块 +│ │ └── server.rs # 控制台服务器 +│ ├── grpc/ +│ │ └── mod.rs # gRPC 服务 +│ ├── license/ +│ │ └── mod.rs # 许可证管理 +│ ├── logging/ +│ │ └── mod.rs # 日志管理 +│ ├── server/ +│ │ ├── mod.rs # 服务器实现 +│ │ ├── connection.rs # 连接处理 +│ │ ├── service.rs # 服务实现 +│ │ └── state.rs # 状态管理 +│ ├── storage/ +│ │ ├── mod.rs # 存储模块 +│ │ └── fs.rs # 文件系统实现 +│ └── utils/ +│ └── mod.rs # 工具函数 \ No newline at end of file diff --git a/rustfs/src/admin/handlers.rs b/rustfs/src/admin/handlers.rs index 886731c87..1152f2ee7 100644 --- a/rustfs/src/admin/handlers.rs +++ b/rustfs/src/admin/handlers.rs @@ -1,11 +1,11 @@ use super::router::Operation; -use ::policy::policy::action::{Action, S3Action}; -use ::policy::policy::resource::Resource; -use ::policy::policy::statement::BPStatement; -use ::policy::policy::{ActionSet, BucketPolicy, Effect, ResourceSet}; +use crate::auth::check_key_valid; +use crate::auth::get_condition_values; +use crate::auth::get_session_token; use bytes::Bytes; use common::error::Error as ec_Error; use ecstore::admin_server_info::get_server_info; +use ecstore::bucket::versioning_sys::BucketVersioningSys; use ecstore::global::GLOBAL_ALlHealState; use ecstore::heal::data_usage::load_data_usage_from_backend; use ecstore::heal::heal_commands::HealOpts; @@ -14,14 +14,22 @@ use ecstore::metrics_realtime::{collect_local_metrics, CollectMetricsOpts, Metri use ecstore::new_object_layer_fn; use ecstore::peer::is_reserved_or_invalid_bucket; use ecstore::store::is_valid_object_prefix; +use ecstore::store_api::BucketOptions; use ecstore::store_api::StorageAPI; use ecstore::utils::path::path_join; use futures::{Stream, StreamExt}; use http::{HeaderMap, Uri}; use hyper::StatusCode; +use iam::get_global_action_cred; +use iam::store::MappedPolicy; use madmin::metrics::RealtimeMetrics; use madmin::utils::parse_duration; use matchit::Params; +use policy::policy::action::Action; +use policy::policy::action::S3Action; +use policy::policy::default::DEFAULT_POLICIES; +use policy::policy::Args; +use policy::policy::BucketPolicy; use s3s::header::CONTENT_TYPE; use s3s::stream::{ByteStream, DynByteStream}; use s3s::{s3_error, Body, S3Error, S3Request, S3Response, S3Result}; @@ -40,7 +48,7 @@ use tokio_stream::wrappers::ReceiverStream; use tracing::{error, info, warn}; pub mod group; -pub mod policy; +pub mod policys; pub mod pools; pub mod service_account; pub mod sts; @@ -59,49 +67,187 @@ pub struct AccountInfoHandler {} #[async_trait::async_trait] impl Operation for AccountInfoHandler { async fn call(&self, req: S3Request, _params: Params<'_, '_>) -> S3Result> { - warn!("handle AccountInfoHandler"); - - let Some(cred) = req.credentials else { return Err(s3_error!(InvalidRequest, "get cred failed")) }; - - warn!("AccountInfoHandler cread {:?}", &cred); - let Some(store) = new_object_layer_fn() else { return Err(S3Error::with_message(S3ErrorCode::InternalError, "Not init".to_string())); }; - // test policy + let Some(input_cred) = req.credentials else { + return Err(s3_error!(InvalidRequest, "get cred failed")); + }; - let mut s3_all_act = HashSet::with_capacity(1); - s3_all_act.insert(Action::S3Action(S3Action::AllActions)); + let (cred, owner) = + check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?; - let mut all_res = HashSet::with_capacity(1); - all_res.insert(Resource::S3("*".to_string())); + let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) }; - let bucket_policy = BucketPolicy { - id: "".into(), - version: "2012-10-17".to_owned(), - statements: vec![BPStatement { - sid: "".into(), - effect: Effect::Allow, - actions: ActionSet(s3_all_act.clone()), - resources: ResourceSet(all_res), + let default_claims = HashMap::new(); + let claims = cred.claims.as_ref().unwrap_or(&default_claims); + + let cred_clone = cred.clone(); + let conditions = get_condition_values(&req.headers, &cred_clone); + let cred_clone = Arc::new(cred_clone); + let conditions = Arc::new(conditions); + + let is_allow = Box::new({ + let iam_clone = Arc::clone(&iam_store); + let cred_clone = Arc::clone(&cred_clone); + let conditions = Arc::clone(&conditions); + move |name: String| { + let iam_clone = Arc::clone(&iam_clone); + let cred_clone = Arc::clone(&cred_clone); + let conditions = Arc::clone(&conditions); + async move { + let (mut rd, mut wr) = (false, false); + if !iam_clone + .is_allowed(&Args { + account: &cred_clone.access_key, + groups: &cred_clone.groups, + action: Action::S3Action(S3Action::ListBucketAction), + bucket: &name, + conditions: &conditions, + is_owner: owner, + object: "", + claims, + deny_only: false, + }) + .await + { + rd = true + } + + if !iam_clone + .is_allowed(&Args { + account: &cred_clone.access_key, + groups: &cred_clone.groups, + action: Action::S3Action(S3Action::GetBucketLocationAction), + bucket: &name, + conditions: &conditions, + is_owner: owner, + object: "", + claims, + deny_only: false, + }) + .await + { + rd = true + } + + if !iam_clone + .is_allowed(&Args { + account: &cred_clone.access_key, + groups: &cred_clone.groups, + action: Action::S3Action(S3Action::PutObjectAction), + bucket: &name, + conditions: &conditions, + is_owner: owner, + object: "", + claims, + deny_only: false, + }) + .await + { + wr = true + } + + (rd, wr) + } + } + }); + + let account_name = if cred.is_temp() || cred.is_service_account() { + cred.parent_user.clone() + } else { + cred.access_key.clone() + }; + + let claims_args = Args { + account: "", + groups: &None, + action: Action::None, + bucket: "", + conditions: &HashMap::new(), + is_owner: false, + object: "", + claims, + deny_only: false, + }; + + let role_arn = claims_args.get_role_arn(); + + // TODO: get_policies_from_claims(claims); + + let Some(admin_cred) = get_global_action_cred() else { + return Err(S3Error::with_message( + S3ErrorCode::InternalError, + "get_global_action_cred failed".to_string(), + )); + }; + + let mut effective_policy: policy::policy::Policy = Default::default(); + + if account_name == admin_cred.access_key { + for (name, p) in DEFAULT_POLICIES.iter() { + if *name == "consoleAdmin" { + effective_policy = p.clone(); + break; + } + } + } else if let Some(arn) = role_arn { + let (_, policy_name) = iam_store + .get_role_policy(arn) + .await + .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, e.to_string()))?; + + let policies = MappedPolicy::new(&policy_name).to_slice(); + effective_policy = iam_store.get_combined_policy(&policies).await; + } else { + let policies = iam_store + .policy_db_get(&account_name, &cred.groups) + .await + .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("get policy failed: {}", e)))?; + + effective_policy = iam_store.get_combined_policy(&policies).await; + }; + + let policy_str = serde_json::to_string(&effective_policy) + .map_err(|_e| S3Error::with_message(S3ErrorCode::InternalError, "parse policy failed"))?; + + let mut account_info = madmin::AccountInfo { + account_name, + server: store.backend_info().await, + policy: serde_json::Value::String(policy_str), + ..Default::default() + }; + + // TODO: bucket policy + let buckets = store + .list_bucket(&BucketOptions { + cached: true, ..Default::default() - }], - }; + }) + .await + .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, e.to_string()))?; - // let policy = bucket_policy - // .marshal_msg() - // .map_err(|_e| S3Error::with_message(S3ErrorCode::InternalError, "parse policy failed"))?; + for bucket in buckets.iter() { + let (rd, wr) = is_allow(bucket.name.clone()).await; + if rd || wr { + // TODO: BucketQuotaSys + // TODO: other attributes + account_info.buckets.push(madmin::BucketAccessInfo { + name: bucket.name.clone(), + details: Some(madmin::BucketDetails { + versioning: BucketVersioningSys::enabled(bucket.name.as_str()).await, + versioning_suspended: BucketVersioningSys::suspended(bucket.name.as_str()).await, + ..Default::default() + }), + created: bucket.created, + access: madmin::AccountAccess { read: rd, write: wr }, + ..Default::default() + }); + } + } - let backend_info = store.backend_info().await; - - let info = AccountInfo { - account_name: cred.access_key, - server: backend_info, - policy: bucket_policy, - }; - - let data = serde_json::to_vec(&info) + let data = serde_json::to_vec(&account_info) .map_err(|_e| S3Error::with_message(S3ErrorCode::InternalError, "parse accountInfo failed"))?; let mut header = HeaderMap::new(); @@ -126,8 +272,6 @@ pub struct ServerInfoHandler {} #[async_trait::async_trait] impl Operation for ServerInfoHandler { async fn call(&self, _req: S3Request, _params: Params<'_, '_>) -> S3Result> { - warn!("handle ServerInfoHandler"); - let info = get_server_info(true).await; let data = serde_json::to_vec(&info) diff --git a/rustfs/src/admin/handlers/policy.rs b/rustfs/src/admin/handlers/policys.rs similarity index 99% rename from rustfs/src/admin/handlers/policy.rs rename to rustfs/src/admin/handlers/policys.rs index 4d650a593..2ef7f42f8 100644 --- a/rustfs/src/admin/handlers/policy.rs +++ b/rustfs/src/admin/handlers/policys.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use crate::admin::{router::Operation, utils::has_space_be}; use http::{HeaderMap, StatusCode}; use iam::{error::is_err_no_such_user, get_global_action_cred, store::MappedPolicy}; @@ -8,6 +6,7 @@ use policy::policy::Policy; use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result}; use serde::Deserialize; use serde_urlencoded::from_bytes; +use std::collections::HashMap; use tracing::warn; #[derive(Debug, Deserialize, Default)] diff --git a/rustfs/src/admin/handlers/pools.rs b/rustfs/src/admin/handlers/pools.rs index 413cdb4dd..bd3152790 100644 --- a/rustfs/src/admin/handlers/pools.rs +++ b/rustfs/src/admin/handlers/pools.rs @@ -4,6 +4,7 @@ use matchit::Params; use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result}; use serde::Deserialize; use serde_urlencoded::from_bytes; +use tokio::sync::broadcast; use tracing::warn; use crate::{admin::router::Operation, storage::error::to_s3_error}; @@ -159,6 +160,9 @@ impl Operation for StartDecommission { let pools: Vec<&str> = query.pool.split(",").collect(); let mut pools_indices = Vec::with_capacity(pools.len()); + // TODO: ctx + let (_ctx_tx, ctx_rx) = broadcast::channel::(1); + for pool in pools.iter() { let idx = { if is_byid { @@ -188,7 +192,7 @@ impl Operation for StartDecommission { } if !pools_indices.is_empty() { - store.decommission(pools_indices).await.map_err(to_s3_error)?; + store.decommission(ctx_rx, pools_indices).await.map_err(to_s3_error)?; } Ok(S3Response::new((StatusCode::OK, Body::default()))) diff --git a/rustfs/src/admin/handlers/service_account.rs b/rustfs/src/admin/handlers/service_account.rs index 7d26e9bad..f7d561ed5 100644 --- a/rustfs/src/admin/handlers/service_account.rs +++ b/rustfs/src/admin/handlers/service_account.rs @@ -1,4 +1,5 @@ use crate::admin::utils::has_space_be; +use crate::auth::{get_condition_values, get_session_token}; use crate::{admin::router::Operation, auth::check_key_valid}; use http::HeaderMap; use hyper::StatusCode; @@ -12,7 +13,8 @@ use madmin::{ ServiceAccountInfo, UpdateServiceAccountReq, }; use matchit::Params; -use policy::policy::Policy; +use policy::policy::action::{Action, AdminAction}; +use policy::policy::{Args, Policy}; use s3s::S3ErrorCode::InvalidRequest; use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result}; use serde::Deserialize; @@ -29,7 +31,8 @@ impl Operation for AddServiceAccount { return Err(s3_error!(InvalidRequest, "get cred failed")); }; - let (cred, _owner) = check_key_valid(&req.headers, &req_cred.access_key).await?; + let (cred, owner) = + check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &req_cred.access_key).await?; let mut input = req.input; let body = match input.store_all_unlimited().await { @@ -89,6 +92,25 @@ impl Operation for AddServiceAccount { let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) }; + let deny_only = cred.access_key == target_user || cred.parent_user == target_user; + + if !iam_store + .is_allowed(&Args { + account: &cred.access_key, + groups: &cred.groups, + action: Action::AdminAction(AdminAction::CreateServiceAccountAdminAction), + bucket: "", + conditions: &get_condition_values(&req.headers, &cred), + is_owner: owner, + object: "", + claims: cred.claims.as_ref().unwrap_or(&HashMap::new()), + deny_only, + }) + .await + { + return Err(s3_error!(AccessDenied, "access denied")); + } + if target_user != cred.access_key { let has_user = iam_store.get_user(&target_user).await; if has_user.is_none() && target_user != sys_cred.access_key { @@ -210,7 +232,31 @@ impl Operation for UpdateServiceAccount { update_req .validate() .map_err(|e| S3Error::with_message(InvalidRequest, e.to_string()))?; - // TODO: is_allowed + + let Some(input_cred) = req.credentials else { + return Err(s3_error!(InvalidRequest, "get cred failed")); + }; + + let (cred, owner) = + check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?; + + if !iam_store + .is_allowed(&Args { + account: &cred.access_key, + groups: &cred.groups, + action: Action::AdminAction(AdminAction::UpdateServiceAccountAdminAction), + bucket: "", + conditions: &get_condition_values(&req.headers, &cred), + is_owner: owner, + object: "", + claims: cred.claims.as_ref().unwrap_or(&HashMap::new()), + deny_only: false, + }) + .await + { + return Err(s3_error!(AccessDenied, "access denied")); + } + let sp = { if let Some(policy) = update_req.new_policy { let sp = Policy::parse_config(policy.as_bytes()).map_err(|e| { @@ -278,7 +324,36 @@ impl Operation for InfoServiceAccount { s3_error!(InternalError, "get service account failed") })?; - // TODO: is_allowed + let Some(input_cred) = req.credentials else { + return Err(s3_error!(InvalidRequest, "get cred failed")); + }; + + let (cred, owner) = + check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?; + + if !iam_store + .is_allowed(&Args { + account: &cred.access_key, + groups: &cred.groups, + action: Action::AdminAction(AdminAction::ListServiceAccountsAdminAction), + bucket: "", + conditions: &get_condition_values(&req.headers, &cred), + is_owner: owner, + object: "", + claims: cred.claims.as_ref().unwrap_or(&HashMap::new()), + deny_only: false, + }) + .await + { + let user = if cred.parent_user.is_empty() { + &cred.access_key + } else { + &cred.parent_user + }; + if user != &svc_account.parent_user { + return Err(s3_error!(AccessDenied, "access denied")); + } + } let implied_policy = if let Some(policy) = session_policy.as_ref() { policy.version.is_empty() && policy.statements.is_empty() @@ -357,27 +432,55 @@ impl Operation for ListServiceAccount { return Err(s3_error!(InvalidRequest, "get cred failed")); }; - let (cred, _owner) = check_key_valid(&req.headers, &input_cred.access_key).await.map_err(|e| { - debug!("check key failed: {e:?}"); - s3_error!(InternalError, "check key failed") - })?; + let (cred, owner) = + check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key) + .await + .map_err(|e| { + debug!("check key failed: {e:?}"); + s3_error!(InternalError, "check key failed") + })?; - let target_account = if let Some(user) = query.user { - if user != input_cred.access_key { - user - } else if cred.parent_user.is_empty() { - input_cred.access_key - } else { - cred.parent_user + // let target_account = if let Some(user) = query.user { + // if user != input_cred.access_key { + // user + // } else if cred.parent_user.is_empty() { + // input_cred.access_key + // } else { + // cred.parent_user + // } + // } else if cred.parent_user.is_empty() { + // input_cred.access_key + // } else { + // cred.parent_user + // }; + + let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) }; + + let target_account = if query.user.as_ref().is_some_and(|v| v != &cred.access_key) { + if !iam_store + .is_allowed(&Args { + account: &cred.access_key, + groups: &cred.groups, + action: Action::AdminAction(AdminAction::UpdateServiceAccountAdminAction), + bucket: "", + conditions: &get_condition_values(&req.headers, &cred), + is_owner: owner, + object: "", + claims: cred.claims.as_ref().unwrap_or(&HashMap::new()), + deny_only: false, + }) + .await + { + return Err(s3_error!(AccessDenied, "access denied")); } + + query.user.unwrap_or_default() } else if cred.parent_user.is_empty() { - input_cred.access_key + cred.access_key } else { cred.parent_user }; - let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) }; - let service_accounts = iam_store.list_service_accounts(&target_account).await.map_err(|e| { debug!("list service account failed: {e:?}"); s3_error!(InternalError, "list service account failed") @@ -415,10 +518,13 @@ impl Operation for DeleteServiceAccount { return Err(s3_error!(InvalidRequest, "get cred failed")); }; - let (_cred, _owner) = check_key_valid(&req.headers, &input_cred.access_key).await.map_err(|e| { - debug!("check key failed: {e:?}"); - s3_error!(InternalError, "check key failed") - })?; + let (cred, owner) = + check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key) + .await + .map_err(|e| { + debug!("check key failed: {e:?}"); + s3_error!(InternalError, "check key failed") + })?; let query = { if let Some(query) = req.uri.query() { @@ -436,7 +542,7 @@ impl Operation for DeleteServiceAccount { let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) }; - let _svc_account = match iam_store.get_service_account(&query.access_key).await { + let svc_account = match iam_store.get_service_account(&query.access_key).await { Ok((res, _)) => Some(res), Err(err) => { if is_err_no_such_service_account(&err) { @@ -447,7 +553,30 @@ impl Operation for DeleteServiceAccount { } }; - // TODO: is_allowed + if !iam_store + .is_allowed(&Args { + account: &cred.access_key, + groups: &cred.groups, + action: Action::AdminAction(AdminAction::RemoveServiceAccountAdminAction), + bucket: "", + conditions: &get_condition_values(&req.headers, &cred), + is_owner: owner, + object: "", + claims: cred.claims.as_ref().unwrap_or(&HashMap::new()), + deny_only: false, + }) + .await + { + let user = if cred.parent_user.is_empty() { + &cred.access_key + } else { + &cred.parent_user + }; + + if svc_account.is_some_and(|v| &v.parent_user != user) { + return Err(s3_error!(InvalidRequest, "service account not exist")); + } + } iam_store.delete_service_account(&query.access_key).await.map_err(|e| { debug!("delete service account failed, e: {:?}", e); diff --git a/rustfs/src/admin/handlers/sts.rs b/rustfs/src/admin/handlers/sts.rs index 14efd2222..025866cde 100644 --- a/rustfs/src/admin/handlers/sts.rs +++ b/rustfs/src/admin/handlers/sts.rs @@ -42,12 +42,13 @@ impl Operation for AssumeRoleHandle { let Some(user) = req.credentials else { return Err(s3_error!(InvalidRequest, "get cred failed")) }; - let session_token = get_session_token(&req.headers); + let session_token = get_session_token(&req.uri, &req.headers); if session_token.is_some() { return Err(s3_error!(InvalidRequest, "AccessDenied1")); } - let (cred, _owner) = check_key_valid(&req.headers, &user.access_key).await?; + let (cred, _owner) = + check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &user.access_key).await?; // // TODO: 判断权限, 不允许sts访问 if cred.is_temp() || cred.is_service_account() { diff --git a/rustfs/src/admin/handlers/user.rs b/rustfs/src/admin/handlers/user.rs index ae55f97f7..36d7c5939 100644 --- a/rustfs/src/admin/handlers/user.rs +++ b/rustfs/src/admin/handlers/user.rs @@ -1,9 +1,13 @@ -use std::str::from_utf8; +use std::{collections::HashMap, str::from_utf8}; use http::{HeaderMap, StatusCode}; use iam::get_global_action_cred; use madmin::{AccountStatus, AddOrUpdateUserReq}; use matchit::Params; +use policy::policy::{ + action::{Action, AdminAction}, + Args, +}; use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result}; use serde::Deserialize; use serde_urlencoded::from_bytes; @@ -11,7 +15,7 @@ use tracing::warn; use crate::{ admin::{router::Operation, utils::has_space_be}, - auth::check_key_valid, + auth::{check_key_valid, get_condition_values, get_session_token}, }; #[derive(Debug, Deserialize, Default)] @@ -39,7 +43,8 @@ impl Operation for AddUser { return Err(s3_error!(InvalidRequest, "get cred failed")); }; - let (cred, _owner) = check_key_valid(&req.headers, &input_cred.access_key).await?; + let (cred, owner) = + check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?; let ak = query.access_key.as_deref().unwrap_or_default(); @@ -62,8 +67,6 @@ impl Operation for AddUser { let args: AddOrUpdateUserReq = serde_json::from_slice(&body) .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("unmarshal body err {}", e)))?; - warn!("add user args {:?}", args); - if args.secret_key.is_empty() { return Err(s3_error!(InvalidArgument, "access key is empty")); } @@ -88,13 +91,24 @@ impl Operation for AddUser { return Err(s3_error!(InvalidArgument, "access key is not utf8")); } - // let check_deny_only = if ak == cred.access_key { - // true - // } else { - // false - // }; - - // TODO: is_allowed + let deny_only = ak == cred.access_key; + let conditions = get_condition_values(&req.headers, &cred); + if !iam_store + .is_allowed(&Args { + account: &cred.access_key, + groups: &cred.groups, + action: Action::AdminAction(AdminAction::CreateUserAdminAction), + bucket: "", + conditions: &conditions, + is_owner: owner, + object: "", + claims: cred.claims.as_ref().unwrap_or(&HashMap::new()), + deny_only, + }) + .await + { + return Err(s3_error!(AccessDenied, "access denied")); + } iam_store .create_user(ak, &args) @@ -112,8 +126,6 @@ pub struct SetUserStatus {} #[async_trait::async_trait] impl Operation for SetUserStatus { async fn call(&self, req: S3Request, _params: Params<'_, '_>) -> S3Result> { - warn!("handle SetUserStatus"); - let query = { if let Some(query) = req.uri.query() { let input: AddUserQuery = @@ -164,8 +176,6 @@ pub struct ListUsers {} #[async_trait::async_trait] impl Operation for ListUsers { async fn call(&self, req: S3Request, _params: Params<'_, '_>) -> S3Result> { - warn!("handle ListUsers"); - let query = { if let Some(query) = req.uri.query() { let input: BucketQuery = @@ -213,8 +223,6 @@ pub struct RemoveUser {} #[async_trait::async_trait] impl Operation for RemoveUser { async fn call(&self, req: S3Request, _params: Params<'_, '_>) -> S3Result> { - warn!("handle RemoveUser"); - let query = { if let Some(query) = req.uri.query() { let input: AddUserQuery = @@ -246,7 +254,8 @@ impl Operation for RemoveUser { return Err(s3_error!(InvalidRequest, "get cred failed")); }; - let (cred, _owner) = check_key_valid(&req.headers, &input_cred.access_key).await?; + let (cred, _owner) = + check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?; let sys_cred = get_global_action_cred() .ok_or_else(|| S3Error::with_message(S3ErrorCode::InternalError, "get_global_action_cred failed"))?; @@ -275,8 +284,6 @@ pub struct GetUserInfo {} #[async_trait::async_trait] impl Operation for GetUserInfo { async fn call(&self, req: S3Request, _params: Params<'_, '_>) -> S3Result> { - warn!("handle GetUserInfo"); - let query = { if let Some(query) = req.uri.query() { let input: AddUserQuery = @@ -295,6 +302,32 @@ impl Operation for GetUserInfo { let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) }; + let Some(input_cred) = req.credentials else { + return Err(s3_error!(InvalidRequest, "get cred failed")); + }; + + let (cred, owner) = + check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?; + + let deny_only = ak == cred.access_key; + let conditions = get_condition_values(&req.headers, &cred); + if !iam_store + .is_allowed(&Args { + account: &cred.access_key, + groups: &cred.groups, + action: Action::AdminAction(AdminAction::GetUserAdminAction), + bucket: "", + conditions: &conditions, + is_owner: owner, + object: "", + claims: cred.claims.as_ref().unwrap_or(&HashMap::new()), + deny_only, + }) + .await + { + return Err(s3_error!(AccessDenied, "access denied")); + } + let info = iam_store .get_user_info(ak) .await diff --git a/rustfs/src/admin/mod.rs b/rustfs/src/admin/mod.rs index 5870b284d..0289cce26 100644 --- a/rustfs/src/admin/mod.rs +++ b/rustfs/src/admin/mod.rs @@ -6,10 +6,11 @@ pub mod utils; use common::error::Result; // use ecstore::global::{is_dist_erasure, is_erasure}; use handlers::{ - group, policy, pools, + group, policys, pools, service_account::{AddServiceAccount, DeleteServiceAccount, InfoServiceAccount, ListServiceAccount, UpdateServiceAccount}, sts, user, }; + use hyper::Method; use router::{AdminOperation, S3Router}; use rpc::regist_rpc_route; @@ -231,35 +232,35 @@ fn regist_user_route(r: &mut S3Router) -> Result<()> { r.insert( Method::GET, format!("{}{}", ADMIN_PREFIX, "/v3/list-canned-policies").as_str(), - AdminOperation(&policy::ListCannedPolicies {}), + AdminOperation(&policys::ListCannedPolicies {}), )?; // info-canned-policy?name=xxx r.insert( Method::GET, format!("{}{}", ADMIN_PREFIX, "/v3/info-canned-policy").as_str(), - AdminOperation(&policy::InfoCannedPolicy {}), + AdminOperation(&policys::InfoCannedPolicy {}), )?; // add-canned-policy?name=xxx r.insert( Method::PUT, format!("{}{}", ADMIN_PREFIX, "/v3/add-canned-policy").as_str(), - AdminOperation(&policy::AddCannedPolicy {}), + AdminOperation(&policys::AddCannedPolicy {}), )?; // remove-canned-policy?name=xxx r.insert( Method::DELETE, format!("{}{}", ADMIN_PREFIX, "/v3/remove-canned-policy").as_str(), - AdminOperation(&policy::RemoveCannedPolicy {}), + AdminOperation(&policys::RemoveCannedPolicy {}), )?; // set-user-or-group-policy?policyName=xxx&userOrGroup=xxx&isGroup=xxx r.insert( Method::PUT, format!("{}{}", ADMIN_PREFIX, "/v3/set-user-or-group-policy").as_str(), - AdminOperation(&policy::SetPolicyForUserOrGroup {}), + AdminOperation(&policys::SetPolicyForUserOrGroup {}), )?; Ok(()) diff --git a/rustfs/src/admin/router.rs b/rustfs/src/admin/router.rs index 4fb605d69..d19d4da2c 100644 --- a/rustfs/src/admin/router.rs +++ b/rustfs/src/admin/router.rs @@ -67,14 +67,16 @@ where uri.path().starts_with(ADMIN_PREFIX) || uri.path().starts_with(RPC_PREFIX) } - async fn call(&self, req: S3Request) -> S3Result> { + async fn call(&self, req: S3Request) -> S3Result> { let uri = format!("{}|{}", &req.method, req.uri.path()); // warn!("get uri {}", &uri); if let Ok(mat) = self.router.at(&uri) { let op: &T = mat.value; - return op.call(req, mat.params).await; + let mut resp = op.call(req, mat.params).await?; + resp.status = Some(resp.output.0); + return Ok(resp.map_output(|x| x.1)); } return Err(s3_error!(NotImplemented)); diff --git a/rustfs/src/auth.rs b/rustfs/src/auth.rs index 42d4a464d..6102960b7 100644 --- a/rustfs/src/auth.rs +++ b/rustfs/src/auth.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use http::HeaderMap; +use http::Uri; use iam::error::Error as IamError; use iam::get_global_action_cred; use iam::sys::SESSION_POLICY_NAME; @@ -48,7 +49,7 @@ impl S3Auth for IAMAuth { } // check_key_valid checks the key is valid or not. return the user's credentials and if the user is the owner. -pub async fn check_key_valid(header: &HeaderMap, access_key: &str) -> S3Result<(auth::Credentials, bool)> { +pub async fn check_key_valid(session_token: &str, access_key: &str) -> S3Result<(auth::Credentials, bool)> { let Some(mut cred) = get_global_action_cred() else { return Err(S3Error::with_message( S3ErrorCode::InternalError, @@ -88,7 +89,7 @@ pub async fn check_key_valid(header: &HeaderMap, access_key: &str) -> S3Result<( cred = u.credentials; } - let claims = check_claims_from_token(header, &cred) + let claims = check_claims_from_token(session_token, &cred) .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("check claims failed {}", e)))?; cred.claims = if !claims.is_empty() { Some(claims) } else { None }; @@ -105,23 +106,21 @@ pub async fn check_key_valid(header: &HeaderMap, access_key: &str) -> S3Result<( Ok((cred, owner)) } -pub fn check_claims_from_token(header: &HeaderMap, cred: &auth::Credentials) -> S3Result> { - let token = get_session_token(header).unwrap_or_default(); - +pub fn check_claims_from_token(token: &str, cred: &auth::Credentials) -> S3Result> { if !token.is_empty() && cred.access_key.is_empty() { return Err(s3_error!(InvalidRequest, "no access key")); } if token.is_empty() && cred.is_temp() && !cred.is_service_account() { - return Err(s3_error!(InvalidRequest, "invalid token")); + return Err(s3_error!(InvalidRequest, "invalid token1")); } if !token.is_empty() && !cred.is_temp() { - return Err(s3_error!(InvalidRequest, "invalid token")); + return Err(s3_error!(InvalidRequest, "invalid token2")); } if !cred.is_service_account() && cred.is_temp() && token != cred.session_token { - return Err(s3_error!(InvalidRequest, "invalid token")); + return Err(s3_error!(InvalidRequest, "invalid token3")); } if cred.is_temp() && cred.is_expired() { @@ -149,8 +148,10 @@ pub fn check_claims_from_token(header: &HeaderMap, cred: &auth::Credentials) -> Ok(HashMap::new()) } -pub fn get_session_token(hds: &HeaderMap) -> Option<&str> { - hds.get("x-amz-security-token").map(|v| v.to_str().unwrap_or_default()) +pub fn get_session_token<'a>(uri: &'a Uri, hds: &'a HeaderMap) -> Option<&'a str> { + hds.get("x-amz-security-token") + .map(|v| v.to_str().unwrap_or_default()) + .or_else(|| get_query_param(uri.query().unwrap_or_default(), "x-amz-security-token")) } pub fn get_condition_values(header: &HeaderMap, cred: &auth::Credentials) -> HashMap> { @@ -295,3 +296,17 @@ pub fn get_condition_values(header: &HeaderMap, cred: &auth::Credentials) -> Has args } + +pub fn get_query_param<'a>(query: &'a str, param_name: &str) -> Option<&'a str> { + let param_name = param_name.to_lowercase(); + + for pair in query.split('&') { + let mut parts = pair.splitn(2, '='); + if let (Some(key), Some(value)) = (parts.next(), parts.next()) { + if key.to_lowercase() == param_name { + return Some(value); + } + } + } + None +} diff --git a/rustfs/src/config/mod.rs b/rustfs/src/config/mod.rs index cd8f238fd..1efac71f7 100644 --- a/rustfs/src/config/mod.rs +++ b/rustfs/src/config/mod.rs @@ -1,11 +1,39 @@ use clap::Parser; use const_str::concat; use ecstore::global::DEFAULT_PORT; - +use std::string::ToString; shadow_rs::shadow!(build); +/// Default Access Key +/// Default value: rustfsadmin +/// Environment variable: RUSTFS_ACCESS_KEY +/// Command line argument: --access-key +/// Example: RUSTFS_ACCESS_KEY=rustfsadmin +/// Example: --access-key rustfsadmin pub const DEFAULT_ACCESS_KEY: &str = "rustfsadmin"; +/// Default Secret Key +/// Default value: rustfsadmin +/// Environment variable: RUSTFS_SECRET_KEY +/// Command line argument: --secret-key +/// Example: RUSTFS_SECRET_KEY=rustfsadmin +/// Example: --secret-key rustfsadmin pub const DEFAULT_SECRET_KEY: &str = "rustfsadmin"; +/// Default configuration file for observability +/// Default value: config/obs.toml +/// Environment variable: RUSTFS_OBS_CONFIG +/// Command line argument: --obs-config +/// Example: RUSTFS_OBS_CONFIG=config/obs.toml +/// Example: --obs-config config/obs.toml +/// Example: --obs-config /etc/rustfs/obs.toml +pub const DEFAULT_OBS_CONFIG: &str = "config/obs.toml"; + +/// Default TLS key for rustfs +/// This is the default key for TLS. +pub(crate) const RUSTFS_TLS_KEY: &str = "rustfs_private.key"; + +/// Default TLS cert for rustfs +/// This is the default cert for TLS. +pub(crate) const RUSTFS_TLS_CERT: &str = "rustfs_public.crt"; #[allow(clippy::const_is_empty)] const SHORT_VERSION: &str = { @@ -31,7 +59,7 @@ const LONG_VERSION: &str = concat!( concat!("git status :\n", build::GIT_STATUS_FILE), ); -#[derive(Debug, Parser)] +#[derive(Debug, Parser, Clone)] #[command(version = SHORT_VERSION, long_version = LONG_VERSION)] pub struct Opt { /// DIR points to a directory on a filesystem. @@ -42,6 +70,7 @@ pub struct Opt { #[arg(long, default_value_t = format!("0.0.0.0:{}", DEFAULT_PORT), env = "RUSTFS_ADDRESS")] pub address: String, + /// Domain name used for virtual-hosted-style requests. #[arg(long, env = "RUSTFS_SERVER_DOMAINS")] pub server_domains: Vec, @@ -53,13 +82,37 @@ pub struct Opt { #[arg(long, default_value_t = DEFAULT_SECRET_KEY.to_string(), env = "RUSTFS_SECRET_KEY")] pub secret_key: String, - /// Domain name used for virtual-hosted-style requests. - #[arg(long, env = "RUSTFS_DOMAIN_NAME")] - pub domain_name: Option, - #[arg(long, default_value_t = false, env = "RUSTFS_CONSOLE_ENABLE")] pub console_enable: bool, #[arg(long, default_value_t = format!("127.0.0.1:{}", 9002), env = "RUSTFS_CONSOLE_ADDRESS")] pub console_address: String, + + /// rustfs endpoint for console + #[arg(long, env = "RUSTFS_CONSOLE_FS_ENDPOINT")] + pub console_fs_endpoint: Option, + + /// Observability configuration file + /// Default value: config/obs.toml + #[arg(long, default_value_t = DEFAULT_OBS_CONFIG.to_string(), env = "RUSTFS_OBS_CONFIG")] + pub obs_config: String, + + /// tls path for rustfs api and console. + #[arg(long, env = "RUSTFS_TLS_PATH")] + pub tls_path: Option, + + #[arg(long, env = "RUSTFS_LICENSE")] + pub license: Option, } + +// lazy_static::lazy_static! { +// pub(crate) static ref OPT: OnceLock = OnceLock::new(); +// } + +// pub fn init_config(opt: Opt) { +// OPT.set(opt).expect("Failed to set global config"); +// } + +// pub fn get_config() -> &'static Opt { +// OPT.get().expect("Global config not initialized") +// } diff --git a/rustfs/src/console.rs b/rustfs/src/console.rs index 8ccf0fb88..df6ffaee8 100644 --- a/rustfs/src/console.rs +++ b/rustfs/src/console.rs @@ -1,19 +1,25 @@ +use crate::config::{RUSTFS_TLS_CERT, RUSTFS_TLS_KEY}; +use crate::license::get_license; use axum::{ body::Body, - extract::Host, http::{Response, StatusCode}, response::IntoResponse, routing::get, Router, }; +use axum_extra::extract::Host; +use axum_server::tls_rustls::RustlsConfig; +use http::Uri; use mime_guess::from_path; use rust_embed::RustEmbed; use serde::Serialize; use shadow_rs::shadow; -use std::net::{Ipv4Addr, SocketAddr, ToSocketAddrs}; +use std::net::{Ipv4Addr, SocketAddr}; use std::sync::OnceLock; -use tracing::info; +use std::time::Duration; +use tokio::signal; +use tracing::{debug, error, info, instrument}; shadow!(build); @@ -153,31 +159,85 @@ pub(crate) fn init_console_cfg(local_ip: Ipv4Addr, port: u16) { // host.parse::().is_ok() || host.parse::().is_ok() // } +async fn license_handler() -> impl IntoResponse { + let license = get_license().unwrap_or_default(); + + Response::builder() + .header("content-type", "application/json") + .status(StatusCode::OK) + .body(Body::from(serde_json::to_string(&license).unwrap_or_default())) + .unwrap() +} + +fn _is_private_ip(ip: std::net::IpAddr) -> bool { + match ip { + std::net::IpAddr::V4(ip) => { + let octets = ip.octets(); + // 10.0.0.0/8 + octets[0] == 10 || + // 172.16.0.0/12 + (octets[0] == 172 && (octets[1] >= 16 && octets[1] <= 31)) || + // 192.168.0.0/16 + (octets[0] == 192 && octets[1] == 168) + } + std::net::IpAddr::V6(_) => false, + } +} + #[allow(clippy::const_is_empty)] -async fn config_handler(Host(host): Host) -> impl IntoResponse { - let host_with_port = if host.contains(':') { host } else { format!("{}:80", host) }; +#[instrument(fields(host))] +async fn config_handler(uri: Uri, Host(host): Host) -> impl IntoResponse { + let scheme = uri.scheme().map(|s| s.as_str()).unwrap_or("http"); - let is_addr = host_with_port - .to_socket_addrs() - .map(|addrs| { - addrs.into_iter().find(|v| { - if let SocketAddr::V4(ipv4) = v { - !ipv4.ip().is_private() && !ipv4.ip().is_loopback() && !ipv4.ip().is_unspecified() - } else { - false - } - }) - }) - .unwrap_or_default(); + // 从 uri 中获取 host,如果没有则使用 Host extractor 的值 + let host = uri.host().unwrap_or(host.as_str()); + let host = if host.contains(':') { + let (host, _) = host.split_once(':').unwrap_or((host, "80")); + host + } else { + host + }; + + // 将当前配置复制一份 let mut cfg = CONSOLE_CONFIG.get().unwrap().clone(); - let url = if let Some(addr) = is_addr { - format!("http://{}:{}", addr.ip(), cfg.port) - } else { - let (host, _) = host_with_port.split_once(':').unwrap_or_default(); - format!("http://{}:{}", host, cfg.port) - }; + let url = format!("{}://{}:{}", scheme, host, cfg.port); + + // // 如果指定入口, 直接使用 + // let url = if let Some(endpoint) = &config::get_config().console_fs_endpoint { + // debug!("axum Using rustfs endpoint address: {}", endpoint); + // endpoint.clone() + // } else { + // let host_with_port = if host.contains(':') { + // host.clone() + // } else { + // format!("{}:80", host) + // }; + // // 尝试解析为 socket address,但不强制要求一定要是 IP 地址 + // let socket_addr = host_with_port.to_socket_addrs().ok().and_then(|mut addrs| addrs.next()); + // debug!("axum Using host with port: {}, Socket address: {:?}", host_with_port, socket_addr); + // match socket_addr { + // Some(addr) if addr.ip().is_ipv4() => { + // let ipv4 = addr.ip().to_string(); + // // 如果是私有 IP、环回地址或未指定地址,保留原始域名 + // if is_private_ip(addr.ip()) || addr.ip().is_loopback() || addr.ip().is_unspecified() { + // let (host, _) = host_with_port.split_once(':').unwrap_or((&host, "80")); + // debug!("axum Using private IPv4 address: {}", host); + // format!("http://{}:{}", host, cfg.port) + // } else { + // debug!("axum Using public IPv4 address"); + // format!("http://{}:{}", ipv4, cfg.port) + // } + // } + // _ => { + // // 如果不是有效的 IPv4 地址,保留原始域名 + // let (host, _) = host_with_port.split_once(':').unwrap_or((&host, "80")); + // debug!("axum Using domain address: {}", host); + // format!("http://{}:{}", host, cfg.port) + // } + // } + // }; cfg.api.base_url = format!("{}{}", url, RUSTFS_ADMIN_PREFIX); cfg.s3.endpoint = url; @@ -189,18 +249,105 @@ async fn config_handler(Host(host): Host) -> impl IntoResponse { .unwrap() } -pub async fn start_static_file_server(addrs: &str, local_ip: Ipv4Addr, access_key: &str, secret_key: &str) { - // 创建路由 +pub async fn start_static_file_server( + addrs: &str, + local_ip: Ipv4Addr, + access_key: &str, + secret_key: &str, + tls_path: Option, +) { + // Create a route let app = Router::new() + .route("/license", get(license_handler)) .route("/config.json", get(config_handler)) - .nest_service("/", get(static_handler)); - - let listener = tokio::net::TcpListener::bind(addrs).await.unwrap(); - let local_addr = listener.local_addr().unwrap(); - + .fallback_service(get(static_handler)); + let local_addr: SocketAddr = addrs.parse().expect("Failed to parse socket address"); info!("WebUI: http://{}:{} http://127.0.0.1:{}", local_ip, local_addr.port(), local_addr.port()); info!(" RootUser: {}", access_key); info!(" RootPass: {}", secret_key); - axum::serve(listener, app).await.unwrap(); + // Check and start the HTTPS/HTTP server + match start_server(addrs, local_addr, tls_path, app.clone()).await { + Ok(_) => info!("Server shutdown gracefully"), + Err(e) => error!("Server error: {}", e), + } +} +async fn start_server(addrs: &str, local_addr: SocketAddr, tls_path: Option, app: Router) -> std::io::Result<()> { + let tls_path = tls_path.unwrap_or_default(); + let key_path = format!("{}/{}", tls_path, RUSTFS_TLS_KEY); + let cert_path = format!("{}/{}", tls_path, RUSTFS_TLS_CERT); + let has_tls_certs = tokio::try_join!(tokio::fs::metadata(&key_path), tokio::fs::metadata(&cert_path)).is_ok(); + debug!("Console TLS certs: {:?}", has_tls_certs); + if has_tls_certs { + debug!("Found TLS certificates, starting with HTTPS"); + match tokio::try_join!(tokio::fs::read(&key_path), tokio::fs::read(&cert_path)) { + Ok((key_data, cert_data)) => { + match RustlsConfig::from_pem(cert_data, key_data).await { + Ok(config) => { + let handle = axum_server::Handle::new(); + // create a signal off listening task + let handle_clone = handle.clone(); + tokio::spawn(async move { + shutdown_signal().await; + handle_clone.graceful_shutdown(Some(Duration::from_secs(10))); + }); + info!("Starting HTTPS server..."); + axum_server::bind_rustls(local_addr, config) + .handle(handle.clone()) + .serve(app.into_make_service()) + .await + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + + Ok(()) + } + Err(e) => { + error!("Failed to create TLS config: {}", e); + start_http_server(addrs, app).await + } + } + } + Err(e) => { + error!("Failed to read TLS certificates: {}", e); + start_http_server(addrs, app).await + } + } + } else { + debug!("TLS certificates not found at {} and {}", key_path, cert_path); + start_http_server(addrs, app).await + } +} + +async fn start_http_server(addrs: &str, app: Router) -> std::io::Result<()> { + debug!("Starting HTTP server..."); + let listener = tokio::net::TcpListener::bind(addrs).await?; + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)) +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c().await.expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => { + info!("shutdown_signal ctrl_c") + }, + _ = terminate => { + info!("shutdown_signal terminate") + }, + } } diff --git a/rustfs/src/license.rs b/rustfs/src/license.rs new file mode 100644 index 000000000..25d09c82c --- /dev/null +++ b/rustfs/src/license.rs @@ -0,0 +1,62 @@ +use appauth::token::Token; +use common::error::{Error, Result}; +use std::sync::OnceLock; +use std::time::SystemTime; +use std::time::UNIX_EPOCH; +use tracing::error; +use tracing::info; + +lazy_static::lazy_static! { + static ref LICENSE: OnceLock = OnceLock::new(); +} + +pub fn init_license(license: Option) { + if license.is_none() { + error!("License is None"); + return; + } + let license = license.unwrap(); + let token = appauth::token::parse_license(&license).unwrap_or_default(); + + LICENSE.set(token).unwrap_or_else(|_| { + error!("Failed to set license"); + }); +} + +pub fn get_license() -> Option { + LICENSE.get().cloned() +} + +#[allow(unreachable_code)] +pub fn license_check() -> Result<()> { + return Ok(()); + let inval_license = LICENSE.get().map(|token| { + if token.expired < SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() { + error!("License expired"); + return Err(Error::from_string("Incorrect license, please contact RustFS.".to_string())); + } + info!("License is valid ! expired at {}", token.expired); + Ok(()) + }); + + // let inval_license = config::get_config().license.as_ref().map(|license| { + // if license.is_empty() { + // error!("License is empty"); + // return Err(Error::from_string("Incorrect license, please contact RustFS.".to_string())); + // } + // let token = appauth::token::parse_license(license)?; + // if token.expired < SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() { + // error!("License expired"); + // return Err(Error::from_string("Incorrect license, please contact RustFS.".to_string())); + // } + + // info!("License is valid ! expired at {}", token.expired); + // Ok(()) + // }); + + if inval_license.is_none() || inval_license.is_some_and(|v| v.is_err()) { + return Err(Error::from_string("Incorrect license, please contact RustFS.".to_string())); + } + + Ok(()) +} diff --git a/rustfs/src/logging/mod.rs b/rustfs/src/logging/mod.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/rustfs/src/logging/mod.rs @@ -0,0 +1 @@ + diff --git a/rustfs/src/main.rs b/rustfs/src/main.rs index 35187ba9f..d7ea61d1b 100644 --- a/rustfs/src/main.rs +++ b/rustfs/src/main.rs @@ -3,19 +3,24 @@ mod auth; mod config; mod console; mod grpc; +pub mod license; +mod logging; +mod server; mod service; mod storage; mod utils; - use crate::auth::IAMAuth; use crate::console::{init_console_cfg, CONSOLE_CONFIG}; +// Ensure the correct path for parse_license is imported +use crate::server::{wait_for_shutdown, ServiceState, ServiceStateManager, ShutdownSignal, SHUTDOWN_TIMEOUT}; +use crate::utils::error; use chrono::Datelike; use clap::Parser; use common::{ error::{Error, Result}, globals::set_global_addr, }; -use config::{DEFAULT_ACCESS_KEY, DEFAULT_SECRET_KEY}; +use config::{DEFAULT_ACCESS_KEY, DEFAULT_SECRET_KEY, RUSTFS_TLS_CERT, RUSTFS_TLS_KEY}; use ecstore::heal::background_heal_ops::init_auto_heal; use ecstore::utils::net::{self, get_available_port}; use ecstore::{ @@ -27,40 +32,27 @@ use ecstore::{ }; use ecstore::{global::set_global_rustfs_port, notification_sys::new_global_notification_sys}; use grpc::make_server; +use hyper_util::server::graceful::GracefulShutdown; use hyper_util::{ rt::{TokioExecutor, TokioIo}, server::conn::auto::Builder as ConnBuilder, service::TowerToHyperService, }; use iam::init_iam_sys; +use license::init_license; use protos::proto_gen::node_service::node_service_server::NodeServiceServer; +use rustfs_obs::{init_obs, load_config, set_global_guard, InitLogStatus}; +use rustls::ServerConfig; use s3s::{host::MultiDomain, service::S3ServiceBuilder}; use service::hybrid; -use std::{io::IsTerminal, net::SocketAddr}; +use std::net::SocketAddr; +use std::sync::Arc; use tokio::net::TcpListener; +use tokio::signal::unix::{signal, SignalKind}; +use tokio_rustls::TlsAcceptor; use tonic::{metadata::MetadataValue, Request, Status}; use tower_http::cors::CorsLayer; -use tracing::{debug, error, info, warn}; -use tracing_error::ErrorLayer; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - -fn setup_tracing() { - use tracing_subscriber::EnvFilter; - - let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); - let enable_color = std::io::stdout().is_terminal(); - - let subscriber = tracing_subscriber::fmt::fmt() - .pretty() - .with_env_filter(env_filter) - .with_ansi(enable_color) - .with_file(true) - .with_line_number(true) - .finish() - .with(ErrorLayer::default()); - - subscriber.try_init().expect("failed to set global default subscriber"); -} +use tracing::{debug, error, info, info_span, warn}; fn check_auth(req: Request<()>) -> Result, Status> { let token: MetadataValue<_> = "rustfs rpc".parse().unwrap(); @@ -83,22 +75,39 @@ fn print_server_info() { info!("Docs: {}", cfg.doc()); } -fn main() -> Result<()> { - //解析获得到的参数 +#[tokio::main] +async fn main() -> Result<()> { + // Parse the obtained parameters let opt = config::Opt::parse(); - //设置trace - setup_tracing(); + // config::init_config(opt.clone()); - //运行参数 - run(opt) + init_license(opt.license.clone()); + + // Load the configuration file + let config = load_config(Some(opt.clone().obs_config)); + + // Initialize Observability + let (_logger, guard) = init_obs(config.clone()).await; + + // Store in global storage + set_global_guard(guard)?; + + // Log initialization status + InitLogStatus::init_start_log(&config.observability).await?; + + // Run parameters + run(opt).await } -#[tokio::main] +// #[tokio::main] async fn run(opt: config::Opt) -> Result<()> { + let span = info_span!("trace-main-run"); + let _enter = span.enter(); + debug!("opt: {:?}", &opt); - let mut server_addr = net::check_local_server_addr(opt.address.as_str()).unwrap(); + let mut server_addr = net::check_local_server_addr(opt.address.as_str())?; if server_addr.port() == 0 { server_addr.set_port(get_available_port()); @@ -110,18 +119,18 @@ async fn run(opt: config::Opt) -> Result<()> { debug!("server_address {}", &server_address); - //设置AK和SK + //设置 AK 和 SK + iam::init_global_action_cred(Some(opt.access_key.clone()), Some(opt.secret_key.clone()))?; - iam::init_global_action_cred(Some(opt.access_key.clone()), Some(opt.secret_key.clone())).unwrap(); set_global_rustfs_port(server_port); - //监听地址,端口从参数中获取 + //监听地址,端口从参数中获取 let listener = TcpListener::bind(server_address.clone()).await?; //获取监听地址 let local_addr: SocketAddr = listener.local_addr()?; let local_ip = utils::get_local_ip().ok_or(local_addr.ip()).unwrap(); - // 用于rpc + // 用于 rpc let (endpoint_pools, setup_type) = EndpointServerPools::from_volumes(server_address.clone().as_str(), opt.volumes.clone()) .map_err(|err| Error::from_string(err.to_string()))?; @@ -172,15 +181,15 @@ async fn run(opt: config::Opt) -> Result<()> { .map_err(|err| Error::from_string(err.to_string()))?; // Setup S3 service - // 本项目使用s3s库来实现s3服务 - let service = { + // 本项目使用 s3s 库来实现 s3 服务 + let s3_service = { let store = storage::ecfs::FS::new(); // let mut b = S3ServiceBuilder::new(storage::ecfs::FS::new(server_address.clone(), endpoint_pools).await?); let mut b = S3ServiceBuilder::new(store.clone()); let access_key = opt.access_key.clone(); let secret_key = opt.secret_key.clone(); - //显示info信息 + //显示 info 信息 debug!("authentication is enabled {}, {}", &access_key, &secret_key); b.set_auth(IAMAuth::new(access_key, secret_key)); @@ -213,50 +222,153 @@ async fn run(opt: config::Opt) -> Result<()> { let rpc_service = NodeServiceServer::with_interceptor(make_server(), check_auth); - tokio::spawn(async move { - let hyper_service = service.into_shared(); + let tls_path = opt.tls_path.clone().unwrap_or_default(); + let key_path = format!("{}/{}", tls_path, RUSTFS_TLS_KEY); + let cert_path = format!("{}/{}", tls_path, RUSTFS_TLS_CERT); + let has_tls_certs = tokio::try_join!(tokio::fs::metadata(key_path.clone()), tokio::fs::metadata(cert_path.clone())).is_ok(); + debug!("Main TLS certs: {:?}", has_tls_certs); + let tls_acceptor = if has_tls_certs { + debug!("Found TLS certificates, starting with HTTPS"); + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + let certs = utils::load_certs(cert_path.as_str()).map_err(|e| error(e.to_string()))?; + let key = utils::load_private_key(key_path.as_str()).map_err(|e| error(e.to_string()))?; + let mut server_config = ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(certs, key) + .map_err(|e| error(e.to_string()))?; + server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()]; + Some(TlsAcceptor::from(Arc::new(server_config))) + } else { + debug!("TLS certificates not found, starting with HTTP"); + None + }; + let state_manager = ServiceStateManager::new(); + let worker_state_manager = state_manager.clone(); + // 更新服务状态为启动中 + state_manager.update(ServiceState::Starting); + + // Create shutdown channel + let (shutdown_tx, mut shutdown_rx) = tokio::sync::broadcast::channel(1); + let shutdown_tx_clone = shutdown_tx.clone(); + + tokio::spawn(async move { + // 错误处理改进 + let sigterm_inner = match signal(SignalKind::terminate()) { + Ok(signal) => signal, + Err(e) => { + error!("Failed to create SIGTERM signal handler: {}", e); + return; + } + }; + let sigint_inner = match signal(SignalKind::interrupt()) { + Ok(signal) => signal, + Err(e) => { + error!("Failed to create SIGINT signal handler: {}", e); + return; + } + }; + + let mut sigterm_inner = sigterm_inner; + let mut sigint_inner = sigint_inner; let hybrid_service = TowerToHyperService::new( tower::ServiceBuilder::new() .layer(CorsLayer::permissive()) - .service(hybrid(hyper_service, rpc_service)), + .service(hybrid(s3_service, rpc_service)), ); let http_server = ConnBuilder::new(TokioExecutor::new()); let mut ctrl_c = std::pin::pin!(tokio::signal::ctrl_c()); - let graceful = hyper_util::server::graceful::GracefulShutdown::new(); + let graceful = GracefulShutdown::new(); + debug!("graceful initiated"); + + // 服务准备就绪 + worker_state_manager.update(ServiceState::Ready); loop { + debug!("waiting for SIGINT or SIGTERM has_tls_certs: {}", has_tls_certs); + // Wait for a connection let (socket, _) = tokio::select! { res = listener.accept() => { match res { Ok(conn) => conn, Err(err) => { - tracing::error!("error accepting connection: {err}"); + error!("error accepting connection: {err}"); continue; } } } _ = ctrl_c.as_mut() => { + info!("Ctrl-C received in worker thread"); + let _ = shutdown_tx_clone.send(()); + break; + } + + _ = sigint_inner.recv() => { + info!("SIGINT received in worker thread"); + let _ = shutdown_tx_clone.send(()); + break; + } + _ = sigterm_inner.recv() => { + info!("SIGTERM received in worker thread"); + let _ = shutdown_tx_clone.send(()); + break; + } + _ = shutdown_rx.recv() => { + info!("Shutdown signal received in worker thread"); break; } }; - let conn = http_server.serve_connection(TokioIo::new(socket), hybrid_service.clone()); - let conn = graceful.watch(conn.into_owned()); - tokio::spawn(async move { - let _ = conn.await; - }); - } - - tokio::select! { - () = graceful.shutdown() => { - tracing::debug!("Gracefully shutdown!"); - }, - () = tokio::time::sleep(std::time::Duration::from_secs(10)) => { - tracing::debug!("Waited 10 seconds for graceful shutdown, aborting..."); + if has_tls_certs { + debug!("TLS certificates found, starting with SIGINT"); + let tls_socket = match tls_acceptor + .as_ref() + .ok_or_else(|| error("TLS not configured".to_string())) + .unwrap() + .accept(socket) + .await + { + Ok(tls_socket) => tls_socket, + Err(err) => { + error!("TLS handshake failed {}", err); + continue; + } + }; + let conn = http_server.serve_connection(TokioIo::new(tls_socket), hybrid_service.clone()); + let conn = graceful.watch(conn.into_owned()); + tokio::task::spawn_blocking(move || { + tokio::runtime::Runtime::new() + .expect("Failed to create runtime") + .block_on(async move { + if let Err(err) = conn.await { + error!("Https Connection error: {}", err); + } + }); + }); + debug!("TLS handshake success"); + } else { + debug!("Http handshake start"); + let conn = http_server.serve_connection(TokioIo::new(socket), hybrid_service.clone()); + let conn = graceful.watch(conn.into_owned()); + tokio::spawn(async move { + if let Err(err) = conn.await { + error!("Http Connection error: {}", err); + } + }); + debug!("Http handshake success"); } } + worker_state_manager.update(ServiceState::Stopping); + tokio::select! { + () = graceful.shutdown() => { + debug!("Gracefully shutdown!"); + }, + () = tokio::time::sleep(std::time::Duration::from_secs(10)) => { + debug!("Waited 10 seconds for graceful shutdown, aborting..."); + } + } + worker_state_manager.update(ServiceState::Stopped); }); // init store @@ -268,15 +380,15 @@ async fn run(opt: config::Opt) -> Result<()> { })?; ECStore::init(store.clone()).await.map_err(|err| { - error!("ECStore init faild {:?}", &err); + error!("ECStore init failed {:?}", &err); Error::from_string(err.to_string()) })?; debug!("init store success!"); - init_iam_sys(store.clone()).await.unwrap(); + init_iam_sys(store.clone()).await?; new_global_notification_sys(endpoint_pools.clone()).await.map_err(|err| { - error!("new_global_notification_sys faild {:?}", &err); + error!("new_global_notification_sys failed {:?}", &err); Error::from_string(err.to_string()) })?; @@ -294,17 +406,36 @@ async fn run(opt: config::Opt) -> Result<()> { let access_key = opt.access_key.clone(); let secret_key = opt.secret_key.clone(); let console_address = opt.console_address.clone(); + let tls_path = opt.tls_path.clone(); + + if console_address.is_empty() { + error!("console_address is empty"); + return Err(Error::from_string("console_address is empty".to_string())); + } + tokio::spawn(async move { - console::start_static_file_server(&console_address, local_ip, &access_key, &secret_key).await; + console::start_static_file_server(&console_address, local_ip, &access_key, &secret_key, tls_path).await; }); } - tokio::select! { - _ = tokio::signal::ctrl_c() => { - + // Perform hibernation for 1 second + tokio::time::sleep(SHUTDOWN_TIMEOUT).await; + // listen to the shutdown signal + match wait_for_shutdown().await { + ShutdownSignal::CtrlC | ShutdownSignal::Sigint | ShutdownSignal::Sigterm => { + info!("Shutdown signal received in main thread"); + // update the status to stopping first + state_manager.update(ServiceState::Stopping); + info!("Server is stopping..."); + let _ = shutdown_tx.send(()); + // Wait for the worker thread to complete the cleaning work + tokio::time::sleep(SHUTDOWN_TIMEOUT).await; + // the last updated status is stopped + state_manager.update(ServiceState::Stopped); + info!("Server stopped current "); } } - info!("server is stopped"); + info!("server is stopped state: {:?}", state_manager.current_state()); Ok(()) } diff --git a/rustfs/src/server/mod.rs b/rustfs/src/server/mod.rs new file mode 100644 index 000000000..b1c764a18 --- /dev/null +++ b/rustfs/src/server/mod.rs @@ -0,0 +1,6 @@ +mod service_state; +pub(crate) use service_state::wait_for_shutdown; +pub(crate) use service_state::ServiceState; +pub(crate) use service_state::ServiceStateManager; +pub(crate) use service_state::ShutdownSignal; +pub(crate) use service_state::SHUTDOWN_TIMEOUT; diff --git a/rustfs/src/server/service_state.rs b/rustfs/src/server/service_state.rs new file mode 100644 index 000000000..d27296e35 --- /dev/null +++ b/rustfs/src/server/service_state.rs @@ -0,0 +1,152 @@ +use atomic_enum::atomic_enum; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::time::Duration; +use tokio::signal::unix::{signal, SignalKind}; +use tracing::info; + +// a configurable shutdown timeout +pub(crate) const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(1); + +#[cfg(target_os = "linux")] +fn notify_systemd(state: &str) { + use libsystemd::daemon::{notify, NotifyState}; + use tracing::{debug, error}; + let notify_state = match state { + "ready" => NotifyState::Ready, + "stopping" => NotifyState::Stopping, + _ => { + info!("Unsupported state passed to notify_systemd: {}", state); + return; + } + }; + + if let Err(e) = notify(false, &[notify_state]) { + error!("Failed to notify systemd: {}", e); + } else { + debug!("Successfully notified systemd: {}", state); + } + info!("Systemd notifications are enabled on linux (state: {})", state); +} + +#[cfg(not(target_os = "linux"))] +fn notify_systemd(state: &str) { + info!("Systemd notifications are not available on this platform not linux (state: {})", state); +} + +#[derive(Debug)] +pub enum ShutdownSignal { + CtrlC, + Sigterm, + Sigint, +} + +#[atomic_enum] +#[derive(PartialEq)] +pub(crate) enum ServiceState { + Starting, + Ready, + Stopping, + Stopped, +} + +pub(crate) async fn wait_for_shutdown() -> ShutdownSignal { + let mut sigterm = signal(SignalKind::terminate()).expect("failed to create SIGTERM signal handler"); + let mut sigint = signal(SignalKind::interrupt()).expect("failed to create SIGINT signal handler"); + + tokio::select! { + _ = tokio::signal::ctrl_c() => { + info!("Received Ctrl-C signal"); + ShutdownSignal::CtrlC + } + _ = sigint.recv() => { + info!("Received SIGINT signal"); + ShutdownSignal::Sigint + } + _ = sigterm.recv() => { + info!("Received SIGTERM signal"); + ShutdownSignal::Sigterm + } + } +} + +#[derive(Clone)] +pub(crate) struct ServiceStateManager { + state: Arc, +} + +impl ServiceStateManager { + pub fn new() -> Self { + Self { + state: Arc::new(AtomicServiceState::new(ServiceState::Starting)), + } + } + + pub fn update(&self, new_state: ServiceState) { + self.state.store(new_state, Ordering::SeqCst); + self.notify_systemd(&new_state); + } + + pub fn current_state(&self) -> ServiceState { + self.state.load(Ordering::SeqCst) + } + + fn notify_systemd(&self, state: &ServiceState) { + match state { + ServiceState::Starting => { + info!("Service is starting..."); + #[cfg(target_os = "linux")] + if let Err(e) = libsystemd::daemon::notify(false, &[libsystemd::daemon::NotifyState::Status("Starting...".to_string())]) { + tracing::error!("Failed to notify systemd of starting state: {}", e); + } + } + ServiceState::Ready => { + info!("Service is ready"); + notify_systemd("ready"); + } + ServiceState::Stopping => { + info!("Service is stopping..."); + notify_systemd("stopping"); + } + ServiceState::Stopped => { + info!("Service has stopped"); + #[cfg(target_os = "linux")] + if let Err(e) = libsystemd::daemon::notify(false, &[libsystemd::daemon::NotifyState::Status("Stopped".to_string())]) { + tracing::error!("Failed to notify systemd of stopped state: {}", e); + } + } + } + } +} + +impl Default for ServiceStateManager { + fn default() -> Self { + Self::new() + } +} + +// 使用示例 +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_service_state_manager() { + let manager = ServiceStateManager::new(); + + // 初始状态应该是 Starting + assert_eq!(manager.current_state(), ServiceState::Starting); + + // 更新状态到 Ready + manager.update(ServiceState::Ready); + assert_eq!(manager.current_state(), ServiceState::Ready); + + // 更新状态到 Stopping + manager.update(ServiceState::Stopping); + assert_eq!(manager.current_state(), ServiceState::Stopping); + + // 更新状态到 Stopped + manager.update(ServiceState::Stopped); + assert_eq!(manager.current_state(), ServiceState::Stopped); + } +} diff --git a/rustfs/src/service.rs b/rustfs/src/service.rs index 7262fb689..d04729e25 100644 --- a/rustfs/src/service.rs +++ b/rustfs/src/service.rs @@ -80,8 +80,7 @@ pin_project! { impl Default for HybridBody where RestBody: Default, - - GrpcBody: Default, + // GrpcBody: Default, { fn default() -> Self { Self::Rest { diff --git a/rustfs/src/storage/access.rs b/rustfs/src/storage/access.rs index 0e1562cad..e2cad0c07 100644 --- a/rustfs/src/storage/access.rs +++ b/rustfs/src/storage/access.rs @@ -1,5 +1,6 @@ use super::ecfs::FS; -use crate::auth::{check_key_valid, get_condition_values}; +use crate::auth::{check_key_valid, get_condition_values, get_session_token}; +use crate::license::license_check; use ecstore::bucket::policy_sys::PolicySys; use iam::error::Error as IamError; use policy::auth; @@ -155,7 +156,8 @@ impl S3Access for FS { // ); let (cred, is_owner) = if let Some(input_cred) = cx.credentials() { - let (cred, is_owner) = check_key_valid(cx.headers(), &input_cred.access_key).await?; + let (cred, is_owner) = + check_key_valid(get_session_token(cx.uri(), cx.headers()).unwrap_or_default(), &input_cred.access_key).await?; (Some(cred), is_owner) } else { (None, false) @@ -179,6 +181,8 @@ impl S3Access for FS { /// /// This method returns `Ok(())` by default. async fn create_bucket(&self, req: &mut S3Request) -> S3Result<()> { + license_check().map_err(|er| s3_error!(AccessDenied, "{:?}", er.to_string()))?; + let req_info = req.extensions.get_mut::().expect("ReqInfo not found"); req_info.bucket = Some(req.input.bucket.clone()); @@ -240,6 +244,7 @@ impl S3Access for FS { /// /// This method returns `Ok(())` by default. async fn create_multipart_upload(&self, _req: &mut S3Request) -> S3Result<()> { + license_check().map_err(|er| s3_error!(AccessDenied, "{:?}", er.to_string()))?; Ok(()) } @@ -994,6 +999,8 @@ impl S3Access for FS { /// /// This method returns `Ok(())` by default. async fn put_object(&self, req: &mut S3Request) -> S3Result<()> { + license_check().map_err(|er| s3_error!(AccessDenied, "{:?}", er.to_string()))?; + let req_info = req.extensions.get_mut::().expect("ReqInfo not found"); req_info.bucket = Some(req.input.bucket.clone()); req_info.object = Some(req.input.key.clone()); diff --git a/rustfs/src/storage/ecfs.rs b/rustfs/src/storage/ecfs.rs index 51cf0ea9c..f1f53b9fb 100644 --- a/rustfs/src/storage/ecfs.rs +++ b/rustfs/src/storage/ecfs.rs @@ -4,8 +4,14 @@ use super::options::extract_metadata; use super::options::put_opts; use crate::auth::get_condition_values; use crate::storage::access::ReqInfo; +use api::query::Context; +use api::query::Query; +use api::server::dbms::DatabaseManagerSystem; use bytes::Bytes; use common::error::Result; +use datafusion::arrow::csv::WriterBuilder as CsvWriterBuilder; +use datafusion::arrow::json::writer::JsonArray; +use datafusion::arrow::json::WriterBuilder as JsonWriterBuilder; use ecstore::bucket::error::BucketMetadataError; use ecstore::bucket::metadata::BUCKET_LIFECYCLE_CONFIG; use ecstore::bucket::metadata::BUCKET_NOTIFICATION_CONFIG; @@ -40,13 +46,13 @@ use futures::pin_mut; use futures::{Stream, StreamExt}; use http::HeaderMap; use lazy_static::lazy_static; -use log::warn; use policy::auth; use policy::policy::action::Action; use policy::policy::action::S3Action; use policy::policy::BucketPolicy; use policy::policy::BucketPolicyArgs; use policy::policy::Validator; +use query::instance::make_rustfsms; use s3s::dto::*; use s3s::s3_error; use s3s::S3Error; @@ -56,11 +62,14 @@ use s3s::S3; use s3s::{S3Request, S3Response}; use std::fmt::Debug; use std::str::FromStr; +use tokio::sync::mpsc; +use tokio_stream::wrappers::ReceiverStream; use tokio_util::io::ReaderStream; use tokio_util::io::StreamReader; use tracing::debug; use tracing::error; use tracing::info; +use tracing::warn; use transform_stream::AsyncTryStream; use uuid::Uuid; @@ -221,7 +230,7 @@ impl S3 for FS { #[tracing::instrument(level = "debug", skip(self, req))] async fn delete_bucket(&self, req: S3Request) -> S3Result> { let input = req.input; - // TODO: DeleteBucketInput 没有force参数? + // TODO: DeleteBucketInput 没有 force 参数? let Some(store) = new_object_layer_fn() else { return Err(S3Error::with_message(S3ErrorCode::InternalError, "Not init".to_string())); }; @@ -252,14 +261,7 @@ impl S3 for FS { .await .map_err(to_s3_error)?; - let version_id = opts - .version_id - .as_ref() - .map(|v| match Uuid::parse_str(v) { - Ok(id) => Some(id), - Err(_) => None, - }) - .unwrap_or_default(); + let version_id = opts.version_id.as_ref().map(|v| Uuid::parse_str(v).ok()).unwrap_or_default(); let dobj = ObjectToDelete { object_name: key, version_id, @@ -316,14 +318,7 @@ impl S3 for FS { .objects .iter() .map(|v| { - let version_id = v - .version_id - .as_ref() - .map(|v| match Uuid::parse_str(v) { - Ok(id) => Some(id), - Err(_) => None, - }) - .unwrap_or_default(); + let version_id = v.version_id.as_ref().map(|v| Uuid::parse_str(v).ok()).unwrap_or_default(); ObjectToDelete { object_name: v.key.clone(), version_id, @@ -398,8 +393,6 @@ impl S3 for FS { async fn get_object(&self, req: S3Request) -> S3Result> { // mc get 3 - // warn!("get_object input {:?}, vid {:?}", &req.input, req.input.version_id); - let GetObjectInput { bucket, key, @@ -438,8 +431,6 @@ impl S3 for FS { return Err(s3_error!(InvalidArgument, "range and part_number invalid")); } - // let metadata = extract_metadata(&req.headers); - let opts: ObjectOptions = get_opts(&bucket, &key, version_id, part_number, &req.headers) .await .map_err(to_s3_error)?; @@ -507,16 +498,49 @@ impl S3 for FS { #[tracing::instrument(level = "debug", skip(self, req))] async fn head_object(&self, req: S3Request) -> S3Result> { // mc get 2 - let HeadObjectInput { bucket, key, .. } = req.input; + let HeadObjectInput { + bucket, + key, + version_id, + part_number, + range, + .. + } = req.input; + + let part_number = part_number.map(|v| v as usize); + + if let Some(part_num) = part_number { + if part_num == 0 { + return Err(s3_error!(InvalidArgument, "part_numer invalid")); + } + } + + let rs = range.map(|v| match v { + Range::Int { first, last } => HTTPRangeSpec { + is_suffix_length: false, + start: first as usize, + end: last.map(|v| v as usize), + }, + Range::Suffix { length } => HTTPRangeSpec { + is_suffix_length: true, + start: length as usize, + end: None, + }, + }); + + if rs.is_some() && part_number.is_some() { + return Err(s3_error!(InvalidArgument, "range and part_number invalid")); + } + + let opts: ObjectOptions = get_opts(&bucket, &key, version_id, part_number, &req.headers) + .await + .map_err(to_s3_error)?; let Some(store) = new_object_layer_fn() else { return Err(S3Error::with_message(S3ErrorCode::InternalError, "Not init".to_string())); }; - let info = store - .get_object_info(&bucket, &key, &ObjectOptions::default()) - .await - .map_err(to_s3_error)?; + let info = store.get_object_info(&bucket, &key, &opts).await.map_err(to_s3_error)?; // warn!("head_object info {:?}", &info); @@ -1045,11 +1069,11 @@ impl S3 for FS { #[tracing::instrument(level = "debug", skip(self))] async fn get_bucket_tagging(&self, req: S3Request) -> S3Result> { - let GetBucketTaggingInput { bucket, .. } = req.input; + let bucket = req.input.bucket.clone(); // check bucket exists. let _bucket = self - .head_bucket(S3Request::new(HeadBucketInput { - bucket: bucket.clone(), + .head_bucket(req.map_input(|input| HeadBucketInput { + bucket: input.bucket, expected_bucket_owner: None, })) .await?; @@ -1859,6 +1883,69 @@ impl S3 for FS { } Ok(S3Response::new(PutObjectAclOutput::default())) } + + async fn select_object_content( + &self, + req: S3Request, + ) -> S3Result> { + info!("handle select_object_content"); + + let input = req.input; + info!("{:?}", input); + + let db = make_rustfsms(input.clone(), false).await.map_err(|e| { + error!("make db failed, {}", e.to_string()); + s3_error!(InternalError) + })?; + let query = Query::new(Context { input: input.clone() }, input.request.expression); + let result = db.execute(&query).await.map_err(|_| s3_error!(InternalError))?; + + let results = result.result().chunk_result().await.unwrap().to_vec(); + + let mut buffer = Vec::new(); + if input.request.output_serialization.csv.is_some() { + let mut csv_writer = CsvWriterBuilder::new().with_header(false).build(&mut buffer); + for batch in results { + csv_writer + .write(&batch) + .map_err(|e| s3_error!(InternalError, "cann't encode output to csv. e: {}", e.to_string()))?; + } + } else if input.request.output_serialization.json.is_some() { + let mut json_writer = JsonWriterBuilder::new() + .with_explicit_nulls(true) + .build::<_, JsonArray>(&mut buffer); + for batch in results { + json_writer + .write(&batch) + .map_err(|e| s3_error!(InternalError, "cann't encode output to json. e: {}", e.to_string()))?; + } + json_writer + .finish() + .map_err(|e| s3_error!(InternalError, "writer output into json error, e: {}", e.to_string()))?; + } else { + return Err(s3_error!(InvalidArgument, "unknow output format")); + } + + let (tx, rx) = mpsc::channel::>(2); + let stream = ReceiverStream::new(rx); + tokio::spawn(async move { + let _ = tx + .send(Ok(SelectObjectContentEvent::Cont(ContinuationEvent::default()))) + .await; + let _ = tx + .send(Ok(SelectObjectContentEvent::Records(RecordsEvent { + payload: Some(Bytes::from(buffer)), + }))) + .await; + let _ = tx.send(Ok(SelectObjectContentEvent::End(EndEvent::default()))).await; + + drop(tx); + }); + + Ok(S3Response::new(SelectObjectContentOutput { + payload: Some(SelectObjectContentEventStream::new(stream)), + })) + } } #[allow(dead_code)] diff --git a/rustfs/src/utils.rs b/rustfs/src/utils.rs deleted file mode 100644 index 1496bd3f6..000000000 --- a/rustfs/src/utils.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::net::IpAddr; - -pub(crate) fn get_local_ip() -> Option { - match local_ip_address::local_ip() { - Ok(IpAddr::V4(ip)) => Some(ip), - Err(_) => None, - Ok(IpAddr::V6(_)) => todo!(), - } -} diff --git a/rustfs/src/utils/mod.rs b/rustfs/src/utils/mod.rs new file mode 100644 index 000000000..2d12f64d2 --- /dev/null +++ b/rustfs/src/utils/mod.rs @@ -0,0 +1,37 @@ +use rustls_pemfile::{certs, private_key}; +use rustls_pki_types::{CertificateDer, PrivateKeyDer}; +use std::net::IpAddr; +use std::{fs, io}; + +pub(crate) fn get_local_ip() -> Option { + match local_ip_address::local_ip() { + Ok(IpAddr::V4(ip)) => Some(ip), + Err(_) => None, + Ok(IpAddr::V6(_)) => todo!(), + } +} + +/// Load public certificate from file. +pub(crate) fn load_certs(filename: &str) -> io::Result>> { + // Open certificate file. + let cert_file = fs::File::open(filename).map_err(|e| error(format!("failed to open {}: {}", filename, e)))?; + let mut reader = io::BufReader::new(cert_file); + + // Load and return certificate. + let certs = certs(&mut reader).collect::, _>>()?; + Ok(certs) +} + +/// Load private key from file. +pub(crate) fn load_private_key(filename: &str) -> io::Result> { + // Open keyfile. + let keyfile = fs::File::open(filename).map_err(|e| error(format!("failed to open {}: {}", filename, e)))?; + let mut reader = io::BufReader::new(keyfile); + + // Load and return a single private key. + private_key(&mut reader)?.ok_or_else(|| error(format!("no private key found in {}", filename))) +} + +pub(crate) fn error(err: String) -> io::Error { + io::Error::new(io::ErrorKind::Other, err) +} diff --git a/rustfs/static/readme.md b/rustfs/static/readme.md deleted file mode 100644 index 325303e1a..000000000 --- a/rustfs/static/readme.md +++ /dev/null @@ -1 +0,0 @@ -console static path, do not delete \ No newline at end of file diff --git a/s3select/api/Cargo.toml b/s3select/api/Cargo.toml new file mode 100644 index 000000000..1936e846a --- /dev/null +++ b/s3select/api/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "api" +version.workspace = true +edition.workspace = true + +[dependencies] +async-trait.workspace = true +bytes.workspace = true +chrono.workspace = true +datafusion = { workspace = true } +ecstore.workspace = true +futures = { workspace = true } +futures-core = "0.3.31" +http.workspace = true +object_store = "0.11.2" +s3s.workspace = true +snafu = { workspace = true, features = ["backtrace"] } +tokio.workspace = true +tokio-util.workspace = true +tracing.workspace = true +transform-stream.workspace = true +url.workspace = true \ No newline at end of file diff --git a/s3select/api/src/lib.rs b/s3select/api/src/lib.rs new file mode 100644 index 000000000..acaead722 --- /dev/null +++ b/s3select/api/src/lib.rs @@ -0,0 +1,77 @@ +use std::fmt::Display; + +use datafusion::{common::DataFusionError, sql::sqlparser::parser::ParserError}; +use snafu::{Backtrace, Location, Snafu}; + +pub mod object_store; +pub mod query; +pub mod server; + +pub type QueryResult = Result; + +#[derive(Debug, Snafu)] +#[snafu(visibility(pub))] +pub enum QueryError { + Datafusion { + source: DataFusionError, + location: Location, + backtrace: Backtrace, + }, + + #[snafu(display("This feature is not implemented: {}", err))] + NotImplemented { err: String }, + + #[snafu(display("Multi-statement not allow, found num:{}, sql:{}", num, sql))] + MultiStatement { num: usize, sql: String }, + + #[snafu(display("Failed to build QueryDispatcher. err: {}", err))] + BuildQueryDispatcher { err: String }, + + #[snafu(display("The query has been canceled"))] + Cancel, + + #[snafu(display("{}", source))] + Parser { source: ParserError }, + + #[snafu(display("Udf not exists, name:{}.", name))] + FunctionNotExists { name: String }, + + #[snafu(display("Udf already exists, name:{}.", name))] + FunctionExists { name: String }, + + #[snafu(display("Store Error, e:{}.", e))] + StoreError { e: String }, +} + +impl From for QueryError { + fn from(value: DataFusionError) -> Self { + match value { + DataFusionError::External(e) if e.downcast_ref::().is_some() => *e.downcast::().unwrap(), + + v => Self::Datafusion { + source: v, + location: Default::default(), + backtrace: Backtrace::capture(), + }, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResolvedTable { + // path + table: String, +} + +impl ResolvedTable { + pub fn table(&self) -> &str { + &self.table + } +} + +impl Display for ResolvedTable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { table } = self; + write!(f, "{table}") + } +} diff --git a/s3select/api/src/object_store.rs b/s3select/api/src/object_store.rs new file mode 100644 index 000000000..7772936ce --- /dev/null +++ b/s3select/api/src/object_store.rs @@ -0,0 +1,177 @@ +use async_trait::async_trait; +use bytes::Bytes; +use chrono::Utc; +use ecstore::io::READ_BUFFER_SIZE; +use ecstore::new_object_layer_fn; +use ecstore::store::ECStore; +use ecstore::store_api::ObjectIO; +use ecstore::store_api::ObjectOptions; +use ecstore::StorageAPI; +use futures::pin_mut; +use futures::{Stream, StreamExt}; +use futures_core::stream::BoxStream; +use http::HeaderMap; +use object_store::path::Path; +use object_store::Attributes; +use object_store::GetOptions; +use object_store::GetResult; +use object_store::ListResult; +use object_store::MultipartUpload; +use object_store::ObjectMeta; +use object_store::ObjectStore; +use object_store::PutMultipartOpts; +use object_store::PutOptions; +use object_store::PutPayload; +use object_store::PutResult; +use object_store::{Error as o_Error, Result}; +use s3s::dto::SelectObjectContentInput; +use s3s::s3_error; +use s3s::S3Result; +use std::ops::Range; +use std::sync::Arc; +use tokio_util::io::ReaderStream; +use tracing::info; +use transform_stream::AsyncTryStream; + +#[derive(Debug)] +pub struct EcObjectStore { + input: SelectObjectContentInput, + + store: Arc, +} + +impl EcObjectStore { + pub fn new(input: SelectObjectContentInput) -> S3Result { + let Some(store) = new_object_layer_fn() else { + return Err(s3_error!(InternalError, "ec store not inited")); + }; + + Ok(Self { input, store }) + } +} + +impl std::fmt::Display for EcObjectStore { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("EcObjectStore") + } +} + +#[async_trait] +impl ObjectStore for EcObjectStore { + async fn put_opts(&self, _location: &Path, _payload: PutPayload, _opts: PutOptions) -> Result { + unimplemented!() + } + + async fn put_multipart_opts(&self, _location: &Path, _opts: PutMultipartOpts) -> Result> { + unimplemented!() + } + + async fn get_opts(&self, location: &Path, _options: GetOptions) -> Result { + info!("{:?}", location); + let opts = ObjectOptions::default(); + let h = HeaderMap::new(); + let reader = self + .store + .get_object_reader(&self.input.bucket, &self.input.key, None, h, &opts) + .await + .map_err(|_| o_Error::NotFound { + path: format!("{}/{}", self.input.bucket, self.input.key), + source: "can not get object info".into(), + })?; + + // let stream = stream::unfold(reader.stream, |mut blob| async move { + // match blob.next().await { + // Some(Ok(chunk)) => { + // let bytes = chunk; + // Some((Ok(bytes), blob)) + // } + // _ => None, + // } + // }) + // .boxed(); + let meta = ObjectMeta { + location: location.clone(), + last_modified: Utc::now(), + size: reader.object_info.size, + e_tag: reader.object_info.etag, + version: None, + }; + let attributes = Attributes::default(); + + Ok(GetResult { + payload: object_store::GetResultPayload::Stream( + bytes_stream(ReaderStream::with_capacity(reader.stream, READ_BUFFER_SIZE), reader.object_info.size).boxed(), + ), + meta, + range: 0..reader.object_info.size, + attributes, + }) + } + + async fn get_ranges(&self, _location: &Path, _ranges: &[Range]) -> Result> { + unimplemented!() + } + + async fn head(&self, location: &Path) -> Result { + info!("{:?}", location); + let opts = ObjectOptions::default(); + let info = self + .store + .get_object_info(&self.input.bucket, &self.input.key, &opts) + .await + .map_err(|_| o_Error::NotFound { + path: format!("{}/{}", self.input.bucket, self.input.key), + source: "can not get object info".into(), + })?; + + Ok(ObjectMeta { + location: location.clone(), + last_modified: Utc::now(), + size: info.size, + e_tag: info.etag, + version: None, + }) + } + + async fn delete(&self, _location: &Path) -> Result<()> { + unimplemented!() + } + + fn list(&self, _prefix: Option<&Path>) -> BoxStream<'_, Result> { + unimplemented!() + } + + async fn list_with_delimiter(&self, _prefix: Option<&Path>) -> Result { + unimplemented!() + } + + async fn copy(&self, _from: &Path, _to: &Path) -> Result<()> { + unimplemented!() + } + + async fn copy_if_not_exists(&self, _from: &Path, _too: &Path) -> Result<()> { + unimplemented!() + } +} + +pub fn bytes_stream(stream: S, content_length: usize) -> impl Stream> + Send + 'static +where + S: Stream> + Send + 'static, +{ + AsyncTryStream::::new(|mut y| async move { + pin_mut!(stream); + let mut remaining: usize = content_length; + while let Some(result) = stream.next().await { + let mut bytes = result.map_err(|e| o_Error::Generic { + store: "", + source: Box::new(e), + })?; + if bytes.len() > remaining { + bytes.truncate(remaining); + } + remaining -= bytes.len(); + y.yield_ok(bytes).await; + } + Ok(()) + }) +} diff --git a/s3select/api/src/query/analyzer.rs b/s3select/api/src/query/analyzer.rs new file mode 100644 index 000000000..db849566c --- /dev/null +++ b/s3select/api/src/query/analyzer.rs @@ -0,0 +1,12 @@ +use std::sync::Arc; + +use datafusion::logical_expr::LogicalPlan; + +use super::session::SessionCtx; +use crate::QueryResult; + +pub type AnalyzerRef = Arc; + +pub trait Analyzer { + fn analyze(&self, plan: &LogicalPlan, session: &SessionCtx) -> QueryResult; +} diff --git a/s3select/api/src/query/ast.rs b/s3select/api/src/query/ast.rs new file mode 100644 index 000000000..dbe9b4b2a --- /dev/null +++ b/s3select/api/src/query/ast.rs @@ -0,0 +1,8 @@ +use datafusion::sql::sqlparser::ast::Statement; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ExtStatement { + /// ANSI SQL AST node + SqlStatement(Box), + // we can expand command +} diff --git a/s3select/api/src/query/datasource/mod.rs b/s3select/api/src/query/datasource/mod.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/s3select/api/src/query/datasource/mod.rs @@ -0,0 +1 @@ + diff --git a/s3select/api/src/query/dispatcher.rs b/s3select/api/src/query/dispatcher.rs new file mode 100644 index 000000000..433ddf01e --- /dev/null +++ b/s3select/api/src/query/dispatcher.rs @@ -0,0 +1,32 @@ +use std::sync::Arc; + +use async_trait::async_trait; + +use crate::QueryResult; + +use super::{ + execution::{Output, QueryStateMachine}, + logical_planner::Plan, + Query, +}; + +#[async_trait] +pub trait QueryDispatcher: Send + Sync { + // fn create_query_id(&self) -> QueryId; + + // fn query_info(&self, id: &QueryId); + + async fn execute_query(&self, query: &Query) -> QueryResult; + + async fn build_logical_plan(&self, query_state_machine: Arc) -> QueryResult>; + + async fn execute_logical_plan(&self, logical_plan: Plan, query_state_machine: Arc) -> QueryResult; + + async fn build_query_state_machine(&self, query: Query) -> QueryResult>; + + // fn running_query_infos(&self) -> Vec; + + // fn running_query_status(&self) -> Vec; + + // fn cancel_query(&self, id: &QueryId); +} diff --git a/s3select/api/src/query/execution.rs b/s3select/api/src/query/execution.rs new file mode 100644 index 000000000..10c48acc3 --- /dev/null +++ b/s3select/api/src/query/execution.rs @@ -0,0 +1,241 @@ +use std::fmt::Display; +use std::pin::Pin; +use std::sync::atomic::{AtomicPtr, Ordering}; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::time::{Duration, Instant}; + +use async_trait::async_trait; +use datafusion::arrow::datatypes::{Schema, SchemaRef}; +use datafusion::arrow::record_batch::RecordBatch; +use datafusion::physical_plan::SendableRecordBatchStream; +use futures::{Stream, StreamExt, TryStreamExt}; + +use crate::{QueryError, QueryResult}; + +use super::logical_planner::Plan; +use super::session::SessionCtx; +use super::Query; + +pub type QueryExecutionRef = Arc; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum QueryType { + Batch, + Stream, +} + +impl Display for QueryType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Batch => write!(f, "batch"), + Self::Stream => write!(f, "stream"), + } + } +} + +#[async_trait] +pub trait QueryExecution: Send + Sync { + fn query_type(&self) -> QueryType { + QueryType::Batch + } + // 开始 + async fn start(&self) -> QueryResult; + // 停止 + fn cancel(&self) -> QueryResult<()>; +} + +pub enum Output { + StreamData(SendableRecordBatchStream), + Nil(()), +} + +impl Output { + pub fn schema(&self) -> SchemaRef { + match self { + Self::StreamData(stream) => stream.schema(), + Self::Nil(_) => Arc::new(Schema::empty()), + } + } + + pub async fn chunk_result(self) -> QueryResult> { + match self { + Self::Nil(_) => Ok(vec![]), + Self::StreamData(stream) => { + let schema = stream.schema(); + let mut res: Vec = stream.try_collect::>().await?; + if res.is_empty() { + res.push(RecordBatch::new_empty(schema)); + } + Ok(res) + } + } + } + + pub async fn num_rows(self) -> usize { + match self.chunk_result().await { + Ok(rb) => rb.iter().map(|e| e.num_rows()).sum(), + Err(_) => 0, + } + } + + /// Returns the number of records affected by the query operation + /// + /// If it is a select statement, returns the number of rows in the result set + /// + /// -1 means unknown + /// + /// panic! when StreamData's number of records greater than i64::Max + pub async fn affected_rows(self) -> i64 { + self.num_rows().await as i64 + } +} + +impl Stream for Output { + type Item = std::result::Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.get_mut(); + match this { + Output::StreamData(stream) => stream.poll_next_unpin(cx).map_err(|e| e.into()), + Output::Nil(_) => Poll::Ready(None), + } + } +} + +#[async_trait] +pub trait QueryExecutionFactory { + async fn create_query_execution( + &self, + plan: Plan, + query_state_machine: QueryStateMachineRef, + ) -> QueryResult; +} + +pub type QueryStateMachineRef = Arc; + +pub struct QueryStateMachine { + pub session: SessionCtx, + pub query: Query, + + state: AtomicPtr, + start: Instant, +} + +impl QueryStateMachine { + pub fn begin(query: Query, session: SessionCtx) -> Self { + Self { + session, + query, + state: AtomicPtr::new(Box::into_raw(Box::new(QueryState::ACCEPTING))), + start: Instant::now(), + } + } + + pub fn begin_analyze(&self) { + // TODO record time + self.translate_to(Box::new(QueryState::RUNNING(RUNNING::ANALYZING))); + } + + pub fn end_analyze(&self) { + // TODO record time + } + + pub fn begin_optimize(&self) { + // TODO record time + self.translate_to(Box::new(QueryState::RUNNING(RUNNING::OPTMIZING))); + } + + pub fn end_optimize(&self) { + // TODO + } + + pub fn begin_schedule(&self) { + // TODO + self.translate_to(Box::new(QueryState::RUNNING(RUNNING::SCHEDULING))); + } + + pub fn end_schedule(&self) { + // TODO + } + + pub fn finish(&self) { + // TODO + self.translate_to(Box::new(QueryState::DONE(DONE::FINISHED))); + } + + pub fn cancel(&self) { + // TODO + self.translate_to(Box::new(QueryState::DONE(DONE::CANCELLED))); + } + + pub fn fail(&self) { + // TODO + self.translate_to(Box::new(QueryState::DONE(DONE::FAILED))); + } + + pub fn state(&self) -> &QueryState { + unsafe { &*self.state.load(Ordering::Relaxed) } + } + + pub fn duration(&self) -> Duration { + self.start.elapsed() + } + + fn translate_to(&self, state: Box) { + self.state.store(Box::into_raw(state), Ordering::Relaxed); + } +} + +#[derive(Debug, Clone)] +pub enum QueryState { + ACCEPTING, + RUNNING(RUNNING), + DONE(DONE), +} + +impl AsRef for QueryState { + fn as_ref(&self) -> &str { + match self { + QueryState::ACCEPTING => "ACCEPTING", + QueryState::RUNNING(e) => e.as_ref(), + QueryState::DONE(e) => e.as_ref(), + } + } +} + +#[derive(Debug, Clone)] +pub enum RUNNING { + DISPATCHING, + ANALYZING, + OPTMIZING, + SCHEDULING, +} + +impl AsRef for RUNNING { + fn as_ref(&self) -> &str { + match self { + Self::DISPATCHING => "DISPATCHING", + Self::ANALYZING => "ANALYZING", + Self::OPTMIZING => "OPTMIZING", + Self::SCHEDULING => "SCHEDULING", + } + } +} + +#[derive(Debug, Clone)] +pub enum DONE { + FINISHED, + FAILED, + CANCELLED, +} + +impl AsRef for DONE { + fn as_ref(&self) -> &str { + match self { + Self::FINISHED => "FINISHED", + Self::FAILED => "FAILED", + Self::CANCELLED => "CANCELLED", + } + } +} diff --git a/s3select/api/src/query/function.rs b/s3select/api/src/query/function.rs new file mode 100644 index 000000000..3a8c67619 --- /dev/null +++ b/s3select/api/src/query/function.rs @@ -0,0 +1,24 @@ +use std::sync::Arc; + +use datafusion::logical_expr::{AggregateUDF, ScalarUDF, WindowUDF}; + +use crate::QueryResult; + +pub type FuncMetaManagerRef = Arc; +pub trait FunctionMetadataManager { + fn register_udf(&mut self, udf: Arc) -> QueryResult<()>; + + fn register_udaf(&mut self, udaf: Arc) -> QueryResult<()>; + + fn register_udwf(&mut self, udwf: Arc) -> QueryResult<()>; + + fn udf(&self, name: &str) -> QueryResult>; + + fn udaf(&self, name: &str) -> QueryResult>; + + fn udwf(&self, name: &str) -> QueryResult>; + + fn udfs(&self) -> Vec; + fn udafs(&self) -> Vec; + fn udwfs(&self) -> Vec; +} diff --git a/s3select/api/src/query/logical_planner.rs b/s3select/api/src/query/logical_planner.rs new file mode 100644 index 000000000..cef844b37 --- /dev/null +++ b/s3select/api/src/query/logical_planner.rs @@ -0,0 +1,40 @@ +use async_trait::async_trait; +use datafusion::arrow::datatypes::SchemaRef; +use datafusion::logical_expr::LogicalPlan as DFPlan; + +use crate::QueryResult; + +use super::ast::ExtStatement; +use super::session::SessionCtx; + +#[derive(Clone)] +pub enum Plan { + // only support query sql + /// Query plan + Query(QueryPlan), +} + +impl Plan { + pub fn schema(&self) -> SchemaRef { + match self { + Self::Query(p) => SchemaRef::from(p.df_plan.schema().as_ref().to_owned()), + } + } +} + +#[derive(Debug, Clone)] +pub struct QueryPlan { + pub df_plan: DFPlan, + pub is_tag_scan: bool, +} + +impl QueryPlan { + pub fn is_explain(&self) -> bool { + matches!(self.df_plan, DFPlan::Explain(_) | DFPlan::Analyze(_)) + } +} + +#[async_trait] +pub trait LogicalPlanner { + async fn create_logical_plan(&self, statement: ExtStatement, session: &SessionCtx) -> QueryResult; +} diff --git a/s3select/api/src/query/mod.rs b/s3select/api/src/query/mod.rs new file mode 100644 index 000000000..6ddd2dc86 --- /dev/null +++ b/s3select/api/src/query/mod.rs @@ -0,0 +1,41 @@ +use s3s::dto::SelectObjectContentInput; + +pub mod analyzer; +pub mod ast; +pub mod datasource; +pub mod dispatcher; +pub mod execution; +pub mod function; +pub mod logical_planner; +pub mod optimizer; +pub mod parser; +pub mod physical_planner; +pub mod scheduler; +pub mod session; + +#[derive(Clone)] +pub struct Context { + // maybe we need transfer some info? + pub input: SelectObjectContentInput, +} + +#[derive(Clone)] +pub struct Query { + context: Context, + content: String, +} + +impl Query { + #[inline(always)] + pub fn new(context: Context, content: String) -> Self { + Self { context, content } + } + + pub fn context(&self) -> &Context { + &self.context + } + + pub fn content(&self) -> &str { + self.content.as_str() + } +} diff --git a/s3select/api/src/query/optimizer.rs b/s3select/api/src/query/optimizer.rs new file mode 100644 index 000000000..c2392eb92 --- /dev/null +++ b/s3select/api/src/query/optimizer.rs @@ -0,0 +1,15 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use datafusion::physical_plan::ExecutionPlan; + +use super::logical_planner::QueryPlan; +use super::session::SessionCtx; +use crate::QueryResult; + +pub type OptimizerRef = Arc; + +#[async_trait] +pub trait Optimizer { + async fn optimize(&self, plan: &QueryPlan, session: &SessionCtx) -> QueryResult>; +} diff --git a/s3select/api/src/query/parser.rs b/s3select/api/src/query/parser.rs new file mode 100644 index 000000000..76d7e723f --- /dev/null +++ b/s3select/api/src/query/parser.rs @@ -0,0 +1,8 @@ +use std::collections::VecDeque; + +use super::ast::ExtStatement; +use crate::QueryResult; + +pub trait Parser { + fn parse(&self, sql: &str) -> QueryResult>; +} diff --git a/s3select/api/src/query/physical_planner.rs b/s3select/api/src/query/physical_planner.rs new file mode 100644 index 000000000..c71787e9c --- /dev/null +++ b/s3select/api/src/query/physical_planner.rs @@ -0,0 +1,21 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use datafusion::logical_expr::LogicalPlan; +use datafusion::physical_plan::ExecutionPlan; +use datafusion::physical_planner::ExtensionPlanner; + +use super::session::SessionCtx; +use crate::QueryResult; + +#[async_trait] +pub trait PhysicalPlanner { + /// Given a `LogicalPlan`, create an `ExecutionPlan` suitable for execution + async fn create_physical_plan( + &self, + logical_plan: &LogicalPlan, + session_state: &SessionCtx, + ) -> QueryResult>; + + fn inject_physical_transform_rule(&mut self, rule: Arc); +} diff --git a/s3select/api/src/query/scheduler.rs b/s3select/api/src/query/scheduler.rs new file mode 100644 index 000000000..3dd49c225 --- /dev/null +++ b/s3select/api/src/query/scheduler.rs @@ -0,0 +1,32 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use datafusion::common::Result; +use datafusion::execution::context::TaskContext; +use datafusion::physical_plan::{ExecutionPlan, SendableRecordBatchStream}; + +pub type SchedulerRef = Arc; + +#[async_trait] +pub trait Scheduler { + /// Schedule the provided [`ExecutionPlan`] on this [`Scheduler`]. + /// + /// Returns a [`ExecutionResults`] that can be used to receive results as they are produced, + /// as a [`futures::Stream`] of [`RecordBatch`] + async fn schedule(&self, plan: Arc, context: Arc) -> Result; +} + +pub struct ExecutionResults { + stream: SendableRecordBatchStream, +} + +impl ExecutionResults { + pub fn new(stream: SendableRecordBatchStream) -> Self { + Self { stream } + } + + /// Returns a [`SendableRecordBatchStream`] of this execution + pub fn stream(self) -> SendableRecordBatchStream { + self.stream + } +} diff --git a/s3select/api/src/query/session.rs b/s3select/api/src/query/session.rs new file mode 100644 index 000000000..c9d91f51d --- /dev/null +++ b/s3select/api/src/query/session.rs @@ -0,0 +1,86 @@ +use std::sync::Arc; + +use bytes::Bytes; +use datafusion::{ + execution::{context::SessionState, runtime_env::RuntimeEnvBuilder, SessionStateBuilder}, + prelude::SessionContext, +}; +use object_store::{memory::InMemory, path::Path, ObjectStore}; +use tracing::error; + +use crate::{object_store::EcObjectStore, QueryError, QueryResult}; + +use super::Context; + +#[derive(Clone)] +pub struct SessionCtx { + _desc: Arc, + inner: SessionState, +} + +impl SessionCtx { + pub fn inner(&self) -> &SessionState { + &self.inner + } +} + +#[derive(Clone)] +pub struct SessionCtxDesc { + // maybe we need some info +} + +#[derive(Default)] +pub struct SessionCtxFactory { + pub is_test: bool, +} + +impl SessionCtxFactory { + pub async fn create_session_ctx(&self, context: &Context) -> QueryResult { + let df_session_ctx = self.build_df_session_context(context).await?; + + Ok(SessionCtx { + _desc: Arc::new(SessionCtxDesc {}), + inner: df_session_ctx.state(), + }) + } + + async fn build_df_session_context(&self, context: &Context) -> QueryResult { + let path = format!("s3://{}", context.input.bucket); + let store_url = url::Url::parse(&path).unwrap(); + let rt = RuntimeEnvBuilder::new().build()?; + let df_session_state = SessionStateBuilder::new() + .with_runtime_env(Arc::new(rt)) + .with_default_features(); + + let df_session_state = if self.is_test { + let store: Arc = Arc::new(InMemory::new()); + let data = b"id,name,age,department,salary + 1,Alice,25,HR,5000 + 2,Bob,30,IT,6000 + 3,Charlie,35,Finance,7000 + 4,Diana,22,Marketing,4500 + 5,Eve,28,IT,5500 + 6,Frank,40,Finance,8000 + 7,Grace,26,HR,5200 + 8,Henry,32,IT,6200 + 9,Ivy,24,Marketing,4800 + 10,Jack,38,Finance,7500"; + let data_bytes = Bytes::from(data.to_vec()); + let path = Path::from(context.input.key.clone()); + store.put(&path, data_bytes.into()).await.map_err(|e| { + error!("put data into memory failed: {}", e.to_string()); + QueryError::StoreError { e: e.to_string() } + })?; + + df_session_state.with_object_store(&store_url, Arc::new(store)).build() + } else { + let store = + EcObjectStore::new(context.input.clone()).map_err(|_| QueryError::NotImplemented { err: String::new() })?; + df_session_state.with_object_store(&store_url, Arc::new(store)).build() + }; + + let df_session_ctx = SessionContext::new_with_state(df_session_state); + + Ok(df_session_ctx) + } +} diff --git a/s3select/api/src/server/dbms.rs b/s3select/api/src/server/dbms.rs new file mode 100644 index 000000000..85d320555 --- /dev/null +++ b/s3select/api/src/server/dbms.rs @@ -0,0 +1,41 @@ +use async_trait::async_trait; + +use crate::{ + query::{ + execution::{Output, QueryStateMachineRef}, + logical_planner::Plan, + Query, + }, + QueryResult, +}; + +pub struct QueryHandle { + query: Query, + result: Output, +} + +impl QueryHandle { + pub fn new(query: Query, result: Output) -> Self { + Self { query, result } + } + + pub fn query(&self) -> &Query { + &self.query + } + + pub fn result(self) -> Output { + self.result + } +} + +#[async_trait] +pub trait DatabaseManagerSystem { + async fn execute(&self, query: &Query) -> QueryResult; + async fn build_query_state_machine(&self, query: Query) -> QueryResult; + async fn build_logical_plan(&self, query_state_machine: QueryStateMachineRef) -> QueryResult>; + async fn execute_logical_plan( + &self, + logical_plan: Plan, + query_state_machine: QueryStateMachineRef, + ) -> QueryResult; +} diff --git a/s3select/api/src/server/mod.rs b/s3select/api/src/server/mod.rs new file mode 100644 index 000000000..c2e7c7b5c --- /dev/null +++ b/s3select/api/src/server/mod.rs @@ -0,0 +1 @@ +pub mod dbms; diff --git a/s3select/query/Cargo.toml b/s3select/query/Cargo.toml new file mode 100644 index 000000000..8d9b159c2 --- /dev/null +++ b/s3select/query/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "query" +version.workspace = true +edition.workspace = true + +[dependencies] +api = { path = "../api" } +async-recursion = { workspace = true } +async-trait.workspace = true +datafusion = { workspace = true } +derive_builder = { workspace = true } +futures = { workspace = true } +parking_lot = { version = "0.12.3" } +s3s.workspace = true +snafu = { workspace = true, features = ["backtrace"] } +tokio = { workspace = true } +tracing = { workspace = true } diff --git a/s3select/query/src/data_source/mod.rs b/s3select/query/src/data_source/mod.rs new file mode 100644 index 000000000..b0704130e --- /dev/null +++ b/s3select/query/src/data_source/mod.rs @@ -0,0 +1 @@ +pub mod table_source; diff --git a/s3select/query/src/data_source/table_source.rs b/s3select/query/src/data_source/table_source.rs new file mode 100644 index 000000000..77df6e810 --- /dev/null +++ b/s3select/query/src/data_source/table_source.rs @@ -0,0 +1,138 @@ +use std::any::Any; +use std::borrow::Cow; +use std::fmt::Display; +use std::sync::Arc; +use std::write; + +use async_trait::async_trait; +use datafusion::arrow::datatypes::SchemaRef; +use datafusion::common::Result as DFResult; +use datafusion::datasource::listing::ListingTable; +use datafusion::datasource::{provider_as_source, TableProvider}; +use datafusion::error::DataFusionError; +use datafusion::logical_expr::{LogicalPlan, LogicalPlanBuilder, TableProviderFilterPushDown, TableSource}; +use datafusion::prelude::Expr; +use datafusion::sql::TableReference; +use tracing::debug; + +pub const TEMP_LOCATION_TABLE_NAME: &str = "external_location_table"; + +pub struct TableSourceAdapter { + database_name: String, + table_name: String, + table_handle: TableHandle, + + plan: LogicalPlan, +} + +impl TableSourceAdapter { + pub fn try_new( + table_ref: impl Into, + table_name: impl Into, + table_handle: impl Into, + ) -> Result { + let table_name: String = table_name.into(); + + let table_handle = table_handle.into(); + let plan = match &table_handle { + // TableScan + TableHandle::External(t) => { + let table_source = provider_as_source(t.clone()); + LogicalPlanBuilder::scan(table_ref, table_source, None)?.build()? + } + // TableScan + TableHandle::TableProvider(t) => { + let table_source = provider_as_source(t.clone()); + if let Some(plan) = table_source.get_logical_plan() { + LogicalPlanBuilder::from(plan.into_owned()).build()? + } else { + LogicalPlanBuilder::scan(table_ref, table_source, None)?.build()? + } + } + }; + + debug!("Table source logical plan node of {}:\n{}", table_name, plan.display_indent_schema()); + + Ok(Self { + database_name: "default_db".to_string(), + table_name, + table_handle, + plan, + }) + } + + pub fn database_name(&self) -> &str { + &self.database_name + } + + pub fn table_name(&self) -> &str { + &self.table_name + } + + pub fn table_handle(&self) -> &TableHandle { + &self.table_handle + } +} + +#[async_trait] +impl TableSource for TableSourceAdapter { + fn as_any(&self) -> &dyn Any { + self + } + + fn schema(&self) -> SchemaRef { + self.table_handle.schema() + } + + fn supports_filters_pushdown(&self, filter: &[&Expr]) -> DFResult> { + self.table_handle.supports_filters_pushdown(filter) + } + + /// Called by [`InlineTableScan`] + fn get_logical_plan(&self) -> Option> { + Some(Cow::Owned(self.plan.clone())) + } +} + +#[derive(Clone)] +pub enum TableHandle { + TableProvider(Arc), + External(Arc), +} + +impl TableHandle { + pub fn schema(&self) -> SchemaRef { + match self { + Self::External(t) => t.schema(), + Self::TableProvider(t) => t.schema(), + } + } + + pub fn supports_filters_pushdown(&self, filter: &[&Expr]) -> DFResult> { + match self { + Self::External(t) => t.supports_filters_pushdown(filter), + Self::TableProvider(t) => t.supports_filters_pushdown(filter), + } + } +} + +impl From> for TableHandle { + fn from(value: Arc) -> Self { + TableHandle::TableProvider(value) + } +} + +impl From> for TableHandle { + fn from(value: Arc) -> Self { + TableHandle::External(value) + } +} + +impl Display for TableHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::External(e) => write!(f, "External({:?})", e.table_paths()), + Self::TableProvider(_) => write!(f, "TableProvider"), + } + } +} diff --git a/s3select/query/src/dispatcher/manager.rs b/s3select/query/src/dispatcher/manager.rs new file mode 100644 index 000000000..e85e7a543 --- /dev/null +++ b/s3select/query/src/dispatcher/manager.rs @@ -0,0 +1,271 @@ +use std::{ + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; + +use api::{ + query::{ + ast::ExtStatement, + dispatcher::QueryDispatcher, + execution::{Output, QueryStateMachine}, + function::FuncMetaManagerRef, + logical_planner::{LogicalPlanner, Plan}, + parser::Parser, + session::{SessionCtx, SessionCtxFactory}, + Query, + }, + QueryError, QueryResult, +}; +use async_trait::async_trait; +use datafusion::{ + arrow::{datatypes::SchemaRef, record_batch::RecordBatch}, + config::CsvOptions, + datasource::{ + file_format::{csv::CsvFormat, json::JsonFormat, parquet::ParquetFormat}, + listing::{ListingOptions, ListingTable, ListingTableConfig, ListingTableUrl}, + }, + error::Result as DFResult, + execution::{RecordBatchStream, SendableRecordBatchStream}, +}; +use futures::{Stream, StreamExt}; +use s3s::dto::SelectObjectContentInput; + +use crate::{ + execution::factory::QueryExecutionFactoryRef, + metadata::{base_table::BaseTableProvider, ContextProviderExtension, MetadataProvider, TableHandleProviderRef}, + sql::logical::planner::DefaultLogicalPlanner, +}; + +#[derive(Clone)] +pub struct SimpleQueryDispatcher { + input: SelectObjectContentInput, + // client for default tenant + _default_table_provider: TableHandleProviderRef, + session_factory: Arc, + // parser + parser: Arc, + // get query execution factory + query_execution_factory: QueryExecutionFactoryRef, + func_manager: FuncMetaManagerRef, +} + +#[async_trait] +impl QueryDispatcher for SimpleQueryDispatcher { + async fn execute_query(&self, query: &Query) -> QueryResult { + let query_state_machine = { self.build_query_state_machine(query.clone()).await? }; + + let logical_plan = self.build_logical_plan(query_state_machine.clone()).await?; + let logical_plan = match logical_plan { + Some(plan) => plan, + None => return Ok(Output::Nil(())), + }; + let result = self.execute_logical_plan(logical_plan, query_state_machine).await?; + Ok(result) + } + + async fn build_logical_plan(&self, query_state_machine: Arc) -> QueryResult> { + let session = &query_state_machine.session; + let query = &query_state_machine.query; + + let scheme_provider = self.build_scheme_provider(session).await?; + + let logical_planner = DefaultLogicalPlanner::new(&scheme_provider); + + let statements = self.parser.parse(query.content())?; + + // not allow multi statement + if statements.len() > 1 { + return Err(QueryError::MultiStatement { + num: statements.len(), + sql: query_state_machine.query.content().to_string(), + }); + } + + let stmt = match statements.front() { + Some(stmt) => stmt.clone(), + None => return Ok(None), + }; + + let logical_plan = self + .statement_to_logical_plan(stmt, &logical_planner, query_state_machine) + .await?; + Ok(Some(logical_plan)) + } + + async fn execute_logical_plan(&self, logical_plan: Plan, query_state_machine: Arc) -> QueryResult { + self.execute_logical_plan(logical_plan, query_state_machine).await + } + + async fn build_query_state_machine(&self, query: Query) -> QueryResult> { + let session = self.session_factory.create_session_ctx(query.context()).await?; + + let query_state_machine = Arc::new(QueryStateMachine::begin(query, session)); + Ok(query_state_machine) + } +} + +impl SimpleQueryDispatcher { + async fn statement_to_logical_plan( + &self, + stmt: ExtStatement, + logical_planner: &DefaultLogicalPlanner<'_, S>, + query_state_machine: Arc, + ) -> QueryResult { + // begin analyze + query_state_machine.begin_analyze(); + let logical_plan = logical_planner + .create_logical_plan(stmt, &query_state_machine.session) + .await?; + query_state_machine.end_analyze(); + + Ok(logical_plan) + } + + async fn execute_logical_plan(&self, logical_plan: Plan, query_state_machine: Arc) -> QueryResult { + let execution = self + .query_execution_factory + .create_query_execution(logical_plan, query_state_machine.clone()) + .await?; + + match execution.start().await { + Ok(Output::StreamData(stream)) => Ok(Output::StreamData(Box::pin(TrackedRecordBatchStream { inner: stream }))), + Ok(nil @ Output::Nil(_)) => Ok(nil), + Err(err) => Err(err), + } + } + + async fn build_scheme_provider(&self, session: &SessionCtx) -> QueryResult { + let path = format!("s3://{}/{}", self.input.bucket, self.input.key); + let table_path = ListingTableUrl::parse(path)?; + let listing_options = if self.input.request.input_serialization.csv.is_some() { + let file_format = CsvFormat::default().with_options(CsvOptions::default().with_has_header(true)); + ListingOptions::new(Arc::new(file_format)).with_file_extension(".csv") + } else if self.input.request.input_serialization.parquet.is_some() { + let file_format = ParquetFormat::new(); + ListingOptions::new(Arc::new(file_format)).with_file_extension(".parquet") + } else if self.input.request.input_serialization.json.is_some() { + let file_format = JsonFormat::default(); + ListingOptions::new(Arc::new(file_format)).with_file_extension(".json") + } else { + return Err(QueryError::NotImplemented { + err: "not support this file type".to_string(), + }); + }; + + let resolve_schema = listing_options.infer_schema(session.inner(), &table_path).await?; + let config = ListingTableConfig::new(table_path) + .with_listing_options(listing_options) + .with_schema(resolve_schema); + let provider = Arc::new(ListingTable::try_new(config)?); + let current_session_table_provider = self.build_table_handle_provider()?; + let metadata_provider = + MetadataProvider::new(provider, current_session_table_provider, self.func_manager.clone(), session.clone()); + + Ok(metadata_provider) + } + + fn build_table_handle_provider(&self) -> QueryResult { + let current_session_table_provider: Arc = Arc::new(BaseTableProvider::default()); + + Ok(current_session_table_provider) + } +} + +pub struct TrackedRecordBatchStream { + inner: SendableRecordBatchStream, +} + +impl RecordBatchStream for TrackedRecordBatchStream { + fn schema(&self) -> SchemaRef { + self.inner.schema() + } +} + +impl Stream for TrackedRecordBatchStream { + type Item = DFResult; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_next_unpin(cx) + } +} + +#[derive(Default, Clone)] +pub struct SimpleQueryDispatcherBuilder { + input: Option, + default_table_provider: Option, + session_factory: Option>, + parser: Option>, + + query_execution_factory: Option, + + func_manager: Option, +} + +impl SimpleQueryDispatcherBuilder { + pub fn with_input(mut self, input: SelectObjectContentInput) -> Self { + self.input = Some(input); + self + } + pub fn with_default_table_provider(mut self, default_table_provider: TableHandleProviderRef) -> Self { + self.default_table_provider = Some(default_table_provider); + self + } + + pub fn with_session_factory(mut self, session_factory: Arc) -> Self { + self.session_factory = Some(session_factory); + self + } + + pub fn with_parser(mut self, parser: Arc) -> Self { + self.parser = Some(parser); + self + } + + pub fn with_query_execution_factory(mut self, query_execution_factory: QueryExecutionFactoryRef) -> Self { + self.query_execution_factory = Some(query_execution_factory); + self + } + + pub fn with_func_manager(mut self, func_manager: FuncMetaManagerRef) -> Self { + self.func_manager = Some(func_manager); + self + } + + pub fn build(self) -> QueryResult> { + let input = self.input.ok_or_else(|| QueryError::BuildQueryDispatcher { + err: "lost of input".to_string(), + })?; + + let session_factory = self.session_factory.ok_or_else(|| QueryError::BuildQueryDispatcher { + err: "lost of session_factory".to_string(), + })?; + + let parser = self.parser.ok_or_else(|| QueryError::BuildQueryDispatcher { + err: "lost of parser".to_string(), + })?; + + let query_execution_factory = self.query_execution_factory.ok_or_else(|| QueryError::BuildQueryDispatcher { + err: "lost of query_execution_factory".to_string(), + })?; + + let func_manager = self.func_manager.ok_or_else(|| QueryError::BuildQueryDispatcher { + err: "lost of func_manager".to_string(), + })?; + + let default_table_provider = self.default_table_provider.ok_or_else(|| QueryError::BuildQueryDispatcher { + err: "lost of default_table_provider".to_string(), + })?; + + let dispatcher = Arc::new(SimpleQueryDispatcher { + input, + _default_table_provider: default_table_provider, + session_factory, + parser, + query_execution_factory, + func_manager, + }); + + Ok(dispatcher) + } +} diff --git a/s3select/query/src/dispatcher/mod.rs b/s3select/query/src/dispatcher/mod.rs new file mode 100644 index 000000000..ff8de9eb9 --- /dev/null +++ b/s3select/query/src/dispatcher/mod.rs @@ -0,0 +1 @@ +pub mod manager; diff --git a/s3select/query/src/execution/factory.rs b/s3select/query/src/execution/factory.rs new file mode 100644 index 000000000..9960d68a3 --- /dev/null +++ b/s3select/query/src/execution/factory.rs @@ -0,0 +1,46 @@ +use std::sync::Arc; + +use api::{ + query::{ + execution::{QueryExecutionFactory, QueryExecutionRef, QueryStateMachineRef}, + logical_planner::Plan, + optimizer::Optimizer, + scheduler::SchedulerRef, + }, + QueryError, +}; +use async_trait::async_trait; + +use super::query::SqlQueryExecution; + +pub type QueryExecutionFactoryRef = Arc; + +pub struct SqlQueryExecutionFactory { + optimizer: Arc, + scheduler: SchedulerRef, +} + +impl SqlQueryExecutionFactory { + #[inline(always)] + pub fn new(optimizer: Arc, scheduler: SchedulerRef) -> Self { + Self { optimizer, scheduler } + } +} + +#[async_trait] +impl QueryExecutionFactory for SqlQueryExecutionFactory { + async fn create_query_execution( + &self, + plan: Plan, + state_machine: QueryStateMachineRef, + ) -> Result { + match plan { + Plan::Query(query_plan) => Ok(Arc::new(SqlQueryExecution::new( + state_machine, + query_plan, + self.optimizer.clone(), + self.scheduler.clone(), + ))), + } + } +} diff --git a/s3select/query/src/execution/mod.rs b/s3select/query/src/execution/mod.rs new file mode 100644 index 000000000..807faf3ea --- /dev/null +++ b/s3select/query/src/execution/mod.rs @@ -0,0 +1,3 @@ +pub mod factory; +pub mod query; +pub mod scheduler; diff --git a/s3select/query/src/execution/query.rs b/s3select/query/src/execution/query.rs new file mode 100644 index 000000000..15d6ef837 --- /dev/null +++ b/s3select/query/src/execution/query.rs @@ -0,0 +1,92 @@ +use std::sync::Arc; + +use api::query::execution::{Output, QueryExecution, QueryStateMachineRef}; +use api::query::logical_planner::QueryPlan; +use api::query::optimizer::Optimizer; +use api::query::scheduler::SchedulerRef; +use api::{QueryError, QueryResult}; +use async_trait::async_trait; +use futures::stream::AbortHandle; +use parking_lot::Mutex; +use tracing::debug; + +pub struct SqlQueryExecution { + query_state_machine: QueryStateMachineRef, + plan: QueryPlan, + optimizer: Arc, + scheduler: SchedulerRef, + + abort_handle: Mutex>, +} + +impl SqlQueryExecution { + pub fn new( + query_state_machine: QueryStateMachineRef, + plan: QueryPlan, + optimizer: Arc, + scheduler: SchedulerRef, + ) -> Self { + Self { + query_state_machine, + plan, + optimizer, + scheduler, + abort_handle: Mutex::new(None), + } + } + + async fn start(&self) -> QueryResult { + // begin optimize + self.query_state_machine.begin_optimize(); + let physical_plan = self.optimizer.optimize(&self.plan, &self.query_state_machine.session).await?; + self.query_state_machine.end_optimize(); + + // begin schedule + self.query_state_machine.begin_schedule(); + let stream = self + .scheduler + .schedule(physical_plan.clone(), self.query_state_machine.session.inner().task_ctx()) + .await? + .stream(); + + debug!("Success build result stream."); + self.query_state_machine.end_schedule(); + + Ok(Output::StreamData(stream)) + } +} + +#[async_trait] +impl QueryExecution for SqlQueryExecution { + async fn start(&self) -> QueryResult { + let (task, abort_handle) = futures::future::abortable(self.start()); + + { + *self.abort_handle.lock() = Some(abort_handle); + } + + task.await.map_err(|_| QueryError::Cancel)? + } + + fn cancel(&self) -> QueryResult<()> { + debug!( + "cancel sql query execution: sql: {}, state: {:?}", + self.query_state_machine.query.content(), + self.query_state_machine.state() + ); + + // change state + self.query_state_machine.cancel(); + // stop future task + if let Some(e) = self.abort_handle.lock().as_ref() { + e.abort() + }; + + debug!( + "canceled sql query execution: sql: {}, state: {:?}", + self.query_state_machine.query.content(), + self.query_state_machine.state() + ); + Ok(()) + } +} diff --git a/s3select/query/src/execution/scheduler/local.rs b/s3select/query/src/execution/scheduler/local.rs new file mode 100644 index 000000000..e105d4b93 --- /dev/null +++ b/s3select/query/src/execution/scheduler/local.rs @@ -0,0 +1,22 @@ +use std::sync::Arc; + +use api::query::scheduler::{ExecutionResults, Scheduler}; +use async_trait::async_trait; +use datafusion::error::DataFusionError; +use datafusion::execution::context::TaskContext; +use datafusion::physical_plan::{execute_stream, ExecutionPlan}; + +pub struct LocalScheduler {} + +#[async_trait] +impl Scheduler for LocalScheduler { + async fn schedule( + &self, + plan: Arc, + context: Arc, + ) -> Result { + let stream = execute_stream(plan, context)?; + + Ok(ExecutionResults::new(stream)) + } +} diff --git a/s3select/query/src/execution/scheduler/mod.rs b/s3select/query/src/execution/scheduler/mod.rs new file mode 100644 index 000000000..27099624c --- /dev/null +++ b/s3select/query/src/execution/scheduler/mod.rs @@ -0,0 +1 @@ +pub mod local; diff --git a/s3select/query/src/function/mod.rs b/s3select/query/src/function/mod.rs new file mode 100644 index 000000000..e76614a0b --- /dev/null +++ b/s3select/query/src/function/mod.rs @@ -0,0 +1 @@ +pub mod simple_func_manager; diff --git a/s3select/query/src/function/simple_func_manager.rs b/s3select/query/src/function/simple_func_manager.rs new file mode 100644 index 000000000..bb3d3852d --- /dev/null +++ b/s3select/query/src/function/simple_func_manager.rs @@ -0,0 +1,105 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use api::query::function::FunctionMetadataManager; +use api::{QueryError, QueryResult}; +use datafusion::execution::SessionStateDefaults; +use datafusion::logical_expr::{AggregateUDF, ScalarUDF, WindowUDF}; +use tracing::debug; + +pub type SimpleFunctionMetadataManagerRef = Arc; + +#[derive(Debug)] +pub struct SimpleFunctionMetadataManager { + /// Scalar functions that are registered with the context + pub scalar_functions: HashMap>, + /// Aggregate functions registered in the context + pub aggregate_functions: HashMap>, + /// Window functions registered in the context + pub window_functions: HashMap>, +} + +impl Default for SimpleFunctionMetadataManager { + fn default() -> Self { + let mut func_meta_manager = Self { + scalar_functions: Default::default(), + aggregate_functions: Default::default(), + window_functions: Default::default(), + }; + SessionStateDefaults::default_scalar_functions().into_iter().for_each(|udf| { + let existing_udf = func_meta_manager.register_udf(udf.clone()); + if let Ok(()) = existing_udf { + debug!("Overwrote an existing UDF: {}", udf.name()); + } + }); + + SessionStateDefaults::default_aggregate_functions() + .into_iter() + .for_each(|udaf| { + let existing_udaf = func_meta_manager.register_udaf(udaf.clone()); + if let Ok(()) = existing_udaf { + debug!("Overwrote an existing UDAF: {}", udaf.name()); + } + }); + + SessionStateDefaults::default_window_functions().into_iter().for_each(|udwf| { + let existing_udwf = func_meta_manager.register_udwf(udwf.clone()); + if let Ok(()) = existing_udwf { + debug!("Overwrote an existing UDWF: {}", udwf.name()); + } + }); + + func_meta_manager + } +} + +impl FunctionMetadataManager for SimpleFunctionMetadataManager { + fn register_udf(&mut self, f: Arc) -> QueryResult<()> { + self.scalar_functions.insert(f.inner().name().to_uppercase(), f); + Ok(()) + } + + fn register_udaf(&mut self, f: Arc) -> QueryResult<()> { + self.aggregate_functions.insert(f.inner().name().to_uppercase(), f); + Ok(()) + } + + fn register_udwf(&mut self, f: Arc) -> QueryResult<()> { + self.window_functions.insert(f.inner().name().to_uppercase(), f); + Ok(()) + } + + fn udf(&self, name: &str) -> QueryResult> { + let result = self.scalar_functions.get(&name.to_uppercase()); + + result + .cloned() + .ok_or_else(|| QueryError::FunctionExists { name: name.to_string() }) + } + + fn udaf(&self, name: &str) -> QueryResult> { + let result = self.aggregate_functions.get(&name.to_uppercase()); + + result + .cloned() + .ok_or_else(|| QueryError::FunctionNotExists { name: name.to_string() }) + } + + fn udwf(&self, name: &str) -> QueryResult> { + let result = self.window_functions.get(&name.to_uppercase()); + + result + .cloned() + .ok_or_else(|| QueryError::FunctionNotExists { name: name.to_string() }) + } + + fn udfs(&self) -> Vec { + self.scalar_functions.keys().cloned().collect() + } + fn udafs(&self) -> Vec { + self.aggregate_functions.keys().cloned().collect() + } + fn udwfs(&self) -> Vec { + self.window_functions.keys().cloned().collect() + } +} diff --git a/s3select/query/src/instance.rs b/s3select/query/src/instance.rs new file mode 100644 index 000000000..3ad8941a8 --- /dev/null +++ b/s3select/query/src/instance.rs @@ -0,0 +1,198 @@ +use std::sync::Arc; + +use api::{ + query::{ + dispatcher::QueryDispatcher, execution::QueryStateMachineRef, logical_planner::Plan, session::SessionCtxFactory, Query, + }, + server::dbms::{DatabaseManagerSystem, QueryHandle}, + QueryResult, +}; +use async_trait::async_trait; +use derive_builder::Builder; +use s3s::dto::SelectObjectContentInput; + +use crate::{ + dispatcher::manager::SimpleQueryDispatcherBuilder, + execution::{factory::SqlQueryExecutionFactory, scheduler::local::LocalScheduler}, + function::simple_func_manager::SimpleFunctionMetadataManager, + metadata::base_table::BaseTableProvider, + sql::{optimizer::CascadeOptimizerBuilder, parser::DefaultParser}, +}; + +#[derive(Builder)] +pub struct RustFSms { + // query dispatcher & query execution + query_dispatcher: Arc, +} + +#[async_trait] +impl DatabaseManagerSystem for RustFSms +where + D: QueryDispatcher, +{ + async fn execute(&self, query: &Query) -> QueryResult { + let result = self.query_dispatcher.execute_query(query).await?; + + Ok(QueryHandle::new(query.clone(), result)) + } + + async fn build_query_state_machine(&self, query: Query) -> QueryResult { + let query_state_machine = self.query_dispatcher.build_query_state_machine(query).await?; + + Ok(query_state_machine) + } + + async fn build_logical_plan(&self, query_state_machine: QueryStateMachineRef) -> QueryResult> { + let logical_plan = self.query_dispatcher.build_logical_plan(query_state_machine).await?; + + Ok(logical_plan) + } + + async fn execute_logical_plan( + &self, + logical_plan: Plan, + query_state_machine: QueryStateMachineRef, + ) -> QueryResult { + let query = query_state_machine.query.clone(); + let result = self + .query_dispatcher + .execute_logical_plan(logical_plan, query_state_machine) + .await?; + + Ok(QueryHandle::new(query.clone(), result)) + } +} + +pub async fn make_rustfsms(input: SelectObjectContentInput, is_test: bool) -> QueryResult { + // init Function Manager, we can define some UDF if need + let func_manager = SimpleFunctionMetadataManager::default(); + // TODO session config need load global system config + let session_factory = Arc::new(SessionCtxFactory { is_test }); + let parser = Arc::new(DefaultParser::default()); + let optimizer = Arc::new(CascadeOptimizerBuilder::default().build()); + // TODO wrap, and num_threads configurable + let scheduler = Arc::new(LocalScheduler {}); + + let query_execution_factory = Arc::new(SqlQueryExecutionFactory::new(optimizer, scheduler)); + + let default_table_provider = Arc::new(BaseTableProvider::default()); + + let query_dispatcher = SimpleQueryDispatcherBuilder::default() + .with_input(input) + .with_func_manager(Arc::new(func_manager)) + .with_default_table_provider(default_table_provider) + .with_session_factory(session_factory) + .with_parser(parser) + .with_query_execution_factory(query_execution_factory) + .build()?; + + let mut builder = RustFSmsBuilder::default(); + + let db_server = builder.query_dispatcher(query_dispatcher).build().expect("build db server"); + + Ok(db_server) +} + +#[cfg(test)] +mod tests { + use api::{ + query::{Context, Query}, + server::dbms::DatabaseManagerSystem, + }; + use datafusion::{arrow::util::pretty, assert_batches_eq}; + use s3s::dto::{ + CSVInput, CSVOutput, ExpressionType, InputSerialization, OutputSerialization, SelectObjectContentInput, + SelectObjectContentRequest, + }; + + use crate::instance::make_rustfsms; + + #[tokio::test] + #[ignore] + async fn test_simple_sql() { + let sql = "select * from S3Object"; + let input = SelectObjectContentInput { + bucket: "dandan".to_string(), + expected_bucket_owner: None, + key: "test.csv".to_string(), + sse_customer_algorithm: None, + sse_customer_key: None, + sse_customer_key_md5: None, + request: SelectObjectContentRequest { + expression: sql.to_string(), + expression_type: ExpressionType::from_static("SQL"), + input_serialization: InputSerialization { + csv: Some(CSVInput::default()), + ..Default::default() + }, + output_serialization: OutputSerialization { + csv: Some(CSVOutput::default()), + ..Default::default() + }, + request_progress: None, + scan_range: None, + }, + }; + let db = make_rustfsms(input.clone(), true).await.unwrap(); + let query = Query::new(Context { input }, sql.to_string()); + + let result = db.execute(&query).await.unwrap(); + + let results = result.result().chunk_result().await.unwrap().to_vec(); + + let expected = [ + "+----------------+---------+-----+------------+--------+", + "| id | name | age | department | salary |", + "+----------------+---------+-----+------------+--------+", + "| 1 | Alice | 25 | HR | 5000 |", + "| 2 | Bob | 30 | IT | 6000 |", + "| 3 | Charlie | 35 | Finance | 7000 |", + "| 4 | Diana | 22 | Marketing | 4500 |", + "| 5 | Eve | 28 | IT | 5500 |", + "| 6 | Frank | 40 | Finance | 8000 |", + "| 7 | Grace | 26 | HR | 5200 |", + "| 8 | Henry | 32 | IT | 6200 |", + "| 9 | Ivy | 24 | Marketing | 4800 |", + "| 10 | Jack | 38 | Finance | 7500 |", + "+----------------+---------+-----+------------+--------+", + ]; + + assert_batches_eq!(expected, &results); + pretty::print_batches(&results).unwrap(); + } + + #[tokio::test] + #[ignore] + async fn test_func_sql() { + let sql = "select count(s.id) from S3Object as s"; + let input = SelectObjectContentInput { + bucket: "dandan".to_string(), + expected_bucket_owner: None, + key: "test.csv".to_string(), + sse_customer_algorithm: None, + sse_customer_key: None, + sse_customer_key_md5: None, + request: SelectObjectContentRequest { + expression: sql.to_string(), + expression_type: ExpressionType::from_static("SQL"), + input_serialization: InputSerialization { + csv: Some(CSVInput::default()), + ..Default::default() + }, + output_serialization: OutputSerialization { + csv: Some(CSVOutput::default()), + ..Default::default() + }, + request_progress: None, + scan_range: None, + }, + }; + let db = make_rustfsms(input.clone(), true).await.unwrap(); + let query = Query::new(Context { input }, sql.to_string()); + + let result = db.execute(&query).await.unwrap(); + + let results = result.result().chunk_result().await.unwrap().to_vec(); + pretty::print_batches(&results).unwrap(); + } +} diff --git a/s3select/query/src/lib.rs b/s3select/query/src/lib.rs new file mode 100644 index 000000000..0a0c12eb2 --- /dev/null +++ b/s3select/query/src/lib.rs @@ -0,0 +1,7 @@ +pub mod data_source; +pub mod dispatcher; +pub mod execution; +pub mod function; +pub mod instance; +pub mod metadata; +pub mod sql; diff --git a/s3select/query/src/metadata/base_table.rs b/s3select/query/src/metadata/base_table.rs new file mode 100644 index 000000000..38dc841ab --- /dev/null +++ b/s3select/query/src/metadata/base_table.rs @@ -0,0 +1,17 @@ +use std::sync::Arc; + +use datafusion::common::Result as DFResult; +use datafusion::datasource::listing::ListingTable; + +use crate::data_source::table_source::TableHandle; + +use super::TableHandleProvider; + +#[derive(Default)] +pub struct BaseTableProvider {} + +impl TableHandleProvider for BaseTableProvider { + fn build_table_handle(&self, provider: Arc) -> DFResult { + Ok(TableHandle::External(provider)) + } +} diff --git a/s3select/query/src/metadata/mod.rs b/s3select/query/src/metadata/mod.rs new file mode 100644 index 000000000..04a71d4e7 --- /dev/null +++ b/s3select/query/src/metadata/mod.rs @@ -0,0 +1,126 @@ +use std::sync::Arc; + +use api::query::{function::FuncMetaManagerRef, session::SessionCtx}; +use async_trait::async_trait; +use datafusion::arrow::datatypes::DataType; +use datafusion::common::Result as DFResult; +use datafusion::datasource::listing::ListingTable; +use datafusion::logical_expr::var_provider::is_system_variables; +use datafusion::logical_expr::{AggregateUDF, ScalarUDF, TableSource, WindowUDF}; +use datafusion::variable::VarType; +use datafusion::{ + config::ConfigOptions, + sql::{planner::ContextProvider, TableReference}, +}; + +use crate::data_source::table_source::{TableHandle, TableSourceAdapter}; + +pub mod base_table; + +#[async_trait] +pub trait ContextProviderExtension: ContextProvider { + fn get_table_source_(&self, name: TableReference) -> datafusion::common::Result>; +} + +pub type TableHandleProviderRef = Arc; + +pub trait TableHandleProvider { + fn build_table_handle(&self, provider: Arc) -> DFResult; +} + +pub struct MetadataProvider { + provider: Arc, + session: SessionCtx, + config_options: ConfigOptions, + func_manager: FuncMetaManagerRef, + current_session_table_provider: TableHandleProviderRef, +} + +impl MetadataProvider { + #[allow(clippy::too_many_arguments)] + pub fn new( + provider: Arc, + current_session_table_provider: TableHandleProviderRef, + func_manager: FuncMetaManagerRef, + session: SessionCtx, + ) -> Self { + Self { + provider, + current_session_table_provider, + config_options: session.inner().config_options().clone(), + session, + func_manager, + } + } + + fn build_table_handle(&self) -> datafusion::common::Result { + self.current_session_table_provider.build_table_handle(self.provider.clone()) + } +} + +impl ContextProviderExtension for MetadataProvider { + fn get_table_source_(&self, table_ref: TableReference) -> datafusion::common::Result> { + let name = table_ref.clone().resolve("", ""); + let table_name = &*name.table; + + let table_handle = self.build_table_handle()?; + + Ok(Arc::new(TableSourceAdapter::try_new(table_ref.clone(), table_name, table_handle)?)) + } +} + +impl ContextProvider for MetadataProvider { + fn get_function_meta(&self, name: &str) -> Option> { + self.func_manager + .udf(name) + .ok() + .or(self.session.inner().scalar_functions().get(name).cloned()) + } + + fn get_aggregate_meta(&self, name: &str) -> Option> { + self.func_manager.udaf(name).ok() + } + + fn get_variable_type(&self, variable_names: &[String]) -> Option { + if variable_names.is_empty() { + return None; + } + + let var_type = if is_system_variables(variable_names) { + VarType::System + } else { + VarType::UserDefined + }; + + self.session + .inner() + .execution_props() + .get_var_provider(var_type) + .and_then(|p| p.get_type(variable_names)) + } + + fn options(&self) -> &ConfigOptions { + // TODO refactor + &self.config_options + } + + fn get_window_meta(&self, name: &str) -> Option> { + self.func_manager.udwf(name).ok() + } + + fn get_table_source(&self, name: TableReference) -> DFResult> { + Ok(self.get_table_source_(name)?) + } + + fn udf_names(&self) -> Vec { + self.func_manager.udfs() + } + + fn udaf_names(&self) -> Vec { + self.func_manager.udafs() + } + + fn udwf_names(&self) -> Vec { + self.func_manager.udwfs() + } +} diff --git a/s3select/query/src/sql/analyzer.rs b/s3select/query/src/sql/analyzer.rs new file mode 100644 index 000000000..6507c8423 --- /dev/null +++ b/s3select/query/src/sql/analyzer.rs @@ -0,0 +1,33 @@ +use api::query::analyzer::Analyzer; +use api::query::session::SessionCtx; +use api::QueryResult; +use datafusion::logical_expr::LogicalPlan; +use datafusion::optimizer::analyzer::Analyzer as DFAnalyzer; + +pub struct DefaultAnalyzer { + inner: DFAnalyzer, +} + +impl DefaultAnalyzer { + pub fn new() -> Self { + let analyzer = DFAnalyzer::default(); + // we can add analyzer rule at here + + Self { inner: analyzer } + } +} + +impl Default for DefaultAnalyzer { + fn default() -> Self { + Self::new() + } +} + +impl Analyzer for DefaultAnalyzer { + fn analyze(&self, plan: &LogicalPlan, session: &SessionCtx) -> QueryResult { + let plan = self + .inner + .execute_and_check(plan.to_owned(), session.inner().config_options(), |_, _| {})?; + Ok(plan) + } +} diff --git a/s3select/query/src/sql/dialect.rs b/s3select/query/src/sql/dialect.rs new file mode 100644 index 000000000..332970938 --- /dev/null +++ b/s3select/query/src/sql/dialect.rs @@ -0,0 +1,18 @@ +use datafusion::sql::sqlparser::dialect::Dialect; + +#[derive(Debug, Default)] +pub struct RustFsDialect; + +impl Dialect for RustFsDialect { + fn is_identifier_start(&self, ch: char) -> bool { + ch.is_alphabetic() || ch == '_' || ch == '#' || ch == '@' + } + + fn is_identifier_part(&self, ch: char) -> bool { + ch.is_alphabetic() || ch.is_ascii_digit() || ch == '@' || ch == '$' || ch == '#' || ch == '_' + } + + fn supports_group_by_expr(&self) -> bool { + true + } +} diff --git a/s3select/query/src/sql/logical/mod.rs b/s3select/query/src/sql/logical/mod.rs new file mode 100644 index 000000000..1ecfae43a --- /dev/null +++ b/s3select/query/src/sql/logical/mod.rs @@ -0,0 +1,2 @@ +pub mod optimizer; +pub mod planner; diff --git a/s3select/query/src/sql/logical/optimizer.rs b/s3select/query/src/sql/logical/optimizer.rs new file mode 100644 index 000000000..e97e29674 --- /dev/null +++ b/s3select/query/src/sql/logical/optimizer.rs @@ -0,0 +1,111 @@ +use std::sync::Arc; + +use api::{ + query::{analyzer::AnalyzerRef, logical_planner::QueryPlan, session::SessionCtx}, + QueryResult, +}; +use datafusion::{ + execution::SessionStateBuilder, + logical_expr::LogicalPlan, + optimizer::{ + common_subexpr_eliminate::CommonSubexprEliminate, decorrelate_predicate_subquery::DecorrelatePredicateSubquery, + eliminate_cross_join::EliminateCrossJoin, eliminate_duplicated_expr::EliminateDuplicatedExpr, + eliminate_filter::EliminateFilter, eliminate_join::EliminateJoin, eliminate_limit::EliminateLimit, + eliminate_outer_join::EliminateOuterJoin, extract_equijoin_predicate::ExtractEquijoinPredicate, + filter_null_join_keys::FilterNullJoinKeys, propagate_empty_relation::PropagateEmptyRelation, + push_down_filter::PushDownFilter, push_down_limit::PushDownLimit, + replace_distinct_aggregate::ReplaceDistinctWithAggregate, scalar_subquery_to_join::ScalarSubqueryToJoin, + simplify_expressions::SimplifyExpressions, single_distinct_to_groupby::SingleDistinctToGroupBy, + unwrap_cast_in_comparison::UnwrapCastInComparison, OptimizerRule, + }, +}; +use tracing::debug; + +use crate::sql::analyzer::DefaultAnalyzer; + +pub trait LogicalOptimizer: Send + Sync { + fn optimize(&self, plan: &QueryPlan, session: &SessionCtx) -> QueryResult; + + fn inject_optimizer_rule(&mut self, optimizer_rule: Arc); +} + +pub struct DefaultLogicalOptimizer { + // fit datafusion + // TODO refactor + analyzer: AnalyzerRef, + rules: Vec>, +} + +impl DefaultLogicalOptimizer { + #[allow(dead_code)] + fn with_optimizer_rules(mut self, rules: Vec>) -> Self { + self.rules = rules; + self + } +} + +impl Default for DefaultLogicalOptimizer { + fn default() -> Self { + let analyzer = Arc::new(DefaultAnalyzer::default()); + + // additional optimizer rule + let rules: Vec> = vec![ + // df default rules start + Arc::new(SimplifyExpressions::new()), + Arc::new(UnwrapCastInComparison::new()), + Arc::new(ReplaceDistinctWithAggregate::new()), + Arc::new(EliminateJoin::new()), + Arc::new(DecorrelatePredicateSubquery::new()), + Arc::new(ScalarSubqueryToJoin::new()), + Arc::new(ExtractEquijoinPredicate::new()), + // simplify expressions does not simplify expressions in subqueries, so we + // run it again after running the optimizations that potentially converted + // subqueries to joins + Arc::new(SimplifyExpressions::new()), + Arc::new(EliminateDuplicatedExpr::new()), + Arc::new(EliminateFilter::new()), + Arc::new(EliminateCrossJoin::new()), + Arc::new(CommonSubexprEliminate::new()), + Arc::new(EliminateLimit::new()), + Arc::new(PropagateEmptyRelation::new()), + Arc::new(FilterNullJoinKeys::default()), + Arc::new(EliminateOuterJoin::new()), + // Filters can't be pushed down past Limits, we should do PushDownFilter after PushDownLimit + Arc::new(PushDownLimit::new()), + Arc::new(PushDownFilter::new()), + Arc::new(SingleDistinctToGroupBy::new()), + // The previous optimizations added expressions and projections, + // that might benefit from the following rules + Arc::new(SimplifyExpressions::new()), + Arc::new(UnwrapCastInComparison::new()), + Arc::new(CommonSubexprEliminate::new()), + // PushDownProjection can pushdown Projections through Limits, do PushDownLimit again. + Arc::new(PushDownLimit::new()), + // df default rules end + // custom rules can add at here + ]; + + Self { analyzer, rules } + } +} + +impl LogicalOptimizer for DefaultLogicalOptimizer { + fn optimize(&self, plan: &QueryPlan, session: &SessionCtx) -> QueryResult { + let analyzed_plan = { self.analyzer.analyze(&plan.df_plan, session)? }; + + debug!("Analyzed logical plan:\n{}\n", plan.df_plan.display_indent_schema(),); + + let optimizeed_plan = { + SessionStateBuilder::new_from_existing(session.inner().clone()) + .with_optimizer_rules(self.rules.clone()) + .build() + .optimize(&analyzed_plan)? + }; + + Ok(optimizeed_plan) + } + + fn inject_optimizer_rule(&mut self, optimizer_rule: Arc) { + self.rules.push(optimizer_rule); + } +} diff --git a/s3select/query/src/sql/logical/planner.rs b/s3select/query/src/sql/logical/planner.rs new file mode 100644 index 000000000..bcdc59c07 --- /dev/null +++ b/s3select/query/src/sql/logical/planner.rs @@ -0,0 +1,3 @@ +use crate::sql::planner::SqlPlanner; + +pub type DefaultLogicalPlanner<'a, S> = SqlPlanner<'a, S>; diff --git a/s3select/query/src/sql/mod.rs b/s3select/query/src/sql/mod.rs new file mode 100644 index 000000000..151fc83de --- /dev/null +++ b/s3select/query/src/sql/mod.rs @@ -0,0 +1,7 @@ +pub mod analyzer; +pub mod dialect; +pub mod logical; +pub mod optimizer; +pub mod parser; +pub mod physical; +pub mod planner; diff --git a/s3select/query/src/sql/optimizer.rs b/s3select/query/src/sql/optimizer.rs new file mode 100644 index 000000000..b424b073c --- /dev/null +++ b/s3select/query/src/sql/optimizer.rs @@ -0,0 +1,82 @@ +use std::sync::Arc; + +use api::{ + query::{logical_planner::QueryPlan, optimizer::Optimizer, physical_planner::PhysicalPlanner, session::SessionCtx}, + QueryResult, +}; +use async_trait::async_trait; +use datafusion::physical_plan::{displayable, ExecutionPlan}; +use tracing::debug; + +use super::{ + logical::optimizer::{DefaultLogicalOptimizer, LogicalOptimizer}, + physical::{optimizer::PhysicalOptimizer, planner::DefaultPhysicalPlanner}, +}; + +pub struct CascadeOptimizer { + logical_optimizer: Arc, + physical_planner: Arc, + physical_optimizer: Arc, +} + +#[async_trait] +impl Optimizer for CascadeOptimizer { + async fn optimize(&self, plan: &QueryPlan, session: &SessionCtx) -> QueryResult> { + debug!("Original logical plan:\n{}\n", plan.df_plan.display_indent_schema(),); + + let optimized_logical_plan = self.logical_optimizer.optimize(plan, session)?; + + debug!("Final logical plan:\n{}\n", optimized_logical_plan.display_indent_schema(),); + + let physical_plan = { + self.physical_planner + .create_physical_plan(&optimized_logical_plan, session) + .await? + }; + + debug!("Original physical plan:\n{}\n", displayable(physical_plan.as_ref()).indent(false)); + + let optimized_physical_plan = { self.physical_optimizer.optimize(physical_plan, session)? }; + + Ok(optimized_physical_plan) + } +} + +#[derive(Default)] +pub struct CascadeOptimizerBuilder { + logical_optimizer: Option>, + physical_planner: Option>, + physical_optimizer: Option>, +} + +impl CascadeOptimizerBuilder { + pub fn with_logical_optimizer(mut self, logical_optimizer: Arc) -> Self { + self.logical_optimizer = Some(logical_optimizer); + self + } + + pub fn with_physical_planner(mut self, physical_planner: Arc) -> Self { + self.physical_planner = Some(physical_planner); + self + } + + pub fn with_physical_optimizer(mut self, physical_optimizer: Arc) -> Self { + self.physical_optimizer = Some(physical_optimizer); + self + } + + pub fn build(self) -> CascadeOptimizer { + let default_logical_optimizer = Arc::new(DefaultLogicalOptimizer::default()); + let default_physical_planner = Arc::new(DefaultPhysicalPlanner::default()); + + let logical_optimizer = self.logical_optimizer.unwrap_or(default_logical_optimizer); + let physical_planner = self.physical_planner.unwrap_or_else(|| default_physical_planner.clone()); + let physical_optimizer = self.physical_optimizer.unwrap_or(default_physical_planner); + + CascadeOptimizer { + logical_optimizer, + physical_planner, + physical_optimizer, + } + } +} diff --git a/s3select/query/src/sql/parser.rs b/s3select/query/src/sql/parser.rs new file mode 100644 index 000000000..ebd2b5d45 --- /dev/null +++ b/s3select/query/src/sql/parser.rs @@ -0,0 +1,92 @@ +use std::{collections::VecDeque, fmt::Display}; + +use api::{ + query::{ast::ExtStatement, parser::Parser as RustFsParser}, + ParserSnafu, +}; +use datafusion::sql::sqlparser::{ + dialect::Dialect, + parser::{Parser, ParserError}, + tokenizer::{Token, Tokenizer}, +}; +use snafu::ResultExt; + +use super::dialect::RustFsDialect; + +pub type Result = std::result::Result; + +// Use `Parser::expected` instead, if possible +macro_rules! parser_err { + ($MSG:expr) => { + Err(ParserError::ParserError($MSG.to_string())) + }; +} + +#[derive(Default)] +pub struct DefaultParser {} + +impl RustFsParser for DefaultParser { + fn parse(&self, sql: &str) -> api::QueryResult> { + ExtParser::parse_sql(sql).context(ParserSnafu) + } +} + +/// SQL Parser +pub struct ExtParser<'a> { + parser: Parser<'a>, +} + +impl<'a> ExtParser<'a> { + /// Parse the specified tokens with dialect + fn new_with_dialect(sql: &str, dialect: &'a dyn Dialect) -> Result { + let mut tokenizer = Tokenizer::new(dialect, sql); + let tokens = tokenizer.tokenize()?; + Ok(ExtParser { + parser: Parser::new(dialect).with_tokens(tokens), + }) + } + + /// Parse a SQL statement and produce a set of statements + pub fn parse_sql(sql: &str) -> Result> { + let dialect = &RustFsDialect {}; + ExtParser::parse_sql_with_dialect(sql, dialect) + } + + /// Parse a SQL statement and produce a set of statements + pub fn parse_sql_with_dialect(sql: &str, dialect: &dyn Dialect) -> Result> { + let mut parser = ExtParser::new_with_dialect(sql, dialect)?; + let mut stmts = VecDeque::new(); + let mut expecting_statement_delimiter = false; + loop { + // ignore empty statements (between successive statement delimiters) + while parser.parser.consume_token(&Token::SemiColon) { + expecting_statement_delimiter = false; + } + + if parser.parser.peek_token() == Token::EOF { + break; + } + if expecting_statement_delimiter { + return parser.expected("end of statement", parser.parser.peek_token()); + } + + let statement = parser.parse_statement()?; + stmts.push_back(statement); + expecting_statement_delimiter = true; + } + + // debug!("Parser sql: {}, stmts: {:#?}", sql, stmts); + + Ok(stmts) + } + + /// Parse a new expression + fn parse_statement(&mut self) -> Result { + Ok(ExtStatement::SqlStatement(Box::new(self.parser.parse_statement()?))) + } + + // Report unexpected token + fn expected(&self, expected: &str, found: impl Display) -> Result { + parser_err!(format!("Expected {}, found: {}", expected, found)) + } +} diff --git a/s3select/query/src/sql/physical/mod.rs b/s3select/query/src/sql/physical/mod.rs new file mode 100644 index 000000000..1ecfae43a --- /dev/null +++ b/s3select/query/src/sql/physical/mod.rs @@ -0,0 +1,2 @@ +pub mod optimizer; +pub mod planner; diff --git a/s3select/query/src/sql/physical/optimizer.rs b/s3select/query/src/sql/physical/optimizer.rs new file mode 100644 index 000000000..12f16e3dc --- /dev/null +++ b/s3select/query/src/sql/physical/optimizer.rs @@ -0,0 +1,12 @@ +use std::sync::Arc; + +use api::query::session::SessionCtx; +use api::QueryResult; +use datafusion::physical_optimizer::PhysicalOptimizerRule; +use datafusion::physical_plan::ExecutionPlan; + +pub trait PhysicalOptimizer { + fn optimize(&self, plan: Arc, session: &SessionCtx) -> QueryResult>; + + fn inject_optimizer_rule(&mut self, optimizer_rule: Arc); +} diff --git a/s3select/query/src/sql/physical/planner.rs b/s3select/query/src/sql/physical/planner.rs new file mode 100644 index 000000000..254c198d6 --- /dev/null +++ b/s3select/query/src/sql/physical/planner.rs @@ -0,0 +1,104 @@ +use std::sync::Arc; + +use api::query::physical_planner::PhysicalPlanner; +use api::query::session::SessionCtx; +use api::QueryResult; +use async_trait::async_trait; +use datafusion::execution::SessionStateBuilder; +use datafusion::logical_expr::LogicalPlan; +use datafusion::physical_optimizer::aggregate_statistics::AggregateStatistics; +use datafusion::physical_optimizer::coalesce_batches::CoalesceBatches; +use datafusion::physical_optimizer::join_selection::JoinSelection; +use datafusion::physical_optimizer::PhysicalOptimizerRule; +use datafusion::physical_plan::ExecutionPlan; +use datafusion::physical_planner::{ + DefaultPhysicalPlanner as DFDefaultPhysicalPlanner, ExtensionPlanner, PhysicalPlanner as DFPhysicalPlanner, +}; + +use super::optimizer::PhysicalOptimizer; + +pub struct DefaultPhysicalPlanner { + ext_physical_transform_rules: Vec>, + /// Responsible for optimizing a physical execution plan + ext_physical_optimizer_rules: Vec>, +} + +impl DefaultPhysicalPlanner { + #[allow(dead_code)] + fn with_physical_transform_rules(mut self, rules: Vec>) -> Self { + self.ext_physical_transform_rules = rules; + self + } +} + +impl DefaultPhysicalPlanner { + #[allow(dead_code)] + fn with_optimizer_rules(mut self, rules: Vec>) -> Self { + self.ext_physical_optimizer_rules = rules; + self + } +} + +impl Default for DefaultPhysicalPlanner { + fn default() -> Self { + let ext_physical_transform_rules: Vec> = vec![ + // can add rules at here + ]; + + // We need to take care of the rule ordering. They may influence each other. + let ext_physical_optimizer_rules: Vec> = vec![ + Arc::new(AggregateStatistics::new()), + // Statistics-based join selection will change the Auto mode to a real join implementation, + // like collect left, or hash join, or future sort merge join, which will influence the + // EnforceDistribution and EnforceSorting rules as they decide whether to add additional + // repartitioning and local sorting steps to meet distribution and ordering requirements. + // Therefore, it should run before EnforceDistribution and EnforceSorting. + Arc::new(JoinSelection::new()), + // The CoalesceBatches rule will not influence the distribution and ordering of the + // whole plan tree. Therefore, to avoid influencing other rules, it should run last. + Arc::new(CoalesceBatches::new()), + ]; + + Self { + ext_physical_transform_rules, + ext_physical_optimizer_rules, + } + } +} + +#[async_trait] +impl PhysicalPlanner for DefaultPhysicalPlanner { + async fn create_physical_plan( + &self, + logical_plan: &LogicalPlan, + session: &SessionCtx, + ) -> QueryResult> { + // 将扩展的物理计划优化规则注入df 的 session state + let new_state = SessionStateBuilder::new_from_existing(session.inner().clone()) + .with_physical_optimizer_rules(self.ext_physical_optimizer_rules.clone()) + .build(); + + // 通过扩展的物理计划转换规则构造df 的 Physical Planner + let planner = DFDefaultPhysicalPlanner::with_extension_planners(self.ext_physical_transform_rules.clone()); + + // 执行df的物理计划规划及优化 + planner + .create_physical_plan(logical_plan, &new_state) + .await + .map_err(|e| e.into()) + } + + fn inject_physical_transform_rule(&mut self, rule: Arc) { + self.ext_physical_transform_rules.push(rule) + } +} + +impl PhysicalOptimizer for DefaultPhysicalPlanner { + fn optimize(&self, plan: Arc, _session: &SessionCtx) -> QueryResult> { + Ok(plan) + } + + fn inject_optimizer_rule(&mut self, optimizer_rule: Arc) { + self.ext_physical_optimizer_rules.push(optimizer_rule); + } +} diff --git a/s3select/query/src/sql/planner.rs b/s3select/query/src/sql/planner.rs new file mode 100644 index 000000000..a6c9f8c1a --- /dev/null +++ b/s3select/query/src/sql/planner.rs @@ -0,0 +1,60 @@ +use api::{ + query::{ + ast::ExtStatement, + logical_planner::{LogicalPlanner, Plan, QueryPlan}, + session::SessionCtx, + }, + QueryError, QueryResult, +}; +use async_recursion::async_recursion; +use async_trait::async_trait; +use datafusion::sql::{planner::SqlToRel, sqlparser::ast::Statement}; + +use crate::metadata::ContextProviderExtension; + +pub struct SqlPlanner<'a, S: ContextProviderExtension> { + _schema_provider: &'a S, + df_planner: SqlToRel<'a, S>, +} + +#[async_trait] +impl LogicalPlanner for SqlPlanner<'_, S> { + async fn create_logical_plan(&self, statement: ExtStatement, session: &SessionCtx) -> QueryResult { + let plan = { self.statement_to_plan(statement, session).await? }; + + Ok(plan) + } +} + +impl<'a, S: ContextProviderExtension + Send + Sync + 'a> SqlPlanner<'a, S> { + /// Create a new query planner + pub fn new(schema_provider: &'a S) -> Self { + SqlPlanner { + _schema_provider: schema_provider, + df_planner: SqlToRel::new(schema_provider), + } + } + + /// Generate a logical plan from an Extent SQL statement + #[async_recursion] + pub(crate) async fn statement_to_plan(&self, statement: ExtStatement, session: &SessionCtx) -> QueryResult { + match statement { + ExtStatement::SqlStatement(stmt) => self.df_sql_to_plan(*stmt, session).await, + } + } + + async fn df_sql_to_plan(&self, stmt: Statement, _session: &SessionCtx) -> QueryResult { + match stmt { + Statement::Query(_) => { + let df_plan = self.df_planner.sql_statement_to_plan(stmt)?; + let plan = Plan::Query(QueryPlan { + df_plan, + is_tag_scan: false, + }); + + Ok(plan) + } + _ => Err(QueryError::NotImplemented { err: stmt.to_string() }), + } + } +} diff --git a/scripts/build.py b/scripts/build.py index 5d6857a5e..106a25fed 100755 --- a/scripts/build.py +++ b/scripts/build.py @@ -34,6 +34,9 @@ def main(args: CliArgs): use_zigbuild = True use_old_glibc = True + if args.target and args.target == "x86_64-unknown-linux-musl": + shell("rustup target add " + args.target) + cmd = ["cargo", "build"] if use_zigbuild: cmd = ["cargo", " zigbuild"] diff --git a/scripts/run.bat b/scripts/run.bat deleted file mode 100644 index e0ec7dc5f..000000000 --- a/scripts/run.bat +++ /dev/null @@ -1,32 +0,0 @@ -@echo off -rem filepath: run.bat - -if not defined SKIP_BUILD ( - cargo build -p rustfs --bins -) - -set current_dir=%cd% - -if not exist .\target\volume\test mkdir .\target\volume\test - -if not defined RUST_LOG ( - set RUST_BACKTRACE=1 - set RUST_LOG=rustfs=debug,ecstore=debug,s3s=debug,iam=debug -) - -rem set RUSTFS_ERASURE_SET_DRIVE_COUNT=5 - -rem set RUSTFS_STORAGE_CLASS_INLINE_BLOCK=512 KB - -rem set RUSTFS_VOLUMES=.\target\volume\test{0...4} -set RUSTFS_VOLUMES=.\target\volume\test -set RUSTFS_ADDRESS=0.0.0.0:9000 -set RUSTFS_CONSOLE_ENABLE=true -set RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9002 -rem set RUSTFS_SERVER_DOMAINS=localhost:9000 - -if not "%~1"=="" ( - set RUSTFS_VOLUMES=%~1 -) - -cargo run --bin rustfs \ No newline at end of file diff --git a/scripts/run.ps1 b/scripts/run.ps1 new file mode 100644 index 000000000..845ed020c --- /dev/null +++ b/scripts/run.ps1 @@ -0,0 +1,51 @@ +# Check if static files need to be downloaded +if (-not (Test-Path .\rustfs\static\index.html)) { + Write-Host "Downloading rustfs-console-latest.zip" + + Invoke-WebRequest -Uri "https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip" -OutFile 'tempfile.zip' + Expand-Archive -Path 'tempfile.zip' -DestinationPath '.\rustfs\static' -Force + Remove-Item tempfile.zip +} + +# Check if build should be skipped +if (-not $env:SKIP_BUILD) { + cargo build -p rustfs --bins +} + +$current_dir = Get-Location + +# Create multiple test directories +$testDirs = @("test0", "test1", "test2", "test3", "test4") +foreach ($dir in $testDirs) { + $path = Join-Path -Path ".\target\volume" -ChildPath $dir + if (-not (Test-Path $path)) { + New-Item -ItemType Directory -Path $path -Force | Out-Null + } +} + +# Set environment variables +if (-not $env:RUST_LOG) { + $env:RUST_BACKTRACE = 1 + $env:RUST_LOG = "rustfs=debug,ecstore=debug,s3s=debug,iam=debug" +} + +# The following environment variables are commented out, uncomment them if needed +# $env:RUSTFS_ERASURE_SET_DRIVE_COUNT = 5 +# $env:RUSTFS_STORAGE_CLASS_INLINE_BLOCK = "512 KB" + +$env:RUSTFS_VOLUMES = ".\target\volume\test{0...4}" +# $env:RUSTFS_VOLUMES = ".\target\volume\test" +$env:RUSTFS_ADDRESS = "127.0.0.1:9000" +$env:RUSTFS_CONSOLE_ENABLE = "true" +$env:RUSTFS_CONSOLE_ADDRESS = "127.0.0.1:9002" +# $env:RUSTFS_SERVER_DOMAINS = "localhost:9000" +# Change to the actual configuration file path, obs.example.toml is for reference only +$env:RUSTFS_OBS_CONFIG = ".\deploy\config\obs.example.toml" + +# Check command line arguments +if ($args.Count -gt 0) { + $env:RUSTFS_VOLUMES = $args[0] +} + +# Run the program +cargo run --bin rustfs \ No newline at end of file diff --git a/scripts/run.sh b/scripts/run.sh index 2b00ade0f..61f82aa82 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -1,5 +1,12 @@ #!/bin/bash -e +# check ./rustfs/static/index.html not exists +if [ ! -f ./rustfs/static/index.html ]; then + echo "Downloading rustfs-console-latest.zip" + # download rustfs-console-latest.zip do not show log + curl -s -L "https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip" -o tempfile.zip && unzip -q -o tempfile.zip -d ./rustfs/static && rm tempfile.zip +fi + if [ -z "$SKIP_BUILD" ]; then cargo build -p rustfs --bins fi @@ -25,18 +32,34 @@ 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" +# HTTPS 证书目录 +# export RUSTFS_TLS_PATH="./deploy/certs" + +# 具体路径修改为配置文件真实路径,obs.example.toml 仅供参考 其中`RUSTFS_OBS_CONFIG` 和下面变量二选一 +export RUSTFS_OBS_CONFIG="./deploy/config/obs.example.toml" + +# 如下变量需要必须参数都有值才可以,以及会覆盖配置文件中的值 +export RUSTFS__OBSERVABILITY__ENDPOINT=http://localhost:4317 +export RUSTFS__OBSERVABILITY__USE_STDOUT=true +export RUSTFS__OBSERVABILITY__SAMPLE_RATIO=2.0 +export RUSTFS__OBSERVABILITY__METER_INTERVAL=30 +export RUSTFS__OBSERVABILITY__SERVICE_NAME=rustfs +export RUSTFS__OBSERVABILITY__SERVICE_VERSION=0.1.0 +export RUSTFS__OBSERVABILITY__ENVIRONMENT=develop +export RUSTFS__OBSERVABILITY__LOGGER_LEVEL=info +export RUSTFS__SINKS__FILE__ENABLED=true +export RUSTFS__SINKS__FILE__PATH="./deploy/logs/rustfs.log" +export RUSTFS__SINKS__WEBHOOK__ENABLED=false +export RUSTFS__SINKS__WEBHOOK__ENDPOINT="" +export RUSTFS__SINKS__WEBHOOK__AUTH_TOKEN="" +export RUSTFS__SINKS__KAFKA__ENABLED=false +export RUSTFS__SINKS__KAFKA__BOOTSTRAP_SERVERS="" +export RUSTFS__SINKS__KAFKA__TOPIC="" +export RUSTFS__LOGGER__QUEUE_CAPACITY=10 if [ -n "$1" ]; then export RUSTFS_VOLUMES="$1" fi - -# check ./rustfs/static/index.html not exists -if [ ! -f ./rustfs/static/index.html ]; then - echo "Downloading rustfs-console-latest.zip" - # download rustfs-console-latest.zip do not show log - curl -s -L "https://dl.rustfs.com/console/rustfs-console-latest.zip" -o tempfile.zip && unzip -q -o tempfile.zip -d ./rustfs/static && rm tempfile.zip -fi - cargo run --bin rustfs \ No newline at end of file diff --git a/scripts/static.sh b/scripts/static.sh index 9fd7469d8..814762ecf 100755 --- a/scripts/static.sh +++ b/scripts/static.sh @@ -1 +1 @@ -curl -L "https://dl.rustfs.com/console/rustfs-console-latest.zip" -o tempfile.zip && unzip -o tempfile.zip -d ./rustfs/static && rm tempfile.zip \ No newline at end of file +curl -L "https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip" -o tempfile.zip && unzip -o tempfile.zip -d ./rustfs/static && rm tempfile.zip \ No newline at end of file