File oci-registry-0.4.5.obscpio of Package oci-registry

07070100000000000081A400000000000000000000000166282B010000001F000000000000000000000000000000000000002100000000oci-registry-0.4.5/.dockerignore.git
dist
profdata
target
test
07070100000001000081A400000000000000000000000166282B0100000008000000000000000000000000000000000000001E00000000oci-registry-0.4.5/.gitignore/target
07070100000002000081A400000000000000000000000166282B0100001650000000000000000000000000000000000000002200000000oci-registry-0.4.5/.gitlab-ci.ymlstages:
  - build
  - deploy
  - cleanup

services:
  - name: docker:dind
    command: ["--tls=false"]

variables:
  CARGO_INCREMENTAL: "0"
  DOCKER_DRIVER: overlay2
  DOCKER_HOST: tcp://localhost:2375
  DOCKER_TLS_CERTDIR: ""
  RUSTC_WRAPPER: /usr/bin/sccache
  SCCACHE_REGION: us-east-1
  SCCACHE_ENDPOINT: rgw.storage.home.quadra-tec.net:7480
  SCCACHE_S3_USE_SSL: "off"
  SCCACHE_BUCKET: gitlab-sccache

cargo build and cargo test:
  stage: build
  image: mcronce/rust-pgo:1.76
  cache:
    - key: rust
      paths:
        - .cargo
  before_script:
    - |
      if [ -d .cargo ]; then
        rm -Rf /usr/local/cargo/registry
        time mv -f .cargo/registry /usr/local/cargo/
      fi
  script:
    - cargo build
    - cargo clippy --no-deps -- -D warnings
    - cargo test
  after_script:
    - if [ ! -d .cargo ]; then mkdir .cargo; fi
    - time mv -f /usr/local/cargo/registry .cargo/
    - /usr/bin/sccache -s

Build x86-64 container image:
  stage: build
  image: docker:20-git
  artifacts:
    paths:
      - images/release.amd64.tar.gz
  before_script:
    - docker version
    - apk add -U bash coreutils
    - bash -ec 'find Cargo.toml src -type f | while read file; do revision="$(git rev-list -n 1 HEAD "${file}")"; timestamp="$(git show --pretty=format:%ai --abbrev-commit "${revision}" | head -n1)"; touch -d "${timestamp}" "${file}"; done'
  script:
    - |
      BUILD_ARGS="--build-arg=CARGO_INCREMENTAL --build-arg=RUSTC_WRAPPER --build-arg=SCCACHE_REGION --build-arg=SCCACHE_ENDPOINT --build-arg=SCCACHE_BUCKET --build-arg=AWS_ACCESS_KEY_ID --build-arg=AWS_SECRET_ACCESS_KEY"
      docker build $BUILD_ARGS -f Dockerfile.x86-64 --cache-from "mcronce/oci-registry-builder" --target=builder -t "mcronce/oci-registry-builder" .
      docker build $BUILD_ARGS -f Dockerfile.x86-64 --cache-from "mcronce/oci-registry-builder" --cache-from "mcronce/oci-registry" -t "mcronce/oci-registry:amd64" .
    - mkdir -pv images
    - time docker save "mcronce/oci-registry:amd64" | gzip > images/release.amd64.tar.gz
  after_script:
    - docker rmi -f "mcronce/oci-registry-builder"
    - docker rmi -f "mcronce/oci-registry:amd64"
  only:
    - tags

Build aarch64 container image:
  stage: build
  image: docker:20-git
  artifacts:
    paths:
      - images/release.arm64v8.tar.gz
  before_script:
    - docker version
    - apk add -U bash coreutils
    - bash -ec 'find Cargo.toml src ui -type f | while read file; do revision="$(git rev-list -n 1 HEAD "${file}")"; timestamp="$(git show --pretty=format:%ai --abbrev-commit "${revision}" | head -n1)"; touch -d "${timestamp}" "${file}"; done'
  script:
    - |
      BUILD_ARGS="--build-arg=CARGO_INCREMENTAL --build-arg=RUSTC_WRAPPER --build-arg=SCCACHE_REGION --build-arg=SCCACHE_ENDPOINT --build-arg=SCCACHE_BUCKET --build-arg=AWS_ACCESS_KEY_ID --build-arg=AWS_SECRET_ACCESS_KEY"
      docker build $BUILD_ARGS -f Dockerfile.aarch64 --cache-from "mcronce/oci-registry-builder" --target=builder -t "mcronce/oci-registry-builder" .
      docker build $BUILD_ARGS -f Dockerfile.aarch64 --cache-from "mcronce/oci-registry-builder" --cache-from "mcronce/oci-registry" -t "mcronce/oci-registry:arm64v8" .
    - mkdir -pv images
    - time docker save "mcronce/oci-registry:arm64v8" | gzip > images/release.arm64v8.tar.gz
  after_script:
    - docker rmi -f "mcronce/oci-registry-builder"
    - docker rmi -f "mcronce/oci-registry:arm64v8"
  only:
    - tags

Lint chart:
  stage: build
  image: alpine/helm:latest
  script:
    - helm lint ./dist/helm

Push container image:
  stage: deploy
  image: docker:20-git
  before_script:
    - docker version
    - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
    - zcat images/release.amd64.tar.gz | docker load
    - zcat images/release.arm64v8.tar.gz | docker load
  script:
    - docker push "mcronce/oci-registry:amd64"
    - docker push "mcronce/oci-registry:arm64v8"
    - docker manifest create "mcronce/oci-registry:latest" --amend "mcronce/oci-registry:amd64" --amend "mcronce/oci-registry:arm64v8"
    - docker manifest push "mcronce/oci-registry:latest"
    - |
      if [ "${CI_COMMIT_TAG}" != '' ]; then
        docker tag "mcronce/oci-registry:amd64" "mcronce/oci-registry:${CI_COMMIT_TAG}-amd64"
        docker tag "mcronce/oci-registry:arm64v8" "mcronce/oci-registry:${CI_COMMIT_TAG}-arm64v8"
        docker push "mcronce/oci-registry:${CI_COMMIT_TAG}-amd64"
        docker push "mcronce/oci-registry:${CI_COMMIT_TAG}-arm64v8"
        docker manifest create "mcronce/oci-registry:${CI_COMMIT_TAG}" --amend "mcronce/oci-registry:${CI_COMMIT_TAG}-amd64" --amend "mcronce/oci-registry:${CI_COMMIT_TAG}-arm64v8"
        docker manifest push "mcronce/oci-registry:${CI_COMMIT_TAG}"
      fi
  after_script:
    - docker rmi -f "mcronce/oci-registry:amd64"
    - docker rmi -f "mcronce/oci-registry:arm64v8"
    - docker manifest rm "mcronce/oci-registry:latest"
    - |
      if [ "${CI_COMMIT_TAG}" != '' ]; then
        docker rmi -f "mcronce/oci-registry:${CI_COMMIT_TAG}-amd64"
        docker rmi -f "mcronce/oci-registry:${CI_COMMIT_TAG}-arm64v8"
        docker manifest rm "mcronce/oci-registry:${CI_COMMIT_TAG}"
      fi
  only:
    - tags

Push chart:
  stage: deploy
  image: alpine/helm:latest
  before_script:
    - apk add -U git
    - helm plugin install https://github.com/chartmuseum/helm-push.git
  script:
    - helm cm-push ./dist/helm https://charts.cronce.io
  only:
    - tags

cargo cache cleanup:
  stage: cleanup
  image: mcronce/cargo-cache
  variables:
    RUSTC_WRAPPER: ""
  cache:
    - key: rust
      paths:
        - .cargo
  before_script: []
  script:
    - CARGO_HOME=.cargo cargo cache clean-unref
  after_script: []

07070100000003000081A400000000000000000000000166282B010000010F000000000000000000000000000000000000002100000000oci-registry-0.4.5/.rustfmt.tomlversion = "Two"

hard_tabs = true
max_width = 250
chain_width = 120
inline_attribute_width = 200
struct_lit_width = 60
trailing_comma = "Never"
match_block_trailing_comma = true

group_imports = "StdExternalCrate"
imports_granularity = "Item"

reorder_impl_items = true

07070100000004000081A400000000000000000000000166282B0100016543000000000000000000000000000000000000001E00000000oci-registry-0.4.5/Cargo.lock# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "actix-codec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
dependencies = [
 "bitflags 2.5.0",
 "bytes",
 "futures-core",
 "futures-sink",
 "memchr",
 "pin-project-lite",
 "tokio",
 "tokio-util",
 "tracing",
]

[[package]]
name = "actix-http"
version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743"
dependencies = [
 "actix-codec",
 "actix-rt",
 "actix-service",
 "actix-utils",
 "ahash",
 "base64 0.21.7",
 "bitflags 2.5.0",
 "brotli",
 "bytes",
 "bytestring",
 "derive_more",
 "encoding_rs",
 "flate2",
 "futures-core",
 "h2",
 "http",
 "httparse",
 "httpdate",
 "itoa",
 "language-tags",
 "local-channel",
 "mime",
 "percent-encoding",
 "pin-project-lite",
 "rand",
 "sha1",
 "smallvec",
 "tokio",
 "tokio-util",
 "tracing",
 "zstd",
]

[[package]]
name = "actix-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "actix-router"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22475596539443685426b6bdadb926ad0ecaefdfc5fb05e5e3441f15463c511"
dependencies = [
 "bytestring",
 "http",
 "regex",
 "serde",
 "tracing",
]

[[package]]
name = "actix-rt"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d"
dependencies = [
 "futures-core",
 "tokio",
]

[[package]]
name = "actix-server"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4"
dependencies = [
 "actix-rt",
 "actix-service",
 "actix-utils",
 "futures-core",
 "futures-util",
 "mio",
 "socket2",
 "tokio",
 "tracing",
]

[[package]]
name = "actix-service"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a"
dependencies = [
 "futures-core",
 "paste",
 "pin-project-lite",
]

[[package]]
name = "actix-utils"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8"
dependencies = [
 "local-waker",
 "pin-project-lite",
]

[[package]]
name = "actix-web"
version = "4.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984"
dependencies = [
 "actix-codec",
 "actix-http",
 "actix-macros",
 "actix-router",
 "actix-rt",
 "actix-server",
 "actix-service",
 "actix-utils",
 "actix-web-codegen",
 "ahash",
 "bytes",
 "bytestring",
 "cfg-if 1.0.0",
 "cookie",
 "derive_more",
 "encoding_rs",
 "futures-core",
 "futures-util",
 "itoa",
 "language-tags",
 "log",
 "mime",
 "once_cell",
 "pin-project-lite",
 "regex",
 "serde",
 "serde_json",
 "serde_urlencoded",
 "smallvec",
 "socket2",
 "time",
 "url",
]

[[package]]
name = "actix-web-codegen"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5"
dependencies = [
 "actix-router",
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "actix-web-prometheus"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad5228fd1a6b5d0f60d636776c2a70acc9fc667034bb4ac02ec4259f0eeeab6c"
dependencies = [
 "actix-service",
 "actix-web",
 "futures-lite 1.13.0",
 "pin-project",
 "prometheus",
 "quanta",
 "thiserror",
]

[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
 "gimli",
]

[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"

[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"

[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
 "cfg-if 1.0.0",
 "getrandom",
 "once_cell",
 "version_check",
 "zerocopy",
]

[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
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 = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"

[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
 "libc",
]

[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"

[[package]]
name = "anstream"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
dependencies = [
 "anstyle",
 "anstyle-parse",
 "anstyle-query",
 "anstyle-wincon",
 "colorchoice",
 "utf8parse",
]

[[package]]
name = "anstyle"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"

[[package]]
name = "anstyle-parse"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
 "utf8parse",
]

[[package]]
name = "anstyle-query"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
 "windows-sys 0.52.0",
]

[[package]]
name = "anstyle-wincon"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
 "anstyle",
 "windows-sys 0.52.0",
]

[[package]]
name = "arcerror"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e27245b5205290df2dfe1069e064736c2e66afd8bf670ecbdef665ef5472ffa"

[[package]]
name = "arcstr"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f907281554a3d0312bb7aab855a8e0ef6cbf1614d06de54105039ca8b34460e"
dependencies = [
 "serde",
]

[[package]]
name = "async-broadcast"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb"
dependencies = [
 "event-listener 5.3.0",
 "event-listener-strategy 0.5.1",
 "futures-core",
 "pin-project-lite",
]

[[package]]
name = "async-channel"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928"
dependencies = [
 "concurrent-queue",
 "event-listener 5.3.0",
 "event-listener-strategy 0.5.1",
 "futures-core",
 "pin-project-lite",
]

[[package]]
name = "async-fs"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1"
dependencies = [
 "async-lock",
 "blocking",
 "futures-lite 2.3.0",
]

[[package]]
name = "async-lock"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b"
dependencies = [
 "event-listener 4.0.3",
 "event-listener-strategy 0.4.0",
 "pin-project-lite",
]

[[package]]
name = "async-stream"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
dependencies = [
 "async-stream-impl",
 "futures-core",
 "pin-project-lite",
]

[[package]]
name = "async-stream-impl"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "async-task"
version = "4.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"

[[package]]
name = "async-trait"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "async-walkdir"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f6338023cbfc0555eccb8e83d3d4dcf1183b51ca9140a03b1dbb8a559193db"
dependencies = [
 "async-fs",
 "futures-lite 2.3.0",
]

[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"

[[package]]
name = "autocfg"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"

[[package]]
name = "backtrace"
version = "0.3.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
dependencies = [
 "addr2line",
 "cc",
 "cfg-if 1.0.0",
 "libc",
 "miniz_oxide",
 "object",
 "rustc-demangle",
]

[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"

[[package]]
name = "base64"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"

[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"

[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"

[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
 "generic-array",
]

[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
 "generic-array",
]

[[package]]
name = "blocking"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118"
dependencies = [
 "async-channel",
 "async-lock",
 "async-task",
 "fastrand 2.0.2",
 "futures-io",
 "futures-lite 2.3.0",
 "piper",
 "tracing",
]

[[package]]
name = "brotli"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391"
dependencies = [
 "alloc-no-stdlib",
 "alloc-stdlib",
 "brotli-decompressor",
]

[[package]]
name = "brotli-decompressor"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
dependencies = [
 "alloc-no-stdlib",
 "alloc-stdlib",
]

[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"

[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"

[[package]]
name = "bytes"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
dependencies = [
 "serde",
]

[[package]]
name = "bytestring"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72"
dependencies = [
 "bytes",
]

[[package]]
name = "camino"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"

[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"

[[package]]
name = "castaway"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
dependencies = [
 "rustversion",
]

[[package]]
name = "cc"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b"
dependencies = [
 "jobserver",
 "libc",
 "once_cell",
]

[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "chrono"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
 "android-tzdata",
 "iana-time-zone",
 "num-traits",
 "serde",
 "windows-targets 0.52.5",
]

[[package]]
name = "ciborium"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
dependencies = [
 "ciborium-io",
 "ciborium-ll",
 "serde",
]

[[package]]
name = "ciborium-io"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"

[[package]]
name = "ciborium-ll"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
dependencies = [
 "ciborium-io",
 "half",
]

[[package]]
name = "clap"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
dependencies = [
 "clap_builder",
 "clap_derive",
]

[[package]]
name = "clap_builder"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [
 "anstream",
 "anstyle",
 "clap_lex",
 "strsim 0.11.1",
]

[[package]]
name = "clap_derive"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
dependencies = [
 "heck 0.5.0",
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "clap_lex"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"

[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"

[[package]]
name = "compact_str"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
dependencies = [
 "castaway",
 "cfg-if 1.0.0",
 "itoa",
 "ryu",
 "serde",
 "static_assertions",
]

[[package]]
name = "concurrent-queue"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363"
dependencies = [
 "crossbeam-utils",
]

[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"

[[package]]
name = "cookie"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
dependencies = [
 "percent-encoding",
 "time",
 "version_check",
]

[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
 "core-foundation-sys",
 "libc",
]

[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"

[[package]]
name = "cow-utils"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79"

[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
 "libc",
]

[[package]]
name = "crc32fast"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
 "cfg-if 1.0.0",
]

[[package]]
name = "criterion"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
 "anes",
 "cast",
 "ciborium",
 "clap",
 "criterion-plot",
 "is-terminal",
 "itertools 0.10.5",
 "num-traits",
 "once_cell",
 "oorandom",
 "plotters",
 "rayon",
 "regex",
 "serde",
 "serde_derive",
 "serde_json",
 "tinytemplate",
 "walkdir",
]

[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
 "cast",
 "itertools 0.10.5",
]

[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
 "crossbeam-epoch",
 "crossbeam-utils",
]

[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
 "crossbeam-utils",
]

[[package]]
name = "crossbeam-utils"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"

[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"

[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
 "generic-array",
 "typenum",
]

[[package]]
name = "crypto-mac"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
dependencies = [
 "generic-array",
 "subtle",
]

[[package]]
name = "darling"
version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
dependencies = [
 "darling_core",
 "darling_macro",
]

[[package]]
name = "darling_core"
version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
dependencies = [
 "fnv",
 "ident_case",
 "proc-macro2",
 "quote",
 "strsim 0.10.0",
 "syn 2.0.60",
]

[[package]]
name = "darling_macro"
version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [
 "darling_core",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
 "powerfmt",
 "serde",
]

[[package]]
name = "derive_more"
version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
 "convert_case",
 "proc-macro2",
 "quote",
 "rustc_version",
 "syn 1.0.109",
]

[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
 "generic-array",
]

[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
 "block-buffer 0.10.4",
 "crypto-common",
]

[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
 "cfg-if 1.0.0",
 "dirs-sys-next",
]

[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
 "libc",
 "redox_users",
 "winapi",
]

[[package]]
name = "dkregistry"
version = "0.5.1-alpha.0"
source = "git+https://github.com/mcronce/dkregistry-rs.git#baa34b7653a9263416302fb7454692c7292ec02f"
dependencies = [
 "arcstr",
 "async-stream",
 "base64 0.21.7",
 "bytes",
 "compact_str",
 "cow-utils",
 "futures",
 "http",
 "itertools 0.11.0",
 "libflate",
 "log",
 "mime",
 "pin-project",
 "regex",
 "reqwest",
 "serde",
 "serde_ignored",
 "serde_json",
 "serde_with",
 "sha2 0.10.8",
 "strum",
 "strum_macros",
 "tar",
 "thiserror",
 "tokio",
 "url",
]

[[package]]
name = "either"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"

[[package]]
name = "encoding_rs"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
 "cfg-if 1.0.0",
]

[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"

[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
 "libc",
 "windows-sys 0.52.0",
]

[[package]]
name = "event-listener"
version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e"
dependencies = [
 "concurrent-queue",
 "parking",
 "pin-project-lite",
]

[[package]]
name = "event-listener"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24"
dependencies = [
 "concurrent-queue",
 "parking",
 "pin-project-lite",
]

[[package]]
name = "event-listener-strategy"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3"
dependencies = [
 "event-listener 4.0.3",
 "pin-project-lite",
]

[[package]]
name = "event-listener-strategy"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3"
dependencies = [
 "event-listener 5.3.0",
 "pin-project-lite",
]

[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
 "instant",
]

[[package]]
name = "fastrand"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"

[[package]]
name = "filetime"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "redox_syscall",
 "windows-sys 0.52.0",
]

[[package]]
name = "flate2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
 "crc32fast",
 "miniz_oxide",
]

[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"

[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
 "percent-encoding",
]

[[package]]
name = "futures"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
 "futures-channel",
 "futures-core",
 "futures-executor",
 "futures-io",
 "futures-sink",
 "futures-task",
 "futures-util",
]

[[package]]
name = "futures-channel"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
 "futures-core",
 "futures-sink",
]

[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"

[[package]]
name = "futures-executor"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
 "futures-core",
 "futures-task",
 "futures-util",
]

[[package]]
name = "futures-io"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"

[[package]]
name = "futures-lite"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
dependencies = [
 "fastrand 1.9.0",
 "futures-core",
 "futures-io",
 "memchr",
 "parking",
 "pin-project-lite",
 "waker-fn",
]

[[package]]
name = "futures-lite"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
dependencies = [
 "fastrand 2.0.2",
 "futures-core",
 "futures-io",
 "parking",
 "pin-project-lite",
]

[[package]]
name = "futures-macro"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"

[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"

[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
 "futures-channel",
 "futures-core",
 "futures-io",
 "futures-macro",
 "futures-sink",
 "futures-task",
 "memchr",
 "pin-project-lite",
 "pin-utils",
 "slab",
]

[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
 "typenum",
 "version_check",
]

[[package]]
name = "getrandom"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "wasi 0.11.0+wasi-snapshot-preview1",
]

[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"

[[package]]
name = "h2"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
dependencies = [
 "bytes",
 "fnv",
 "futures-core",
 "futures-sink",
 "futures-util",
 "http",
 "indexmap 2.2.6",
 "slab",
 "tokio",
 "tokio-util",
 "tracing",
]

[[package]]
name = "half"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
dependencies = [
 "cfg-if 1.0.0",
 "crunchy",
]

[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"

[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"

[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"

[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"

[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"

[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"

[[package]]
name = "hmac"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
dependencies = [
 "crypto-mac",
 "digest 0.9.0",
]

[[package]]
name = "http"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [
 "bytes",
 "fnv",
 "itoa",
]

[[package]]
name = "http-body"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
 "bytes",
 "http",
 "pin-project-lite",
]

[[package]]
name = "httparse"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"

[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"

[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"

[[package]]
name = "hyper"
version = "0.14.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
dependencies = [
 "bytes",
 "futures-channel",
 "futures-core",
 "futures-util",
 "h2",
 "http",
 "http-body",
 "httparse",
 "httpdate",
 "itoa",
 "pin-project-lite",
 "socket2",
 "tokio",
 "tower-service",
 "tracing",
 "want",
]

[[package]]
name = "hyper-rustls"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c"
dependencies = [
 "http",
 "hyper",
 "log",
 "rustls 0.20.9",
 "rustls-native-certs",
 "tokio",
 "tokio-rustls 0.23.4",
]

[[package]]
name = "hyper-rustls"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
 "futures-util",
 "http",
 "hyper",
 "rustls 0.21.11",
 "tokio",
 "tokio-rustls 0.24.1",
]

[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
 "android_system_properties",
 "core-foundation-sys",
 "iana-time-zone-haiku",
 "js-sys",
 "wasm-bindgen",
 "windows-core",
]

[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
 "cc",
]

[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"

[[package]]
name = "idna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
 "unicode-bidi",
 "unicode-normalization",
]

[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
 "autocfg",
 "hashbrown 0.12.3",
 "serde",
]

[[package]]
name = "indexmap"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
 "equivalent",
 "hashbrown 0.14.3",
 "serde",
]

[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
 "cfg-if 1.0.0",
]

[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
 "hermit-abi",
 "libc",
 "windows-sys 0.48.0",
]

[[package]]
name = "ipnet"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"

[[package]]
name = "is-terminal"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
dependencies = [
 "hermit-abi",
 "libc",
 "windows-sys 0.52.0",
]

[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
 "either",
]

[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
 "either",
]

[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"

[[package]]
name = "jobserver"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
dependencies = [
 "libc",
]

[[package]]
name = "js-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
 "wasm-bindgen",
]

[[package]]
name = "language-tags"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"

[[package]]
name = "lazy-regex"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c"
dependencies = [
 "lazy-regex-proc_macros",
 "once_cell",
 "regex",
]

[[package]]
name = "lazy-regex-proc_macros"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b"
dependencies = [
 "proc-macro2",
 "quote",
 "regex",
 "syn 2.0.60",
]

[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"

[[package]]
name = "libflate"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ff4ae71b685bbad2f2f391fe74f6b7659a34871c08b210fdc039e43bee07d18"
dependencies = [
 "adler32",
 "crc32fast",
 "libflate_lz77",
]

[[package]]
name = "libflate_lz77"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a52d3a8bfc85f250440e4424db7d857e241a3aebbbe301f3eb606ab15c39acbf"
dependencies = [
 "rle-decode-fast",
]

[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
 "bitflags 2.5.0",
 "libc",
]

[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"

[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"

[[package]]
name = "local-channel"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8"
dependencies = [
 "futures-core",
 "futures-sink",
 "local-waker",
]

[[package]]
name = "local-waker"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"

[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
 "autocfg",
 "scopeguard",
]

[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"

[[package]]
name = "mach"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
dependencies = [
 "libc",
]

[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
 "regex-automata 0.1.10",
]

[[package]]
name = "md-5"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
dependencies = [
 "block-buffer 0.9.0",
 "digest 0.9.0",
 "opaque-debug",
]

[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"

[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"

[[package]]
name = "miniz_oxide"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
 "adler",
]

[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
 "libc",
 "log",
 "wasi 0.11.0+wasi-snapshot-preview1",
 "windows-sys 0.48.0",
]

[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
 "overload",
 "winapi",
]

[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"

[[package]]
name = "num-traits"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
 "autocfg",
]

[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
 "hermit-abi",
 "libc",
]

[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
 "memchr",
]

[[package]]
name = "oci-registry"
version = "0.4.5"
dependencies = [
 "actix-web",
 "actix-web-prometheus",
 "arcerror",
 "arcstr",
 "async-broadcast",
 "async-stream",
 "async-walkdir",
 "bytes",
 "camino",
 "clap",
 "compact_str",
 "criterion",
 "dkregistry",
 "futures",
 "hex",
 "humantime",
 "lazy-regex",
 "once_cell",
 "pin-project",
 "prometheus",
 "regex",
 "rusoto_core",
 "rusoto_credential",
 "rusoto_s3",
 "serde",
 "serde_json",
 "serde_with",
 "serde_yaml",
 "sha2 0.10.8",
 "socket-address",
 "thiserror",
 "tikv-jemallocator-global",
 "time",
 "tokio",
 "tracing",
 "tracing-subscriber",
]

[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
dependencies = [
 "parking_lot_core",
]

[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"

[[package]]
name = "opaque-debug"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"

[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"

[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"

[[package]]
name = "parking"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"

[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
 "lock_api",
 "parking_lot_core",
]

[[package]]
name = "parking_lot_core"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "redox_syscall",
 "smallvec",
 "windows-targets 0.48.5",
]

[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"

[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"

[[package]]
name = "pin-project"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
dependencies = [
 "pin-project-internal",
]

[[package]]
name = "pin-project-internal"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"

[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"

[[package]]
name = "piper"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4"
dependencies = [
 "atomic-waker",
 "fastrand 2.0.2",
 "futures-io",
]

[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"

[[package]]
name = "plotters"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
 "num-traits",
 "plotters-backend",
 "plotters-svg",
 "wasm-bindgen",
 "web-sys",
]

[[package]]
name = "plotters-backend"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"

[[package]]
name = "plotters-svg"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
 "plotters-backend",
]

[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"

[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"

[[package]]
name = "proc-macro2"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
dependencies = [
 "unicode-ident",
]

[[package]]
name = "procfs"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69"
dependencies = [
 "bitflags 1.3.2",
 "byteorder",
 "hex",
 "lazy_static",
 "rustix 0.36.17",
]

[[package]]
name = "prometheus"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c"
dependencies = [
 "cfg-if 1.0.0",
 "fnv",
 "lazy_static",
 "libc",
 "memchr",
 "parking_lot",
 "procfs",
 "thiserror",
]

[[package]]
name = "quanta"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e31331286705f455e56cca62e0e717158474ff02b7936c1fa596d983f4ae27"
dependencies = [
 "crossbeam-utils",
 "libc",
 "mach",
 "once_cell",
 "raw-cpuid",
 "wasi 0.10.2+wasi-snapshot-preview1",
 "web-sys",
 "winapi",
]

[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
 "libc",
 "rand_chacha",
 "rand_core",
]

[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
 "ppv-lite86",
 "rand_core",
]

[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
 "getrandom",
]

[[package]]
name = "raw-cpuid"
version = "10.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
dependencies = [
 "bitflags 1.3.2",
]

[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
 "either",
 "rayon-core",
]

[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
 "crossbeam-deque",
 "crossbeam-utils",
]

[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
 "bitflags 1.3.2",
]

[[package]]
name = "redox_users"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
dependencies = [
 "getrandom",
 "libredox",
 "thiserror",
]

[[package]]
name = "regex"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
 "aho-corasick",
 "memchr",
 "regex-automata 0.4.6",
 "regex-syntax 0.8.3",
]

[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
 "regex-syntax 0.6.29",
]

[[package]]
name = "regex-automata"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
 "aho-corasick",
 "memchr",
 "regex-syntax 0.8.3",
]

[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"

[[package]]
name = "regex-syntax"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"

[[package]]
name = "reqwest"
version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [
 "base64 0.21.7",
 "bytes",
 "encoding_rs",
 "futures-core",
 "futures-util",
 "h2",
 "http",
 "http-body",
 "hyper",
 "hyper-rustls 0.24.2",
 "ipnet",
 "js-sys",
 "log",
 "mime",
 "once_cell",
 "percent-encoding",
 "pin-project-lite",
 "rustls 0.21.11",
 "rustls-pemfile",
 "serde",
 "serde_json",
 "serde_urlencoded",
 "sync_wrapper",
 "system-configuration",
 "tokio",
 "tokio-rustls 0.24.1",
 "tokio-util",
 "tower-service",
 "url",
 "wasm-bindgen",
 "wasm-bindgen-futures",
 "wasm-streams",
 "web-sys",
 "webpki-roots",
 "winreg",
]

[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
 "cc",
 "libc",
 "once_cell",
 "spin 0.5.2",
 "untrusted 0.7.1",
 "web-sys",
 "winapi",
]

[[package]]
name = "ring"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
 "cc",
 "cfg-if 1.0.0",
 "getrandom",
 "libc",
 "spin 0.9.8",
 "untrusted 0.9.0",
 "windows-sys 0.52.0",
]

[[package]]
name = "rle-decode-fast"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"

[[package]]
name = "rusoto_core"
version = "0.48.0"
source = "git+https://github.com/mcronce/rusoto.git?branch=enable-http1#bf26da8fa88b9f05311488754c14b50340e85364"
dependencies = [
 "async-trait",
 "base64 0.13.1",
 "bytes",
 "crc32fast",
 "flate2",
 "futures",
 "http",
 "hyper",
 "hyper-rustls 0.23.2",
 "lazy_static",
 "log",
 "rusoto_credential",
 "rusoto_signature",
 "rustc_version",
 "serde",
 "serde_json",
 "tokio",
 "xml-rs",
]

[[package]]
name = "rusoto_credential"
version = "0.48.0"
source = "git+https://github.com/mcronce/rusoto.git?branch=enable-http1#bf26da8fa88b9f05311488754c14b50340e85364"
dependencies = [
 "async-trait",
 "chrono",
 "dirs-next",
 "futures",
 "hyper",
 "serde",
 "serde_json",
 "shlex",
 "tokio",
 "zeroize",
]

[[package]]
name = "rusoto_s3"
version = "0.48.0"
source = "git+https://github.com/mcronce/rusoto.git?branch=enable-http1#bf26da8fa88b9f05311488754c14b50340e85364"
dependencies = [
 "async-trait",
 "bytes",
 "futures",
 "rusoto_core",
 "xml-rs",
]

[[package]]
name = "rusoto_signature"
version = "0.48.0"
source = "git+https://github.com/mcronce/rusoto.git?branch=enable-http1#bf26da8fa88b9f05311488754c14b50340e85364"
dependencies = [
 "base64 0.13.1",
 "bytes",
 "chrono",
 "digest 0.9.0",
 "futures",
 "hex",
 "hmac",
 "http",
 "hyper",
 "log",
 "md-5",
 "percent-encoding",
 "pin-project-lite",
 "rusoto_credential",
 "rustc_version",
 "serde",
 "sha2 0.9.9",
 "tokio",
]

[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"

[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
 "semver",
]

[[package]]
name = "rustix"
version = "0.36.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed"
dependencies = [
 "bitflags 1.3.2",
 "errno",
 "io-lifetimes",
 "libc",
 "linux-raw-sys 0.1.4",
 "windows-sys 0.45.0",
]

[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
 "bitflags 2.5.0",
 "errno",
 "libc",
 "linux-raw-sys 0.4.13",
 "windows-sys 0.52.0",
]

[[package]]
name = "rustls"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
dependencies = [
 "log",
 "ring 0.16.20",
 "sct",
 "webpki",
]

[[package]]
name = "rustls"
version = "0.21.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4"
dependencies = [
 "log",
 "ring 0.17.8",
 "rustls-webpki",
 "sct",
]

[[package]]
name = "rustls-native-certs"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
dependencies = [
 "openssl-probe",
 "rustls-pemfile",
 "schannel",
 "security-framework",
]

[[package]]
name = "rustls-pemfile"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
 "base64 0.21.7",
]

[[package]]
name = "rustls-webpki"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
 "ring 0.17.8",
 "untrusted 0.9.0",
]

[[package]]
name = "rustversion"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"

[[package]]
name = "ryu"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"

[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
 "winapi-util",
]

[[package]]
name = "schannel"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
dependencies = [
 "windows-sys 0.52.0",
]

[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"

[[package]]
name = "sct"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
 "ring 0.17.8",
 "untrusted 0.9.0",
]

[[package]]
name = "security-framework"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6"
dependencies = [
 "bitflags 1.3.2",
 "core-foundation",
 "core-foundation-sys",
 "libc",
 "security-framework-sys",
]

[[package]]
name = "security-framework-sys"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef"
dependencies = [
 "core-foundation-sys",
 "libc",
]

[[package]]
name = "semver"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"

[[package]]
name = "serde"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
dependencies = [
 "serde_derive",
]

[[package]]
name = "serde_derive"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "serde_ignored"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8e319a36d1b52126a0d608f24e93b2d81297091818cd70625fcf50a15d84ddf"
dependencies = [
 "serde",
]

[[package]]
name = "serde_json"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
dependencies = [
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
 "form_urlencoded",
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "serde_with"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a"
dependencies = [
 "base64 0.21.7",
 "chrono",
 "hex",
 "indexmap 1.9.3",
 "indexmap 2.2.6",
 "serde",
 "serde_derive",
 "serde_json",
 "serde_with_macros",
 "time",
]

[[package]]
name = "serde_with_macros"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655"
dependencies = [
 "darling",
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
 "indexmap 2.2.6",
 "itoa",
 "ryu",
 "serde",
 "unsafe-libyaml",
]

[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
 "cfg-if 1.0.0",
 "cpufeatures",
 "digest 0.10.7",
]

[[package]]
name = "sha2"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
 "block-buffer 0.9.0",
 "cfg-if 1.0.0",
 "cpufeatures",
 "digest 0.9.0",
 "opaque-debug",
]

[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
 "cfg-if 1.0.0",
 "cpufeatures",
 "digest 0.10.7",
 "sha2-asm",
]

[[package]]
name = "sha2-asm"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27ba7066011e3fb30d808b51affff34f0a66d3a03a58edd787c6e420e40e44e"
dependencies = [
 "cc",
]

[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
 "lazy_static",
]

[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"

[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
 "libc",
]

[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
 "autocfg",
]

[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"

[[package]]
name = "socket-address"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e009e8acc78acbfe17c42852dca91e985536b7fdbc6a5cac41c4a34955e03569"
dependencies = [
 "thiserror",
]

[[package]]
name = "socket2"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
dependencies = [
 "libc",
 "windows-sys 0.52.0",
]

[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"

[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"

[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"

[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"

[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"

[[package]]
name = "strum"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"

[[package]]
name = "strum_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
 "heck 0.4.1",
 "proc-macro2",
 "quote",
 "rustversion",
 "syn 2.0.60",
]

[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"

[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-ident",
]

[[package]]
name = "syn"
version = "2.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-ident",
]

[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"

[[package]]
name = "system-configuration"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
 "bitflags 1.3.2",
 "core-foundation",
 "system-configuration-sys",
]

[[package]]
name = "system-configuration-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
 "core-foundation-sys",
 "libc",
]

[[package]]
name = "tar"
version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb"
dependencies = [
 "filetime",
 "libc",
 "xattr",
]

[[package]]
name = "thiserror"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
dependencies = [
 "thiserror-impl",
]

[[package]]
name = "thiserror-impl"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
 "cfg-if 1.0.0",
 "once_cell",
]

[[package]]
name = "tikv-jemalloc-sys"
version = "0.5.4+5.3.0-patched"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1"
dependencies = [
 "cc",
 "libc",
]

[[package]]
name = "tikv-jemallocator"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "965fe0c26be5c56c94e38ba547249074803efd52adfb66de62107d95aab3eaca"
dependencies = [
 "libc",
 "tikv-jemalloc-sys",
]

[[package]]
name = "tikv-jemallocator-global"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28047b535f87b21a79b0b450c5aed8235b32a5aaf8dcc6706b638c1eadea11f"
dependencies = [
 "cfg-if 0.1.10",
 "tikv-jemallocator",
]

[[package]]
name = "time"
version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
 "deranged",
 "itoa",
 "num-conv",
 "powerfmt",
 "serde",
 "time-core",
 "time-macros",
]

[[package]]
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"

[[package]]
name = "time-macros"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
 "num-conv",
 "time-core",
]

[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
 "serde",
 "serde_json",
]

[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
 "tinyvec_macros",
]

[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"

[[package]]
name = "tokio"
version = "1.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
dependencies = [
 "backtrace",
 "bytes",
 "libc",
 "mio",
 "num_cpus",
 "parking_lot",
 "pin-project-lite",
 "signal-hook-registry",
 "socket2",
 "tokio-macros",
 "windows-sys 0.48.0",
]

[[package]]
name = "tokio-macros"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "tokio-rustls"
version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
dependencies = [
 "rustls 0.20.9",
 "tokio",
 "webpki",
]

[[package]]
name = "tokio-rustls"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
 "rustls 0.21.11",
 "tokio",
]

[[package]]
name = "tokio-util"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
dependencies = [
 "bytes",
 "futures-core",
 "futures-sink",
 "pin-project-lite",
 "tokio",
 "tracing",
]

[[package]]
name = "tower-service"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"

[[package]]
name = "tracing"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
 "log",
 "pin-project-lite",
 "tracing-attributes",
 "tracing-core",
]

[[package]]
name = "tracing-attributes"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "tracing-core"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
 "once_cell",
 "valuable",
]

[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
 "log",
 "once_cell",
 "tracing-core",
]

[[package]]
name = "tracing-subscriber"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
 "matchers",
 "nu-ansi-term",
 "once_cell",
 "regex",
 "sharded-slab",
 "smallvec",
 "thread_local",
 "tracing",
 "tracing-core",
 "tracing-log",
]

[[package]]
name = "try-lock"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"

[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"

[[package]]
name = "unicode-bidi"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"

[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

[[package]]
name = "unicode-normalization"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
dependencies = [
 "tinyvec",
]

[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"

[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"

[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"

[[package]]
name = "url"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
 "form_urlencoded",
 "idna",
 "percent-encoding",
]

[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"

[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"

[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"

[[package]]
name = "waker-fn"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690"

[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
 "same-file",
 "winapi-util",
]

[[package]]
name = "want"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
dependencies = [
 "try-lock",
]

[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"

[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

[[package]]
name = "wasm-bindgen"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
 "cfg-if 1.0.0",
 "wasm-bindgen-macro",
]

[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
 "bumpalo",
 "log",
 "once_cell",
 "proc-macro2",
 "quote",
 "syn 2.0.60",
 "wasm-bindgen-shared",
]

[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
dependencies = [
 "cfg-if 1.0.0",
 "js-sys",
 "wasm-bindgen",
 "web-sys",
]

[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
 "quote",
 "wasm-bindgen-macro-support",
]

[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.60",
 "wasm-bindgen-backend",
 "wasm-bindgen-shared",
]

[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"

[[package]]
name = "wasm-streams"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
dependencies = [
 "futures-util",
 "js-sys",
 "wasm-bindgen",
 "wasm-bindgen-futures",
 "web-sys",
]

[[package]]
name = "web-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [
 "js-sys",
 "wasm-bindgen",
]

[[package]]
name = "webpki"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
dependencies = [
 "ring 0.17.8",
 "untrusted 0.9.0",
]

[[package]]
name = "webpki-roots"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"

[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
 "winapi-i686-pc-windows-gnu",
 "winapi-x86_64-pc-windows-gnu",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

[[package]]
name = "winapi-util"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134306a13c5647ad6453e8deaec55d3a44d6021970129e6188735e74bf546697"
dependencies = [
 "windows-sys 0.52.0",
]

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
 "windows-targets 0.52.5",
]

[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
 "windows-targets 0.42.2",
]

[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
 "windows-targets 0.48.5",
]

[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
 "windows-targets 0.52.5",
]

[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
 "windows_aarch64_gnullvm 0.42.2",
 "windows_aarch64_msvc 0.42.2",
 "windows_i686_gnu 0.42.2",
 "windows_i686_msvc 0.42.2",
 "windows_x86_64_gnu 0.42.2",
 "windows_x86_64_gnullvm 0.42.2",
 "windows_x86_64_msvc 0.42.2",
]

[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
 "windows_aarch64_gnullvm 0.48.5",
 "windows_aarch64_msvc 0.48.5",
 "windows_i686_gnu 0.48.5",
 "windows_i686_msvc 0.48.5",
 "windows_x86_64_gnu 0.48.5",
 "windows_x86_64_gnullvm 0.48.5",
 "windows_x86_64_msvc 0.48.5",
]

[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
 "windows_aarch64_gnullvm 0.52.5",
 "windows_aarch64_msvc 0.52.5",
 "windows_i686_gnu 0.52.5",
 "windows_i686_gnullvm",
 "windows_i686_msvc 0.52.5",
 "windows_x86_64_gnu 0.52.5",
 "windows_x86_64_gnullvm 0.52.5",
 "windows_x86_64_msvc 0.52.5",
]

[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"

[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"

[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"

[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"

[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"

[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"

[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"

[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"

[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"

[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"

[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"

[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"

[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"

[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"

[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"

[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"

[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"

[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"

[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"

[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

[[package]]
name = "winreg"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
 "cfg-if 1.0.0",
 "windows-sys 0.48.0",
]

[[package]]
name = "xattr"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
dependencies = [
 "libc",
 "linux-raw-sys 0.4.13",
 "rustix 0.38.34",
]

[[package]]
name = "xml-rs"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"

[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [
 "zerocopy-derive",
]

[[package]]
name = "zerocopy-derive"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.60",
]

[[package]]
name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"

[[package]]
name = "zstd"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
dependencies = [
 "zstd-safe",
]

[[package]]
name = "zstd-safe"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
dependencies = [
 "zstd-sys",
]

[[package]]
name = "zstd-sys"
version = "2.0.10+zstd.1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
dependencies = [
 "cc",
 "pkg-config",
]
07070100000005000081A400000000000000000000000166282B01000009BC000000000000000000000000000000000000001E00000000oci-registry-0.4.5/Cargo.toml[package]
name = "oci-registry"
version = "0.4.5"
edition = "2021"
license = "MIT"
repository = "https://github.com/mcronce/oci-registry"
description = "An implementation of the OCI Registry spec with filesystem and S3 storage back-ends"
keywords = ["container", "docker", "registry", "mirror"]
include = ["Cargo.toml", "src"]

[profile.dev]
debug = 1

[profile.release]
debug = 1
lto = "fat"
codegen-units = 1

[[bench]]
name = "main"
harness = false

[dependencies]
actix-web = "4.2.1"
actix-web-prometheus = { version = "0.1.2", features = ["process"] }
arcerror = "0.1.5"
arcstr = { version = "1.1.5", features = ["serde"] }
async-broadcast = "0.7.0"
async-stream = "0.3.3"
async-walkdir = "1.0.0"
bytes = { version = "1.2.1", features = ["serde"] }
camino = "1.1.1"
clap = { version = "4.0.12", features = ["derive", "env"] }
compact_str = { version = "0.7.0", features = ["serde"] }
dkregistry = { version = "0.5.1-alpha.0", git = "https://github.com/mcronce/dkregistry-rs.git", default-features = false, features = ["reqwest-rustls"] }
futures = "0.3.24"
hex = "0.4.3"
humantime = "2.1.0"
lazy-regex = "3.0.0"
once_cell = { version = "1.18.0", default-features = false, features = ["parking_lot"] }
pin-project = "1.1.4"
prometheus = { version = "0.13.3", default-features = false }
regex = "1.6.0"
rusoto_core = { version = "0.48.0", default-features = false, features = ["hyper-rustls", "flate2"] }
rusoto_credential = "0.48.0"
rusoto_s3 = { version = "0.48.0", default-features = false, features = ["rustls"] }
serde = { version = "1.0.145", features = ["derive"] }
serde_json = "1.0.86"
serde_with = { version = "3.0.0", default-features = false, features = ["hex"] }
serde_yaml = "0.9.13"
sha2 = { version = "0.10.6", features = ["asm"] }
socket-address = "0.1.0"
thiserror = "1.0.37"
tikv-jemallocator-global = { version = "0.5.0", features = ["tikv-jemallocator"] }
time = { version = "0.3.15", features = ["parsing"] }
tokio = { version = "1.24.1", features = ["fs", "io-util"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }

[dev-dependencies]
criterion = "0.5.1"

# TODO:  Once https://github.com/rusoto/rusoto/pull/1981 is merged, remove these.
[patch.crates-io]
rusoto_core = {git = "https://github.com/mcronce/rusoto.git", branch = "enable-http1"}
rusoto_credential = {git = "https://github.com/mcronce/rusoto.git", branch = "enable-http1"}
rusoto_s3 = {git = "https://github.com/mcronce/rusoto.git", branch = "enable-http1"}

070701000000060000A1FF00000000000000000000000166282B0100000011000000000000000000000000000000000000001E00000000oci-registry-0.4.5/DockerfileDockerfile.x86-6407070100000007000081A400000000000000000000000166282B01000004E8000000000000000000000000000000000000002600000000oci-registry-0.4.5/Dockerfile.aarch64FROM mcronce/rust-pgo:1.76 AS builder

ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
ARG \
	RUSTC_WRAPPER \
	SCCACHE_REGION \
	SCCACHE_ENDPOINT \
	SCCACHE_S3_USE_SSL=off \
	SCCACHE_BUCKET \
	AWS_ACCESS_KEY_ID \
	AWS_SECRET_ACCESS_KEY

WORKDIR /repo

COPY Cargo.toml /repo/
COPY benches /repo/benches
RUN \
	mkdir -v /repo/src && \
	echo 'fn main() {}' > /repo/src/main.rs && \
	cargo build --release --target=aarch64-unknown-linux-gnu && \
	bash -exc "if [ '${RUSTC_WRAPPER}' == '/usr/bin/sccache' ]; then /usr/bin/sccache -s; fi" && \
	rm -Rvf /repo/src

COPY src /repo/src

ENV RUST_LOG=info,actix-web=debug
RUN \
	touch src/main.rs && \
	cargo build --release --target=aarch64-unknown-linux-gnu && \
	bash -exc "if [ '${RUSTC_WRAPPER}' == '/usr/bin/sccache' ]; then /usr/bin/sccache -s; fi"

RUN aarch64-linux-gnu-strip /repo/target/aarch64-unknown-linux-gnu/release/oci-registry

FROM --platform=linux/arm64 arm64v8/debian:12
RUN \
	apt-get update && \
	apt-get install -y ca-certificates \
	&& rm -Rvf /var/lib/apt
COPY --from=builder /repo/target/aarch64-unknown-linux-gnu/release/oci-registry /usr/local/bin/oci-registry
EXPOSE 80
ENV \
	PORT=80 \
	RUST_LOG=info,actix-web=debug
ENTRYPOINT ["/usr/local/bin/oci-registry"]

07070100000008000081A400000000000000000000000166282B0100000670000000000000000000000000000000000000002500000000oci-registry-0.4.5/Dockerfile.x86-64FROM mcronce/rust-pgo:1.76 AS builder

ARG \
	RUSTC_WRAPPER \
	SCCACHE_REGION \
	SCCACHE_ENDPOINT \
	SCCACHE_S3_USE_SSL=off \
	SCCACHE_BUCKET \
	AWS_ACCESS_KEY_ID \
	AWS_SECRET_ACCESS_KEY

RUN apt-get update && apt-get install -y s3cmd ncat jq

WORKDIR /repo

COPY Cargo.toml /repo/
COPY benches /repo/benches
RUN \
	mkdir -v /repo/src && \
	echo 'fn main() {}' > /repo/src/main.rs && \
	cargo pgo build && \
	bash -exc "if [ '${RUSTC_WRAPPER}' == '/usr/bin/sccache' ]; then /usr/bin/sccache -s; fi" && \
	rm -Rvf /repo/src

COPY src /repo/src

ENV RUST_LOG=info,actix-web=debug
RUN \
	touch src/main.rs && \
	cargo pgo build && \
	bash -exc "if [ '${RUSTC_WRAPPER}' == '/usr/bin/sccache' ]; then /usr/bin/sccache -s; fi"

ADD tools /repo/tools
ADD testdata /repo/testdata

RUN mv -vf /repo/testdata/s3cfg ~/.s3cfg
RUN \
	export LLVM_PROFILE_FILE=/repo/target/pgo-profiles/oci-registry_%m_%p.profraw && \
	./tools/generate-profiles '' | sed 's/^/[ pgo] /'

RUN \
	cargo pgo bolt build --with-pgo && \
	bash -exc "if [ '${RUSTC_WRAPPER}' == '/usr/bin/sccache' ]; then /usr/bin/sccache -s; fi" && \
	./tools/generate-profiles -bolt-instrumented | sed 's/^/[bolt] /'

RUN \
	cargo pgo bolt optimize --with-pgo && \
	bash -exc "if [ '${RUSTC_WRAPPER}' == '/usr/bin/sccache' ]; then /usr/bin/sccache -s; fi"

RUN strip /repo/target/x86_64-unknown-linux-gnu/release/oci-registry-bolt-optimized

FROM gcr.io/distroless/cc-debian12
COPY --from=builder /repo/target/x86_64-unknown-linux-gnu/release/oci-registry-bolt-optimized /usr/local/bin/oci-registry
EXPOSE 80
ENV \
	PORT=80 \
	RUST_LOG=info,actix-web=debug
ENTRYPOINT ["/usr/local/bin/oci-registry"]

07070100000009000081A400000000000000000000000166282B010000041F000000000000000000000000000000000000001B00000000oci-registry-0.4.5/LICENSECopyright (c) 2022 Mike Cronce

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
0707010000000A000081A400000000000000000000000166282B0100001E6A000000000000000000000000000000000000001D00000000oci-registry-0.4.5/README.md`oci-registry` is an implementation of the OCI Registry spec with filesystem and S3 storage back-ends.

[[_TOC_]]

# Features
* Pull-through cache for _any_ registry, not just docker.io
	* This includes private, authenticated registries.  **This means that you can create an unauthenticated mirror of a private registry and expose it to the Internet.  Easily.  Don't do that.**
* Two storage back-ends
	* S3
	* Local filesystem
* Small footprint; in my test system, the official `registry` uses approximately 130 MiB of memory to mirror docker.io; five replicas of `oci-registry` combined use approximately 60 MiB to mirror everything in [example.yaml](example.yaml), plus one private registry.  CPU is negligible for both.
* A [helm chart][artifacthub]

# Limitations
* Pushing is not currently implemented; `oci-registry` only supports being a pull-through cache (a mirror) at this time.  Push support is planned.
* Authentication is not currently implemented, but is planned
* Only SHA256 content hashes are supported, but supporting other schemes is planned
* Connecting to `oci-registry` with TLS (https) is not supported and support will not be added.
	* [Using nginx as a TLS termination proxy][nginx-proxy] is easy, well-supported, and well-documented; if you require TLS between the client and `oci-registry`, that is the recommended configuration
	* Connecting to upstream registries with TLS is supported, recommended, and usually required.
* If two clients request the same blob simultaneously, it will be downloaded from upstream twice in parallel instead of having the later request wait for the download to finish, then serve it from cache.  There are no data corruption issues, but it is suboptimal.  No fix is currently planned, but I'm open to one.
* Has not yet had the [OCI distribution spec conformance test suite][oci-test-suite] run against it; only manual compatibility testing with `docker` and `containerd` has been performed.  This is planned after push support is implemented.

# Examples
## `docker`
Mirroring `docker.io` is the default configuration.  Start `oci-registry`:
```bash
oci-registry --listen 0.0.0.0:8080 filesystem --root /tmp/oci-mirror
```

Configure Docker's `daemon.json` to use the registry:
```json
{
	"registry-mirrors": ["http://localhost:8080"]
}
```

Restart Docker and it will start pulling images from `docker.io` through `oci-registry`.

**NOTE**:  Mirroring registries other than `docker.io` is not possible with Docker.

## `containerd`
`containerd` provides a mechanism for mirroring any registry you want, and sends the upstream registry as a querystring parameter in all its requests.  This means that we can mirror any number of registries to `containerd` with a single instance of `oci-registry`.

## `cri-o`
`cri-o` requires defining each registry you want to mirror, but you can use a separate path for each registry to inform `oci-registry` of which registry the request is for.

### Configure `oci-registry`
`oci-registry`'s default configuration is to mirror any registry for which it receives requests, connecting to upstream with HTTPS, rejecting invalid certs, and using the namespace as the upstream registry host - e.g. requests for `gcr.io` images will be made to https://gcr.io/ - with the exception of `docker.io`, which will be pointed to https://registry-1.docker.io

In short, `oci-registry`'s default configuration will work for most public registries, but can be added to with `--upstream-config-file`.  See [example.yaml](example.yaml) for real world examples, or the following contrived private registry example:
```yaml
# namespace and host are the only two required keys
- namespace: example.com
  host: registry.example.com
  # Connecting with TLS is the default
  tls: true
  # Requiring valid TLS certs is the default
  accept_invalid_certs: false
  # This hypothetical registry checks the HTTP User-Agent header to make sure there's no malarkey going on, so pretend to be containerd
  user_agent: "containerd/1.6.8"
  # This hypothetical registry requires authentication, so let's give it our username and password
  username: example
  password: hunter2
  # This hypothetical registry is used for active development, so let's _always_ see if we have the latest manifest for a given image
  manifest_invalidation_time: 0s
  # Blobs are identified by the SHA256 hash of their contents, so they probably won't change frequently, if ever
  blob_invalidation_time: 30d
```

To avoid having to store credentials in a plaintext file, they can be set by storing a JSON map in the `$UPSTREAM_CREDENTIALS` environment variable, like so:
```
UPSTREAM_CREDENTIALS='{"example.com": {"username": "example", "password": "hunter2"}, "docker.io": {"username": "aaa", "password": "bbb"}}'
```

Note that this is not exposed in the Helm chart, because the configuration is already itself mounted in from a secret.

### Configure `containerd`
Recent versions of `containerd` (1.5+) use [per-host configuration files][containerd-hosts]; for older versions, config instructions can be found in the deprecated section [here][containerd-deprecated].

Assuming default paths, make sure your `/etc/containerd/config.toml` contains the following:
```toml
[plugins."io.containerd.grpc.v1.cri".registry]
	config_path = "/etc/containerd/certs.d"
```

Then, in `/etc/containerd/certs.d`, create a directory for each registry you want to mirror and create a `hosts.toml` pointing at `oci-registry`:
```bash
mkdir /etc/containerd/certs.d/docker.io
cat > /etc/containerd/certs.d/docker.io/hosts.toml <<EOF
server = "https://registry-1.docker.io"

[host."http://localhost:8080"]
	capabilities = ["pull", "resolve"]
EOF

mkdir /etc/containerd/certs.d/gcr.io
cat > /etc/containerd/certs.d/gcr.io/hosts.toml <<EOF
server = "https://gcr.io"

[host."http://localhost:8080"]
	capabilities = ["pull", "resolve"]
EOF
```

The above example will configure `containerd` to attempt to pull `docker.io` and `gcr.io` manifests and blobs from `oci-registry` listening on `localhost:8080`, while sticking with the original hosts for pushing, and using the original hosts if something goes wrong with `oci-registry`.

### Configure `cri-o`
`cri-o` uses the common [`registries.conf`][registries-conf] configuration file to specify which registries to mirror.

Assuming default paths, simply add the following to `/etc/containers/registries.conf`:
```
[[registry]]
location = "docker.io"
[[registry.mirror]]
location = "localhost:8080/docker.io"
insecure = true

[[registry]]
location = "gcr.io"
[[registry.mirror]]
location = "localhost:8080/gcr.io"
insecure = true
```

The above example will configure `cri-o` to attempt to pull `docker.io` and `gcr.io` manifests and blobs from `oci-registry` listening on `localhost:8080`, while sticking with the original hosts for pushing, and using the original hosts if something goes wrong with `oci-registry`.

# Community
The Github repo is a mirror.  Project management is done in the [main repo][gitlab].  In addition, there is a [Matrix room][matrix].

[artifacthub]: https://artifacthub.io/packages/helm/cronce/oci-registry
[nginx-proxy]: https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/
[oci-test-suite]: https://github.com/opencontainers/distribution-spec/tree/main/conformance
[containerd-hosts]: https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration
[containerd-deprecated]: https://github.com/containerd/containerd/blob/main/docs/cri/registry.md#configure-registry-endpoint
[registries-conf]: https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md#remapping-and-mirroring-registries
[gitlab]: https://gitlab.cronce.io/foss/oci-registry
[matrix]: https://matrix.to/#/%23oci-registry%3Acronce.io

0707010000000B000041ED00000000000000000000000266282B0100000000000000000000000000000000000000000000001B00000000oci-registry-0.4.5/benches0707010000000C000081A400000000000000000000000166282B0100000B14000000000000000000000000000000000000002300000000oci-registry-0.4.5/benches/main.rsuse criterion::criterion_group;
use criterion::criterion_main;
use criterion::BenchmarkId;
use criterion::Criterion;

fn split_image_with_ns(c: &mut Criterion) {
	c.bench_with_input(BenchmarkId::new("split_image", "with ns docker.io/envoyproxy/envoy"), &(Some("docker.io"), "envoyproxy/envoy", ""), |b, &(ns, image, default_ns)| {
		b.iter(|| oci_registry::api::split_image(ns, image, default_ns))
	});

	c.bench_with_input(BenchmarkId::new("split_image", "with ns docker.io/library/busybox"), &(Some("docker.io"), "library/busybox", ""), |b, &(ns, image, default_ns)| {
		b.iter(|| oci_registry::api::split_image(ns, image, default_ns))
	});

	c.bench_with_input(BenchmarkId::new("split_image", "with ns gcr.io/distroless/static"), &(Some("gcr.io"), "distroless/static", ""), |b, &(ns, image, default_ns)| {
		b.iter(|| oci_registry::api::split_image(ns, image, default_ns))
	});

	c.bench_with_input(
		BenchmarkId::new("split_image", "with ns ghcr.io/buildbarn/bb-runner-installer"),
		&(Some("ghcr.io"), "buildbarn/bb-runner-installer", ""),
		|b, &(ns, image, default_ns)| b.iter(|| oci_registry::api::split_image(ns, image, default_ns))
	);
}

fn split_image_without_ns(c: &mut Criterion) {
	c.bench_with_input(BenchmarkId::new("split_image", "without ns docker.io/envoyproxy/envoy"), &(None, "docker.io/envoyproxy/envoy", ""), |b, &(ns, image, default_ns)| {
		b.iter(|| oci_registry::api::split_image(ns, image, default_ns))
	});

	c.bench_with_input(BenchmarkId::new("split_image", "without ns docker.io/library/busybox"), &(None, "docker.io/library/busybox", ""), |b, &(ns, image, default_ns)| {
		b.iter(|| oci_registry::api::split_image(ns, image, default_ns))
	});

	c.bench_with_input(BenchmarkId::new("split_image", "without ns gcr.io/distroless/static"), &(None, "gcr.io/distroless/static", ""), |b, &(ns, image, default_ns)| {
		b.iter(|| oci_registry::api::split_image(ns, image, default_ns))
	});

	c.bench_with_input(
		BenchmarkId::new("split_image", "without ns ghcr.io/buildbarn/bb-runner-installer"),
		&(None, "ghcr.io/buildbarn/bb-runner-installer", ""),
		|b, &(ns, image, default_ns)| b.iter(|| oci_registry::api::split_image(ns, image, default_ns))
	);
}

fn split_image_fallback(c: &mut Criterion) {
	c.bench_with_input(BenchmarkId::new("split_image", "fallback docker.io/envoyproxy/envoy"), &(None, "envoyproxy/envoy", "docker.io"), |b, &(ns, image, default_ns)| {
		b.iter(|| oci_registry::api::split_image(ns, image, default_ns))
	});

	c.bench_with_input(BenchmarkId::new("split_image", "fallback docker.io/library/busybox"), &(None, "library/busybox", "docker.io"), |b, &(ns, image, default_ns)| {
		b.iter(|| oci_registry::api::split_image(ns, image, default_ns))
	});
}

criterion_group!(split_image, split_image_with_ns, split_image_without_ns, split_image_fallback);
criterion_main!(split_image);
0707010000000D000041ED00000000000000000000000266282B0100000000000000000000000000000000000000000000001800000000oci-registry-0.4.5/dist0707010000000E000041ED00000000000000000000000266282B0100000000000000000000000000000000000000000000001D00000000oci-registry-0.4.5/dist/helm0707010000000F000081A400000000000000000000000166282B0100000156000000000000000000000000000000000000002800000000oci-registry-0.4.5/dist/helm/Chart.yamlapiVersion: v1
name: oci-registry
description: oci-registry is an implementation of the OCI Registry spec with filesystem and S3 storage back-ends.  Currently it only supports being a pull-through cache (a mirror); pushing is not yet implemented.
version: 0.4.5
appVersion: 0.4.5
maintainers:
  - name: Mike Cronce
    email: mike@cronce.io

07070100000010000081A400000000000000000000000166282B0100000A95000000000000000000000000000000000000002700000000oci-registry-0.4.5/dist/helm/README.md# oci-registry

![Version: 0.3.0](https://img.shields.io/badge/Version-0.3.0-informational?style=flat-square) ![AppVersion: 0.3.0](https://img.shields.io/badge/AppVersion-0.3.0-informational?style=flat-square)

oci-registry is an implementation of the OCI Registry spec with filesystem and S3 storage back-ends.  Currently it only supports being a pull-through cache (a mirror); pushing is not yet implemented.

## Maintainers

| Name | Email | Url |
| ---- | ------ | --- |
| Mike Cronce | <mike@cronce.io> |  |

## Values

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| extraLabels | object | `{}` |  |
| image.name | string | `"oci-registry"` |  |
| image.pullPolicy | string | `"IfNotPresent"` |  |
| image.pullSecret | string | `nil` |  |
| image.registry | string | `"mcronce"` |  |
| image.tag | string | `"v0.3.0"` |  |
| ingress.annotations | object | `{}` |  |
| ingress.class | string | `nil` |  |
| ingress.enabled | bool | `false` |  |
| ingress.hosts[0] | string | `"oci-registry"` |  |
| ingress.path | string | `"/"` |  |
| ingress.tls | list | `[]` |  |
| registry.invalidation_time.blobs | string | `"14d"` |  |
| registry.invalidation_time.manifests | string | `"14d"` |  |
| registry.storage.filesystem.path | string | `"/data"` |  |
| registry.storage.mode | string | `"filesystem"` |  |
| registry.storage.s3.auth_secret.access_key | string | `nil` |  |
| registry.storage.s3.auth_secret.deploy | bool | `true` |  |
| registry.storage.s3.auth_secret.name_override | string | `nil` |  |
| registry.storage.s3.auth_secret.secret_key | string | `nil` |  |
| registry.storage.s3.bucket | string | `"oci-registry"` |  |
| registry.storage.s3.host | string | `nil` |  |
| registry.storage.s3.region | string | `"us-east-1"` |  |
| registry.upstream.config.contents | list | `[]` |  |
| registry.upstream.config.deploy | bool | `true` |  |
| registry.upstream.config.name_override | string | `nil` |  |
| registry.upstream.default_namespace | string | `"docker.io"` |  |
| replicas | int | `2` |  |
| resources.limits.cpu | int | `2` |  |
| resources.limits.memory | string | `"64Mi"` |  |
| resources.requests.cpu | string | `"100m"` |  |
| resources.requests.memory | string | `"16Mi"` |  |
| service.annotations | object | `{}` |  |
| service.clusterIP | string | `nil` |  |
| service.externalIPs | list | `[]` |  |
| service.loadBalancerIP | string | `""` |  |
| service.loadBalancerSourceRanges | list | `[]` |  |
| service.port | int | `80` |  |
| service.type | string | `"ClusterIP"` |  |

----------------------------------------------
Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0)
07070100000011000041ED00000000000000000000000266282B0100000000000000000000000000000000000000000000002700000000oci-registry-0.4.5/dist/helm/templates07070100000012000081A400000000000000000000000166282B0100000780000000000000000000000000000000000000003400000000oci-registry-0.4.5/dist/helm/templates/_helpers.tpl{{- define "oci-registry.name" -}}
	{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{- define "oci-registry.chart" -}}
	{{- .Chart.Name -}}
{{- end -}}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "oci-registry.fullname" -}}
	{{- if .Values.fullnameOverride -}}
		{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
	{{- else -}}
		{{- $name := default .Chart.Name .Values.nameOverride -}}
		{{- if (contains $name .Release.Name) -}}
			{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
		{{- else -}}
			{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
		{{- end -}}
	{{- end -}}
{{- end -}}

{{- define "oci-registry.image" -}}
	{{- with .Values.image -}}
		{{- printf "%s/%s:%s" .registry .name (.tag | toString) -}}
	{{- end -}}
{{- end -}}

{{- define "oci-registry.archiver_image" -}}
	{{- with .Values.archiver.image -}}
		{{- printf "%s/%s:%s" .registry .name (.tag | toString) -}}
	{{- end -}}
{{- end -}}

{{- define "oci-registry.labels" -}}
app.kubernetes.io/name: {{ template "oci-registry.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ template "oci-registry.chart" . }}
{{- if .Values.extraLabels -}}
{{- toYaml .Values.extraLabels -}}
{{- end -}}
{{- end -}}

{{- define "oci-registry.upstream_secret_name" -}}
	{{- default (printf "%s-%s" (include "oci-registry.fullname" .) "upstream") .Values.registry.upstream.config.name_override | quote }}
{{- end -}}

{{- define "oci-registry.s3_secret_name" -}}
	{{- default (printf "%s-%s" (include "oci-registry.fullname" .) "s3") .Values.registry.storage.s3.auth_secret.name_override | quote }}
{{- end -}}

07070100000013000081A400000000000000000000000166282B0100000F62000000000000000000000000000000000000003700000000oci-registry-0.4.5/dist/helm/templates/deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name:  {{ template "oci-registry.fullname" . }}
  labels:
    {{- include "oci-registry.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      {{- include "oci-registry.labels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "oci-registry.labels" . | nindent 8 }}
    spec:
      containers:
        - name: oci-registry
          image: "{{ .Values.image.registry }}/{{ .Values.image.name }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          {{- if eq .Values.registry.storage.mode "s3" }}
          args: ["s3"]
          env:
            - name: S3_HOST
              value: {{ .Values.registry.storage.s3.host | quote }}
            - name: S3_REGION
              value: {{ .Values.registry.storage.s3.region | quote }}
            - name: S3_BUCKET
              value: {{ .Values.registry.storage.s3.bucket | quote }}
            - name: S3_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: {{ template "oci-registry.s3_secret_name" . }}
                  key: access_key
            - name: S3_SECRET_KEY
              valueFrom:
                secretKeyRef:
                  name: {{ template "oci-registry.s3_secret_name" . }}
                  key: secret_key
          {{- else if eq .Values.registry.storage.mode "filesystem" }}
          args: ["filesystem"]
          env:
            - name: FILESYSTEM_ROOT
              value: /data
          {{- else }}
          {{- fail "registry.storage.mode must be either 's3' or 'filesystem'" -}}
          {{- end }}
            {{- if .Values.registry.check_cache_digest }}
            - name: CHECK_CACHE_DIGEST
              value: "true"
            {{- end }}
            - name: UPSTREAM_CONFIG_FILE
              value: /upstream.yaml
            - name: DEFAULT_UPSTREAM_NAMESPACE
              value: {{ .Values.registry.upstream.default_namespace | quote }}
            - name: RUST_LOG
              value: info,actix-web=debug
            {{- if .Values.resources.limits.cpu }}
            - name: TOKIO_WORKER_THREADS
              {{- if typeIs "string" .Values.resources.limits.cpu }}
              {{- $trimmed := trimSuffix "m" .Values.resources.limits.cpu }}
              {{- $value := ternary (float64 $trimmed | ceil | mul 1000) $trimmed (contains "." $trimmed) | int64 }}
              {{- if eq (mod $value 1000) 0 }}
              value: {{ div $value 1000 | quote }}
              {{- else }}
              value: {{ add1 (div $value 1000) | quote }}
              {{- end }}
              {{- else }}
              value: {{ .Values.resources.limits.cpu | quote }}
              {{- end }}
            {{- range .Values.extraEnv }}
            - name: {{ .name }}
              value: {{ .value | quote }}
            {{- end }}
            {{- end }}
          ports:
            - name: http
              containerPort: 80
          volumeMounts:
            - mountPath: /upstream.yaml
              name: upstream
              subPath: upstream.yaml
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 1
            periodSeconds: 2
            failureThreshold: 3
          livenessProbe:
            tcpSocket:
              port: http
            initialDelaySeconds: 1
            periodSeconds: 2
            failureThreshold: 5
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      volumes:
        - name: upstream
          secret:
            secretName: {{ template "oci-registry.upstream_secret_name" . }}
      nodeSelector:
        {{- toYaml .Values.nodeSelector | nindent 8 }}
      affinity:
        {{- toYaml .Values.affinity | nindent 8 }}
      tolerations:
        {{- toYaml .Values.tolerations | nindent 8 }}
07070100000014000081A400000000000000000000000166282B010000076E000000000000000000000000000000000000003400000000oci-registry-0.4.5/dist/helm/templates/ingress.yaml{{- if .Values.ingress.enabled -}} 
{{- $fullName := include "oci-registry.fullname" . -}}
{{- $servicePort := .Values.service.port -}} 
{{- $ingressPath := .Values.ingress.path -}} 
{{- $new := false }}
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" -}}
apiVersion: networking.k8s.io/v1
{{- $new = true }}
{{- else }}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
  name: {{ $fullName }}
  labels:
    {{- include "oci-registry.labels" . | nindent 4 }}
  annotations:
    {{- if and (not $new) .Values.ingress.class }}
    kubernetes.io/ingress.class: {{ .Values.ingress.class | quote }}
    {{- end }}
    {{- if .Values.ingress.annotations }}
    {{- toYaml .Values.ingress.annotations | nindent 4 }}
    {{- end }}
spec:
  {{- if and $new .Values.ingress.class }}
  ingressClassName: {{ .Values.ingress.class | quote }}
  {{- end }}
  {{- if or .Values.ingress.tls (hasKey .Values.ingress.annotations "kubernetes.io/tls-acme") }}
  tls:
    {{- if hasKey .Values.ingress.annotations "kubernetes.io/tls-acme" }}
    - hosts: {{ .Values.ingress.hosts | toYaml | nindent 8 }}
      secretName: {{ printf "%s-tls" .Release.Name }}
    {{- else }}
      {{- toYaml (list .Values.ingress.tls) | nindent 4 }}
    {{- end }}
  {{- end }}
  rules:
    {{- if $new }}
    {{- range .Values.ingress.hosts }}
    - host: {{ . }}
      http:
        paths:
          - path: {{ $ingressPath }}
            pathType: Prefix
            backend:
              service:
                name: {{ $fullName }}
                port:
                  number: {{ $servicePort }}
    {{- end }}
    {{- else }}
    {{- range .Values.ingress.hosts }}
    - host: {{ . }}
      http:
        paths:
          - path: {{ $ingressPath }}
            backend:
              serviceName: {{ $fullName }}
              servicePort: {{ $servicePort }}
    {{- end }}
    {{- end }}
{{- end }}

07070100000015000081A400000000000000000000000166282B01000002CA000000000000000000000000000000000000003600000000oci-registry-0.4.5/dist/helm/templates/secret-s3.yaml{{- if and (eq .Values.registry.storage.mode "s3") .Values.registry.storage.s3.auth_secret.deploy }}
apiVersion: v1
kind: Secret
metadata:
  name: {{ template "oci-registry.s3_secret_name" . }}
  labels:
    {{- include "oci-registry.labels" . | nindent 4 }}
type: Opaque
data:
  access_key: {{ required "If registry.storage.s3.auth_secret.deploy is set to true, registry.storage.s3.auth_secret.access_key is required" .Values.registry.storage.s3.auth_secret.access_key | b64enc | quote }}
  secret_key: {{ required "If registry.storage.s3.auth_secret.deploy is set to true, registry.storage.s3.auth_secret.secret_key is required" .Values.registry.storage.s3.auth_secret.secret_key | b64enc | quote }}
{{- end }}

07070100000016000081A400000000000000000000000166282B0100000148000000000000000000000000000000000000003C00000000oci-registry-0.4.5/dist/helm/templates/secret-upstream.yaml{{- if .Values.registry.upstream.config.deploy }}
apiVersion: v1
kind: Secret
metadata:
  name: {{ template "oci-registry.upstream_secret_name" . }}
  labels:
    {{- include "oci-registry.labels" . | nindent 4 }}
type: Opaque
data:
  upstream.yaml: {{ .Values.registry.upstream.config.contents | toYaml | b64enc }}
{{- end }}

07070100000017000081A400000000000000000000000166282B01000003BF000000000000000000000000000000000000003400000000oci-registry-0.4.5/dist/helm/templates/service.yamlapiVersion: v1
kind: Service
metadata:
  {{- if .Values.service.annotations }}
  annotations:
    {{- toYaml .Values.service.annotations | nindent 4 }}
  {{- end }}
  name: {{ template "oci-registry.fullname" . }}
  labels:
    {{- include "oci-registry.labels" . | nindent 4 }}
spec:
  clusterIP: {{ .Values.service.clusterIP | quote }}
  {{- if .Values.service.externalIPs }}
  externalIPs:
    {{- toYaml .Values.service.externalIPs | nindent 4 }}
  {{- end }}
  {{- if .Values.service.loadBalancerIP }}
  loadBalancerIP: {{ .Values.service.loadBalancerIP | quote }}
  {{- end }}
  {{- if .Values.service.loadBalancerSourceRanges }}
  loadBalancerSourceRanges:
    {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }}
  {{- end }}
  ports:
    - name: http
      protocol: TCP
      port: {{ .Values.service.port }}
      targetPort: http
  selector:
    {{- include "oci-registry.labels" . | nindent 4 }}
  type: {{ .Values.service.type }}

07070100000018000081A400000000000000000000000166282B0100000565000000000000000000000000000000000000002900000000oci-registry-0.4.5/dist/helm/values.yamlimage:
  registry: mcronce
  name: oci-registry
  tag: v0.4.5
  pullPolicy: IfNotPresent
  pullSecret:

replicas: 2

registry:
  check_cache_digest: false
  upstream:
    config:
      deploy: true
      name_override:
      contents: []
        #- namespace: docker.io
        #  host: registry-1.docker.io
        #  tls: true
        #  accept_invalid_certs: false
        #  user_agent: null
        #  username: null
        #  password: null
        #  manifest_invalidation_time: 14d
        #  blob_invalidation_time: 14d
        #- namespace: quay.io
        #  host: quay.io
        #- namespace: gcr.io
        #  host: gcr.io
    default_namespace: docker.io
  storage:
    mode: filesystem
    filesystem:
      path: /data
    s3:
      # Leave blank to use AWS
      host:
      auth_secret:
        name_override:
        deploy: true
        access_key:
        secret_key:
      region: us-east-1
      bucket: oci-registry

service:
  port: 80
  type: ClusterIP
  clusterIP:
  annotations: {}
  externalIPs: []
  loadBalancerIP: ""
  loadBalancerSourceRanges: []

extraLabels: {}
extraEnv: []

ingress:
  enabled: false
  class:
  annotations: {}
  path: /
  hosts:
    - oci-registry
  tls: []
  #  - secretName: oci-registry-tls
  #    hosts:
  #      oci-registry

resources:
  requests:
    cpu: 100m
    memory: 16Mi
  limits:
    cpu: 2
    memory: 128Mi

07070100000019000081A400000000000000000000000166282B010000020B000000000000000000000000000000000000002000000000oci-registry-0.4.5/example.yaml# Including all config items on docker.io for demo purposes
- namespace: docker.io
  host: registry-1.docker.io
  tls: true
  accept_invalid_certs: false
  user_agent: null
  username: null
  password: null
  manifest_invalidation_time: 14d
  blob_invalidation_time: 14d
# Including only required config for the rest
- namespace: quay.io
  host: quay.io
- namespace: gcr.io
  host: gcr.io
- namespace: ghcr.io
  host: ghcr.io
- namespace: k8s.gcr.io
  host: k8s.gcr.io
- namespace: registry.k8s.io
  host: registry.k8s.io

0707010000001A000041ED00000000000000000000000266282B0100000000000000000000000000000000000000000000001700000000oci-registry-0.4.5/src0707010000001B000041ED00000000000000000000000266282B0100000000000000000000000000000000000000000000001B00000000oci-registry-0.4.5/src/api0707010000001C000081A400000000000000000000000166282B010000304C000000000000000000000000000000000000001E00000000oci-registry-0.4.5/src/api.rsuse std::iter;

use actix_web::body::SizedStream;
use actix_web::http;
use actix_web::http::header::HeaderName;
use actix_web::rt;
use actix_web::web;
use actix_web::HttpResponse;
use compact_str::CompactString;
use dkregistry::v2::Client;
use futures::stream::StreamExt;
use futures::stream::TryStreamExt;
use once_cell::sync::Lazy;
use prometheus::register_int_counter_vec;
use prometheus::IntCounterVec;
use serde::Deserialize;
use tokio::sync::Mutex;
use tracing::error;
use tracing::warn;

use crate::image::ImageName;
use crate::image::ImageReference;
use crate::storage::Manifest;
use crate::storage::Repository;
use crate::upstream::Clients;

pub mod error;
use error::should_retry_without_namespace;
use error::Error;
pub mod stream;
use stream::DigestCheckedStream;

pub struct RequestConfig {
	repo: Repository,
	upstream: Mutex<Clients>,
	default_ns: CompactString,
	check_cache_digest: bool
}

impl RequestConfig {
	pub fn new(repo: Repository, upstream: Clients, default_ns: CompactString, check_cache_digest: bool) -> Self {
		Self { repo, upstream: Mutex::new(upstream), default_ns, check_cache_digest }
	}
}

async fn authenticate_with_upstream(upstream: &mut Client, scope: &str) -> Result<(), dkregistry::errors::Error> {
	upstream.authenticate(&[scope]).await?;
	Ok(())
}

pub async fn root(config: web::Data<RequestConfig>, qstr: web::Query<ManifestQueryString>) -> Result<&'static str, Error> {
	let mut upstream = { config.upstream.lock().await.get(qstr.ns.as_deref().unwrap_or_else(|| config.default_ns.as_ref()))?.client.clone() };
	upstream.authenticate(&[]).await?;
	Ok("")
}

#[derive(Debug, Deserialize)]
pub struct ManifestRequest {
	image: ImageName,
	reference: ImageReference
}

impl ManifestRequest {
	fn http_path(&self) -> String {
		format!("/{}/manifests/{}", self.image, self.reference)
	}

	fn storage_path(&self, ns: &str) -> String {
		match self.image.as_ref().split('/').next() {
			Some(part) if part == ns => format!("manifests/{}/{}", self.image, self.reference),
			_ => format!("manifests/{}/{}/{}", ns, self.image, self.reference)
		}
	}
}

#[derive(Debug, Deserialize)]
pub struct ManifestQueryString {
	ns: Option<CompactString>
}

fn manifest_response(manifest: Manifest) -> HttpResponse {
	let mut response = HttpResponse::Ok();
	response.insert_header((http::header::CONTENT_TYPE, manifest.media_type.to_string()));
	if let Some(digest) = manifest.digest {
		response.insert_header((HeaderName::from_static("docker-content-digest"), digest));
	}
	response.body(manifest.manifest)
}

pub async fn manifest(req: web::Path<ManifestRequest>, qstr: web::Query<ManifestQueryString>, config: web::Data<RequestConfig>) -> Result<HttpResponse, Error> {
	static HIT_COUNTER: Lazy<IntCounterVec> = Lazy::new(|| register_int_counter_vec!("manifest_cache_hits", "Number of manifests read from cache", &["namespace"]).unwrap());
	static MISS_COUNTER: Lazy<IntCounterVec> = Lazy::new(|| register_int_counter_vec!("manifest_cache_misses", "Number of manifest requests that went to upstream", &["namespace"]).unwrap());

	let (namespace, image) = split_image(qstr.ns.as_deref(), req.image.as_ref(), config.default_ns.as_ref());

	let max_age = config.upstream.lock().await.get(namespace)?.manifest_invalidation_time;
	let storage_path = req.storage_path(namespace);
	match config.repo.read(&storage_path, max_age).await {
		Ok(stream) => {
			let body = stream.into_inner().try_collect::<web::BytesMut>().await?;
			let manifest = serde_json::from_slice(body.as_ref())?;
			HIT_COUNTER.with_label_values(&[namespace]).inc();
			return Ok(manifest_response(manifest));
		},
		Err(error) => warn!(path = req.http_path(), storage_path, %error, "Manifest not found in repository; pulling from upstream")
	}

	MISS_COUNTER.with_label_values(&[namespace]).inc();
	let manifest = {
		let mut upstream = config.upstream.lock().await.get(namespace)?.clone();
		authenticate_with_upstream(&mut upstream.client, &format!("repository:{}:pull", image)).await?;
		let reference = req.reference.to_str();
		let (manifest, media_type, digest) = match upstream.client.get_raw_manifest_and_metadata(image, reference.as_ref(), Some(namespace)).await {
			Ok(v) => v,
			Err(e) if should_retry_without_namespace(&e) => upstream.client.get_raw_manifest_and_metadata(image, reference.as_ref(), None).await?,
			Err(e) => return Err(e.into())
		};
		Manifest::new(manifest, media_type, digest)
	};

	let body = serde_json::to_vec(&manifest).unwrap();
	let len = body.len().try_into().unwrap_or(i64::MAX);
	if let Err(error) = config
		.repo
		.write(&storage_path, futures::stream::iter(iter::once(Result::<_, std::io::Error>::Ok(body.into()))), len)
		.await
	{
		error!(%error, "Failed to write manifest to storage");
	}

	Ok(manifest_response(manifest))
}

#[derive(Debug, Deserialize)]
pub struct BlobRequest {
	image: ImageName,
	digest: String
}

impl BlobRequest {
	fn http_path(&self) -> String {
		format!("/{}/blobs/{}", self.image, self.digest)
	}

	fn storage_path(&self) -> String {
		let (method, hash) = self.digest.split_once(':').unwrap_or(("_", &self.digest));
		let hash_prefix = hash.get(..2).unwrap_or("_");
		let rest_of_hash = hash.get(2..).unwrap_or(hash);
		format!("blobs/{method}/{hash_prefix}/{rest_of_hash}")
	}
}

pub async fn blob(req: web::Path<BlobRequest>, qstr: web::Query<ManifestQueryString>, config: web::Data<RequestConfig>) -> Result<HttpResponse, Error> {
	static HIT_COUNTER: Lazy<IntCounterVec> = Lazy::new(|| register_int_counter_vec!("blob_cache_hits", "Number of blobs read from cache", &["namespace"]).unwrap());
	static MISS_COUNTER: Lazy<IntCounterVec> = Lazy::new(|| register_int_counter_vec!("blob_cache_misses", "Number of blob requests that went to upstream", &["namespace"]).unwrap());

	let Some(wanted_digest_hex) = req.digest.strip_prefix("sha256:") else {
		return Err(Error::InvalidDigest);
	};
	let wanted_digest = {
		let mut buf = [0u8; 256 / 8];
		if (hex::decode_to_slice(wanted_digest_hex, &mut buf[..]).is_err()) {
			return Err(Error::InvalidDigest);
		}
		buf
	};

	let (namespace, image) = split_image(qstr.ns.as_deref(), req.image.as_ref(), config.default_ns.as_ref());

	let storage_path = req.storage_path();
	let max_age = config.upstream.lock().await.get(namespace)?.blob_invalidation_time;
	match config.repo.read(storage_path.as_ref(), max_age).await {
		Ok(stream) => match config.check_cache_digest {
			true => {
				let hash = stream::hash(stream.into_inner()).await?;
				if (hash == wanted_digest) {
					HIT_COUNTER.with_label_values(&[namespace]).inc();
					let stream = config.repo.read(storage_path.as_ref(), max_age).await?;
					return Ok(HttpResponse::Ok().body(SizedStream::new(stream.length(), stream.into_inner())));
				}
				error!(storage_path, "Digest mismatch");
				config.repo.delete(storage_path.as_ref()).await?;
			},
			false => {
				HIT_COUNTER.with_label_values(&[namespace]).inc();
				let stream = config.repo.read(storage_path.as_ref(), max_age).await?;
				return Ok(HttpResponse::Ok().body(SizedStream::new(stream.length(), stream.into_inner())));
			}
		},
		Err(error) => warn!(path = storage_path, %error, "Blob not found in repository; pulling from upstream")
	};

	MISS_COUNTER.with_label_values(&[namespace]).inc();
	let response = {
		let mut upstream = config.upstream.lock().await.get(namespace)?.clone();
		authenticate_with_upstream(&mut upstream.client, &format!("repository:{}:pull", image)).await?;
		match upstream.client.get_blob_response(image, req.digest.as_ref(), Some(namespace)).await {
			Ok(v) => v,
			Err(e) if should_retry_without_namespace(&e) => upstream.client.get_blob_response(image, req.digest.as_ref(), None).await?,
			Err(e) => return Err(e.into())
		}
	};

	let len = response.size().ok_or(Error::MissingContentLength)?;
	let (tx, rx) = async_broadcast::broadcast(16);
	{
		let mut stream = DigestCheckedStream::<_, crate::storage::Error, _>::new(response.stream().err_into::<crate::storage::Error>(), wanted_digest);
		rt::spawn(async move {
			while let Some(chunk) = stream.next().await {
				let chunk = match chunk {
					Ok(v) => Ok(v),
					Err(error) => {
						error!(%error, "Error reading from upstream");
						Err(error)
					}
				};
				let is_err = chunk.is_err();
				if (tx.broadcast(chunk).await.is_err()) {
					error!(path = req.http_path(), "Readers for proxied blob request all closed");
					return;
				} else if is_err {
					return;
				}
			}
		});
	}

	{
		let rx2 = rx.clone();
		let config = config.clone();
		rt::spawn(async move {
			if let Err(error) = config.repo.write(storage_path.as_ref(), rx2, len.try_into().unwrap_or(i64::MAX)).await {
				error!(%error, "Failed to write blob to storage");
				if let Err(error) = config.repo.delete(storage_path.as_ref()).await {
					error!(%error, "Failed to delete failed blob from storage");
				}
			}
		});
	}

	Ok(HttpResponse::Ok().body(SizedStream::new(len, rx.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)))))
}

#[inline]
pub fn split_image<'a>(ns: Option<&'a str>, image: &'a str, default_ns: &'a str) -> (&'a str, &'a str) {
	match ns {
		Some(v) => (v, image),
		None => match image.split_once('/') {
			Some((ns, image)) if image.contains('/') => (ns, image),
			Some(_) | None => (default_ns, image)
		}
	}
}

pub async fn delete_manifest(req: web::Path<ManifestRequest>, qstr: web::Query<ManifestQueryString>, config: web::Data<RequestConfig>) -> Result<&'static str, Error> {
	let (namespace, _) = split_image(qstr.ns.as_deref(), req.image.as_ref(), config.default_ns.as_ref());
	let storage_path = req.storage_path(namespace);
	config.repo.delete(storage_path.as_ref()).await?;
	Ok("")
}

pub async fn delete_blob(req: web::Path<BlobRequest>, config: web::Data<RequestConfig>) -> Result<&'static str, Error> {
	let storage_path = req.storage_path();
	config.repo.delete(storage_path.as_ref()).await?;
	Ok("")
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn split_image_with_ns() {
		let (ns, image) = split_image(Some("docker.io"), "envoyproxy/envoy", "");
		assert_eq!(ns, "docker.io");
		assert_eq!(image, "envoyproxy/envoy");

		let (ns, image) = split_image(Some("docker.io"), "library/busybox", "");
		assert_eq!(ns, "docker.io");
		assert_eq!(image, "library/busybox");

		let (ns, image) = split_image(Some("docker.io"), "grafana/mimirtool", "");
		assert_eq!(ns, "docker.io");
		assert_eq!(image, "grafana/mimirtool");

		let (ns, image) = split_image(Some("gcr.io"), "distroless/static", "");
		assert_eq!(ns, "gcr.io");
		assert_eq!(image, "distroless/static");

		let (ns, image) = split_image(Some("gcr.io"), "flame-public/buildbuddy-app-onprem", "");
		assert_eq!(ns, "gcr.io");
		assert_eq!(image, "flame-public/buildbuddy-app-onprem");

		let (ns, image) = split_image(Some("ghcr.io"), "buildbarn/bb-runner-installer", "");
		assert_eq!(ns, "ghcr.io");
		assert_eq!(image, "buildbarn/bb-runner-installer");
	}

	#[test]
	fn split_image_without_ns() {
		let (ns, image) = split_image(None, "docker.io/envoyproxy/envoy", "");
		assert_eq!(ns, "docker.io");
		assert_eq!(image, "envoyproxy/envoy");

		let (ns, image) = split_image(None, "docker.io/library/busybox", "");
		assert_eq!(ns, "docker.io");
		assert_eq!(image, "library/busybox");

		let (ns, image) = split_image(None, "docker.io/grafana/mimirtool", "");
		assert_eq!(ns, "docker.io");
		assert_eq!(image, "grafana/mimirtool");

		let (ns, image) = split_image(None, "gcr.io/distroless/static", "");
		assert_eq!(ns, "gcr.io");
		assert_eq!(image, "distroless/static");

		let (ns, image) = split_image(None, "gcr.io/flame-public/buildbuddy-app-onprem", "");
		assert_eq!(ns, "gcr.io");
		assert_eq!(image, "flame-public/buildbuddy-app-onprem");

		let (ns, image) = split_image(None, "ghcr.io/buildbarn/bb-runner-installer", "");
		assert_eq!(ns, "ghcr.io");
		assert_eq!(image, "buildbarn/bb-runner-installer");
	}

	#[test]
	fn split_image_without_ns_fallback() {
		let (ns, image) = split_image(None, "envoyproxy/envoy", "docker.io");
		assert_eq!(ns, "docker.io");
		assert_eq!(image, "envoyproxy/envoy");

		let (ns, image) = split_image(None, "library/busybox", "docker.io");
		assert_eq!(ns, "docker.io");
		assert_eq!(image, "library/busybox");

		let (ns, image) = split_image(None, "grafana/mimirtool", "docker.io");
		assert_eq!(ns, "docker.io");
		assert_eq!(image, "grafana/mimirtool");
	}
}
0707010000001D000081A400000000000000000000000166282B01000009FC000000000000000000000000000000000000002400000000oci-registry-0.4.5/src/api/error.rsuse actix_web::body::BoxBody;
use actix_web::http::StatusCode;
use actix_web::HttpResponse;
use actix_web::HttpResponseBuilder;
use dkregistry::errors::Error as Upstream;
use rusoto_core::request::BufferedHttpResponse;
use rusoto_core::RusotoError;
use rusoto_s3::GetObjectError;
use tracing::error;

use crate::api::stream::DigestMismatchError;
use crate::storage::Error as Storage;

#[derive(Debug, thiserror::Error)]
pub enum Error {
	#[error("Error with storage subsystem: {0}")]
	Storage(#[from] Storage),
	#[error("Error with upstream registry: {0}")]
	Upstream(#[from] Upstream),
	#[error("Not found")]
	InvalidDigest,
	#[error("Missing Content-Length header from upstream")]
	MissingContentLength,
	#[error("I/O error: {0}")]
	Io(#[from] std::io::Error),
	#[error("JSON error: {0}")]
	Json(#[from] serde_json::Error),
	#[error("{0}")]
	DataCorrupt(#[from] DigestMismatchError)
}

impl actix_web::ResponseError for Error {
	fn status_code(&self) -> StatusCode {
		match self {
			Self::Storage(e) => match e {
				Storage::Io(e) if e.kind() == std::io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
				Storage::RusotoGet(e) if matches!(e.as_ref(), &RusotoError::Service(GetObjectError::NoSuchKey(_))) => StatusCode::NOT_FOUND,
				Storage::RusotoDelete(e) if matches!(e.as_ref(), &RusotoError::Unknown(BufferedHttpResponse { status: StatusCode::NOT_FOUND, .. })) => StatusCode::NOT_FOUND,
				_ => StatusCode::INTERNAL_SERVER_ERROR
			},
			Self::Upstream(e) => match e {
				Upstream::UnexpectedHttpStatus(StatusCode::NOT_FOUND) => StatusCode::NOT_FOUND,
				Upstream::UnexpectedHttpStatus(_) => StatusCode::INTERNAL_SERVER_ERROR,
				Upstream::Client { status: StatusCode::NOT_FOUND } => StatusCode::NOT_FOUND,
				Upstream::Client { .. } => StatusCode::INTERNAL_SERVER_ERROR,
				_ => StatusCode::INTERNAL_SERVER_ERROR
			},
			Self::InvalidDigest => StatusCode::NOT_FOUND,
			Self::MissingContentLength => StatusCode::INTERNAL_SERVER_ERROR,
			Self::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
			Self::Json(_) => StatusCode::INTERNAL_SERVER_ERROR,
			Self::DataCorrupt(_) => StatusCode::INTERNAL_SERVER_ERROR
		}
	}

	fn error_response(&self) -> HttpResponse<BoxBody> {
		let status_code = self.status_code();
		error!("{}: {}", status_code.as_u16(), self);
		HttpResponseBuilder::new(status_code).body(self.to_string())
	}
}

pub fn should_retry_without_namespace(err: &Upstream) -> bool {
	matches!(err, dkregistry::errors::Error::Reqwest(_) | dkregistry::errors::Error::UnexpectedHttpStatus(_) | dkregistry::errors::Error::Client { .. })
}
0707010000001E000081A400000000000000000000000166282B0100000C55000000000000000000000000000000000000002500000000oci-registry-0.4.5/src/api/stream.rsuse core::fmt;
use core::marker::PhantomData;
use core::pin::Pin;

use actix_web::web::Bytes;
use futures::stream::Stream;
use futures::stream::StreamExt;
use futures::task::Context;
use futures::task::Poll;
use pin_project::pin_project;
use sha2::Digest;
use sha2::Sha256;

#[pin_project]
pub struct DigestCheckedStream<S, E, IE>
where
	S: Stream<Item = Result<Bytes, IE>> + Unpin,
	E: std::error::Error + From<IE> + From<DigestMismatchError> + 'static
{
	#[pin]
	inner: S,
	wanted_digest: [u8; 32],
	hasher: Option<Sha256>,
	_e: PhantomData<E>
}

impl<S, E, IE> Stream for DigestCheckedStream<S, E, IE>
where
	S: Stream<Item = Result<Bytes, IE>> + Unpin,
	E: std::error::Error + From<IE> + From<DigestMismatchError> + 'static
{
	type Item = Result<Bytes, E>;

	fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
		let Poll::Ready(chunk) = Pin::new(&mut self.inner).poll_next(ctx) else {
			return Poll::Pending;
		};
		let Some(chunk) = chunk else {
			// When the stream has ended, we have one last step - finalize the hash.
			//   * If self.hasher is None, we've already done that; either the caller has made a
			//     mistake or we've previously returned a DigestMismatchError.
			//   * If the hash matches self.wanted_digest, return None to signal the end of the
			//     stream.
			//   * If the hash does not match self.wanted_digest, return a DigestMismatchError.
			return match std::mem::take(&mut self.hasher) {
				None => Poll::Ready(None),
				Some(hasher) => {
					let result: [u8; 32] = hasher.finalize().into();
					match (result == self.wanted_digest) {
						true => Poll::Ready(None),
						false => {
							let error = DigestMismatchError { expected: self.wanted_digest, actual: result };
							Poll::Ready(Some(Err(error.into())))
						}
					}
				}
			};
		};
		let Ok(chunk) = chunk else {
			return Poll::Ready(Some(chunk.map_err(Into::into)));
		};
		if let Some(h) = self.hasher.as_mut() {
			h.update(&chunk);
		}
		Poll::Ready(Some(Ok(chunk)))
	}
}

impl<S, E, IE> DigestCheckedStream<S, E, IE>
where
	S: Stream<Item = Result<Bytes, IE>> + Unpin,
	E: std::error::Error + From<IE> + From<DigestMismatchError> + 'static
{
	pub fn new(inner: S, wanted_digest: [u8; 32]) -> Self {
		Self {
			inner,
			wanted_digest,
			hasher: Some(Sha256::new()),
			_e: PhantomData
		}
	}
}

#[derive(Debug, Clone)]
pub struct DigestMismatchError {
	expected: [u8; 32],
	actual: [u8; 32]
}

impl fmt::Display for DigestMismatchError {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		f.write_str("Digest '")?;
		for byte in self.actual.iter() {
			write!(f, "{:x}", byte)?;
		}
		f.write_str("' did not match expected '")?;
		for byte in self.expected.iter() {
			write!(f, "{:x}", byte)?;
		}
		f.write_str("'")
	}
}

impl std::error::Error for DigestMismatchError {}

pub async fn hash<S, E>(mut stream: S) -> Result<[u8; 32], E>
where
	S: Stream<Item = Result<Bytes, E>> + Unpin,
	E: std::error::Error + 'static
{
	let mut hasher = Sha256::new();
	while let Some(chunk) = stream.next().await {
		let chunk = chunk?;
		hasher.update(&chunk);
	}
	Ok(hasher.finalize().into())
}
0707010000001F000041ED00000000000000000000000266282B0100000000000000000000000000000000000000000000001D00000000oci-registry-0.4.5/src/image07070100000020000081A400000000000000000000000166282B010000084C000000000000000000000000000000000000002000000000oci-registry-0.4.5/src/image.rsuse std::borrow::Cow;
use std::fmt;
use std::str::FromStr;

use compact_str::CompactString;
use lazy_regex::lazy_regex;
use lazy_regex::Lazy;
use lazy_regex::Regex;
use serde_with::DeserializeFromStr;

mod error;

static RE_IMAGE: Lazy<Regex> = lazy_regex!("^[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*$");
static RE_TAG: Lazy<Regex> = lazy_regex!("^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$");

fn is_valid_sha256(s: &str) -> bool {
	s.len() == 64 && s.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit()) && hex::decode(s).is_ok()
}

#[derive(Debug, DeserializeFromStr)]
pub struct ImageName(CompactString);
impl FromStr for ImageName {
	type Err = error::InvalidImageName;

	fn from_str(input: &str) -> Result<Self, Self::Err> {
		match RE_IMAGE.is_match(input) {
			false => Err(error::InvalidImageName(input.to_string())),
			true => Ok(ImageName(input.into()))
		}
	}
}

impl AsRef<str> for ImageName {
	#[inline]
	fn as_ref(&self) -> &str {
		self.0.as_ref()
	}
}

impl fmt::Display for ImageName {
	#[inline]
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		self.0.fmt(f)
	}
}

#[derive(Debug, DeserializeFromStr)]
pub enum ImageReference {
	Tag(CompactString),
	Sha256(String)
}

impl FromStr for ImageReference {
	type Err = error::InvalidImageReference;

	fn from_str(input: &str) -> Result<Self, Self::Err> {
		match input.strip_prefix("sha256:") {
			None => match RE_TAG.is_match(input) {
				false => Err(error::InvalidImageReference(input.to_string())),
				true => Ok(ImageReference::Tag(input.into()))
			},
			Some(s) => match is_valid_sha256(s) {
				false => Err(error::InvalidImageReference(input.to_string())),
				true => Ok(ImageReference::Sha256(s.into()))
			}
		}
	}
}

impl fmt::Display for ImageReference {
	#[inline]
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			Self::Tag(s) => s.fmt(f),
			Self::Sha256(s) => write!(f, "sha256:{s}")
		}
	}
}

impl ImageReference {
	pub fn to_str(&self) -> Cow<'_, str> {
		match self {
			Self::Tag(s) => Cow::Borrowed(s.as_ref()),
			Self::Sha256(s) => Cow::Owned(format!("sha256:{s}"))
		}
	}
}
07070100000021000081A400000000000000000000000166282B01000000ED000000000000000000000000000000000000002600000000oci-registry-0.4.5/src/image/error.rs#[derive(Debug, thiserror::Error)]
#[error("Invalid image name '{0}'")]
pub struct InvalidImageName(pub String);

#[derive(Debug, thiserror::Error)]
#[error("Invalid image reference '{0}'")]
pub struct InvalidImageReference(pub String);
07070100000022000081A400000000000000000000000166282B010000006C000000000000000000000000000000000000001E00000000oci-registry-0.4.5/src/lib.rs#![allow(dead_code)]
#![allow(unused_parens)]

pub mod api;
mod image;
mod storage;
mod upstream;
mod util;
07070100000023000081A400000000000000000000000166282B01000014C9000000000000000000000000000000000000001F00000000oci-registry-0.4.5/src/main.rs#![allow(unused_parens)]
use core::future;
use core::time::Duration;
use std::time::SystemTime;

use actix_web::dev::Service;
use actix_web::http::header::HeaderName;
use actix_web::http::header::HeaderValue;
use actix_web::web;
use actix_web::HttpResponse;
use actix_web_prometheus::PrometheusMetricsBuilder;
use clap::Parser;
use compact_str::CompactString;
use futures::future::FutureExt;
use tokio::sync::oneshot;
use tracing::error;
use tracing::info;
use tracing::warn;

mod api;
mod image;
mod storage;
mod upstream;
mod util;

use storage::StorageConfig;
use upstream::InvalidationConfig;
use upstream::UpstreamConfig;

#[derive(Debug, Parser)]
struct Config {
	/// An IP address and port combination to listen on a network socket, or a path prefixed with
	/// "unix:" to listen on a Unix domain socket
	#[clap(env, long, default_value = "0.0.0.0:80")]
	listen: socket_address::Address,
	#[clap(env, long, default_value = "docker.io")]
	default_namespace: CompactString,
	/// If enabled, will validate a blob's SHA256 digest when reading it from cache storage; if the
	/// digest doesn't match what was expected based on the request URL, it will be deleted from
	/// storage and re-retrieved from upstream.  This has an impact on performance, as the entire
	/// blob needs to be read from storage twice instead of just once.
	#[clap(env, long, default_value_t = false)]
	check_cache_digest: bool,
	#[clap(flatten)]
	upstream: UpstreamConfig,
	#[clap(subcommand)]
	storage: StorageConfig
}

#[inline]
fn liveness() -> future::Ready<HttpResponse> {
	future::ready(HttpResponse::Ok().body(""))
}

#[allow(dead_code)] // TODO:  Implement
#[inline]
async fn readiness() -> Result<&'static str, api::error::Error> {
	// TODO:  Check upstream and storage
	Ok("")
}

async fn cleanup(upstream: &InvalidationConfig, repo: &storage::Repository) {
	let now = SystemTime::now();
	let mut count = match repo.delete_old_blobs(now - upstream.blob).await {
		Ok(v) => v,
		Err(error) => {
			error!(%error, "Error cleaning up blobs");
			0
		}
	};
	for (ns, age) in upstream.manifests.iter() {
		let ns: &str = ns.as_ref();
		match repo.delete_old_manifests(ns, now - *age).await {
			Ok(v) => count += v,
			Err(error) => error!(%error, namespace = ns, "Error cleaning up manifests")
		};
	}

	if (count > 0) {
		warn!(count, "Aged out objects");
	} else {
		info!(count, "Aged out objects");
	}
}

#[actix_web::main]
async fn main() {
	let config = Config::parse();

	tracing_subscriber::fmt().with_env_filter(tracing_subscriber::EnvFilter::from_default_env()).compact().init();

	let repo = config.storage.repository();
	let upstream = config.upstream.clients().await.unwrap();
	let (shutdown_tx, mut shutdown_rx) = oneshot::channel();
	let background = {
		let repo = repo.clone();
		let upstream = upstream.invalidation_config();
		tokio::task::spawn(async move {
			let mut interval = tokio::time::interval(Duration::from_secs(300));
			loop {
				tokio::select! {
					_ = interval.tick() => (),
					_ = &mut shutdown_rx => break
				};
				cleanup(&upstream, &repo).await;
			}
		})
	};

	let prometheus = PrometheusMetricsBuilder::new("http").endpoint("/metrics").build().unwrap();
	let per_request_config = web::Data::new(api::RequestConfig::new(repo, upstream, config.default_namespace, config.check_cache_digest));

	let server = actix_web::HttpServer::new(move || {
		actix_web::App::new()
			.app_data(per_request_config.clone())
			.wrap(prometheus.clone())
			.service(
				web::scope("/v2")
					.wrap(actix_web::middleware::Logger::default())
					.route("/", web::get().to(api::root))
					// /v2/library/telegraf/manifests/1.24-alpine
					// /v2/library/redis/manifests/sha256:226cbafc637cd58cf008bf87ec9d1548ad1b672ef4279433495bdff100cdb883
					// /v2/docker.io/library/telegraf/manifests/1.24-alpine
					// /v2/docker.io/library/redis/manifests/sha256:226cbafc637cd58cf008bf87ec9d1548ad1b672ef4279433495bdff100cdb883
					.route("/{image:[^{}]+}/manifests/{reference}", web::head().to(api::manifest))
					.route("/{image:[^{}]+}/manifests/{reference}", web::get().to(api::manifest))
					// /v2/grafana/grafana/blobs/sha256:6864e61916f58174557076c34e7122753331cf28077edb0f23e1fb5419dd6acd
					// /v2/docker.io/grafana/grafana/blobs/sha256:6864e61916f58174557076c34e7122753331cf28077edb0f23e1fb5419dd6acd
					.route("/{image:[^{}]+}/blobs/{digest}", web::get().to(api::blob))
					.wrap_fn(|req, srv| {
						srv.call(req).map(|response| {
							response.map(|mut ok| {
								ok.headers_mut()
									.insert(HeaderName::from_static("docker-distribution-api-version"), HeaderValue::from_static("registry/2.0"));
								ok
							})
						})
					})
			)
			.service(
				web::scope("/_admin")
					.wrap(actix_web::middleware::Logger::default())
					.route("/{image:[^{}]+}/manifests/{reference}", web::delete().to(api::delete_manifest))
					.route("/{image:[^{}]+}/blobs/{digest}", web::delete().to(api::delete_blob))
			)
			.route("/", web::get().to(liveness))
	});
	match config.listen {
		socket_address::Address::Network(addr) => server.shutdown_timeout(10).bind(&addr).unwrap().run().await.unwrap(),
		socket_address::Address::UnixSocket(path) => server.shutdown_timeout(10).bind_uds(&path).unwrap().run().await.unwrap()
	};
	shutdown_tx.send(()).unwrap();
	background.await.unwrap();
}
07070100000024000041ED00000000000000000000000266282B0100000000000000000000000000000000000000000000001F00000000oci-registry-0.4.5/src/storage07070100000025000081A400000000000000000000000166282B0100000D7A000000000000000000000000000000000000002200000000oci-registry-0.4.5/src/storage.rsuse core::time::Duration;
use std::time::SystemTime;

use actix_web::body::SizedStream;
use bytes::Bytes;
use clap::Subcommand;
use compact_str::format_compact;
use dkregistry::mediatypes::MediaTypes;
use futures::stream::BoxStream;
use futures::stream::TryStream;
use futures::stream::TryStreamExt;
use serde::Deserialize;
use serde::Serialize;

mod error;
pub mod filesystem;
pub mod s3;

pub use error::Error;

#[derive(Clone, Debug, Subcommand)]
pub enum StorageConfig {
	S3(s3::Config),
	Filesystem(filesystem::Config)
}

impl StorageConfig {
	pub fn repository(&self) -> Repository {
		match self {
			Self::S3(config) => Repository::S3(config.repository()),
			Self::Filesystem(config) => Repository::Filesystem(config.repository())
		}
	}
}

#[derive(Clone)]
pub enum Repository {
	S3(s3::Repository),
	Filesystem(filesystem::Repository)
}

pub struct ReadStream {
	length: u64,
	inner: BoxStream<'static, Result<Bytes, std::io::Error>>
}

impl ReadStream {
	pub fn new(length: u64, inner: BoxStream<'static, Result<Bytes, std::io::Error>>) -> Self {
		Self { length, inner }
	}

	pub fn length(&self) -> u64 {
		self.length
	}

	pub fn into_inner(self) -> BoxStream<'static, Result<Bytes, std::io::Error>> {
		self.inner
	}
}

impl From<ReadStream> for SizedStream<BoxStream<'static, Result<Bytes, Box<dyn std::error::Error + 'static>>>> {
	fn from(stream: ReadStream) -> Self {
		SizedStream::new(stream.length, Box::pin(stream.inner.err_into()))
	}
}

impl Repository {
	pub async fn read(&self, object: &str, invalidation: Duration) -> Result<ReadStream, Error> {
		let result = match self {
			Self::S3(r) => r.read(object, invalidation).await?,
			Self::Filesystem(r) => r.read(object.into(), invalidation).await?
		};
		Ok(result)
	}

	pub async fn write<S, E>(&self, object: &str, reader: S, length: i64) -> Result<(), Error>
	where
		S: TryStream<Ok = Bytes, Error = E> + Unpin + Send + 'static,
		E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
		Error: From<E>
	{
		#[allow(clippy::let_unit_value)] // Because it's likely that we will change the return type eventually, it'll require fewer changes, and it's harmless as-is.
		let result = match self {
			Self::S3(r) => r.write(object, reader, length).await?,
			Self::Filesystem(r) => r.write(object.into(), reader).await?
		};
		Ok(result)
	}

	pub async fn delete(&self, object: &str) -> Result<(), Error> {
		match self {
			Self::S3(r) => r.delete(object).await?,
			Self::Filesystem(r) => r.delete(object.as_ref()).await?
		};
		Ok(())
	}

	pub async fn delete_old_blobs(&self, older_than: SystemTime) -> Result<usize, Error> {
		match self {
			Self::S3(r) => r.delete_old_objects(older_than, "blobs/").await,
			Self::Filesystem(r) => r.delete_old_files(older_than, "blobs".as_ref()).await
		}
	}

	pub async fn delete_old_manifests(&self, ns: &str, older_than: SystemTime) -> Result<usize, Error> {
		let prefix = format_compact!("manifests/{ns}");
		let prefix: &str = prefix.as_ref();
		match self {
			Self::S3(r) => r.delete_old_objects(older_than, prefix).await,
			Self::Filesystem(r) => r.delete_old_files(older_than, prefix.as_ref()).await
		}
	}
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Manifest {
	pub manifest: Bytes,
	pub media_type: MediaTypes,
	pub digest: Option<String>
}

impl Manifest {
	pub fn new(manifest: Bytes, media_type: MediaTypes, digest: Option<String>) -> Self {
		Self { manifest, media_type, digest }
	}
}
07070100000026000081A400000000000000000000000166282B01000007F4000000000000000000000000000000000000002800000000oci-registry-0.4.5/src/storage/error.rsuse arcerror::ArcError;
use rusoto_core::RusotoError;

use crate::api::stream::DigestMismatchError;

#[derive(Debug, Clone, thiserror::Error)]
pub enum Error {
	#[error("I/O error: {0}")]
	Io(ArcError<std::io::Error>),
	#[error("Failed to list objects in S3: {0:?}")]
	RusotoList(ArcError<RusotoError<rusoto_s3::ListObjectsV2Error>>),
	#[error("Failed to get object from S3: {0:?}")]
	RusotoGet(ArcError<RusotoError<rusoto_s3::GetObjectError>>),
	#[error("Failed to put object into S3: {0:?}")]
	RusotoPut(ArcError<RusotoError<rusoto_s3::PutObjectError>>),
	#[error("Failed to delete object from S3: {0:?}")]
	RusotoDelete(ArcError<RusotoError<rusoto_s3::DeleteObjectError>>),
	#[error("Failed to parse datetime: {0}")]
	ParseTime(#[from] time::error::Parse),
	#[error("Object too old: {0}")]
	ObjectTooOld(humantime::Duration),
	#[error("Error reading from upstream: {0}")]
	Upstream(ArcError<dkregistry::errors::Error>),
	#[error("{0}")]
	DataCorrupt(#[from] DigestMismatchError)
}

impl From<std::io::Error> for Error {
	#[inline]
	fn from(inner: std::io::Error) -> Self {
		Self::Io(ArcError::from(inner))
	}
}

impl From<RusotoError<rusoto_s3::ListObjectsV2Error>> for Error {
	#[inline]
	fn from(inner: RusotoError<rusoto_s3::ListObjectsV2Error>) -> Self {
		Self::RusotoList(ArcError::from(inner))
	}
}

impl From<RusotoError<rusoto_s3::GetObjectError>> for Error {
	#[inline]
	fn from(inner: RusotoError<rusoto_s3::GetObjectError>) -> Self {
		Self::RusotoGet(ArcError::from(inner))
	}
}

impl From<RusotoError<rusoto_s3::PutObjectError>> for Error {
	#[inline]
	fn from(inner: RusotoError<rusoto_s3::PutObjectError>) -> Self {
		Self::RusotoPut(ArcError::from(inner))
	}
}

impl From<RusotoError<rusoto_s3::DeleteObjectError>> for Error {
	#[inline]
	fn from(inner: RusotoError<rusoto_s3::DeleteObjectError>) -> Self {
		Self::RusotoDelete(ArcError::from(inner))
	}
}

impl From<dkregistry::errors::Error> for Error {
	#[inline]
	fn from(inner: dkregistry::errors::Error) -> Self {
		Self::Upstream(ArcError::from(inner))
	}
}
07070100000027000081A400000000000000000000000166282B0100001205000000000000000000000000000000000000002D00000000oci-registry-0.4.5/src/storage/filesystem.rsuse core::time::Duration;
use std::path::Path;
use std::time::SystemTime;

use actix_web::web::Bytes;
use async_stream::try_stream;
use async_walkdir::WalkDir;
use camino::Utf8Component;
use camino::Utf8Path;
use camino::Utf8PathBuf;
use clap::Parser;
use futures::stream::StreamExt;
use futures::stream::TryStream;
use futures::stream::TryStreamExt;
use tokio::fs::create_dir_all;
use tokio::fs::remove_file;
use tokio::fs::symlink_metadata;
use tokio::fs::File;
use tokio::fs::OpenOptions;
use tokio::io::AsyncBufReadExt;
use tokio::io::AsyncWriteExt;
use tokio::io::BufReader;
use tokio::io::BufWriter;
use tracing::error;
use tracing::info;

use super::ReadStream;

#[derive(Clone, Debug, Parser)]
pub struct Config {
	#[clap(env = "FILESYSTEM_ROOT", long)]
	root: Utf8PathBuf
}

impl Config {
	pub fn repository(&self) -> Repository {
		Repository { root: self.root.clone() }
	}
}

#[derive(Debug, Clone)]
pub struct Repository {
	root: Utf8PathBuf
}

impl Repository {
	fn full_path(&self, path: &Utf8Path) -> Utf8PathBuf {
		let path = path.components().filter(|c| matches!(c, Utf8Component::ParentDir | Utf8Component::Normal(_))).collect::<Utf8PathBuf>();
		self.root.join(path)
	}

	pub async fn read(&self, object: &Utf8Path, invalidation: Duration) -> Result<ReadStream, super::Error> {
		let path = self.full_path(object);
		let (age, length) = {
			let metadata = symlink_metadata(&path).await?;
			(SystemTime::now().duration_since(metadata.modified()?).unwrap_or_default(), metadata.len())
		};
		if (age > invalidation) {
			return Err(super::Error::ObjectTooOld(age.into()));
		}
		let mut file = BufReader::with_capacity(16384, File::open(path).await?);
		Ok(ReadStream::new(
			length,
			Box::pin(try_stream! {
				loop {
					let buf = file.fill_buf().await?;
					if(buf.is_empty()) {
						break;
					}
					let len = buf.len();
					yield Bytes::copy_from_slice(buf);
					file.consume(len);
				}
			})
		))
	}

	pub async fn write<S, E>(&self, object: &Utf8Path, reader: S) -> Result<(), super::Error>
	where
		S: TryStream<Ok = Bytes, Error = E> + Unpin,
		E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
		super::Error: From<E>
	{
		async fn _write<S, E>(file: &mut BufWriter<File>, mut reader: S) -> Result<(), super::Error>
		where
			S: TryStream<Ok = Bytes, Error = E> + Unpin,
			E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
			super::Error: From<E>
		{
			while let Some(buf) = reader.try_next().await? {
				if (buf.is_empty()) {
					break;
				}
				file.write_all(buf.as_ref()).await.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
			}
			Ok(())
		}

		let path = self.full_path(object);
		if let Some(parent) = path.parent() {
			create_dir_all(parent).await?;
		}
		let file = OpenOptions::default().create(true).read(false).write(true).truncate(true).open(&path).await?;
		let mut file = BufWriter::with_capacity(16384, file);

		match _write(&mut file, reader).await {
			Ok(_) => Ok(file.flush().await?),
			Err(e) => {
				self.delete(object.as_ref()).await?;
				Err(e)
			}
		}
	}

	pub async fn delete(&self, path: &Path) -> Result<(), std::io::Error> {
		remove_file(path).await
	}

	pub async fn delete_old_files(&self, older_than: SystemTime, prefix: &Utf8Path) -> Result<usize, super::Error> {
		let mut count = 0;
		let root = self.root.join(prefix);
		let mut entries = WalkDir::new(root);
		let mut first_iteration = true;
		while let Some(entry) = entries.next().await {
			let entry = match entry {
				Ok(v) => v,
				// If we get a NotFound error on the first iteration, it only means that we haven't cached anything under this prefix yet
				Err(e) if e.kind() == std::io::ErrorKind::NotFound && first_iteration => continue,
				Err(error) => {
					error!(path = %prefix, %error, "Error walking directory");
					continue;
				}
			};
			first_iteration = false;
			let path = entry.path();
			let metadata = match entry.metadata().await {
				Ok(v) => v,
				Err(error) => {
					error!(path = %path.display(), %error, "Error reading metadata");
					continue;
				}
			};
			if (!metadata.is_file()) {
				continue;
			}
			let modified = match metadata.modified() {
				Ok(v) => v,
				Err(error) => {
					error!(path = %path.display(), %error, "Error reading mtime");
					continue;
				}
			};
			if (modified < older_than) {
				match self.delete(&path).await {
					Ok(_) => info!(path = %path.display(), "Aged out"),
					Err(error) => {
						error!(path = %path.display(), %error, "Error deleting object");
						continue;
					}
				}
				count += 1;
			}
		}
		Ok(count)
	}
}
07070100000028000081A400000000000000000000000166282B0100001A9A000000000000000000000000000000000000002500000000oci-registry-0.4.5/src/storage/s3.rsuse core::pin::Pin;
use core::time::Duration;
use std::str::FromStr;
use std::time::SystemTime;
use std::vec::IntoIter;

use actix_web::web::Bytes;
use clap::Parser;
use compact_str::CompactString;
use futures::future::BoxFuture;
use futures::future::FutureExt;
use futures::stream::Stream;
use futures::stream::StreamExt;
use futures::stream::TryStream;
use futures::stream::TryStreamExt;
use futures::task::Context;
use futures::task::Poll;
use rusoto_core::request::HttpClient;
use rusoto_core::ByteStream;
use rusoto_core::Region;
use rusoto_core::RusotoError;
use rusoto_credential::StaticProvider;
use rusoto_s3::DeleteObjectError;
use rusoto_s3::DeleteObjectRequest;
use rusoto_s3::GetObjectError;
use rusoto_s3::GetObjectOutput;
use rusoto_s3::GetObjectRequest;
use rusoto_s3::ListObjectsV2Error;
use rusoto_s3::ListObjectsV2Output;
use rusoto_s3::ListObjectsV2Request;
use rusoto_s3::PutObjectRequest;
use rusoto_s3::S3Client;
use rusoto_s3::S3;
use time::format_description::well_known::Rfc2822;
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;
use tracing::info;

use super::ReadStream;

#[derive(Clone, Debug, Parser)]
pub struct Config {
	#[clap(env = "S3_HOST", long)]
	host: Option<String>,
	#[clap(env = "S3_ACCESS_KEY", long)]
	access_key: CompactString,
	#[clap(env = "S3_SECRET_KEY", long)]
	secret_key: String,
	#[clap(env = "S3_REGION", long, default_value = "us-east-1")]
	region: CompactString,
	#[clap(env = "S3_BUCKET", long)]
	bucket: CompactString
}

impl Config {
	pub fn repository(&self) -> Repository {
		let region = match self.host.clone() {
			Some(s) => Region::Custom { name: self.region.to_string(), endpoint: s },
			None => Region::from_str(&self.region).unwrap()
		};
		let creds = StaticProvider::new(self.access_key.to_string(), self.secret_key.clone(), None, None);
		let http = HttpClient::new().unwrap();
		Repository {
			inner: S3Client::new_with(http, creds, region),
			bucket: self.bucket.clone()
		}
	}
}

struct ListObjectsStream {
	client: S3Client,
	bucket: CompactString,
	current_continuation_token: Option<String>,
	current_contents: IntoIter<rusoto_s3::Object>,
	current_future: Option<BoxFuture<'static, Result<ListObjectsV2Output, RusotoError<ListObjectsV2Error>>>>
}

impl Stream for ListObjectsStream {
	type Item = Result<rusoto_s3::Object, RusotoError<ListObjectsV2Error>>;

	#[rustfmt::skip]
	fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
		if let Some(obj) = self.current_contents.next() {
			return Poll::Ready(Some(Ok(obj)));
		}
		if let Some(mut future) = self.current_future.take() {
			let output = match future.poll_unpin(ctx) {
				Poll::Ready(Ok(v)) => v,
				Poll::Ready(Err(e)) => return Poll::Ready(Some(Err(e))),
				Poll::Pending => {
					self.current_future = Some(future);
					return Poll::Pending;
				}
			};
			self.current_continuation_token = output.continuation_token;
			self.current_contents = match output.contents {
				Some(v) => v.into_iter(),
				None => vec![].into_iter()
			};
			ctx.waker().wake_by_ref();
			return Poll::Pending;
		}
		let Some(token) = self.current_continuation_token.take() else {
			return Poll::Ready(None);
		};
		self.current_future = {
			let client = Box::pin(self.client.clone());
			let bucket = self.bucket.to_string();
			Some(Box::pin(async move {
				client.list_objects_v2(ListObjectsV2Request{
					bucket,
					continuation_token: Some(token),
					..Default::default()
				}).await
			}))
		};
		ctx.waker().wake_by_ref();
		Poll::Pending
	}

	fn size_hint(&self) -> (usize, Option<usize>) {
		match self.current_continuation_token.is_some() || self.current_future.is_some() {
			true => (self.current_contents.len(), None),
			false => (self.current_contents.len(), Some(self.current_contents.len()))
		}
	}
}

#[derive(Clone)]
pub struct Repository {
	inner: S3Client,
	bucket: CompactString
}

impl Repository {
	async fn list_objects(&self, prefix: &str) -> Result<ListObjectsStream, RusotoError<ListObjectsV2Error>> {
		let req = ListObjectsV2Request {
			bucket: self.bucket.to_string(),
			prefix: Some(prefix.into()),
			..Default::default()
		};
		let result = self.inner.list_objects_v2(req).await?;
		Ok(ListObjectsStream {
			client: self.inner.clone(),
			bucket: self.bucket.clone(),
			current_continuation_token: result.continuation_token,
			current_contents: result.contents.unwrap_or_default().into_iter(),
			current_future: None
		})
	}

	async fn get_object(&self, object: &str) -> Result<GetObjectOutput, RusotoError<GetObjectError>> {
		let req = GetObjectRequest {
			bucket: self.bucket.to_string(),
			key: object.into(),
			..Default::default()
		};
		self.inner.get_object(req).await
	}

	pub async fn read(&self, object: &str, invalidation: Duration) -> Result<ReadStream, super::Error> {
		let obj = self.get_object(object).await?;
		let time = obj.last_modified.map(|s| OffsetDateTime::parse(&s, &Rfc2822)).transpose()?.unwrap_or(OffsetDateTime::UNIX_EPOCH);
		let age = Duration::try_from(SystemTime::now() - time).unwrap_or_default();
		if (age > invalidation) {
			return Err(super::Error::ObjectTooOld(age.into()));
		}

		Ok(ReadStream::new(obj.content_length.unwrap().try_into().unwrap_or_default(), Box::pin(obj.body.unwrap())))
	}

	pub async fn write<S, E>(&self, object: &str, reader: S, length: i64) -> Result<(), super::Error>
	where
		S: TryStream<Ok = Bytes, Error = E> + Unpin + Send + 'static,
		E: std::error::Error + Send + Sync + 'static,
		super::Error: From<E>
	{
		let req = PutObjectRequest {
			bucket: self.bucket.to_string(),
			key: object.into(),
			content_length: Some(length),
			body: Some(ByteStream::new(reader.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)))),
			..Default::default()
		};

		if let Err(e) = self.inner.put_object(req).await {
			self.delete(object).await?;
			return Err(e.into());
		}

		Ok(())
	}

	pub async fn delete(&self, object: &str) -> Result<(), RusotoError<DeleteObjectError>> {
		let req = DeleteObjectRequest {
			bucket: self.bucket.to_string(),
			key: object.to_owned(),
			..Default::default()
		};
		self.inner.delete_object(req).await?;
		Ok(())
	}

	pub async fn delete_old_objects(&self, older_than: SystemTime, prefix: &str) -> Result<usize, super::Error> {
		let mut count = 0;
		let mut stream = self.list_objects(prefix).await?;
		while let Some(obj) = stream.next().await {
			let obj = obj?;
			let Some(key) = obj.key else {
				continue;
			};
			let modified = obj.last_modified.and_then(|s| OffsetDateTime::parse(&s, &Rfc3339).ok()).unwrap_or(OffsetDateTime::UNIX_EPOCH);
			if (modified < older_than) {
				match self.delete(key.as_ref()).await {
					Ok(_) => info!(object = key, "Aged out"),
					Err(_) => continue
				};
				count += 1;
			}
		}
		Ok(count)
	}
}
07070100000029000081A400000000000000000000000166282B0100001CB2000000000000000000000000000000000000002300000000oci-registry-0.4.5/src/upstream.rsuse std::collections::HashMap;

use camino::Utf8PathBuf;
use clap::Parser;
use compact_str::CompactString;
use dkregistry::errors::Error;
use dkregistry::v2::Client as InnerClient;
use humantime::Duration;
use serde::Deserialize;
use serde_with::serde_as;
use serde_with::DisplayFromStr;
use tokio::fs::read_to_string;
use tracing::info;
use tracing::warn;

use crate::util::SecretString;

#[derive(Clone, Debug)]
pub struct Client {
	pub client: InnerClient,
	pub manifest_invalidation_time: core::time::Duration,
	pub blob_invalidation_time: core::time::Duration
}

pub struct Clients(HashMap<CompactString, Client>);
impl Clients {
	pub fn get<'a>(&'a mut self, key: &str) -> Result<&'a mut Client, Error> {
		if (!self.0.contains_key(key)) {
			warn!(namespace = key, "Unknown namespace passed; configuring with default settings");
			self.insert(key.into(), SingleUpstreamConfig::new(key.into()))?;
		}
		Ok(self.0.get_mut(key).unwrap())
	}

	fn insert(&mut self, key: CompactString, config: SingleUpstreamConfig) -> Result<(), Error> {
		self.0.insert(key, config.try_into()?);
		Ok(())
	}

	pub fn invalidation_config(&self) -> InvalidationConfig {
		let mut config = InvalidationConfig {
			blob: core::time::Duration::from_secs(10),
			manifests: HashMap::with_capacity(self.0.len())
		};
		for (ns, client) in self.0.iter() {
			if (ns.is_empty()) {
				continue;
			}
			config.manifests.insert(ns.clone(), client.manifest_invalidation_time);
			if (client.blob_invalidation_time > config.blob) {
				config.blob = client.blob_invalidation_time;
			}
		}
		config
	}
}

impl FromIterator<(CompactString, Client)> for Clients {
	fn from_iter<T: IntoIterator<Item = (CompactString, Client)>>(iter: T) -> Self {
		Self(iter.into_iter().collect())
	}
}

#[derive(Clone, Debug)]
pub struct InvalidationConfig {
	pub blob: core::time::Duration,
	pub manifests: HashMap<CompactString, core::time::Duration>
}

const fn truth() -> bool {
	true
}

fn default_manifest_invalidation_time() -> Duration {
	core::time::Duration::from_secs(14 * 86400).into()
}

fn default_blob_invalidation_time() -> Duration {
	core::time::Duration::from_secs(14 * 86400).into()
}

#[serde_as]
#[derive(Clone, Debug, Deserialize)]
pub struct SingleUpstreamConfig {
	namespace: CompactString,
	host: CompactString,
	#[serde(default = "truth")]
	tls: bool,
	#[serde(default)]
	accept_invalid_certs: bool,
	#[serde(default)]
	user_agent: Option<arcstr::ArcStr>,
	#[serde(default)]
	username: Option<SecretString>,
	#[serde(default)]
	password: Option<SecretString>,
	#[serde(default = "default_manifest_invalidation_time")]
	#[serde_as(as = "DisplayFromStr")]
	manifest_invalidation_time: Duration,
	#[serde(default = "default_blob_invalidation_time")]
	#[serde_as(as = "DisplayFromStr")]
	blob_invalidation_time: Duration
}

impl SingleUpstreamConfig {
	fn new(namespace: CompactString) -> Self {
		Self::with_host(namespace.clone(), namespace)
	}

	fn with_host(namespace: CompactString, host: CompactString) -> Self {
		Self {
			namespace,
			host,
			tls: true,
			accept_invalid_certs: false,
			user_agent: None,
			username: None,
			password: None,
			manifest_invalidation_time: default_manifest_invalidation_time(),
			blob_invalidation_time: default_blob_invalidation_time()
		}
	}
}

impl TryFrom<SingleUpstreamConfig> for Client {
	type Error = Error;

	fn try_from(config: SingleUpstreamConfig) -> Result<Self, Self::Error> {
		let client = InnerClient::configure()
			.registry(&config.host)
			.insecure_registry(!config.tls)
			.accept_invalid_certs(config.accept_invalid_certs)
			.user_agent(config.user_agent)
			.username(config.username.map(|s| s.into_inner()))
			.password(config.password.map(|s| s.into_inner()))
			.build()?;
		Ok(Self {
			client,
			manifest_invalidation_time: config.manifest_invalidation_time.into(),
			blob_invalidation_time: config.blob_invalidation_time.into()
		})
	}
}

#[derive(Debug, Parser)]
pub struct UpstreamConfig {
	#[clap(env, long, default_value = "docker.io")]
	/// For requests made without a namespace (I'm looking at you, Docker), this namespace will be
	/// used.
	default_upstream_namespace: CompactString,
	#[clap(env, long)]
	/// If not passed, will default to a configuration that works for Docker Hub.  If a client
	/// passes in a `ns` parameter that isn't found in the configuration, the namespace will be
	/// treated as an upstream hostname, and we will try to connect with TLS enabled, requiring
	/// valid certs, and with no user-agent/credentials.
	upstream_config_file: Option<Utf8PathBuf>,
	#[clap(env, long, default_value = "{}")]
	/// For security reasons, upstream registry credentials can be passed this way instead of
	/// storing them in a config file - this is expected in JSON format, as a map from namespace
	/// to credentials.
	///
	/// Example: `{"docker.io": {"username": "foo", "password": "bar"}, "namespace2": {"username":
	/// {"aaa", "pasword": "bbb"}}`
	upstream_credentials: String
}

#[derive(Debug, Deserialize)]
pub struct CredentialsOverride<'a> {
	username: &'a str,
	password: &'a str
}

impl UpstreamConfig {
	pub async fn clients(&self) -> Result<Clients, Error> {
		let mut upstream_credentials: HashMap<&str, CredentialsOverride<'_>> = serde_json::from_str(self.upstream_credentials.as_ref()).unwrap();
		let mut clients = match self.upstream_config_file.as_ref() {
			Some(file) => {
				let upstream_config = read_to_string(file).await.unwrap();
				let upstream_config: Vec<SingleUpstreamConfig> = serde_yaml::from_str(&upstream_config).unwrap();
				upstream_config
					.into_iter()
					.map(|conf| {
						info!(config = ?conf, "Parsed upstream config");
						conf
					})
					.map(|mut conf| match upstream_credentials.remove::<str>(conf.namespace.as_ref()) {
						Some(cred) => {
							if (conf.username.is_some() || conf.password.is_some()) {
								let namespace: &str = conf.namespace.as_ref();
								warn!(namespace, "Found namespace in UPSTREAM_CREDENTIALS override, and it already has credentials set in the config file");
							}
							conf.username = Some(cred.username.into());
							conf.password = Some(cred.password.into());
							conf
						},
						None => conf
					})
					.map(|conf| Ok::<_, Error>((conf.namespace.clone(), conf.try_into()?)))
					.collect::<Result<Clients, _>>()?
			},
			None => {
				let (username, password) = match upstream_credentials.remove("docker.io") {
					Some(creds) => (Some(creds.username.into()), Some(creds.password.into())),
					None => (None, None)
				};
				#[rustfmt::skip]
				let client = SingleUpstreamConfig{
					namespace: "docker.io".into(),
					host: "registry-1.docker.io".into(),
					tls: true,
					accept_invalid_certs: false,
					user_agent: None,
					username,
					password,
					manifest_invalidation_time: default_manifest_invalidation_time(),
					blob_invalidation_time: default_blob_invalidation_time()
				}.try_into()?;
				let mut map = HashMap::with_capacity(1);
				map.insert("docker.io".into(), client);
				Clients(map)
			}
		};

		for (namespace, _) in upstream_credentials {
			warn!(namespace, "Namespace found in UPSTREAM_CREDENTIALS, but not in upstream config file; will be ignored.");
		}

		let default_client = clients.get(&self.default_upstream_namespace)?.clone();
		clients.0.insert("".into(), default_client);
		Ok(clients)
	}
}
0707010000002A000081A400000000000000000000000166282B0100000389000000000000000000000000000000000000001F00000000oci-registry-0.4.5/src/util.rsuse core::convert::Infallible;
use core::fmt;
use core::str::FromStr;

use compact_str::CompactString;
use serde::Deserialize;
use serde::Deserializer;

#[derive(Clone)]
pub(crate) struct SecretString(CompactString);
impl FromStr for SecretString {
	type Err = Infallible;

	#[inline]
	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Ok(s.into())
	}
}

impl From<&str> for SecretString {
	#[inline]
	fn from(s: &str) -> Self {
		Self(s.into())
	}
}

impl fmt::Debug for SecretString {
	#[inline]
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		f.write_str("\"REDACTED\"")
	}
}

impl<'de> Deserialize<'de> for SecretString {
	#[inline]
	fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
		let s = CompactString::deserialize(deserializer)?;
		Ok(Self(s))
	}
}

impl SecretString {
	#[inline]
	pub(crate) fn into_inner(self) -> CompactString {
		self.0
	}
}
0707010000002B000081ED00000000000000000000000166282B01000005CA000000000000000000000000000000000000001B00000000oci-registry-0.4.5/test.sh#!/bin/bash

# Expects oci-registry to be listening on localhost:8080, for example:
# RUST_LOG=info,actix-web=debug cargo run -- --listen 0.0.0.0:8080 filesystem --root /tmp/oci-mirror

set -eu

test() {
  echo "--- Testing $1/$2"

  rm -rf /tmp/oci-mirror

  # containerd
  url="localhost:8080/v2/$2?ns=$1"
  curl -s -o /dev/null -w "  %{http_code}: $url\n" "$url"

  rm -rf /tmp/oci-mirror

  # cri-o
  url="localhost:8080/v2/$1/$2"
  curl -s -o /dev/null -w "  %{http_code}: $url\n" "$url"

  rm -rf /tmp/oci-mirror

  # Special Docker Hub case falling back to default_ns
  if [ "$1" == "docker.io" ]; then
    url="localhost:8080/v2/$2"
    curl -s -o /dev/null -w "  %{http_code}: $url\n" "$url"
  fi
}

test 'docker.io' 'envoyproxy/envoy/manifests/v1.26.2'
test 'docker.io' 'library/busybox/manifests/latest'
test 'docker.io' 'grafana/mimirtool/blobs/sha256:31e352740f534f9ad170f75378a84fe453d6156e40700b882d737a8f4a6988a3'

test 'gcr.io' 'distroless/static/manifests/latest'
test 'gcr.io' 'distroless/static/blobs/sha256:fe5ca62666f04366c8e7f605aa82997d71320183e99962fa76b3209fdfbb8b58'
test 'gcr.io' 'flame-public/buildbuddy-app-onprem/manifests/sha256:68932b6227b4c16d6bbc08997620284ef71f7b10ee9b9dc47b631ae92f31a6a3'

test 'ghcr.io' 'buildbarn/bb-runner-installer/manifests/sha256:beb72a39662a613a889f17e7a15254f7e03849e4f7000f399ea7bdabe4a25f79'
test 'ghcr.io' 'buildbarn/bb-runner-installer/blobs/sha256:e2334dd9fee4b77e48a8f2d793904118a3acf26f1f2e72a3d79c6cae993e07f0'
0707010000002C000041ED00000000000000000000000266282B0100000000000000000000000000000000000000000000001C00000000oci-registry-0.4.5/testdata0707010000002D000081A400000000000000000000000166282B01000005A5000000000000000000000000000000000000002700000000oci-registry-0.4.5/testdata/images.txt#   ns             repository            tag
docker.io	tarampampam/webhook-tester	0.4.2
docker.io	stpaquet/alpinemailcatcher	latest
docker.io	stedolan/jq	latest
docker.io	sig9/snmptrapd	latest
docker.io	rabbitmqoperator/cluster-operator	2.0.0
docker.io	prom/pushgateway	v1.2.0
docker.io	metrics-server/metrics-server	v0.6.1
docker.io	metallb/speaker	latest
docker.io	metallb/controller	latest
docker.io	library/telegraf	1.24-alpine
docker.io	library/redis	6.2.6
docker.io	library/rabbitmq	3.10.2
docker.io	library/kong	2.8
docker.io	library/busybox	latest
docker.io	library/busybox	1.31.1
docker.io	kubernetesui/metrics-scraper	latest
docker.io	kubernetesui/dashboard	latest
docker.io	kong/kubernetes-ingress-controller	2.3.1
docker.io	kiwigrid/k8s-sidecar	1.15.6
docker.io	k8s-minikube/minikube-ingress-dns	latest
docker.io	jetstack/cert-manager-webhook	v1.8.0
docker.io	jetstack/cert-manager-controller	v1.8.0
docker.io	jetstack/cert-manager-cainjector	v1.8.0
docker.io	ingress-nginx/kube-webhook-certgen	latest
docker.io	ingress-nginx/controller	latest
docker.io	grafana/promtail	2.6.1
docker.io	grafana/loki	2.6.1
docker.io	grafana/grafana	9.2.0
docker.io	grafana/grafana	8.3.5
docker.io	gospatial/tegola	v0.13.0
docker.io	external-secrets/external-secrets	v0.5.1
docker.io	curlimages/curl	7.85.0
docker.io	bitnami/postgresql	14.5.0-debian-11-r24
docker.io	bitnami/keycloak	19.0.3-debian-11-r3
docker.io	bitnami/influxdb	2.4.0-debian-11-r11
0707010000002E000081A400000000000000000000000166282B01000000BF000000000000000000000000000000000000002200000000oci-registry-0.4.5/testdata/s3cfg[default]
access_key = F504CLZ37ECLH011V4XB
secret_key = Btj2sAMtCs7GFpkmrKuMojvSdivXWt8EXy5DDZJ5
host_base = 192.168.1.200:7480
host_bucket = 192.168.1.200:7480/%(bucket)
use_https = False

0707010000002F000081A400000000000000000000000166282B01000001E4000000000000000000000000000000000000002A00000000oci-registry-0.4.5/testdata/upstream.yaml- namespace: docker.io
  host: 10.254.20.0
  tls: false
  accept_invalid_certs: false
  user_agent: null
  username: null
  password: null
  manifest_invalidation_time: 14d
  blob_invalidation_time: 14d
- namespace: quay.io
  host: 10.254.20.0
  tls: false
- namespace: gcr.io
  host: 10.254.20.0
  tls: false
- namespace: ghcr.io
  host: 10.254.20.0
  tls: false
- namespace: k8s.gcr.io
  host: 10.254.20.0
  tls: false
- namespace: registry.k8s.io
  host: 10.254.20.0
  tls: false

07070100000030000041ED00000000000000000000000266282B0100000000000000000000000000000000000000000000001900000000oci-registry-0.4.5/tools07070100000031000081ED00000000000000000000000166282B010000029E000000000000000000000000000000000000003500000000oci-registry-0.4.5/tools/generate-filesystem-profile#!/bin/bash
DIR="$(cd "$(dirname "${0}")"; pwd)"

mkdir -pv test
rm -Rf test/*
RUST_LOG=info,actix-web=debug
RUST_BACKTRACE=1
"./target/x86_64-unknown-linux-gnu/release/oci-registry${1}" \
	--listen 0.0.0.0:16385 \
	--upstream-config-file=testdata/upstream.yaml \
	filesystem \
	--root=test \
	| sed 's/^/[fs] /' &
pid="$!"
sleep 0.1

while true; do
	echo | ncat localhost 16385
	sleep 0.1
done &
loop_pid="$!"

"${DIR}/make-test-requests" http://localhost:16385 | sed 's/^/[fs] /'
sleep 2
echo '[fs] done requests'
ps -ef | grep "oci-registry${1}" | grep -- '0\.0\.0\.0:16385' | awk '{print $2}' | xargs kill
echo '[fs] killed oci-registry'
kill "$loop_pid"
sleep 0.5

07070100000032000081ED00000000000000000000000166282B01000000B4000000000000000000000000000000000000002B00000000oci-registry-0.4.5/tools/generate-profiles#!/bin/sh
DIR="$(cd "$(dirname "${0}")"; pwd)"

"${DIR}/generate-filesystem-profile" "$1" &
pid_fs="$!"

"${DIR}/generate-s3-profile" "$1" &
pid_s3="$!"

wait "$pid_fs" "$pid_s3"

07070100000033000081ED00000000000000000000000166282B0100000325000000000000000000000000000000000000002D00000000oci-registry-0.4.5/tools/generate-s3-profile#!/bin/bash
DIR="$(cd "$(dirname "${0}")"; pwd)"

s3cmd rm -rf s3://oci-registry-test
RUST_LOG=info,actix-web=debug
RUST_BACKTRACE=1
"./target/x86_64-unknown-linux-gnu/release/oci-registry${1}" \
	--listen 0.0.0.0:16384 \
	--upstream-config-file=testdata/upstream.yaml \
	s3 \
	--host=http://192.168.1.200:7480 \
	--access-key=F504CLZ37ECLH011V4XB \
	--secret-key=Btj2sAMtCs7GFpkmrKuMojvSdivXWt8EXy5DDZJ5 \
	--bucket=oci-registry-test \
	| sed 's/^/[s3] /' &
sleep 0.1

while true; do
	echo | ncat localhost 16384
	sleep 0.1
done &
loop_pid="$!"

"${DIR}/make-test-requests" http://localhost:16384 | sed 's/^/[s3] /'
sleep 2
echo '[s3] done requests'
ps -ef | grep "oci-registry${1}" | grep -- '0\.0\.0\.0:16384' | awk '{print $2}' | xargs kill
echo '[s3] killed oci-registry'
kill "$loop_pid"
sleep 0.5

07070100000034000081ED00000000000000000000000166282B0100000387000000000000000000000000000000000000002C00000000oci-registry-0.4.5/tools/make-test-requests#!/bin/sh
(
	for i in {0..200}; do
		echo 'GET /'
		sleep 0.1
	done

	for i in {0..3}; do
		COUNT=0;
		cat "$(cd "$(dirname "${0}")"; pwd)/../testdata/images.txt" | grep -v '^#' | while read ns repo tag; do
			manifest_hash="$(curl -svI "$1/v2/$repo/manifests/$tag?ns=$ns" 2>&1 | grep docker-content-digest: | head -n1 | cut -d: -f2- | sed -e 's/^\s\+//' -e 's/\s\+$//')";
			curl -s "$1/v2/$repo/manifests/$manifest_hash?ns=$ns" | jq -r '.manifests[].digest' | while read manifest_hash; do
				curl -s "$1/v2/$repo/manifests/$manifest_hash?ns=$ns" | jq -r '.config.digest, .layers[].digest' | while read blob_hash; do
					echo "GET /v2/$repo/blobs/$blob_hash?ns=$ns";
				done &
				echo 'GET /';
			done &
			echo 'GET /';
		done

		for i in {0..500}; do
			echo 'GET /'
			sleep 0.1
		done
	done
) | xargs -n2 -P40 bash -c 'curl -o /dev/null -X "${1}" "'"${1}"'${2}" &>/dev/null; echo $? $@' argv0

07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!380 blocks
openSUSE Build Service is sponsored by