diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..d1d091bbd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,48 @@ +name: Bug report +description: Create a report to help us improve +labels: + - kind/bug +body: + - type: markdown + attributes: + value: | + Thank you for taking the time to report a bug! + If this is a security issue please report it to the [Distributions Security Mailing List](mailto:cncf-distribution-security@lists.cncf.io). + - type: textarea + id: description + attributes: + label: Description + description: Please give a clear and concise description of the bug + validations: + required: true + - type: textarea + id: repro + attributes: + label: Reproduce + description: Steps to reproduce the bug + placeholder: | + 1. start registry version X ... + 2. `docker push image:tag` ... + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What is the expected behavior? + placeholder: | + E.g. "registry returns an incorrect API error" + - type: textarea + id: version + attributes: + label: registry version + description: Output of `registry --version`. Alternatively tell us the docker image tag. + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional Info + description: Additional info you want to provide such as logs, system info, environment, etc. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..8965f2084 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Security and Vulnerabilities + url: https://github.com/distribution/distribution/blob/main/SECURITY.md + about: Please report any security issues or vulnerabilities responsibly to the distribution maintainers team. Please do not use the public issue tracker. + - name: Questions and Discussions + url: https://github.com/distribution/distribution/discussions/new/choose + about: Use Github Discussions to ask questions and/or open discussion topics. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..08ea159fc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,12 @@ +name: Feature request +description: Missing functionality? Come tell us about it! +labels: + - kind/feature +body: + - type: textarea + id: description + attributes: + label: Description + description: What is the feature you want to see? + validations: + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..078c42045 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + labels: + - "dependencies" diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000..74730fc73 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,61 @@ +area/api: + - changed-files: + - any-glob-to-any-file: + - registry/api/** + - registry/handlers/** +area/auth: + - changed-files: + - any-glob-to-any-file: + - registry/auth/** +area/build: + - changed-files: + - any-glob-to-any-file: + - Makefile + - Dockerfile + - docker-bake.hcl + - dockerfiles/** +area/cache: + - changed-files: + - any-glob-to-any-file: + - registry/storage/cache/** +area/ci: + - changed-files: + - any-glob-to-any-file: + - .github/** + - tests/** + - testutil/** +area/config: + - changed-files: + - any-glob-to-any-file: + - configuration/** +area/docs: + - changed-files: + - any-glob-to-any-file: + - README.md + - docs/**/*.md +area/proxy: + - changed-files: + - any-glob-to-any-file: + - registry/proxy/** +area/storage: + - changed-files: + - any-glob-to-any-file: + - registry/storage/** +area/storage/azure: + - changed-files: + - any-glob-to-any-file: + - registry/storage/driver/azure/** +area/storage/gcs: + - changed-files: + - any-glob-to-any-file: + - registry/storage/driver/gcs/** +area/storage/s3: + - changed-files: + - any-glob-to-any-file: + - registry/storage/driver/s3-aws/** +dependencies: + - changed-files: + - any-glob-to-any-file: + - vendor/** + - go.mod + - go.sum diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e4dc65f2..92e79105e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,18 +27,18 @@ jobs: fail-fast: false matrix: go: - - 1.20.12 - - 1.21.5 + - 1.21.8 + - 1.22.1 target: - test-coverage - test-cloud-storage steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - @@ -47,7 +47,7 @@ jobs: make ${{ matrix.target }} - name: Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: directory: ./ @@ -62,13 +62,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Docker meta id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: | ${{ env.DOCKERHUB_SLUG }} @@ -94,43 +94,53 @@ jobs: org.opencontainers.image.description=The toolkit to pack, ship, store, and distribute container content - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub if: github.event_name != 'pull_request' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Log in to GitHub Container registry if: github.event_name != 'pull_request' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build artifacts - uses: docker/bake-action@v2 + uses: docker/bake-action@v4 with: targets: artifact-all - - name: Move artifacts + name: Rename provenance + run: | + for pdir in ./bin/*/; do + ( + cd "$pdir" + binname=$(find . -name '*.tar.gz') + filename=$(basename "${binname%.tar.gz}") + mv "provenance.json" "${filename}.provenance.json" + ) + done + - + name: Move and list artifacts run: | mv ./bin/**/* ./bin/ + tree -nh ./bin - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.0 with: name: registry path: ./bin/* if-no-files-found: error - name: Build image - uses: docker/bake-action@v2 + uses: docker/bake-action@v4 with: files: | ./docker-bake.hcl @@ -145,6 +155,7 @@ jobs: draft: true files: | bin/*.tar.gz + bin/*.provenance.json bin/*.sha256 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 76bf31ecf..27fe0da34 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,7 +34,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 - @@ -44,12 +44,12 @@ jobs: git checkout HEAD^2 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3.22.12 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3.22.12 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3.22.12 diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index ca8fdfb64..58b0a6d17 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -17,12 +17,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Build image - uses: docker/bake-action@v2 + uses: docker/bake-action@v4 with: targets: image-local - @@ -49,7 +49,7 @@ jobs: run: mkdir -p .out/ && mv {report.html,junit.xml} .out/ - name: Upload test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.3.0 with: name: oci-test-results-${{ github.sha }} path: .out/ diff --git a/.github/workflows/dockerhub-readme.yml b/.github/workflows/dockerhub-readme.yml index 6ace8cd93..8f60a1970 100644 --- a/.github/workflows/dockerhub-readme.yml +++ b/.github/workflows/dockerhub-readme.yml @@ -27,7 +27,7 @@ jobs: uses: actions/checkout@v4 - name: Update Docker Hub README - uses: peter-evans/dockerhub-description@v3 + uses: peter-evans/dockerhub-description@v4 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c37f59e04..2681345c6 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -26,27 +26,26 @@ jobs: uses: actions/checkout@v4 - name: Setup Pages id: pages - uses: actions/configure-pages@v3 + uses: actions/configure-pages@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Build docs - uses: docker/bake-action@v3 + uses: docker/bake-action@v4 with: files: | docker-bake.hcl targets: docs-export + provenance: false set: | *.cache-from=type=gha,scope=docs *.cache-to=type=gha,scope=docs,mode=max - env: - DOCS_BASEURL: ${{ steps.pages.outputs.base_path }} - name: Fix permissions run: | chmod -c -R +rX "./build/docs" | while read line; do echo "::warning title=Invalid file permissions automatically fixed::$line" done - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v2 + uses: actions/upload-pages-artifact@v3 with: path: ./build/docs @@ -70,4 +69,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v2 # or the latest "vX.X.X" version tag for this action + uses: actions/deploy-pages@v4 # or the latest "vX.X.X" version tag for this action diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c5ce9d320..73896b223 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -20,12 +20,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Build image - uses: docker/bake-action@v2 + uses: docker/bake-action@v4 with: targets: image-local - @@ -42,7 +42,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index 9b4419d64..a71bd2092 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -17,9 +17,9 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run FOSSA scan and upload build data - uses: fossa-contrib/fossa-action@v2 + uses: fossa-contrib/fossa-action@v3 with: fossa-api-key: cac3dc8d4f2ba86142f6c0f2199a160f diff --git a/.github/workflows/label.yaml b/.github/workflows/label.yaml new file mode 100644 index 000000000..6dda19bcd --- /dev/null +++ b/.github/workflows/label.yaml @@ -0,0 +1,19 @@ +name: Pull Request Labeler + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + pull_request_target: + +jobs: + labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 + with: + dot: true diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 63135599e..0476c0e26 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -22,12 +22,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # tag=v3.0.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # tag=v4.1.1 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # tag=v2.0.6 + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # tag=v2.3.1 with: results_file: results.sarif results_format: sarif @@ -46,7 +46,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # tag=v3.0.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # tag=v4.3.0 with: name: SARIF file path: results.sarif @@ -54,7 +54,7 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # tag=v1.0.26 + uses: github/codeql-action/upload-sarif@1500a131381b66de0c52ac28abb13cd79f4b7ecc # tag=v2.22.12 with: sarif_file: results.sarif diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 348496cca..b9e18b28d 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run run: | diff --git a/.golangci.yml b/.golangci.yml index ec637d63b..823cc43de 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,7 +6,7 @@ linters: - goimports - revive - ineffassign - - vet + - govet - unused - misspell - bodyclose @@ -22,7 +22,7 @@ linters-settings: - name: unused-parameter disabled: true -run: +issues: deadline: 2m - skip-dirs: + exlude-dirs: - vendor diff --git a/.mailmap b/.mailmap index 0f48321d4..463214edc 100644 --- a/.mailmap +++ b/.mailmap @@ -1,32 +1,194 @@ -Stephen J Day Stephen Day -Stephen J Day Stephen Day -Olivier Gambier Olivier Gambier -Brian Bland Brian Bland -Brian Bland Brian Bland -Josh Hawn Josh Hawn -Richard Scothern Richard -Richard Scothern Richard Scothern -Andrew Meredith Andrew Meredith -harche harche -Jessie Frazelle -Sharif Nassar Sharif Nassar -Sven Dowideit Sven Dowideit -Vincent Giersch Vincent Giersch -davidli davidli -Omer Cohen Omer Cohen -Eric Yang Eric Yang -Nikita Tarasov Nikita -Yu Wang yuwaMSFT2 +Aaron Lehmann +Aaron Lehmann +Akihiro Suda +Akihiro Suda +Akihiro Suda +Alexander Morozov +Alexander Morozov +Anders Ingemann +Andrew Meredith +Andrew Meredith +Andrey Smirnov +Andrii Soldatenko +Andrii Soldatenko +Anthony Ramahay +Antonio Murdaca +Antonio Murdaca +Antonio Murdaca +Antonio Murdaca +Antonio Murdaca +Antonio Murdaca +Austin Vazquez +Benjamin Schanzel +Brian Bland +Brian Bland +Brian Bland +Chad Faragher +Cory Snider +CrazyMax +CrazyMax <1951866+crazy-max@users.noreply.github.com> +CrazyMax +Cristian Staretu +Cristian Staretu +Cristian Staretu +Daniel Nephin +Daniel Nephin +David Karlsson +David Karlsson <35727626+dvdksn@users.noreply.github.com> +David Wu +David Wu +Derek McGowan +Derek McGowan +Dimitar Kostadinov +Doug Davis +Doug Davis +Emmanuel Ferdman +Eng Zer Jun +Eric Yang +Eric Yang +Eric Yang +Erica Windisch +Erica Windisch +Guillaume J. Charmes +Guillaume J. Charmes +Guillaume J. Charmes +Guillaume J. Charmes +Guillaume J. Charmes +Hayley Swimelar +Ismail Alidzhikov +Jaime Martinez +James Hewitt +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Joao Fernandes +Joao Fernandes +João Pereira <484633+joaodrp@users.noreply.github.com> +Joffrey F +Joffrey F +Joffrey F +Johan Euphrosine +Johan Euphrosine +John Howard +John Howard +Josh Hawn +Josh Hawn +Joyce Brum +Joyce Brum +Justin Cormack +Justin Cormack +Justin Cormack +Kirat Singh +Kirat Singh +Kirat Singh +Kyle Squizzato +Liang Zheng +Luca Bruno +Luca Bruno +Mahmoud Kandil <47168819+MahmoudKKandil@users.noreply.github.com> +Manish Tomar +Manish Tomar +Maria Bermudez +Maria Bermudez +Markus Thömmes +Matt Linville +Matt Linville +Matt Linville +Michael Crosby +Michael Crosby +Michael Crosby +Michael Crosby +Michael Crosby +Michal Minar +Michal Minar Michal Minář +Mike Brown +Mike Brown +Mikel Rychliski +Milos Gajdos +Milos Gajdos <1392526+milosgajdos@users.noreply.github.com> +Milos Gajdos +Nikita Tarasov +Nikita Tarasov +Oleg Bulatov +Oleg Bulatov +Olivier Gambier +Olivier Gambier +Omer Cohen +Omer Cohen +Paul Meyer <49727155+katexochen@users.noreply.github.com> +Per Lundberg +Per Lundberg +Peter Dave Hello +Peter Dave Hello +Phil Estes +Phil Estes +Phil Estes +Richard Scothern +Richard Scothern +Rober Morales-Chaparro +Rober Morales-Chaparro +Robin Ketelbuters +Sebastiaan van Stijn +Sebastiaan van Stijn +Sebastiaan van Stijn +Sebastiaan van Stijn +Sharif Nassar +Sharif Nassar +Solomon Hykes +Solomon Hykes +Solomon Hykes +Solomon Hykes +Solomon Hykes +Stephen Day +Stephen Day +Stephen Day +Steven Kalt +Sven Dowideit +Sven Dowideit +Sylvain DESGRAIS +Tadeusz Dudkiewicz +Tibor Vass +Tibor Vass +Tibor Vass +Victor Vieux +Victor Vieux +Victor Vieux +Victor Vieux +Victor Vieux +Victor Vieux +Victor Vieux +Victor Vieux +Victoria Bialas +Victoria Bialas +Vincent Batts +Vincent Batts +Vincent Demeester +Vincent Demeester +Vincent Demeester +Vincent Demeester +Vincent Giersch +Vincent Giersch +Wang Yan +Wen-Quan Li +Wen-Quan Li +Wen-Quan Li +Yu Wang Yu Wang Yu Wang (UC) -Olivier Gambier dmp -Olivier Gambier Olivier -Olivier Gambier Olivier -Elsan Li 李楠 elsanli(李楠) -Rui Cao ruicao -Gwendolynne Barr gbarr01 -Haibing Zhou 周海兵 zhouhaibing089 -Feng Honglin tifayuki -Helen Xie Helen-xie -Mike Brown Mike Brown -Manish Tomar Manish Tomar -Sakeven Jiang sakeven +baojiangnan +baojiangnan +erezrokah +goodactive +gotgelf +guoguangwu +harche +harche +icefed +oliver-goetz +xiaoxiangxianzi diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..bee5821ca --- /dev/null +++ b/AUTHORS @@ -0,0 +1,530 @@ +# This file lists all individuals having contributed content to the repository. +# For how it is generated, see dockerfiles/authors.Dockerfile. + +a-palchikov +Aaron Lehmann +Aaron Schlesinger +Aaron Vinson +Adam Dobrawy +Adam Duke +Adam Enger +Adam Kaplan +Adam Wolfe Gordon +AdamKorcz +Adrian Mouat +Adrian Plata +Adrien Duermael +Ahmet Alp Balkan +Aidan Hobson Sayers +Akihiro Suda +Aleksejs Sinicins +Alex +Alex Chan +Alex Elman +Alex Laties +Alexander Larsson +Alexander Morozov +Alexey Gladkov +Alfonso Acosta +allencloud +Alvin Feng +amitshukla +Amy Lindburg +Andreas Hassing +Andrew Bulford +Andrew Hsu +Andrew Lavery +Andrew Leung +Andrew Lively +Andrew Meredith +Andrew T Nguyen +Andrews Medina +Andrey Kostov +Andrii Soldatenko +Andy Goldstein +andyzhangx +Anian Z +Anil Belur +Anis Elleuch +Ankush Agarwal +Anne Henmi <41210220+ahh-docker@users.noreply.github.com> +Anton Tiurin +Antonio Mercado +Antonio Murdaca +Antonio Ojea +Anusha Ragunathan +Arien Holthuizen +Arko Dasgupta +Arnaud Porterie +Arthur Baars +Arthur Gautier +Asuka Suzuki +Avi Miller +Aviral Takkar +Ayose Cazorla +BadZen +baojiangnan +Ben Bodenmiller +Ben De St Paer-Gotch +Ben Emamian +Ben Firshman +Ben Kochie +Ben Manuel +Bhavin Gandhi +Bill +bin liu +Bouke van der Bijl +Bracken Dawson +Brandon Mitchell +Brandon Philips +Brett Higgins +Brian Bland +Brian Goff +burnettk +Caleb Spare +Carson A +Cezar Sa Espinola +Chad Faragher +Chaos John +Charles Smith +Cheng Zheng +chlins +Chris Aniszczyk +Chris Dillon +Chris K. Wong +Chris Patterson +Christopher Yeleighton +Christy Perez +Chuanying Du +Clayton Coleman +Collin Shoop +Corey Quon +Cory Snider +CrazyMax +cressie176 +Cristian Staretu +cui fliter +cuiwei13 +cyli +Daehyeok Mun +Daisuke Fujita +Damien Mathieu +Dan Fredell +Dan Walsh +Daniel Helfand +Daniel Huhn +Daniel Menet +Daniel Mizyrycki +Daniel Nephin +Daniel, Dao Quang Minh +Danila Fominykh +Darren Shepherd +Dave +Dave Trombley +Dave Tucker +David Calavera +David Justice +David Karlsson +David Lawrence +David Luu +David Mackey +David van der Spek +David Verhasselt +David Wu +David Xia +Dawn W Docker +ddelange <14880945+ddelange@users.noreply.github.com> +Dejan Golja +Denis Andrejew +dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> +Derek +Derek McGowan +Deshi Xiao +Dimitar Kostadinov +Diogo Mónica +DJ Enriquez +Djibril Koné +dmp +Don Bowman +Don Kjer +Donald Huang +Doug Davis +drornir +duanhongyi +ducksecops +E. M. Bray +Edgar Lee +Elliot Pahl +elsanli(李楠) +Elton Stoneman +Emmanuel Briney +Eng Zer Jun +Eohyung Lee +Eric Yang +Erica Windisch +Erik Hollensbe +Etki +Eugene Lubarsky +eyjhb +eyjhbb@gmail.com +Fabio Berchtold +Fabio Falci +Fabio Huser +farmerworking +fate-grand-order +Felix Bünemann +Felix Yan +Feng Honglin +Fernando Mayo Fernandez +Flavian Missi +Florentin Raud +forkbomber +Frank Chen +Frederick F. Kautz IV +Gabor Nagy +gabriell nascimento +Gaetan +gary schaetz +gbarr01 +Geoffrey Hausheer +ghodsizadeh +Giovanni Toraldo +Gladkov Alexey +Gleb M Borisov +Gleb Schukin +glefloch +Glyn Owen Hanmer <1295698+glynternet@users.noreply.github.com> +gotgelf +Grachev Mikhail +Grant Watters +Greg Rebholz +Guillaume J. Charmes +Guillaume Rose +Gábor Lipták +harche +hasheddan +Hayley Swimelar +Helen-xie +Henri Gomez +Honglin Feng +Hu Keping +Hua Wang +HuKeping +Huu Nguyen +ialidzhikov +Ian Babrou +iasoon +igayoso +Igor Dolzhikov +Igor Morozov +Ihor Dvoretskyi +Ilion Beyst +Ina Panova +Irene Diez +Ismail Alidzhikov +Jack Baines +Jack Griffin +Jacob Atzen +Jake Moshenko +Jakob Ackermann +Jakub Mikulas +James Findley +James Hewitt +James Lal +Jason Freidman +Jason Heiss +Javier Palomo Almena +jdolitsky <393494+jdolitsky@users.noreply.github.com> +Jeff Nickoloff +Jeffrey van Gogh +jerae-duffin <83294991+jerae-duffin@users.noreply.github.com> +Jeremy THERIN +Jesse Brown +Jesse Haka +Jessica Frazelle +jhaohai +Jianqing Wang +Jihoon Chung +Jim Galasyn +Joao Fernandes +Joffrey F +Johan Euphrosine +John Howard +John Mulhausen +John Starks +Jon Johnson +Jon Poler +Jonas Hecht +Jonathan Boulle +Jonathan Lee +Jonathan Rudenberg +Jordan Liggitt +Jose D. Gomez R +Josh Chorlton +Josh Dolitsky +Josh Hawn +Josiah Kiehl +Joyce Brum +João Pereira <484633+joaodrp@users.noreply.github.com> +Julien Bordellier <1444415+jstoja@users.noreply.github.com> +Julien Fernandez +Justas Brazauskas +Justin Cormack +Justin I. Nevill +Justin Santa Barbara +kaiwentan +Ke Xu +Keerthan Mala +Kelsey Hightower +Ken Cochrane +Kenneth Lim +Kenny Leung +Kevin Lin +Kevin Robatel +Kira +Kirat Singh +L-Hudson <44844738+L-Hudson@users.noreply.github.com> +Lachlan Cooper +Laura Brehm +Lei Jitang +Lenny Linux +Leonardo Azize Martins +leonstrand +Li Yi +Liam White +libo.huang +LingFaKe +Liron Levin +lisong +Littlemoon917 <18084421+Littlemoon917@users.noreply.github.com> +Liu Hua +liuchang0812 +liyongxin +Lloyd Ramey +lostsquirrel +Louis Kottmann +Luca Bruno +Lucas França de Oliveira +Lucas Santos +Luis Lobo Borobia +Luke Carpenter +Ma Shimiao +Makoto Oda +mallchin +Manish Tomar +Marco Hennings +Marcus Martins +Maria Bermudez +Mark Sagi-Kazar +Mary Anthony +Masataka Mizukoshi +Matin Rahmanian +MATSUMOTO TAKEAKI +Matt Bentley +Matt Duch +Matt Linville +Matt Moore +Matt Robenolt +Matt Tescher +Matthew Balvanz +Matthew Green +Matthew Riley +Maurice Sotzny +Meaglith Ma +Michael Bonfils +Michael Crosby +Michael Prokop +Michael Vetter +Michal Fojtik +Michal Gebauer +Michal Guerquin +Michal Minar +Mike Brown +Mike Lundy +Mike Truman +Milos Gajdos +Miquel Sabaté +mlmhl <409107750@qq.com> +Monika Katiyar +Morgan Bauer +moxiegirl +mqliang +Muesli +Nan Monnand Deng +Nat Zimmermann +Nathan Sullivan +Naveed Jamil +Neil Wilson +nevermosby +Nghia Tran +Nicolas De Loof +Nikita Tarasov +ning xie +Nishant Totla +Noah Treuhaft +Novak Ivanovski +Nuutti Kotivuori +Nycholas de Oliveira e Oliveira +Oilbeater +Oleg Bulatov +olegburov +Olivier +Olivier Gambier +Olivier Jacques +ollypom +Omer Cohen +Oscar Caballero +Owen W. Taylor +paigehargrave +Parth Mehrotra +Pascal Borreli +Patrick Devine +Patrick Easters +Paul Cacheux +Pavel Antonov +Paweł Gronowski +Per Lundberg +Peter Choi +Peter Dave Hello +Peter Kokot +Phil Estes +Philip Misiowiec +Pierre-Yves Ritschard +Pieter Scheffers +Qiang Huang +Qiao Anran +Radon Rosborough +Randy Barlow +Raphaël Enrici +Ricardo Maraschini +Richard Scothern +Rick Wieman +Rik Nijessen +Riyaz Faizullabhoy +Rober Morales-Chaparro +Robert Kaussow +Robert Steward +Roberto G. Hashioka +Rodolfo Carvalho +ROY +Rui Cao +ruicao +Rusty Conover +Ryan Abrams +Ryan Thomas +sakeven +Sam Alba +Samuel Karp +sangluo +Santiago Torres +Sargun Dhillon +sayboras +Sean Boran +Sean P. Kane +Sebastiaan van Stijn +Sebastien Coavoux +Serge Dubrouski +Sevki Hasirci +Sharif Nassar +Shawn Chen +Shawn Falkner-Horine +Shawnpku +Shengjing Zhu +Shiela M Parker +Shishir Mahajan +Shreyas Karnik +Silvin Lubecki <31478878+silvin-lubecki@users.noreply.github.com> +Simon +Simon Thulbourn +Simone Locci +Smasherr +Solomon Hykes +Sora Morimoto +spacexnice +Spencer Rinehart +srajmane <31947381+srajmane@users.noreply.github.com> +Srini Brahmaroutu +Stan Hu +Stefan Lörwald <10850250+stefanloerwald@users.noreply.github.com> +Stefan Majewsky +Stefan Nica +Stefan Weil +Stephen Day +Steve Lasker +Steven Hanna +Steven Kalt +Steven Taylor +stonezdj +sun jian +Sungho Moon +Sven Dowideit +Sylvain Baubeau +syntaxkim <40621244+syntaxkim@users.noreply.github.com> +T N +t-eimizu +Tariq Ibrahim +TaylorKanper +Ted Reed +Terin Stock +tgic +Thomas Berger +Thomas Sjögren +Tianon Gravi +Tibor Vass +tifayuki +Tiger Kaovilai +Tobias Fuhrimann +Tobias Schwab +Tom Hayward +Tom Hu +Tonis Tiigi +Tony Holdstock-Brown +Tosone +Trapier Marshall +Trevor Pounds +Trevor Wood +Troels Thomsen +uhayate +Usha Mandya <47779042+usha-mandya@users.noreply.github.com> +Usha Mandya +Vaidas Jablonskis +Vega Chou +Veres Lajos +Victor Vieux +Victoria Bialas +Vidar +Viktor Stanchev +Vincent Batts +Vincent Demeester +Vincent Giersch +Vishesh Jindal +W. Trevor King +Wang Jie +Wang Yan +Wassim Dhif +wayne +Wei Fu +Wei Meng +weiyuan.yl +Wen-Quan Li +Wenkai Yin +william wei <1342247033@qq.com> +xg.song +xiekeyang +Xueshan Feng +Yann ROBERT +Yannick Fricke +yaoyao.xyy +yixi zhang +Yong Tang +Yong Wen Chua +Yongxin Li +Yu Wang +yuexiao-wang +YuJie <390282283@qq.com> +yuzou +Zhang Wei +zhipengzuo +zhouhaibing089 +zounengren +姜继忠 diff --git a/Dockerfile b/Dockerfile index 7c88b1ce7..822b04a4f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.21.5 -ARG ALPINE_VERSION=3.18 +ARG GO_VERSION=1.22.4 +ARG ALPINE_VERSION=3.20 ARG XX_VERSION=1.2.1 FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx @@ -16,7 +16,7 @@ FROM base AS version ARG PKG=github.com/distribution/distribution/v3 RUN --mount=target=. \ VERSION=$(git describe --match 'v[0-9]*' --dirty='.m' --always --tags) REVISION=$(git rev-parse HEAD)$(if ! git diff --no-ext-diff --quiet --exit-code; then echo .m; fi); \ - echo "-X ${PKG}/version.Version=${VERSION#v} -X ${PKG}/version.Revision=${REVISION} -X ${PKG}/version.Package=${PKG}" | tee /tmp/.ldflags; \ + echo "-X ${PKG}/version.version=${VERSION#v} -X ${PKG}/version.revision=${REVISION} -X ${PKG}/version.mainpkg=${PKG}" | tee /tmp/.ldflags; \ echo -n "${VERSION}" | tee /tmp/.version; FROM base AS build @@ -52,9 +52,9 @@ COPY --from=releaser /out / FROM alpine:${ALPINE_VERSION} RUN apk add --no-cache ca-certificates -COPY cmd/registry/config-dev.yml /etc/docker/registry/config.yml +COPY cmd/registry/config-dev.yml /etc/distribution/config.yml COPY --from=binary /registry /bin/registry VOLUME ["/var/lib/registry"] EXPOSE 5000 ENTRYPOINT ["registry"] -CMD ["serve", "/etc/docker/registry/config.yml"] +CMD ["serve", "/etc/distribution/config.yml"] diff --git a/Makefile b/Makefile index 2723a2aff..97873bc2c 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ WHALE = "+" TESTFLAGS_RACE= GOFILES=$(shell find . -type f -name '*.go') GO_TAGS=$(if $(BUILDTAGS),-tags "$(BUILDTAGS)",) -GO_LDFLAGS=-ldflags '-extldflags "-Wl,-z,now" -s -w -X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) -X $(PKG)/version.Package=$(PKG) $(EXTRA_LDFLAGS)' +GO_LDFLAGS=-ldflags '-extldflags "-Wl,-z,now" -s -w -X $(PKG)/version.version=$(VERSION) -X $(PKG)/version.revision=$(REVISION) -X $(PKG)/version.mainpkg=$(PKG) $(EXTRA_LDFLAGS)' BINARIES=$(addprefix bin/,$(COMMANDS)) @@ -45,7 +45,7 @@ BINARIES=$(addprefix bin/,$(COMMANDS)) TESTFLAGS ?= -v $(TESTFLAGS_RACE) TESTFLAGS_PARALLEL ?= 8 -.PHONY: all build binaries clean test test-race test-full integration test-coverage validate lint validate-git validate-vendor vendor mod-outdated image +.PHONY: all build binaries clean test test-race test-full integration test-coverage validate lint validate-git validate-vendor vendor mod-outdated image validate-authors authors .DEFAULT: all .PHONY: FORCE @@ -86,6 +86,9 @@ vendor: ## update vendor mod-outdated: ## check outdated dependencies docker buildx bake $@ +authors: ## generate authors + docker buildx bake $@ + ##@ Test test: ## run tests, except integration test with test.short @@ -172,6 +175,9 @@ validate-git: ## validate git validate-vendor: ## validate vendor docker buildx bake $@ +validate-authors: ## validate authors + docker buildx bake $@ + .PHONY: help help: @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z0-9_\/%-]+:.*?##/ { printf " \033[36m%-27s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) diff --git a/README.md b/README.md index 01563cde5..76f837a3a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

-[![Build Status](https://github.com/distribution/distribution/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/distribution/distribution/actions?query=workflow%3ACI) +[![Build Status](https://github.com/distribution/distribution/workflows/build/badge.svg?branch=main&event=push)](https://github.com/distribution/distribution/actions/workflows/build.yml?query=workflow%3Abuild) [![GoDoc](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/distribution/distribution) [![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE) [![codecov](https://codecov.io/gh/distribution/distribution/branch/main/graph/badge.svg)](https://codecov.io/gh/distribution/distribution) @@ -27,7 +27,7 @@ This repository contains the following components: |--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **registry** | An implementation of the [OCI Distribution Specification](https://github.com/opencontainers/distribution-spec). | | **libraries** | A rich set of libraries for interacting with distribution components. Please see [godoc](https://pkg.go.dev/github.com/distribution/distribution) for details. **Note**: The interfaces for these libraries are **unstable**. | -| **documentation** | Docker's full documentation set is available at [docs.docker.com](https://docs.docker.com). This repository [contains the subset](docs/) related just to the registry. | +| **documentation** | Full documentation is available at [https://distribution.github.io/distribution](https://distribution.github.io/distribution/). ### How does this integrate with Docker, containerd, and other OCI client? diff --git a/blobs.go b/blobs.go index eac8bc937..3f81f35cb 100644 --- a/blobs.go +++ b/blobs.go @@ -140,12 +140,6 @@ type BlobDescriptorServiceFactory interface { BlobAccessController(svc BlobDescriptorService) BlobDescriptorService } -// ReadSeekCloser is the primary reader type for blob data, combining -// io.ReadSeeker with io.Closer. -// -// Deprecated: use [io.ReadSeekCloser]. -type ReadSeekCloser = io.ReadSeekCloser - // BlobProvider describes operations for getting blob data. type BlobProvider interface { // Get returns the entire blob identified by digest along with the descriptor. diff --git a/cmd/registry/config-cache.yml b/cmd/registry/config-cache.yml index d648303d9..421d964f0 100644 --- a/cmd/registry/config-cache.yml +++ b/cmd/registry/config-cache.yml @@ -12,6 +12,8 @@ storage: maintenance: uploadpurging: enabled: false + tag: + concurrencylimit: 8 http: addr: :5000 secret: asecretforlocaldevelopment @@ -20,11 +22,10 @@ http: headers: X-Content-Type-Options: [nosniff] redis: - addr: localhost:6379 - pool: - maxidle: 16 - maxactive: 64 - idletimeout: 300s + addrs: [localhost:6379] + maxidleconns: 16 + poolsize: 64 + connmaxidletime: 300s dialtimeout: 10ms readtimeout: 10ms writetimeout: 10ms diff --git a/cmd/registry/config-dev.yml b/cmd/registry/config-dev.yml index 9bf36583e..68fd83c8f 100644 --- a/cmd/registry/config-dev.yml +++ b/cmd/registry/config-dev.yml @@ -14,6 +14,8 @@ storage: maintenance: uploadpurging: enabled: false + tag: + concurrencylimit: 8 http: addr: :5000 debug: diff --git a/cmd/registry/config-example.yml b/cmd/registry/config-example.yml index c760cd567..2cb402062 100644 --- a/cmd/registry/config-example.yml +++ b/cmd/registry/config-example.yml @@ -7,6 +7,8 @@ storage: blobdescriptor: inmemory filesystem: rootdirectory: /var/lib/registry + tag: + concurrencylimit: 8 http: addr: :5000 headers: diff --git a/cmd/registry/main.go b/cmd/registry/main.go index 28264e577..17c1c1624 100644 --- a/cmd/registry/main.go +++ b/cmd/registry/main.go @@ -15,6 +15,7 @@ import ( _ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" _ "github.com/distribution/distribution/v3/registry/storage/driver/middleware/cloudfront" _ "github.com/distribution/distribution/v3/registry/storage/driver/middleware/redirect" + _ "github.com/distribution/distribution/v3/registry/storage/driver/middleware/rewrite" _ "github.com/distribution/distribution/v3/registry/storage/driver/s3-aws" ) diff --git a/configuration/configuration.go b/configuration/configuration.go index e4d4311d5..724e39a11 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -8,6 +8,8 @@ import ( "reflect" "strings" "time" + + "github.com/redis/go-redis/v9" ) // Configuration is a versioned registry configuration, intended to be provided by a yaml file, and @@ -157,9 +159,15 @@ type Configuration struct { // HTTP2 configuration options HTTP2 struct { // Specifies whether the registry should disallow clients attempting - // to connect via http2. If set to true, only http/1.1 is supported. + // to connect via HTTP/2. If set to true, only HTTP/1.1 is supported. Disabled bool `yaml:"disabled,omitempty"` } `yaml:"http2,omitempty"` + + H2C struct { + // Enables H2C (HTTP/2 Cleartext). Enable to support HTTP/2 without needing to configure TLS + // Useful when deploying the registry behind a load balancer (e.g. Cloud Run) + Enabled bool `yaml:"enabled,omitempty"` + } `yaml:"h2c,omitempty"` } `yaml:"http,omitempty"` // Notifications specifies configuration about various endpoint to which @@ -175,25 +183,7 @@ type Configuration struct { Proxy Proxy `yaml:"proxy,omitempty"` // Validation configures validation options for the registry. - Validation struct { - // Enabled enables the other options in this section. This field is - // deprecated in favor of Disabled. - Enabled bool `yaml:"enabled,omitempty"` - // Disabled disables the other options in this section. - Disabled bool `yaml:"disabled,omitempty"` - // Manifests configures manifest validation. - Manifests struct { - // URLs configures validation for URLs in pushed manifests. - URLs struct { - // Allow specifies regular expressions (https://godoc.org/regexp/syntax) - // that URLs in pushed manifests must match. - Allow []string `yaml:"allow,omitempty"` - // Deny specifies regular expressions (https://godoc.org/regexp/syntax) - // that URLs in pushed manifests must not match. - Deny []string `yaml:"deny,omitempty"` - } `yaml:"urls,omitempty"` - } `yaml:"manifests,omitempty"` - } `yaml:"validation,omitempty"` + Validation Validation `yaml:"validation,omitempty"` // Policy configures registry policy options. Policy struct { @@ -271,44 +261,6 @@ type FileChecker struct { Threshold int `yaml:"threshold,omitempty"` } -// Redis configures the redis pool available to the registry webapp. -type Redis struct { - // Addr specifies the the redis instance available to the application. - Addr string `yaml:"addr,omitempty"` - - // Usernames can be used as a finer-grained permission control since the introduction of the redis 6.0. - Username string `yaml:"username,omitempty"` - - // Password string to use when making a connection. - Password string `yaml:"password,omitempty"` - - // DB specifies the database to connect to on the redis instance. - DB int `yaml:"db,omitempty"` - - // TLS configures settings for redis in-transit encryption - TLS struct { - Enabled bool `yaml:"enabled,omitempty"` - } `yaml:"tls,omitempty"` - - DialTimeout time.Duration `yaml:"dialtimeout,omitempty"` // timeout for connect - ReadTimeout time.Duration `yaml:"readtimeout,omitempty"` // timeout for reads of data - WriteTimeout time.Duration `yaml:"writetimeout,omitempty"` // timeout for writes of data - - // Pool configures the behavior of the redis connection pool. - Pool struct { - // MaxIdle sets the maximum number of idle connections. - MaxIdle int `yaml:"maxidle,omitempty"` - - // MaxActive sets the maximum number of connections that should be - // opened before blocking a connection request. - MaxActive int `yaml:"maxactive,omitempty"` - - // IdleTimeout sets the amount time to wait before closing - // inactive connections. - IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` - } `yaml:"pool,omitempty"` -} - // HTTPChecker is a type of entry in the health section for checking HTTP URIs. type HTTPChecker struct { // Timeout is the duration to wait before timing out the HTTP request @@ -360,6 +312,13 @@ type Health struct { } `yaml:"storagedriver,omitempty"` } +type Platform struct { + // Architecture is the architecture for this platform + Architecture string `yaml:"architecture,omitempty"` + // OS is the operating system for this platform + OS string `yaml:"os,omitempty"` +} + // v0_1Configuration is a Version 0.1 Configuration struct // This is currently aliased to Configuration, as it is the current version type v0_1Configuration Configuration @@ -435,6 +394,8 @@ func (storage Storage) Type() string { // allow configuration of delete case "redirect": // allow configuration of redirect + case "tag": + // allow configuration of tag default: storageType = append(storageType, k) } @@ -448,6 +409,19 @@ func (storage Storage) Type() string { return "" } +// TagParameters returns the Parameters map for a Storage tag configuration +func (storage Storage) TagParameters() Parameters { + return storage["tag"] +} + +// setTagParameter changes the parameter at the provided key to the new value +func (storage Storage) setTagParameter(key string, value interface{}) { + if _, ok := storage["tag"]; !ok { + storage["tag"] = make(Parameters) + } + storage["tag"][key] = value +} + // Parameters returns the Parameters map for a Storage configuration func (storage Storage) Parameters() Parameters { return storage[storage.Type()] @@ -476,6 +450,8 @@ func (storage *Storage) UnmarshalYAML(unmarshal func(interface{}) error) error { // allow configuration of delete case "redirect": // allow configuration of redirect + case "tag": + // allow configuration of tag default: types = append(types, k) } @@ -630,6 +606,62 @@ type Proxy struct { TTL *time.Duration `yaml:"ttl,omitempty"` } +type Validation struct { + // Enabled enables the other options in this section. This field is + // deprecated in favor of Disabled. + Enabled bool `yaml:"enabled,omitempty"` + // Disabled disables the other options in this section. + Disabled bool `yaml:"disabled,omitempty"` + // Manifests configures manifest validation. + Manifests ValidationManifests `yaml:"manifests,omitempty"` +} + +type ValidationManifests struct { + // URLs configures validation for URLs in pushed manifests. + URLs struct { + // Allow specifies regular expressions (https://godoc.org/regexp/syntax) + // that URLs in pushed manifests must match. + Allow []string `yaml:"allow,omitempty"` + // Deny specifies regular expressions (https://godoc.org/regexp/syntax) + // that URLs in pushed manifests must not match. + Deny []string `yaml:"deny,omitempty"` + } `yaml:"urls,omitempty"` + // ImageIndexes configures validation of image indexes + Indexes ValidationIndexes `yaml:"indexes,omitempty"` +} + +type ValidationIndexes struct { + // Platforms configures the validation applies to the platform images included in an image index + Platforms Platforms `yaml:"platforms"` + // PlatformList filters the set of platforms to validate for image existence. + PlatformList []Platform `yaml:"platformlist,omitempty"` +} + +// Platforms configures the validation applies to the platform images included in an image index +// This can be all, none, or list +type Platforms string + +// UnmarshalYAML implements the yaml.Umarshaler interface +// Unmarshals a string into a Platforms option, lowercasing the string and validating that it represents a +// valid option +func (platforms *Platforms) UnmarshalYAML(unmarshal func(interface{}) error) error { + var platformsString string + err := unmarshal(&platformsString) + if err != nil { + return err + } + + platformsString = strings.ToLower(platformsString) + switch platformsString { + case "all", "none", "list": + default: + return fmt.Errorf("invalid platforms option %s Must be one of [all, none, list]", platformsString) + } + + *platforms = Platforms(platformsString) + return nil +} + // Parse parses an input configuration yaml document into a Configuration struct // This should generally be capable of handling old configuration format versions // @@ -682,3 +714,172 @@ func Parse(rd io.Reader) (*Configuration, error) { return config, nil } + +type RedisOptions = redis.UniversalOptions + +type RedisTLSOptions struct { + Certificate string `yaml:"certificate,omitempty"` + Key string `yaml:"key,omitempty"` + ClientCAs []string `yaml:"clientcas,omitempty"` +} + +type Redis struct { + Options RedisOptions `yaml:",inline"` + TLS RedisTLSOptions `yaml:"tls,omitempty"` +} + +func (c Redis) MarshalYAML() (interface{}, error) { + fields := make(map[string]interface{}) + + val := reflect.ValueOf(c.Options) + typ := val.Type() + + for i := 0; i < val.NumField(); i++ { + field := typ.Field(i) + fieldValue := val.Field(i) + + // ignore funcs fields in redis.UniversalOptions + if fieldValue.Kind() == reflect.Func { + continue + } + + fields[strings.ToLower(field.Name)] = fieldValue.Interface() + } + + // Add TLS fields if they're not empty + if c.TLS.Certificate != "" || c.TLS.Key != "" || len(c.TLS.ClientCAs) > 0 { + fields["tls"] = c.TLS + } + + return fields, nil +} + +func (c *Redis) UnmarshalYAML(unmarshal func(interface{}) error) error { + var fields map[string]interface{} + err := unmarshal(&fields) + if err != nil { + return err + } + + val := reflect.ValueOf(&c.Options).Elem() + typ := val.Type() + + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + fieldName := strings.ToLower(field.Name) + + if value, ok := fields[fieldName]; ok { + fieldValue := val.Field(i) + if fieldValue.CanSet() { + switch field.Type { + case reflect.TypeOf(time.Duration(0)): + durationStr, ok := value.(string) + if !ok { + return fmt.Errorf("invalid duration value for field: %s", fieldName) + } + duration, err := time.ParseDuration(durationStr) + if err != nil { + return fmt.Errorf("failed to parse duration for field: %s, error: %v", fieldName, err) + } + fieldValue.Set(reflect.ValueOf(duration)) + default: + if err := setFieldValue(fieldValue, value); err != nil { + return fmt.Errorf("failed to set value for field: %s, error: %v", fieldName, err) + } + } + } + } + } + + // Handle TLS fields + if tlsData, ok := fields["tls"]; ok { + tlsMap, ok := tlsData.(map[interface{}]interface{}) + if !ok { + return fmt.Errorf("invalid TLS data structure") + } + + if cert, ok := tlsMap["certificate"]; ok { + var isString bool + c.TLS.Certificate, isString = cert.(string) + if !isString { + return fmt.Errorf("Redis TLS certificate must be a string") + } + } + if key, ok := tlsMap["key"]; ok { + var isString bool + c.TLS.Key, isString = key.(string) + if !isString { + return fmt.Errorf("Redis TLS (private) key must be a string") + } + } + if cas, ok := tlsMap["clientcas"]; ok { + caList, ok := cas.([]interface{}) + if !ok { + return fmt.Errorf("invalid clientcas data structure") + } + for _, ca := range caList { + if caStr, ok := ca.(string); ok { + c.TLS.ClientCAs = append(c.TLS.ClientCAs, caStr) + } + } + } + } + + return nil +} + +func setFieldValue(field reflect.Value, value interface{}) error { + if value == nil { + return nil + } + + switch field.Kind() { + case reflect.String: + stringValue, ok := value.(string) + if !ok { + return fmt.Errorf("failed to convert value to string") + } + field.SetString(stringValue) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + intValue, ok := value.(int) + if !ok { + return fmt.Errorf("failed to convert value to integer") + } + field.SetInt(int64(intValue)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + uintValue, ok := value.(uint) + if !ok { + return fmt.Errorf("failed to convert value to unsigned integer") + } + field.SetUint(uint64(uintValue)) + case reflect.Float32, reflect.Float64: + floatValue, ok := value.(float64) + if !ok { + return fmt.Errorf("failed to convert value to float") + } + field.SetFloat(floatValue) + case reflect.Bool: + boolValue, ok := value.(bool) + if !ok { + return fmt.Errorf("failed to convert value to boolean") + } + field.SetBool(boolValue) + case reflect.Slice: + slice := reflect.MakeSlice(field.Type(), 0, 0) + valueSlice, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("failed to convert value to slice") + } + for _, item := range valueSlice { + sliceValue := reflect.New(field.Type().Elem()).Elem() + if err := setFieldValue(sliceValue, item); err != nil { + return err + } + slice = reflect.Append(slice, sliceValue) + } + field.Set(slice) + default: + return fmt.Errorf("unsupported field type: %v", field.Type()) + } + return nil +} diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go index 7bce8dfed..d55c05fca 100644 --- a/configuration/configuration_test.go +++ b/configuration/configuration_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/redis/go-redis/v9" "github.com/stretchr/testify/suite" "gopkg.in/yaml.v2" ) @@ -39,6 +40,9 @@ var configStruct = Configuration{ "url1": "https://foo.example.com", "path1": "/some-path", }, + "tag": Parameters{ + "concurrencylimit": 10, + }, }, Auth: Auth{ "silly": Parameters{ @@ -97,6 +101,9 @@ var configStruct = Configuration{ HTTP2 struct { Disabled bool `yaml:"disabled,omitempty"` } `yaml:"http2,omitempty"` + H2C struct { + Enabled bool `yaml:"enabled,omitempty"` + } `yaml:"h2c,omitempty"` }{ TLS: struct { Certificate string `yaml:"certificate,omitempty"` @@ -121,24 +128,37 @@ var configStruct = Configuration{ }{ Disabled: false, }, + H2C: struct { + Enabled bool `yaml:"enabled,omitempty"` + }{ + Enabled: true, + }, }, Redis: Redis{ - Addr: "localhost:6379", - Username: "alice", - Password: "123456", - DB: 1, - Pool: struct { - MaxIdle int `yaml:"maxidle,omitempty"` - MaxActive int `yaml:"maxactive,omitempty"` - IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` - }{ - MaxIdle: 16, - MaxActive: 64, - IdleTimeout: time.Second * 300, + Options: redis.UniversalOptions{ + Addrs: []string{"localhost:6379"}, + Username: "alice", + Password: "123456", + DB: 1, + MaxIdleConns: 16, + PoolSize: 64, + ConnMaxIdleTime: time.Second * 300, + DialTimeout: time.Millisecond * 10, + ReadTimeout: time.Millisecond * 10, + WriteTimeout: time.Millisecond * 10, + }, + TLS: RedisTLSOptions{ + Certificate: "/foo/cert.crt", + Key: "/foo/key.pem", + ClientCAs: []string{"/path/to/ca.pem"}, + }, + }, + Validation: Validation{ + Manifests: ValidationManifests{ + Indexes: ValidationIndexes{ + Platforms: "none", + }, }, - DialTimeout: time.Millisecond * 10, - ReadTimeout: time.Millisecond * 10, - WriteTimeout: time.Millisecond * 10, }, } @@ -159,6 +179,8 @@ storage: int1: 42 url1: "https://foo.example.com" path1: "/some-path" + tag: + concurrencylimit: 10 auth: silly: realm: silly @@ -177,22 +199,31 @@ notifications: actions: - pull http: - clientcas: - - /path/to/ca.pem + tls: + clientcas: + - /path/to/ca.pem headers: X-Content-Type-Options: [nosniff] redis: - addr: localhost:6379 + tls: + certificate: /foo/cert.crt + key: /foo/key.pem + clientcas: + - /path/to/ca.pem + addrs: [localhost:6379] username: alice - password: 123456 + password: "123456" db: 1 - pool: - maxidle: 16 - maxactive: 64 - idletimeout: 300s + maxidleconns: 16 + poolsize: 64 + connmaxidletime: 300s dialtimeout: 10ms readtimeout: 10ms writetimeout: 10ms +validation: + manifests: + indexes: + platforms: none ` // inmemoryConfigYamlV0_1 is a Version 0.1 yaml document specifying an inmemory @@ -222,6 +253,10 @@ notifications: http: headers: X-Content-Type-Options: [nosniff] +validation: + manifests: + indexes: + platforms: none ` type ConfigSuite struct { @@ -261,6 +296,7 @@ func (suite *ConfigSuite) TestParseSimple() { func (suite *ConfigSuite) TestParseInmemory() { suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}} suite.expectedConfig.Log.Fields = nil + suite.expectedConfig.HTTP.TLS.ClientCAs = nil suite.expectedConfig.Redis = Redis{} config, err := Parse(bytes.NewReader([]byte(inmemoryConfigYamlV0_1))) @@ -281,7 +317,9 @@ func (suite *ConfigSuite) TestParseIncomplete() { suite.expectedConfig.Auth = Auth{"silly": Parameters{"realm": "silly"}} suite.expectedConfig.Notifications = Notifications{} suite.expectedConfig.HTTP.Headers = nil + suite.expectedConfig.HTTP.TLS.ClientCAs = nil suite.expectedConfig.Redis = Redis{} + suite.expectedConfig.Validation.Manifests.Indexes.Platforms = "" // Note: this also tests that REGISTRY_STORAGE and // REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY can be used together @@ -534,6 +572,9 @@ func copyConfig(config Configuration) *Configuration { for k, v := range config.Storage.Parameters() { configCopy.Storage.setParameter(k, v) } + for k, v := range config.Storage.TagParameters() { + configCopy.Storage.setTagParameter(k, v) + } configCopy.Auth = Auth{config.Auth.Type(): Parameters{}} for k, v := range config.Auth.Parameters() { @@ -547,8 +588,20 @@ func copyConfig(config Configuration) *Configuration { for k, v := range config.HTTP.Headers { configCopy.HTTP.Headers[k] = v } + configCopy.HTTP.TLS.ClientCAs = make([]string, 0, len(config.HTTP.TLS.ClientCAs)) + configCopy.HTTP.TLS.ClientCAs = append(configCopy.HTTP.TLS.ClientCAs, config.HTTP.TLS.ClientCAs...) configCopy.Redis = config.Redis + configCopy.Redis.TLS.Certificate = config.Redis.TLS.Certificate + configCopy.Redis.TLS.Key = config.Redis.TLS.Key + configCopy.Redis.TLS.ClientCAs = make([]string, 0, len(config.Redis.TLS.ClientCAs)) + configCopy.Redis.TLS.ClientCAs = append(configCopy.Redis.TLS.ClientCAs, config.Redis.TLS.ClientCAs...) + + configCopy.Validation = Validation{ + Enabled: config.Validation.Enabled, + Disabled: config.Validation.Disabled, + Manifests: config.Validation.Manifests, + } return configCopy } diff --git a/docker-bake.hcl b/docker-bake.hcl index 0587572a4..9512b17f7 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -39,11 +39,7 @@ target "update-vendor" { target "mod-outdated" { dockerfile = "./dockerfiles/vendor.Dockerfile" target = "outdated" - args = { - // used to invalidate cache for outdated run stage - // can be dropped when https://github.com/moby/buildkit/issues/1213 fixed - _RANDOM = uuidv4() - } + no-cache-filter = ["outdated"] output = ["type=cacheonly"] } @@ -95,15 +91,8 @@ target "image-all" { ] } -variable "DOCS_BASEURL" { - default = null -} - target "_common_docs" { dockerfile = "./dockerfiles/docs.Dockerfile" - args = { - DOCS_BASEURL = DOCS_BASEURL - } } target "docs-export" { @@ -124,3 +113,15 @@ target "docs-test" { target = "test" output = ["type=cacheonly"] } + +target "authors" { + dockerfile = "./dockerfiles/authors.Dockerfile" + target = "update" + output = ["."] +} + +target "validate-authors" { + dockerfile = "./dockerfiles/authors.Dockerfile" + target = "validate" + output = ["type=cacheonly"] +} diff --git a/dockerfiles/authors.Dockerfile b/dockerfiles/authors.Dockerfile new file mode 100644 index 000000000..908898c07 --- /dev/null +++ b/dockerfiles/authors.Dockerfile @@ -0,0 +1,34 @@ +# syntax=docker/dockerfile:1 + +ARG ALPINE_VERSION=3.20 + +FROM alpine:${ALPINE_VERSION} AS gen +RUN apk add --no-cache git +WORKDIR /src +RUN --mount=type=bind,target=. < /out/AUTHORS + cat /out/AUTHORS +EOT + +FROM scratch AS update +COPY --from=gen /out / + +FROM gen AS validate +RUN --mount=type=bind,target=.,rw <&2 'ERROR: Authors result differs. Please update with "make authors"' + git status --porcelain -- AUTHORS + exit 1 + fi +EOT diff --git a/dockerfiles/docs.Dockerfile b/dockerfiles/docs.Dockerfile index f54afdc7f..f75269b40 100644 --- a/dockerfiles/docs.Dockerfile +++ b/dockerfiles/docs.Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.21.5 -ARG ALPINE_VERSION=3.18 +ARG GO_VERSION=1.22.4 +ARG ALPINE_VERSION=3.20 FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base RUN apk add --no-cache git @@ -16,9 +16,8 @@ COPY --from=hugo $GOPATH/bin/hugo /bin/hugo WORKDIR /src FROM build-base AS build -ARG DOCS_BASEURL=/ RUN --mount=type=bind,rw,source=docs,target=. \ - hugo --gc --minify --destination /out -b $DOCS_BASEURL + hugo --gc --minify --destination /out FROM build-base AS server COPY docs . @@ -29,8 +28,12 @@ FROM scratch AS out COPY --from=build /out / FROM wjdp/htmltest:v0.17.0 AS test +# Copy the site to a public/distribution subdirectory +# This is a workaround for a limitation in htmltest, see: +# https://github.com/wjdp/htmltest/issues/45 +WORKDIR /test/public/distribution +COPY --from=build /out . WORKDIR /test -COPY --from=build /out ./public ADD docs/.htmltest.yml .htmltest.yml RUN --mount=type=cache,target=tmp/.htmltest \ htmltest diff --git a/dockerfiles/git.Dockerfile b/dockerfiles/git.Dockerfile index 499c02a5b..bc72f269a 100644 --- a/dockerfiles/git.Dockerfile +++ b/dockerfiles/git.Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.20.12 -ARG ALPINE_VERSION=3.18 +ARG GO_VERSION=1.22.4 +ARG ALPINE_VERSION=3.20 FROM alpine:${ALPINE_VERSION} AS base RUN apk add --no-cache git gpg diff --git a/dockerfiles/lint.Dockerfile b/dockerfiles/lint.Dockerfile index b059e2978..f55ec60e2 100644 --- a/dockerfiles/lint.Dockerfile +++ b/dockerfiles/lint.Dockerfile @@ -1,8 +1,8 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.20.12 -ARG ALPINE_VERSION=3.18 -ARG GOLANGCI_LINT_VERSION=v1.55.2 +ARG GO_VERSION=1.22.4 +ARG ALPINE_VERSION=3.20 +ARG GOLANGCI_LINT_VERSION=v1.59.1 ARG BUILDTAGS="" FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint diff --git a/dockerfiles/vendor.Dockerfile b/dockerfiles/vendor.Dockerfile index c325839a9..ab1062491 100644 --- a/dockerfiles/vendor.Dockerfile +++ b/dockerfiles/vendor.Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.20.12 -ARG ALPINE_VERSION=3.18 +ARG GO_VERSION=1.22.4 +ARG ALPINE_VERSION=3.20 ARG MODOUTDATED_VERSION=v0.8.0 FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base @@ -40,7 +40,6 @@ EOT FROM psampaz/go-mod-outdated:${MODOUTDATED_VERSION} AS go-mod-outdated FROM base AS outdated -ARG _RANDOM RUN --mount=target=.,ro \ --mount=target=/go/pkg/mod,type=cache \ --mount=from=go-mod-outdated,source=/home/go-mod-outdated,target=/usr/bin/go-mod-outdated \ diff --git a/docs/content/about/configuration.md b/docs/content/about/configuration.md index 4107c0b3a..88c4e8589 100644 --- a/docs/content/about/configuration.md +++ b/docs/content/about/configuration.md @@ -50,7 +50,7 @@ specify it in the `docker run` command: ```bash $ docker run -d -p 5000:5000 --restart=always --name registry \ - -v `pwd`/config.yml:/etc/docker/registry/config.yml \ + -v `pwd`/config.yml:/etc/distribution/config.yml \ registry:2 ``` @@ -141,6 +141,8 @@ storage: usedualstack: false loglevel: debug inmemory: # This driver takes no parameters + tag: + concurrencylimit: 8 delete: enabled: false redirect: @@ -166,6 +168,10 @@ auth: service: token-service issuer: registry-token-issuer rootcertbundle: /root/certs/bundle + jwks: /path/to/jwks + signingalgorithms: + - EdDSA + - HS256 htpasswd: realm: basic-realm path: /path/to/htpasswd @@ -220,6 +226,8 @@ http: X-Content-Type-Options: [nosniff] http2: disabled: false + h2c: + enabled: false notifications: events: includereferences: true @@ -239,16 +247,20 @@ notifications: actions: - pull redis: - addr: localhost:6379 + tls: + certificate: /path/to/cert.crt + key: /path/to/key.pem + clientcas: + - /path/to/ca.pem + addrs: [localhost:6379] password: asecret db: 0 dialtimeout: 10ms readtimeout: 10ms writetimeout: 10ms - pool: - maxidle: 16 - maxactive: 64 - idletimeout: 300s + maxidleconns: 16 + poolsize: 64 + connmaxidletime: 300s tls: enabled: false health: @@ -284,6 +296,11 @@ validation: - ^https?://([^/]+\.)*example\.com/ deny: - ^https?://www\.example\.com/ + indexes: + platforms: List + platformlist: + - architecture: amd64 + os: linux ``` In some instances a configuration option is **optional** but it contains child @@ -434,17 +451,17 @@ The `storage` option is **required** and defines which storage backend is in use. You must configure exactly one backend. If you configure more, the registry returns an error. You can choose any of these backend storage drivers: -| Storage driver | Description | -|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `filesystem` | Uses the local disk to store registry files. It is ideal for development and may be appropriate for some small-scale production applications. See the [driver's reference documentation](/storage-drivers/filesystem). | -| `azure` | Uses Microsoft Azure Blob Storage. See the [driver's reference documentation](/storage-drivers/azure). | -| `gcs` | Uses Google Cloud Storage. See the [driver's reference documentation](/storage-drivers/gcs). | -| `s3` | Uses Amazon Simple Storage Service (S3) and compatible Storage Services. See the [driver's reference documentation](/storage-drivers/s3). | +| Storage driver | Description | +| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `filesystem` | Uses the local disk to store registry files. It is ideal for development and may be appropriate for some small-scale production applications. See the [driver's reference documentation](../storage-drivers/filesystem.md). | +| `azure` | Uses Microsoft Azure Blob Storage. See the [driver's reference documentation](../storage-drivers/azure.md). | +| `gcs` | Uses Google Cloud Storage. See the [driver's reference documentation](../storage-drivers/gcs.md). | +| `s3` | Uses Amazon Simple Storage Service (S3) and compatible Storage Services. See the [driver's reference documentation](../storage-drivers/s3.md). | For testing only, you can use the [`inmemory` storage -driver](/storage-drivers/inmemory). +driver](../storage-drivers/inmemory.md). If you would like to run a registry from volatile memory, use the -[`filesystem` driver](/storage-drivers/filesystem) +[`filesystem` driver](../storage-drivers/filesystem.md) on a ramdisk. If you are deploying a registry on Windows, a Windows volume mounted from the @@ -519,6 +536,26 @@ parameter sets a limit on the number of descriptors to store in the cache. The default value is 10000. If this parameter is set to 0, the cache is allowed to grow with no size limit. +### `tag` + +The `tag` subsection provides configuration to set concurrency limit for tag lookup. +When user calls into the registry to delete the manifest, which in turn then does a +lookup for all tags that reference the deleted manifest. To find the tag references, +the registry will iterate every tag in the repository and read it's link file to check +if it matches the deleted manifest (i.e. to see if uses the same sha256 digest). +So, the more tags in repository, the worse the performance will be (as there will +be more S3 API calls occurring for the tag directory lookups and tag file reads if +using S3 storage driver). + +Therefore, add a single flag `concurrencylimit` to set concurrency limit to optimize tag +lookup performance under the `tag` section. When a value is not provided or equal to 0, +`GOMAXPROCS` will be used. + +```yaml +tag: + concurrencylimit: 8 +``` + ### `redirect` The `redirect` subsection provides configuration for managing redirects from @@ -548,6 +585,11 @@ auth: service: token-service issuer: registry-token-issuer rootcertbundle: /root/certs/bundle + jwks: /path/to/jwks + signingalgorithms: + - EdDSA + - HS256 + - ES512 htpasswd: realm: basic-realm path: /path/to/htpasswd @@ -583,17 +625,49 @@ Token-based authentication allows you to decouple the authentication system from the registry. It is an established authentication paradigm with a high degree of security. -| Parameter | Required | Description | -|-----------|----------|-------------------------------------------------------| -| `realm` | yes | The realm in which the registry server authenticates. | -| `service` | yes | The service being authenticated. | -| `issuer` | yes | The name of the token issuer. The issuer inserts this into the token so it must match the value configured for the issuer. | -| `rootcertbundle` | yes | The absolute path to the root certificate bundle. This bundle contains the public part of the certificates used to sign authentication tokens. | -| `autoredirect` | no | When set to `true`, `realm` will automatically be set using the Host header of the request as the domain and a path of `/auth/token/`| +| Parameter | Required | Description | +|----------------------|----------|-------------------------------------------------------| +| `realm` | yes | The realm in which the registry server authenticates. | +| `service` | yes | The service being authenticated. | +| `issuer` | yes | The name of the token issuer. The issuer inserts this into the token so it must match the value configured for the issuer. | +| `rootcertbundle` | yes | The absolute path to the root certificate bundle. This bundle contains the public part of the certificates used to sign authentication tokens. | +| `autoredirect` | no | When set to `true`, `realm` will be set to the Host header of the request as the domain and a path of `/auth/token/`(or specified by `autoredirectpath`), the `realm` URL Scheme will use `X-Forwarded-Proto` header if set, otherwise it will be set to `https`. | +| `autoredirectpath` | no | The path to redirect to if `autoredirect` is set to `true`, default: `/auth/token/`. | +| `signingalgorithms` | no | A list of token signing algorithms to use for verifying token signatures. If left empty the default list of signing algorithms is used. Please see below for allowed values and default. | +| `jwks` | no | The absolute path to the JSON Web Key Set (JWKS) file. The JWKS file contains the trusted keys used to verify the signature of authentication tokens. | +Available `signingalgorithms`: +- EdDSA +- HS256 +- HS384 +- HS512 +- RS256 +- RS384 +- RS512 +- ES256 +- ES384 +- ES512 +- PS256 +- PS384 +- PS512 + +Default `signingalgorithms`: +- EdDSA +- HS256 +- HS384 +- HS512 +- RS256 +- RS384 +- RS512 +- ES256 +- ES384 +- ES512 +- PS256 +- PS384 +- PS512 For more information about Token based authentication configuration, see the -[specification](/spec/auth/token). +[specification](../spec/auth/token.md). ### `htpasswd` @@ -724,6 +798,8 @@ http: X-Content-Type-Options: [nosniff] http2: disabled: false + h2c: + enabled: false ``` The `http` option details the configuration for the HTTP server that hosts the @@ -870,13 +946,24 @@ registry. This header is included in the example configuration file. ### `http2` -The `http2` structure within `http` is **optional**. Use this to control http2 +The `http2` structure within `http` is **optional**. Use this to control HTTP/2 over TLS settings for the registry. +If `tls` is not configured this option is ignored. To enable HTTP/2 over non TLS connections use `h2c` instead. | Parameter | Required | Description | |-----------|----------|-------------------------------------------------------| | `disabled` | no | If `true`, then `http2` support is disabled. | +### `h2c` + +The `h2c` structure within `http` is **optional**. Use this to control H2C (HTTP/2 Cleartext) +settings for the registry. +Useful when deploying the registry behind a load balancer (e.g. Google Cloud Run) + +| Parameter | Required | Description | +|-----------|----------|-------------------------------------------------------| +| `enabled` | no | If `true`, then `h2c` support is enabled. | + ## `notifications` ```yaml @@ -937,72 +1024,46 @@ The `events` structure configures the information provided in event notification ## `redis` +Declare parameters for constructing the `redis` connections. Registry instances +may use the Redis instance for several applications. Currently, it caches +information about immutable blobs. Most of the `redis` options control +how the registry connects to the `redis` instance. + +You should configure Redis with the **allkeys-lru** eviction policy, because the +registry does not set an expiration value on keys. + +Under the hood distribution uses [`go-redis`](https://github.com/redis/go-redis) Go module for +Redis connectivity and its [`UniversalOptions`](https://pkg.go.dev/github.com/redis/go-redis/v9#UniversalOptions) +struct. + +You can optionally specify TLS configuration on top of the `UniversalOptions` settings. + +Use these settings to configure Redis TLS: + +| Parameter | Required | Description | +|-----------|----------|-------------------------------------------------------| +| `certificate` | yes | Absolute path to the x509 certificate file. | +| `key` | yes | Absolute path to the x509 private key file. | +| `clientcas` | no | An array of absolute paths to x509 CA files. | + ```yaml redis: - addr: localhost:6379 + tls: + certificate: /path/to/cert.crt + key: /path/to/key.pem + clientcas: + - /path/to/ca.pem + addrs: [localhost:6379] password: asecret db: 0 dialtimeout: 10ms readtimeout: 10ms writetimeout: 10ms - pool: - maxidle: 16 - maxactive: 64 - idletimeout: 300s - tls: - enabled: false + maxidleconns: 16 + poolsize: 64 + connmaxidletime: 300s ``` -Declare parameters for constructing the `redis` connections. Registry instances -may use the Redis instance for several applications. Currently, it caches -information about immutable blobs. Most of the `redis` options control -how the registry connects to the `redis` instance. You can control the pool's -behavior with the [pool](#pool) subsection. Additionally, you can control -TLS connection settings with the [tls](#tls) subsection (in-transit encryption). - -You should configure Redis with the **allkeys-lru** eviction policy, because the -registry does not set an expiration value on keys. - -| Parameter | Required | Description | -|-----------|----------|-------------------------------------------------------| -| `addr` | yes | The address (host and port) of the Redis instance. | -| `password`| no | A password used to authenticate to the Redis instance.| -| `db` | no | The name of the database to use for each connection. | -| `dialtimeout` | no | The timeout for connecting to the Redis instance. | -| `readtimeout` | no | The timeout for reading from the Redis instance. | -| `writetimeout` | no | The timeout for writing to the Redis instance. | - -### `pool` - -```yaml -pool: - maxidle: 16 - maxactive: 64 - idletimeout: 300s -``` - -Use these settings to configure the behavior of the Redis connection pool. - -| Parameter | Required | Description | -|-----------|----------|-------------------------------------------------------| -| `maxidle` | no | The maximum number of idle connections in the pool. | -| `maxactive`| no | The maximum number of connections which can be open before blocking a connection request. | -| `idletimeout`| no | How long to wait before closing inactive connections. | - -### `tls` - -```yaml -tls: - enabled: false -``` - -Use these settings to configure Redis TLS. - -| Parameter | Required | Description | -|-----------|----------|-------------------------------------- | -| `enabled` | no | Whether or not to use TLS in-transit. | - - ## `health` ```yaml @@ -1100,7 +1161,7 @@ proxy: The `proxy` structure allows a registry to be configured as a pull-through cache to Docker Hub. See -[mirror](/recipes/mirror) +[mirror](../recipes/mirror.md) for more information. Pushing to a registry configured as a pull-through cache is unsupported. @@ -1122,14 +1183,14 @@ username (such as `batman`) and the password for that username. ```yaml validation: - manifests: - urls: - allow: - - ^https?://([^/]+\.)*example\.com/ - deny: - - ^https?://www\.example\.com/ + disabled: false ``` +Use these settings to configure what validation the registry performs on content. + +Validation is performed when content is uploaded to the registry. Changing these +settings will not validate content that has already been accepting into the registry. + ### `disabled` The `disabled` flag disables the other options in the `validation` @@ -1142,6 +1203,16 @@ Use the `manifests` subsection to configure validation of manifests. If #### `urls` +```yaml +validation: + manifests: + urls: + allow: + - ^https?://([^/]+\.)*example\.com/ + deny: + - ^https?://www\.example\.com/ +``` + The `allow` and `deny` options are each a list of [regular expressions](https://pkg.go.dev/regexp/syntax) that restrict the URLs in pushed manifests. @@ -1155,6 +1226,54 @@ one of the `allow` regular expressions **and** one of the following holds: 2. `deny` is set but no URLs within the manifest match any of the `deny` regular expressions. +#### `indexes` + +By default the registry will validate that all platform images exist when an image +index is uploaded to the registry. Disabling this validatation is experimental +because other tooling that uses the registry may expect the image index to be complete. + +validation: + manifests: + indexes: + platforms: [all|none|list] + platformlist: + - os: linux + architecture: amd64 + +Use these settings to configure what validation the registry performs on image +index manifests uploaded to the registry. + +##### `platforms` + +Set `platformexist` to `all` (the default) to validate all platform images exist. +The registry will validate that the images referenced by the index exist in the +registry before accepting the image index. + +Set `platforms` to `none` to disable all validation that images exist when an +image index manifest is uploaded. This allows image lists to be uploaded to the +registry without their associated images. This setting is experimental because +other tooling that uses the registry may expect the image index to be complete. + +Set `platforms` to `list` to selectively validate the existence of platforms +within image index manifests. This setting is experimental because other tooling +that uses the registry may expect the image index to be complete. + +##### `platformlist` + +When `platforms` is set to `list`, set `platformlist` to an array of +platforms to validate. If a platform is included in this the array and in the images +contained within an index, the registry will validate that the platform specific image +exists in the registry before accepting the index. The registry will not validate the +existence of platform specific images in the index that do not appear in the +`platformlist` array. + +This parameter does not validate that the configured platforms are included in every +index. If an image index does not include one of the platform specific images configured +in the `platformlist` array, it may still be accepted by the registry. + +Each platform is a map with two keys, `os` and `architecture`, as defined in the +[OCI Image Index specification](https://github.com/opencontainers/image-spec/blob/main/image-index.md#image-index-property-descriptions). + ## Example: Development configuration You can use this simple example for local development: diff --git a/docs/content/about/deploying.md b/docs/content/about/deploying.md index 1c022bc2e..bfa35955c 100644 --- a/docs/content/about/deploying.md +++ b/docs/content/about/deploying.md @@ -9,7 +9,7 @@ A registry is an instance of the `registry` image, and runs within Docker. This topic provides basic information about deploying and configuring a registry. For an exhaustive list of configuration options, see the -[configuration reference](../configuration). +[configuration reference](configuration.md). If you have an air-gapped datacenter, see [Considerations for air-gapped registries](#considerations-for-air-gapped-registries). @@ -27,7 +27,7 @@ The registry is now ready to use. > **Warning**: These first few examples show registry configurations that are > only appropriate for testing. A production-ready registry must be protected by > TLS and should ideally use an access-control mechanism. Keep reading and then -> continue to the [configuration guide](../configuration) to deploy a +> continue to the [configuration guide](configuration.md) to deploy a > production-ready registry. ## Copy an image from Docker Hub to your registry @@ -94,7 +94,7 @@ To configure the container, you can pass additional or modified options to the `docker run` command. The following sections provide basic guidelines for configuring your registry. -For more details, see the [registry configuration reference](../configuration). +For more details, see the [registry configuration reference](configuration.md). ### Start the registry automatically @@ -166,8 +166,8 @@ $ docker run -d \ By default, the registry stores its data on the local filesystem, whether you use a bind mount or a volume. You can store the registry data in an Amazon S3 bucket, Google Cloud Platform, or on another storage back-end by using -[storage drivers](/storage-drivers). For more information, see -[storage configuration options](../configuration#storage). +[storage drivers](../storage-drivers/_index.md). For more information, see +[storage configuration options](configuration.md#storage). ## Run an externally-accessible registry @@ -252,13 +252,13 @@ The registry supports using Let's Encrypt to automatically obtain a browser-trusted certificate. For more information on Let's Encrypt, see [https://letsencrypt.org/how-it-works/](https://letsencrypt.org/how-it-works/) and the relevant section of the -[registry configuration](../configuration#letsencrypt). +[registry configuration](configuration.md#letsencrypt). ### Use an insecure registry (testing only) It is possible to use a self-signed certificate, or to use our registry insecurely. Unless you have set up verification for your self-signed -certificate, this is for testing only. See [run an insecure registry](../insecure). +certificate, this is for testing only. See [run an insecure registry](insecure.md). ## Run the registry as a service @@ -462,20 +462,20 @@ using htpasswd, all authentication attempts will fail. {{< hint type=note title="X509 errors" >}} X509 errors usually indicate that you are attempting to use a self-signed certificate without configuring the Docker daemon correctly. -See [run an insecure registry](../insecure). +See [run an insecure registry](insecure.md). {{< /hint >}} ### More advanced authentication You may want to leverage more advanced basic auth implementations by using a -proxy in front of the registry. See the [recipes list](/recipes/). +proxy in front of the registry. See the [recipes list](../recipes/_index.md). The registry also supports delegated authentication which redirects users to a specific trusted token server. This approach is more complicated to set up, and only makes sense if you need to fully configure ACLs and need more control over the registry's integration into your global authorization and authentication -systems. Refer to the following [background information](/spec/auth/token) and -[configuration information here](../configuration#auth). +systems. Refer to the following [background information](../spec/auth/token.md) and +[configuration information here](configuration.md#auth). This approach requires you to implement your own authentication system or leverage a third-party implementation. @@ -572,9 +572,9 @@ artifacts. More specific and advanced information is available in the following sections: -- [Configuration reference](../configuration) -- [Working with notifications](../notifications) -- [Advanced "recipes"](/recipes) -- [Registry API](/spec/api) -- [Storage driver model](/storage-drivers) -- [Token authentication](/spec/auth/token) +- [Configuration reference](configuration.md) +- [Working with notifications](notifications.md) +- [Advanced "recipes"](../recipes/_index.md) +- [Registry API](../spec/api.md) +- [Storage driver model](../storage-drivers/_index.md) +- [Token authentication](../spec/auth/token.md) diff --git a/docs/content/about/garbage-collection.md b/docs/content/about/garbage-collection.md index dd1768d60..ee2d2fb2e 100644 --- a/docs/content/about/garbage-collection.md +++ b/docs/content/about/garbage-collection.md @@ -21,15 +21,15 @@ that certain layers no longer exist on the filesystem. Filesystem layers are stored by their content address in the Registry. This has many advantages, one of which is that data is stored once and referred to by manifests. -See [here](../compatibility#content-addressable-storage-cas) for more details. +See [here](compatibility.md#content-addressable-storage-cas) for more details. Layers are therefore shared amongst manifests; each manifest maintains a reference to the layer. As long as a layer is referenced by one manifest, it cannot be garbage collected. Manifests and layers can be `deleted` with the registry API (refer to the API -documentation [here](/spec/api#deleting-a-layer) and -[here](/spec/api#deleting-an-image) for details). This API removes references +documentation [here](../spec/api.md#deleting-a-layer) and +[here](../spec/api.md#deleting-an-image) for details). This API removes references to the target and makes them eligible for garbage collection. It also makes them unable to be read via the API. diff --git a/docs/content/about/insecure.md b/docs/content/about/insecure.md index e9f55f15d..c848f869f 100644 --- a/docs/content/about/insecure.md +++ b/docs/content/about/insecure.md @@ -72,7 +72,7 @@ This is more secure than the insecure registry solution. Be sure to use the name `myregistry.domain.com` as a CN. -2. Use the result to [start your registry with TLS enabled](../deploying#get-a-certificate). +2. Use the result to [start your registry with TLS enabled](deploying.md#get-a-certificate). 3. Instruct every Docker daemon to trust that certificate. The way to do this depends on your OS. diff --git a/docs/content/about/notifications.md b/docs/content/about/notifications.md index d5b11d5d4..af4e44188 100644 --- a/docs/content/about/notifications.md +++ b/docs/content/about/notifications.md @@ -10,7 +10,7 @@ pushes and pulls and layer pushes and pulls. These actions are serialized into events. The events are queued into a registry-internal broadcast system which queues and dispatches events to [_Endpoints_](#endpoints). -![Workflow of registry notifications](../../images/notifications.png) +![Workflow of registry notifications](/distribution/images/notifications.png) ## Endpoints @@ -24,7 +24,7 @@ order is not guaranteed. ## Configuration -To setup a registry instance to send notifications to endpoints, one must add +To set up a registry instance to send notifications to endpoints, one must add them to the configuration. A simple example follows: ```yaml @@ -45,7 +45,7 @@ The above would configure the registry with an endpoint to send events to 5 failures happen consecutively, the registry backs off for 1 second before trying again. -For details on the fields, see the [configuration documentation](../configuration/#notifications). +For details on the fields, see the [configuration documentation](configuration.md#notifications). A properly configured endpoint should lead to a log message from the registry upon startup: diff --git a/docs/content/recipes/apache.md b/docs/content/recipes/apache.md index 846392753..d118c5fe4 100644 --- a/docs/content/recipes/apache.md +++ b/docs/content/recipes/apache.md @@ -12,7 +12,7 @@ Usually, that includes enterprise setups using LDAP/AD on the backend and a SSO ### Alternatives -If you just want authentication for your registry, and are happy maintaining users access separately, you should really consider sticking with the native [basic auth registry feature](/about/deploying#native-basic-auth). +If you just want authentication for your registry, and are happy maintaining users access separately, you should really consider sticking with the native [basic auth registry feature](../about/deploying.md#native-basic-auth). ### Solution diff --git a/docs/content/recipes/mirror.md b/docs/content/recipes/mirror.md index 524ef1dde..c5c5a5dca 100644 --- a/docs/content/recipes/mirror.md +++ b/docs/content/recipes/mirror.md @@ -38,7 +38,7 @@ The following table shows examples of allowed and disallowed mirror URLs. > **Note** > -> Mirrors of Docker Hub are still subject to Docker's [fair usage policy](https://www.docker.com/pricing/resource-consumption-updates){: target="blank" rel="noopener" class=“”}. +> Mirrors of Docker Hub are still subject to Docker's [fair usage policy](https://www.docker.com/pricing/resource-consumption-updates). ### Solution @@ -72,7 +72,7 @@ be configured to use the `filesystem` driver for storage. The easiest way to run a registry as a pull through cache is to run the official Registry image. -At least, you need to specify `proxy.remoteurl` within `/etc/docker/registry/config.yml` +At least, you need to specify `proxy.remoteurl` within `/etc/distribution/config.yml` as described in the following subsection. Multiple registry caches can be deployed over the same back-end. A single @@ -107,7 +107,7 @@ proxy: > **Warning**: For the scheduler to clean up old entries, `delete` must > be enabled in the registry configuration. See -> [Registry Configuration](/about/configuration) for more details. +> [Registry Configuration](../about/configuration.md) for more details. ### Configure the Docker daemon diff --git a/docs/content/recipes/nginx.md b/docs/content/recipes/nginx.md index d2c4ff35d..8db6e4f1f 100644 --- a/docs/content/recipes/nginx.md +++ b/docs/content/recipes/nginx.md @@ -17,7 +17,7 @@ mechanism fronting their internal http portal. If you just want authentication for your registry, and are happy maintaining users access separately, you should really consider sticking with the native -[basic auth registry feature](/about/deploying#native-basic-auth). +[basic auth registry feature](../about/deploying.md#native-basic-auth). ### Solution diff --git a/docs/content/spec/_index.md b/docs/content/spec/_index.md index 5e9729b8d..4f2d0e93f 100644 --- a/docs/content/spec/_index.md +++ b/docs/content/spec/_index.md @@ -6,7 +6,7 @@ keywords: registry, service, images, repository, json # Docker Registry Reference -* [HTTP API V2](api) -* [Storage Driver](/storage-drivers/) -* [Token Authentication Specification](auth/token) -* [Token Authentication Implementation](auth/jwt) +* [HTTP API V2](api.md) +* [Storage Driver](../storage-drivers/_index.md) +* [Token Authentication Specification](auth/token.md) +* [Token Authentication Implementation](auth/jwt.md) diff --git a/docs/content/spec/api.md b/docs/content/spec/api.md index 45d6c0d06..2bab67d36 100644 --- a/docs/content/spec/api.md +++ b/docs/content/spec/api.md @@ -416,7 +416,7 @@ reference may include a tag or digest. The client should include an Accept header indicating which manifest content types it supports. For more details on the manifest format and content types, -see [Image Manifest Version 2, Schema 2](../manifest-v2-2). +see [Image Manifest Version 2, Schema 2](manifest-v2-2.md). In a successful response, the Content-Type header will indicate which manifest type is being returned. A `404 Not Found` response will be returned if the image is unknown to the @@ -840,7 +840,7 @@ Content-Type: The `name` and `reference` fields of the response body must match those specified in the URL. The `reference` field may be a "tag" or a "digest". The content type should match the type of the manifest being uploaded, as specified -in [Image Manifest Version 2, Schema 2](../manifest-v2-2). +in [Image Manifest Version 2, Schema 2](manifest-v2-2.md). If there is a problem with pushing the manifest, a relevant 4xx response will be returned with a JSON error message. Please see the @@ -1088,7 +1088,7 @@ response will be issued instead. Accept: application/vnd.docker.distribution.manifest.v2+json -> for more details, see: [compatibility](/about/compatibility#content-addressable-storage-cas) +> for more details, see: [compatibility](../about/compatibility.md#content-addressable-storage-cas) ## Detail diff --git a/docs/content/spec/auth/oauth.md b/docs/content/spec/auth/oauth.md index ca61d2edd..8e5f759d8 100644 --- a/docs/content/spec/auth/oauth.md +++ b/docs/content/spec/auth/oauth.md @@ -12,7 +12,7 @@ reference for the protocol and HTTP endpoints described here. **Note**: Not all token servers implement oauth2. If the request to the endpoint returns `404` using the HTTP `POST` method, refer to -[Token Documentation](../token) for using the HTTP `GET` method supported by all +[Token Documentation](token.md) for using the HTTP `GET` method supported by all token servers. ## Refresh token format diff --git a/docs/content/spec/auth/scope.md b/docs/content/spec/auth/scope.md index a2236bb75..1f2059add 100644 --- a/docs/content/spec/auth/scope.md +++ b/docs/content/spec/auth/scope.md @@ -144,7 +144,7 @@ Each JWT access token may only have a single subject and audience but multiple resource scopes. The subject and audience are put into standard JWT fields `sub` and `aud`. The resource scope is put into the `access` field. The structure of the access field can be seen in the -[jwt documentation](../jwt). +[jwt documentation](jwt.md). ## Refresh Tokens diff --git a/docs/content/spec/auth/token.md b/docs/content/spec/auth/token.md index cc9d940e8..a878b3281 100644 --- a/docs/content/spec/auth/token.md +++ b/docs/content/spec/auth/token.md @@ -8,7 +8,7 @@ keywords: registry, on-prem, images, tags, repository, distribution, Bearer auth This document outlines the v2 Distribution registry authentication scheme: -![v2 registry auth](../../../images/v2-registry-auth.png) +![v2 registry auth](/distribution/images/v2-registry-auth.png) 1. Attempt to begin a push/pull operation with the registry. 2. If the registry requires authorization it will return a `401 Unauthorized` @@ -188,7 +188,7 @@ https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba The token server should first attempt to authenticate the client using any authentication credentials provided with the request. From Docker 1.11 the -Docker engine supports both Basic Authentication and [OAuth2](../oauth) for +Docker engine supports both Basic Authentication and [OAuth2](oauth.md) for getting tokens. Docker 1.10 and before, the registry client in the Docker Engine only supports Basic Authentication. If an attempt to authenticate to the token server fails, the token server should return a `401 Unauthorized` response diff --git a/docs/content/spec/manifest-v2-2.md b/docs/content/spec/manifest-v2-2.md index 1e85fad73..a4c7e9298 100644 --- a/docs/content/spec/manifest-v2-2.md +++ b/docs/content/spec/manifest-v2-2.md @@ -71,7 +71,7 @@ image manifest based on the Content-Type returned in the HTTP response. - **`digest`** *string* The digest of the content, as defined by the - [Registry V2 HTTP API Specificiation](../api#digest-parameter). + [Registry V2 HTTP API Specification](api.md#digest-parameter). - **`platform`** *object* @@ -187,7 +187,7 @@ image. It's the direct replacement for the schema-1 manifest. - **`digest`** *string* The digest of the content, as defined by the - [Registry V2 HTTP API Specificiation](../api#digest-parameter). + [Registry V2 HTTP API Specification](api.md#digest-parameter). - **`layers`** *array* @@ -213,7 +213,7 @@ image. It's the direct replacement for the schema-1 manifest. - **`digest`** *string* The digest of the content, as defined by the - [Registry V2 HTTP API Specificiation](../api#digest-parameter). + [Registry V2 HTTP API Specification](api.md#digest-parameter). - **`urls`** *array* diff --git a/docs/content/storage-drivers/azure.md b/docs/content/storage-drivers/azure.md index 50f03a2bb..d2b6c54c1 100644 --- a/docs/content/storage-drivers/azure.md +++ b/docs/content/storage-drivers/azure.md @@ -20,7 +20,22 @@ An implementation of the `storagedriver.StorageDriver` interface which uses [Mic ## Related information -* To get information about -[azure-blob-storage](https://azure.microsoft.com/en-us/services/storage/), visit -the Microsoft website. -* You can use Microsoft's [Blob Service REST API](https://docs.microsoft.com/en-us/rest/api/storageservices/Blob-Service-REST-API) to [create a storage container](https://docs.microsoft.com/en-us/rest/api/storageservices/Create-Container). +* To get information about Azure blob storage [the offical docs](https://azure.microsoft.com/en-us/services/storage/). +* You can use Azure [Blob Service REST API](https://docs.microsoft.com/en-us/rest/api/storageservices/Blob-Service-REST-API) to [create a storage container](https://docs.microsoft.com/en-us/rest/api/storageservices/Create-Container). + +## Azure identity + +In order to use managed identity to access Azure blob storage you can use [Microsoft Bicep](https://learn.microsoft.com/en-us/azure/templates/microsoft.app/managedenvironments/storages?pivots=deployment-language-bicep). + +The following will configure credentials that will be used by the Azure storage driver to construct AZ Identity that will be used to access the blob storage: +``` +properties: { + azure: { + accountname: accountname + container: containername + credentials: { + type: default + } + } +} +``` diff --git a/docs/content/storage-drivers/gcs.md b/docs/content/storage-drivers/gcs.md index f970b73e8..a2131d016 100644 --- a/docs/content/storage-drivers/gcs.md +++ b/docs/content/storage-drivers/gcs.md @@ -17,4 +17,8 @@ An implementation of the `storagedriver.StorageDriver` interface which uses Goog {{< hint type=note >}} Instead of a key file you can use [Google Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials). + +To use redirects with default credentials assigned to a virtual machine you have to enable "IAM Service Account Credentials API" and grant `iam.serviceAccounts.signBlob` permission on the used service account. + +To use redirects with default credentials from Google Cloud CLI, in addition to the permissions mentioned above, you have to [impersonate the service account intended to be used by the registry](https://cloud.google.com/sdk/gcloud/reference#--impersonate-service-account). {{< /hint >}} diff --git a/docs/content/storage-drivers/inmemory.md b/docs/content/storage-drivers/inmemory.md index ba9cd93e9..81c49dfcc 100644 --- a/docs/content/storage-drivers/inmemory.md +++ b/docs/content/storage-drivers/inmemory.md @@ -7,7 +7,7 @@ title: In-memory storage driver (testing only) For purely tests purposes, you can use the `inmemory` storage driver. This driver is an implementation of the `storagedriver.StorageDriver` interface which uses local memory for object storage. If you would like to run a registry from -volatile memory, use the [`filesystem` driver](../filesystem) on a ramdisk. +volatile memory, use the [`filesystem` driver](filesystem.md) on a ramdisk. {{< hint type=important >}} This storage driver *does not* persist data across runs. This is why it is only suitable for testing. *Never* use this driver in production. diff --git a/docs/content/storage-drivers/middleware/_index.md b/docs/content/storage-drivers/middleware/_index.md new file mode 100644 index 000000000..979fe92c6 --- /dev/null +++ b/docs/content/storage-drivers/middleware/_index.md @@ -0,0 +1,15 @@ +--- +description: Explains how to use storage middleware +keywords: registry, on-prem, images, tags, repository, distribution, storage drivers, advanced +title: Storage middleware +--- + +This document describes the registry storage middleware. + +## Provided middleware + +This storage driver package comes bundled with several middleware options: + +- cloudfront +- redirect +- [rewrite](rewrite): Partially rewrites the URL returned by the storage driver. diff --git a/docs/content/storage-drivers/middleware/rewrite.md b/docs/content/storage-drivers/middleware/rewrite.md new file mode 100644 index 000000000..cba2a05d7 --- /dev/null +++ b/docs/content/storage-drivers/middleware/rewrite.md @@ -0,0 +1,32 @@ +--- +description: Explains how to use the rewrite storage middleware +keywords: registry, service, driver, images, storage, middleware, rewrite +title: Rewrite middleware +--- + +A storage middleware which allows to rewrite the URL returned by the storage driver. + +For example, it can be used to rewrite the Blob Storage URL returned by the Azure Blob Storage driver to use Azure CDN. + +## Parameters + +* `scheme`: (optional): Rewrite the returned URL scheme (if set). +* `host`: (optional): Rewrite the returned URL host (if set). +* `trimpathprefix` (optional): Trim the prefix from the returned URL path (if set). + +## Example configuration + +```yaml +storage: + azure: + accountname: "ACCOUNT_NAME" + accountkey: "******" + container: container-name +middleware: + storage: + - name: rewrite + options: + scheme: https + host: example-cdn-endpoint.azurefd.net + trimpathprefix: /container-name +``` diff --git a/docs/content/storage-drivers/s3.md b/docs/content/storage-drivers/s3.md index f50cc3d20..86d765a3f 100644 --- a/docs/content/storage-drivers/s3.md +++ b/docs/content/storage-drivers/s3.md @@ -15,7 +15,7 @@ Amazon S3 or S3 compatible services for object storage. | `secretkey` | no | Your AWS Secret Key. If you use [IAM roles](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html), omit to fetch temporary credentials from IAM. | | `region` | yes | The AWS region in which your bucket exists. | | `regionendpoint` | no | Endpoint for S3 compatible storage services (Minio, etc). | -| `forcepathstyle` | no | To enable path-style addressing when the value is set to `true`. The default is `true`. | +| `forcepathstyle` | no | To enable path-style addressing when the value is set to `true`. The default is `false`. | | `bucket` | yes | The bucket name in which you want to store the registry's data. | | `encrypt` | no | Specifies whether the registry stores the image in encrypted format or not. A boolean value. The default is `false`. | | `keyid` | no | Optional KMS key ID to use for encryption (encrypt must be true, or this parameter is ignored). The default is `none`. | @@ -43,7 +43,7 @@ Amazon S3 or S3 compatible services for object storage. `regionendpoint`: (optional) Endpoint URL for S3 compatible APIs. This should not be provided when using Amazon S3. -`forcepathstyle`: (optional) The force path style for S3 compatible APIs. Some manufacturers only support force path style, while others only support DNS based bucket routing. Amazon S3 supports both. +`forcepathstyle`: (optional) Force path style for S3 compatible APIs. Some manufacturers only support force path style, while others only support DNS based bucket routing. Amazon S3 supports both. The value of this parameter applies, regardless of the region settings. `bucket`: The name of your S3 bucket where you wish to store objects. The bucket must exist prior to the driver initialization. diff --git a/docs/dockerhub.md b/docs/dockerhub.md index 55bb8aab8..69c10495c 100644 --- a/docs/dockerhub.md +++ b/docs/dockerhub.md @@ -5,7 +5,7 @@ This repository provides container images for the Open Source Registry implement -[![Build Status](https://github.com/distribution/distribution/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/distribution/distribution/actions?query=workflow%3ACI) +[![Build Status](https://github.com/distribution/distribution/workflows/build/badge.svg?branch=main&event=push)](https://github.com/distribution/distribution/actions/workflows/build.yml?query=workflow%3Abuild) [![OCI Conformance](https://github.com/distribution/distribution/workflows/conformance/badge.svg)](https://github.com/distribution/distribution/actions?query=workflow%3Aconformance) [![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE) @@ -31,12 +31,12 @@ docker tag alpine localhost:5000/alpine docker push localhost:5000/alpine ``` -⚠️ Beware the default configuration uses [`filesystem` storage driver](https://github.com/distribution/distribution/blob/main/docs/storage-drivers/filesystem.md) +⚠️ Beware the default configuration uses [`filesystem` storage driver](https://github.com/distribution/distribution/blob/main/docs/content/storage-drivers/filesystem.md) and the above example command does not mount a local filesystem volume into the running container. If you wish to mount the local filesystem to the `rootdirectory` of the `filesystem` storage driver run the following command: ``` -docker run -d -p 5000:5000 $PWD/FS/PATH:/var/lib/registry --restart always --name registry distribution/distribution:edge +docker run -d -p 5000:5000 -v $PWD/FS/PATH:/var/lib/registry --restart always --name registry distribution/distribution:edge ``` ### Custom configuration @@ -44,7 +44,7 @@ docker run -d -p 5000:5000 $PWD/FS/PATH:/var/lib/registry --restart always --nam If you don't wan to use the default configuration file, you can supply your own custom configuration file as follows: ``` -docker run -d -p 5000:5000 $PWD/PATH/TO/config.yml:/etc/docker/registry/config.yml --restart always --name registry distribution/distribution:edge +docker run -d -p 5000:5000 -v $PWD/PATH/TO/config.yml:/etc/distribution/config.yml --restart always --name registry distribution/distribution:edge ``` ## Communication diff --git a/docs/hugo.yaml b/docs/hugo.yaml index 4d5c75c97..926fc2e24 100644 --- a/docs/hugo.yaml +++ b/docs/hugo.yaml @@ -1,4 +1,4 @@ -baseURL: / +baseURL: https://distribution.github.io/distribution languageCode: en-us title: CNCF Distribution theme: hugo-geekdoc @@ -22,3 +22,7 @@ disablePathToLower: true params: geekdocRepo: "https://github.com/distribution/distribution" geekdocEditPath: edit/main/docs + geekdocLegalNotice: "https://www.linuxfoundation.org/legal/trademark-usage" + geekdocContentLicense: + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ diff --git a/docs/i18n/en.yaml b/docs/i18n/en.yaml new file mode 100644 index 000000000..f0eb3c489 --- /dev/null +++ b/docs/i18n/en.yaml @@ -0,0 +1 @@ +footer_legal_notice: Trademarks \ No newline at end of file diff --git a/docs/layouts/_default/_markup/render-link.html b/docs/layouts/_default/_markup/render-link.html new file mode 100644 index 000000000..01ef350c8 --- /dev/null +++ b/docs/layouts/_default/_markup/render-link.html @@ -0,0 +1,5 @@ +{{- if (strings.HasPrefix .Destination "http") -}} + {{ safe.HTML .Text }} +{{- else -}} + {{ safe.HTML .Text }} +{{- end -}} diff --git a/go.mod b/go.mod index 031775080..7a8df5e53 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.21 toolchain go1.21.4 require ( - cloud.google.com/go/storage v1.30.1 - git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240402141532-e5040d35e99d + cloud.google.com/go/storage v1.36.0 + git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240716083621-e18b91623138 git.frostfs.info/TrueCloudLab/tzhash v1.8.0 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 @@ -15,17 +15,17 @@ require ( github.com/aws/aws-sdk-go v1.48.10 github.com/bshuster-repo/logrus-logstash-hook v1.0.0 github.com/coreos/go-systemd/v22 v22.5.0 - github.com/distribution/reference v0.5.0 + github.com/distribution/reference v0.6.0 github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c github.com/docker/go-metrics v0.0.1 - github.com/go-jose/go-jose/v3 v3.0.1 + github.com/go-jose/go-jose/v4 v4.0.2 github.com/google/uuid v1.6.0 - github.com/gorilla/handlers v1.5.1 + github.com/gorilla/handlers v1.5.2 github.com/gorilla/mux v1.8.1 github.com/hashicorp/golang-lru/arc/v2 v2.0.5 github.com/klauspost/compress v1.17.4 - github.com/mitchellh/mapstructure v1.1.2 - github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc + github.com/mitchellh/mapstructure v1.5.0 + github.com/nspcc-dev/neo-go v0.106.2 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 @@ -35,23 +35,29 @@ require ( github.com/stretchr/testify v1.9.0 github.com/testcontainers/testcontainers-go v0.29.1 go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 + go.opentelemetry.io/otel v1.22.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 go.opentelemetry.io/otel/sdk v1.21.0 + go.opentelemetry.io/otel/trace v1.22.0 + go.uber.org/zap v1.27.0 golang.org/x/crypto v0.24.0 - golang.org/x/oauth2 v0.11.0 - google.golang.org/api v0.126.0 + golang.org/x/net v0.26.0 + golang.org/x/oauth2 v0.16.0 + golang.org/x/sync v0.7.0 + google.golang.org/api v0.162.0 + google.golang.org/grpc v1.62.0 gopkg.in/yaml.v2 v2.4.0 ) -require github.com/golang-jwt/jwt/v5 v5.2.1 // indirect - require ( - cloud.google.com/go v0.110.7 // indirect - cloud.google.com/go/compute v1.23.0 // indirect + cloud.google.com/go v0.112.0 // indirect + cloud.google.com/go/compute v1.24.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.1 // indirect + cloud.google.com/go/iam v1.1.6 // indirect dario.cat/mergo v1.0.0 // indirect - git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240327095603-491a47e7fe24 // indirect - git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb // indirect + git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3 // indirect + git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3 // indirect git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 // indirect git.frostfs.info/TrueCloudLab/hrw v1.2.1 // indirect git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect @@ -61,23 +67,37 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/hcsshim v0.11.4 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect - github.com/benbjohnson/clock v1.1.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/containerd v1.7.12 // indirect github.com/containerd/log v0.1.0 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/docker v25.0.3+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.11.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/golang-lru v0.6.0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/moby/patternmatcher v0.6.0 // indirect @@ -86,54 +106,31 @@ require ( github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect - github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 // indirect - github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce // indirect - github.com/nspcc-dev/rfc6979 v0.2.0 // indirect + github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 // indirect + github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d // indirect + github.com/nspcc-dev/rfc6979 v0.2.1 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/prometheus/client_golang v1.19.0 // indirect; updated to latest + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil/v3 v3.23.12 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/twmb/murmur3 v1.1.8 // indirect github.com/urfave/cli v1.22.12 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.24.0 - golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect -) - -require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.3.0 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/google/s2a-go v0.1.4 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.17.0 // indirect; updated to latest - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect - github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect - github.com/spf13/pflag v1.0.5 // indirect + go.etcd.io/bbolt v1.3.9 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 - go.opentelemetry.io/otel v1.21.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect @@ -141,21 +138,20 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.44.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 // indirect - go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.21.0 // indirect - go.opentelemetry.io/otel/trace v1.21.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sync v0.7.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect - google.golang.org/grpc v1.59.0 + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 433b5ef6f..0ae343386 100644 --- a/go.sum +++ b/go.sum @@ -1,56 +1,24 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= -cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= +cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= +cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= +cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240327095603-491a47e7fe24 h1:uIkl0mKWwDICUZTbNWZ38HLYDBI9rMgdAhYQWZ0C9iQ= -git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240327095603-491a47e7fe24/go.mod h1:OBDSr+DqV1z4VDouoX3YMleNc4DPBVBWTG3WDT2PK1o= -git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb h1:S/TrbOOu9qEXZRZ9/Ddw7crnxbBUQLo68PSzQWYrc9M= -git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb/go.mod h1:nkR5gaGeez3Zv2SE7aceP0YwxG2FzIB5cGKpQO2vV2o= +git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3 h1:H5GvrVlowIMWfzqQkhY0p0myooJxQ1sMRVSFfXawwWg= +git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3/go.mod h1:OBDSr+DqV1z4VDouoX3YMleNc4DPBVBWTG3WDT2PK1o= +git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3 h1:zt8OAof+3YKIdRqg8EaLfbqq+4TnWGnsrXEXyGMNz5M= +git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc= git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk= git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU= -git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240402141532-e5040d35e99d h1:MYlPcYV/8j3PP0Yv0t6kJwPwOnhHNTybHi05g+ofyw0= -git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240402141532-e5040d35e99d/go.mod h1:s2ISRBm7AjfTdfZ3aF6gQt9VXz+n8cwSZcRiaVUMVI0= +git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240716083621-e18b91623138 h1:mL4hzieiQBGKTD72QAgnXTT8AcZRiOoVer4SNgt/+pg= +git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240716083621-e18b91623138/go.mod h1:OoYaejNkEDhSkqPSWw0N6i7WXqKlaxpyB3yoE8VsidI= git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc= git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM= git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA= @@ -73,39 +41,22 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mx github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0= -github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig= -github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I= -github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= -github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= -github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= -github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs= -github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/aws/aws-sdk-go v1.48.10 h1:0LIFG3wp2Dt6PsxKWCg1Y1xRrn2vZnW5/gWdgaBalKg= github.com/aws/aws-sdk-go v1.48.10/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= +github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= @@ -113,63 +64,41 @@ github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0= github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc= +github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ= github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -183,43 +112,28 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= -github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= -github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= +github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-redis/redis v6.10.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -227,27 +141,15 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -260,110 +162,64 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= -github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw= github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU= -github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= -github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -372,18 +228,11 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= @@ -396,63 +245,35 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= -github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= -github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= -github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= -github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y= -github.com/nspcc-dev/dbft v0.0.0-20220902113116-58a5e763e647/go.mod h1:g9xisXmX9NP9MjioaTe862n9SlZTrP+6PVUWLBYOr98= -github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= -github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg= -github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= -github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU= -github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= -github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM= -github.com/nspcc-dev/neo-go v0.99.4/go.mod h1:mKTolfRUfKjFso5HPvGSQtUZc70n0VKBMs16eGuC5gA= -github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc h1:fySIWvUQsitK5e5qYIHnTDCXuPpwzz89SEUEIyY11sg= -github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc/go.mod h1:s9QhjMC784MWqTURovMbyYduIJc86mnCruxcMiAebpc= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220927123257-24c107e3a262/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce h1:vLGuUNDkmQrWMa4rr4vTd1u8ULqejWxVmNz1L7ocTEI= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= -github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= -github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= -github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= -github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= -github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= -github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40= -github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= -github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= -github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= +github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU3v+zJcnHAVmHnZKt3I++tvn30gBj2rP2PocZMk= +github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc= +github.com/nspcc-dev/neo-go v0.106.2 h1:KXSJ2J5Oacc7LrX3r4jvnC8ihKqHs5NB21q4f2S3r9o= +github.com/nspcc-dev/neo-go v0.106.2/go.mod h1:Ojwfx3/lv0VTeEHMpQ17g0wTnXcCSoFQVq5GEeCZmGo= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY= +github.com/nspcc-dev/rfc6979 v0.2.1 h1:8wWxkamHWFmO790GsewSoKUSJjVnL1fmdRpokU/RgRM= +github.com/nspcc-dev/rfc6979 v0.2.1/go.mod h1:Tk7h5kyUWkhjyO3zUgFFhy1v2vQv3BvQEntakdtqrWc= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -462,38 +283,22 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:Om github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho= github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= @@ -501,12 +306,8 @@ github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnA github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= @@ -515,13 +316,9 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -532,9 +329,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -542,7 +336,6 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/testcontainers/testcontainers-go v0.29.1 h1:z8kxdFlovA2y97RWx98v/TQ+tR+SXZm6p35M+xB92zk= @@ -551,42 +344,27 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= -github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo= -github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1:RmMWU37GKR2s6pgrIEB4ixgpVCt/cf7dnJv3fuH1J1c= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= -go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 h1:ysCfPZB9AjUlMa1UHYup3c9dAOCMQX/6sxSfPBUoxHw= go.opentelemetry.io/contrib/exporters/autoexport v0.46.1/go.mod h1:ha0aiYm+DOPsLHjh0zoQ8W8sLT+LJ58J3j47lGpSLrU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps= @@ -603,149 +381,67 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0 h1:dEZWPjVN22urgY go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0/go.mod h1:sTt30Evb7hJB/gEk27qLb1+l9n4Tb8HvHkR0Wx3S6CU= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 h1:VhlEQAPp9R1ktYfrPk5SOryw1e9LDDTZCbIPFrho0ec= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0/go.mod h1:kB3ufRbfU+CQ4MlUcqtW8Z7YEOBeK2DJ6CmR5rYYF3E= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0= go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -753,58 +449,21 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -816,69 +475,26 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= @@ -888,87 +504,28 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps= +google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= +google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 h1:x9PwdEgd11LgK+orcck69WVRo7DezSO4VUMPI4xpc8A= +google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= +google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -977,29 +534,20 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -1011,12 +559,6 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/internal/client/auth/challenge/authchallenge.go b/internal/client/auth/challenge/authchallenge.go index 34684dd55..4d26e5688 100644 --- a/internal/client/auth/challenge/authchallenge.go +++ b/internal/client/auth/challenge/authchallenge.go @@ -72,7 +72,7 @@ type Manager interface { // AddResponse adds the response to the challenge // manager. The challenges will be parsed out of - // the WWW-Authenicate headers and added to the + // the WWW-Authenticate headers and added to the // URL which was produced the response. If the // response was authorized, any challenges for the // endpoint will be cleared. diff --git a/internal/client/auth/session.go b/internal/client/auth/session.go index 9669dfd02..104290ab4 100644 --- a/internal/client/auth/session.go +++ b/internal/client/auth/session.go @@ -29,9 +29,9 @@ var ( const defaultClientID = "registry-client" // AuthenticationHandler is an interface for authorizing a request from -// params from a "WWW-Authenicate" header for a single scheme. +// params from a "WWW-Authenticate" header for a single scheme. type AuthenticationHandler interface { - // Scheme returns the scheme as expected from the "WWW-Authenicate" header. + // Scheme returns the scheme as expected from the "WWW-Authenticate" header. Scheme() string // AuthorizeRequest adds the authorization header to a request (if needed) diff --git a/internal/client/errors.go b/internal/client/errors.go index 4077f373e..5131ef813 100644 --- a/internal/client/errors.go +++ b/internal/client/errors.go @@ -46,8 +46,14 @@ func parseHTTPErrorResponse(resp *http.Response) error { } statusCode := resp.StatusCode - ctHeader := resp.Header.Get("Content-Type") + // A HEAD request for example validly does not contain any body, while + // still returning a JSON content-type. + if len(body) == 0 { + return makeError(statusCode, "") + } + + ctHeader := resp.Header.Get("Content-Type") if ctHeader == "" { return makeError(statusCode, string(body)) } diff --git a/internal/client/errors_test.go b/internal/client/errors_test.go index 432e2bcc0..e7a285cd9 100644 --- a/internal/client/errors_test.go +++ b/internal/client/errors_test.go @@ -57,6 +57,22 @@ func TestHandleHTTPResponseError401WithInvalidBody(t *testing.T) { } } +func TestHandleHTTPResponseError401WithNoBody(t *testing.T) { + json := "" + response := &http.Response{ + Status: "401 Unauthorized", + StatusCode: 401, + Body: nopCloser{bytes.NewBufferString(json)}, + Header: http.Header{"Content-Type": []string{"application/json; charset=utf-8"}}, + } + err := HandleHTTPResponseError(response) + + expectedMsg := "unauthorized: " + if !strings.Contains(err.Error(), expectedMsg) { + t.Errorf("Expected %q, got: %q", expectedMsg, err.Error()) + } +} + func TestHandleHTTPResponseErrorExpectedStatusCode400ValidBody(t *testing.T) { json := `{"errors":[{"code":"DIGEST_INVALID","message":"provided digest does not match"}]}` response := &http.Response{ diff --git a/internal/client/transport/http_reader.go b/internal/client/transport/http_reader.go index b7dba8558..e0833e913 100644 --- a/internal/client/transport/http_reader.go +++ b/internal/client/transport/http_reader.go @@ -26,11 +26,6 @@ var ( ErrWrongCodeForByteRange = errors.New("expected HTTP 206 from byte range request") ) -// ReadSeekCloser combines io.ReadSeeker with io.Closer. -// -// Deprecated: use [io.ReadSeekCloser]. -type ReadSeekCloser = io.ReadSeekCloser - // NewHTTPReadSeeker handles reading from an HTTP endpoint using a GET // request. When seeking and starting a read from a non-zero offset // the a "Range" header will be added which sets the offset. diff --git a/manifests.go b/manifests.go index 8f84a220a..f38d3ce8b 100644 --- a/manifests.go +++ b/manifests.go @@ -47,7 +47,7 @@ type ManifestBuilder interface { AppendReference(dependency Describable) error } -// ManifestService describes operations on image manifests. +// ManifestService describes operations on manifests. type ManifestService interface { // Exists returns true if the manifest exists. Exists(ctx context.Context, dgst digest.Digest) (bool, error) diff --git a/registry/api/v2/descriptors.go b/registry/api/v2/descriptors.go index 7f9135fa0..cf000b274 100644 --- a/registry/api/v2/descriptors.go +++ b/registry/api/v2/descriptors.go @@ -269,7 +269,7 @@ type RouteDescriptor struct { // should match. Path string - // Entity should be a short, human-readalbe description of the object + // Entity should be a short, human-readable description of the object // targeted by the endpoint. Entity string diff --git a/registry/api/v2/urls.go b/registry/api/v2/urls.go index 1a76565aa..80617fd3d 100644 --- a/registry/api/v2/urls.go +++ b/registry/api/v2/urls.go @@ -202,7 +202,7 @@ func (ub *URLBuilder) BuildBlobUploadChunkURL(name reference.Named, uuid string, return appendValuesURL(uploadURL, values...).String(), nil } -// clondedRoute returns a clone of the named route from the router. Routes +// cloneRoute returns a clone of the named route from the router. Routes // must be cloned to avoid modifying them during url generation. func (ub *URLBuilder) cloneRoute(name string) clonedRoute { route := new(mux.Route) diff --git a/registry/auth/auth.go b/registry/auth/auth.go index b9c3b01dc..687002c3b 100644 --- a/registry/auth/auth.go +++ b/registry/auth/auth.go @@ -46,7 +46,7 @@ var ( ) // InitFunc is the type of an AccessController factory function and is used -// to register the constructor for different AccesController backends. +// to register the constructor for different AccessController backends. type InitFunc func(options map[string]interface{}) (AccessController, error) var accessControllers map[string]InitFunc @@ -56,7 +56,7 @@ func init() { } // UserInfo carries information about -// an autenticated/authorized client. +// an authenticated/authorized client. type UserInfo struct { Name string } diff --git a/registry/auth/token/accesscontroller.go b/registry/auth/token/accesscontroller.go index 8f610c46d..e6dbaf48f 100644 --- a/registry/auth/token/accesscontroller.go +++ b/registry/auth/token/accesscontroller.go @@ -9,11 +9,12 @@ import ( "fmt" "io" "net/http" + "net/url" "os" "strings" "github.com/distribution/distribution/v3/registry/auth" - "github.com/go-jose/go-jose/v3" + "github.com/go-jose/go-jose/v4" "github.com/sirupsen/logrus" ) @@ -83,11 +84,12 @@ var ( // authChallenge implements the auth.Challenge interface. type authChallenge struct { - err error - realm string - autoRedirect bool - service string - accessSet accessSet + err error + realm string + autoRedirect bool + autoRedirectPath string + service string + accessSet accessSet } var _ auth.Challenge = authChallenge{} @@ -102,13 +104,28 @@ func (ac authChallenge) Status() int { return http.StatusUnauthorized } +func buildAutoRedirectURL(r *http.Request, autoRedirectPath string) string { + scheme := "https" + + if forwardedProto := r.Header.Get("X-Forwarded-Proto"); len(forwardedProto) > 0 { + scheme = forwardedProto + } + + u := &url.URL{ + Scheme: scheme, + Host: r.Host, + Path: autoRedirectPath, + } + return u.String() +} + // challengeParams constructs the value to be used in // the WWW-Authenticate response challenge header. // See https://tools.ietf.org/html/rfc6750#section-3 func (ac authChallenge) challengeParams(r *http.Request) string { var realm string if ac.autoRedirect { - realm = fmt.Sprintf("https://%s/auth/token", r.Host) + realm = buildAutoRedirectURL(r, ac.autoRedirectPath) } else { realm = ac.realm } @@ -127,30 +144,38 @@ func (ac authChallenge) challengeParams(r *http.Request) string { return str } -// SetChallenge sets the WWW-Authenticate value for the response. +// SetHeaders sets the WWW-Authenticate value for the response. func (ac authChallenge) SetHeaders(r *http.Request, w http.ResponseWriter) { w.Header().Add("WWW-Authenticate", ac.challengeParams(r)) } // accessController implements the auth.AccessController interface. type accessController struct { - realm string - autoRedirect bool - issuer string - service string - rootCerts *x509.CertPool - trustedKeys map[string]crypto.PublicKey + realm string + autoRedirect bool + autoRedirectPath string + issuer string + service string + rootCerts *x509.CertPool + trustedKeys map[string]crypto.PublicKey + signingAlgorithms []jose.SignatureAlgorithm } +const ( + defaultAutoRedirectPath = "/auth/token" +) + // tokenAccessOptions is a convenience type for handling -// options to the contstructor of an accessController. +// options to the constructor of an accessController. type tokenAccessOptions struct { - realm string - autoRedirect bool - issuer string - service string - rootCertBundle string - jwks string + realm string + autoRedirect bool + autoRedirectPath string + issuer string + service string + rootCertBundle string + jwks string + signingAlgorithms []string } // checkOptions gathers the necessary options @@ -183,10 +208,32 @@ func checkOptions(options map[string]interface{}) (tokenAccessOptions, error) { if ok { autoRedirect, ok := autoRedirectVal.(bool) if !ok { - return opts, fmt.Errorf("token auth requires a valid option bool: autoredirect") + return opts, errors.New("token auth requires a valid option bool: autoredirect") } opts.autoRedirect = autoRedirect } + if opts.autoRedirect { + autoRedirectPathVal, ok := options["autoredirectpath"] + if ok { + autoRedirectPath, ok := autoRedirectPathVal.(string) + if !ok { + return opts, errors.New("token auth requires a valid option string: autoredirectpath") + } + opts.autoRedirectPath = autoRedirectPath + } + if opts.autoRedirectPath == "" { + opts.autoRedirectPath = defaultAutoRedirectPath + } + } + + signingAlgos, ok := options["signingalgorithms"] + if ok { + signingAlgorithmsVals, ok := signingAlgos.([]string) + if !ok { + return opts, errors.New("signingalgorithms must be a list of signing algorithms") + } + opts.signingAlgorithms = signingAlgorithmsVals + } return opts, nil } @@ -243,6 +290,18 @@ func getJwks(path string) (*jose.JSONWebKeySet, error) { return &jwks, nil } +func getSigningAlgorithms(algos []string) ([]jose.SignatureAlgorithm, error) { + signAlgVals := make([]jose.SignatureAlgorithm, 0, len(algos)) + for _, alg := range algos { + alg, ok := signingAlgorithms[alg] + if !ok { + return nil, fmt.Errorf("unsupported signing algorithm: %s", alg) + } + signAlgVals = append(signAlgVals, alg) + } + return signAlgVals, nil +} + // newAccessController creates an accessController using the given options. func newAccessController(options map[string]interface{}) (auth.AccessController, error) { config, err := checkOptions(options) @@ -253,6 +312,7 @@ func newAccessController(options map[string]interface{}) (auth.AccessController, var ( rootCerts []*x509.Certificate jwks *jose.JSONWebKeySet + signAlgos []jose.SignatureAlgorithm ) if config.rootCertBundle != "" { @@ -286,13 +346,25 @@ func newAccessController(options map[string]interface{}) (auth.AccessController, } } + signAlgos, err = getSigningAlgorithms(config.signingAlgorithms) + if err != nil { + return nil, err + } + if len(signAlgos) == 0 { + // NOTE: this is to maintain backwards compat + // with existing registry deployments + signAlgos = defaultSigningAlgorithms + } + return &accessController{ - realm: config.realm, - autoRedirect: config.autoRedirect, - issuer: config.issuer, - service: config.service, - rootCerts: rootPool, - trustedKeys: trustedKeys, + realm: config.realm, + autoRedirect: config.autoRedirect, + autoRedirectPath: config.autoRedirectPath, + issuer: config.issuer, + service: config.service, + rootCerts: rootPool, + trustedKeys: trustedKeys, + signingAlgorithms: signAlgos, }, nil } @@ -300,10 +372,11 @@ func newAccessController(options map[string]interface{}) (auth.AccessController, // for actions on resources described by the given access items. func (ac *accessController) Authorized(req *http.Request, accessItems ...auth.Access) (*auth.Grant, error) { challenge := &authChallenge{ - realm: ac.realm, - autoRedirect: ac.autoRedirect, - service: ac.service, - accessSet: newAccessSet(accessItems...), + realm: ac.realm, + autoRedirect: ac.autoRedirect, + autoRedirectPath: ac.autoRedirectPath, + service: ac.service, + accessSet: newAccessSet(accessItems...), } prefix, rawToken, ok := strings.Cut(req.Header.Get("Authorization"), " ") @@ -312,7 +385,7 @@ func (ac *accessController) Authorized(req *http.Request, accessItems ...auth.Ac return nil, challenge } - token, err := NewToken(rawToken) + token, err := NewToken(rawToken, ac.signingAlgorithms) if err != nil { challenge.err = err return nil, challenge diff --git a/registry/auth/token/accesscontroller_test.go b/registry/auth/token/accesscontroller_test.go new file mode 100644 index 000000000..fde203114 --- /dev/null +++ b/registry/auth/token/accesscontroller_test.go @@ -0,0 +1,89 @@ +package token + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestBuildAutoRedirectURL(t *testing.T) { + cases := []struct { + name string + reqGetter func() *http.Request + autoRedirectPath string + expectedURL string + }{{ + name: "http", + reqGetter: func() *http.Request { + req := httptest.NewRequest("GET", "http://example.com/", nil) + return req + }, + autoRedirectPath: "/auth", + expectedURL: "https://example.com/auth", + }, { + name: "x-forwarded", + reqGetter: func() *http.Request { + req := httptest.NewRequest("GET", "http://example.com/", nil) + req.Header.Set("X-Forwarded-Proto", "http") + return req + }, + autoRedirectPath: "/auth/token", + expectedURL: "http://example.com/auth/token", + }} + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + req := tc.reqGetter() + result := buildAutoRedirectURL(req, tc.autoRedirectPath) + if result != tc.expectedURL { + t.Errorf("expected %s, got %s", tc.expectedURL, result) + } + }) + } +} + +func TestCheckOptions(t *testing.T) { + realm := "https://auth.example.com/token/" + issuer := "test-issuer.example.com" + service := "test-service.example.com" + + options := map[string]interface{}{ + "realm": realm, + "issuer": issuer, + "service": service, + "rootcertbundle": "", + "autoredirect": true, + "autoredirectpath": "/auth", + } + + ta, err := checkOptions(options) + if err != nil { + t.Fatal(err) + } + if ta.autoRedirect != true { + t.Fatal("autoredirect should be true") + } + if ta.autoRedirectPath != "/auth" { + t.Fatal("autoredirectpath should be /auth") + } + + options = map[string]interface{}{ + "realm": realm, + "issuer": issuer, + "service": service, + "rootcertbundle": "", + "autoredirect": true, + "autoredirectforcetlsdisabled": true, + } + + ta, err = checkOptions(options) + if err != nil { + t.Fatal(err) + } + if ta.autoRedirect != true { + t.Fatal("autoredirect should be true") + } + if ta.autoRedirectPath != "/auth/token" { + t.Fatal("autoredirectpath should be /auth/token") + } +} diff --git a/registry/auth/token/fuzz_test.go b/registry/auth/token/fuzz_test.go index 7cbfbb7a8..7e37e541f 100644 --- a/registry/auth/token/fuzz_test.go +++ b/registry/auth/token/fuzz_test.go @@ -4,6 +4,7 @@ import ( "testing" fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/go-jose/go-jose/v4" ) func FuzzToken1(f *testing.F) { @@ -18,7 +19,7 @@ func FuzzToken1(f *testing.F) { if err != nil { return } - token, err := NewToken(rawToken) + token, err := NewToken(rawToken, []jose.SignatureAlgorithm{jose.EdDSA, jose.RS384}) if err != nil { return } diff --git a/registry/auth/token/token.go b/registry/auth/token/token.go index 83fe58c7f..5a0810698 100644 --- a/registry/auth/token/token.go +++ b/registry/auth/token/token.go @@ -7,8 +7,8 @@ import ( "fmt" "time" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" log "github.com/sirupsen/logrus" "github.com/distribution/distribution/v3/registry/auth" @@ -23,6 +23,38 @@ const ( Leeway = 60 * time.Second ) +var signingAlgorithms = map[string]jose.SignatureAlgorithm{ + "EdDSA": jose.EdDSA, + "HS256": jose.HS256, + "HS384": jose.HS384, + "HS512": jose.HS512, + "RS256": jose.RS256, + "RS384": jose.RS384, + "RS512": jose.RS512, + "ES256": jose.ES256, + "ES384": jose.ES384, + "ES512": jose.ES512, + "PS256": jose.PS256, + "PS384": jose.PS384, + "PS512": jose.PS512, +} + +var defaultSigningAlgorithms = []jose.SignatureAlgorithm{ + jose.EdDSA, + jose.HS256, + jose.HS384, + jose.HS512, + jose.RS256, + jose.RS384, + jose.RS512, + jose.ES256, + jose.ES384, + jose.ES512, + jose.PS256, + jose.PS384, + jose.PS512, +} + // Errors used by token parsing and verification. var ( ErrMalformedToken = errors.New("malformed token") @@ -69,8 +101,8 @@ type VerifyOptions struct { // NewToken parses the given raw token string // and constructs an unverified JSON Web Token. -func NewToken(rawToken string) (*Token, error) { - token, err := jwt.ParseSigned(rawToken) +func NewToken(rawToken string, signingAlgs []jose.SignatureAlgorithm) (*Token, error) { + token, err := jwt.ParseSigned(rawToken, signingAlgs) if err != nil { return nil, ErrMalformedToken } @@ -140,6 +172,13 @@ func (t *Token) VerifySigningKey(verifyOpts VerifyOptions) (signingKey crypto.Pu // verifying the first one in the list only at the moment. header := t.JWT.Headers[0] + signingKey, err = verifyCertChain(header, verifyOpts.Roots) + // NOTE(milosgajdos): if the x5c header is missing + // the token may have been signed by a JWKS. + if err != nil && err != jose.ErrMissingX5cHeader { + return + } + switch { case header.JSONWebKey != nil: signingKey, err = verifyJWK(header, verifyOpts) @@ -149,7 +188,7 @@ func (t *Token) VerifySigningKey(verifyOpts VerifyOptions) (signingKey crypto.Pu err = fmt.Errorf("token signed by untrusted key with ID: %q", header.KeyID) } default: - signingKey, err = verifyCertChain(header, verifyOpts.Roots) + err = ErrInvalidToken } return @@ -226,7 +265,7 @@ func getCertPubKey(chains [][]*x509.Certificate) crypto.PublicKey { // NOTE: we dont have to verify that the public key in the leaf cert // *is* the signing key: if it's not the signing then token claims - // verifcation with this key fails + // verification with this key fails return cert.PublicKey.(crypto.PublicKey) } diff --git a/registry/auth/token/token_test.go b/registry/auth/token/token_test.go index a96546af3..b982ab14b 100644 --- a/registry/auth/token/token_test.go +++ b/registry/auth/token/token_test.go @@ -19,8 +19,8 @@ import ( "time" "github.com/distribution/distribution/v3/registry/auth" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" ) func makeRootKeys(numKeys int) ([]*ecdsa.PrivateKey, error) { @@ -123,12 +123,12 @@ func makeTestToken(jwk *jose.JSONWebKey, issuer, audience string, access []*Reso Access: access, } - tokenString, err := jwt.Signed(signer).Claims(claimSet).CompactSerialize() + tokenString, err := jwt.Signed(signer).Claims(claimSet).Serialize() if err != nil { return nil, fmt.Errorf("unable to build token string: %v", err) } - return NewToken(tokenString) + return NewToken(tokenString, []jose.SignatureAlgorithm{signingKey.Algorithm}) } // NOTE(milosgajdos): certTemplateInfo type as well diff --git a/registry/handlers/api_test.go b/registry/handlers/api_test.go index a4091af33..43d3ae146 100644 --- a/registry/handlers/api_test.go +++ b/registry/handlers/api_test.go @@ -1709,6 +1709,33 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Name // ------------------ // Fetch by tag name + + // HEAD requests should not contain a body + headReq, err := http.NewRequest(http.MethodHead, manifestURL, nil) + if err != nil { + t.Fatalf("Error constructing request: %s", err) + } + headResp, err := http.DefaultClient.Do(headReq) + if err != nil { + t.Fatalf("unexpected error head manifest: %v", err) + } + defer headResp.Body.Close() + + checkResponse(t, "head uploaded manifest", headResp, http.StatusOK) + checkHeaders(t, headResp, http.Header{ + "Docker-Content-Digest": []string{dgst.String()}, + "ETag": []string{fmt.Sprintf(`"%s"`, dgst)}, + }) + + headBody, err := io.ReadAll(headResp.Body) + if err != nil { + t.Fatalf("reading body for head manifest: %v", err) + } + + if len(headBody) > 0 { + t.Fatalf("unexpected body length for head manifest: %d", len(headBody)) + } + req, err := http.NewRequest(http.MethodGet, manifestURL, nil) if err != nil { t.Fatalf("Error constructing request: %s", err) @@ -1744,6 +1771,32 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Name // --------------- // Fetch by digest + + // HEAD requests should not contain a body + headReq, err = http.NewRequest(http.MethodHead, manifestDigestURL, nil) + if err != nil { + t.Fatalf("Error constructing request: %s", err) + } + headResp, err = http.DefaultClient.Do(headReq) + if err != nil { + t.Fatalf("unexpected error head manifest: %v", err) + } + defer headResp.Body.Close() + + checkResponse(t, "head uploaded manifest by digest", headResp, http.StatusOK) + checkHeaders(t, headResp, http.Header{ + "Docker-Content-Digest": []string{dgst.String()}, + "ETag": []string{fmt.Sprintf(`"%s"`, dgst)}, + }) + + headBody, err = io.ReadAll(headResp.Body) + if err != nil { + t.Fatalf("reading body for head manifest by digest: %v", err) + } + + if len(headBody) > 0 { + t.Fatalf("unexpected body length for head manifest: %d", len(headBody)) + } req, err = http.NewRequest(http.MethodGet, manifestDigestURL, nil) if err != nil { t.Fatalf("Error constructing request: %s", err) @@ -2461,7 +2514,7 @@ func pushChunk(t *testing.T, ub *v2.URLBuilder, name reference.Named, uploadURLB func checkResponse(t *testing.T, msg string, resp *http.Response, expectedStatus int) { if resp.StatusCode != expectedStatus { - t.Logf("unexpected status %s: %v != %v", msg, resp.StatusCode, expectedStatus) + t.Logf("unexpected status %s: expected %v, got %v", msg, resp.StatusCode, expectedStatus) maybeDumpResponse(t, resp) t.FailNow() } @@ -2543,6 +2596,8 @@ func maybeDumpResponse(t *testing.T, resp *http.Response) { // test will fail. If a passed in header value is "*", any non-zero value will // suffice as a match. func checkHeaders(t *testing.T, resp *http.Response, headers http.Header) { + t.Helper() + for k, vs := range headers { if resp.Header.Get(k) == "" { t.Fatalf("response missing header %q", k) diff --git a/registry/handlers/app.go b/registry/handlers/app.go index 64e49e411..301fc9706 100644 --- a/registry/handlers/app.go +++ b/registry/handlers/app.go @@ -3,6 +3,8 @@ package handlers import ( "context" "crypto/rand" + "crypto/tls" + "crypto/x509" "expvar" "fmt" "math" @@ -77,7 +79,7 @@ type App struct { source notifications.SourceRecord } - redis *redis.Client + redis redis.UniversalClient // isCache is true if this registry is configured as a pull through cache isCache bool @@ -114,7 +116,7 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App { storageParams = make(configuration.Parameters) } if storageParams["useragent"] == "" { - storageParams["useragent"] = fmt.Sprintf("distribution/%s %s", version.Version, runtime.Version()) + storageParams["useragent"] = fmt.Sprintf("distribution/%s %s", version.Version(), runtime.Version()) } var err error @@ -155,7 +157,11 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App { panic(err) } - app.configureSecret(config) + // Do not configure HTTP secret for a proxy registry as HTTP secret + // is only used for blob uploads and a proxy registry does not support blob uploads. + if !app.isCache { + app.configureSecret(config) + } app.configureEvents(config) app.configureRedis(config) app.configureLogHook(config) @@ -184,6 +190,21 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App { } } + // configure tag lookup concurrency limit + if p := config.Storage.TagParameters(); p != nil { + l, ok := p["concurrencylimit"] + if ok { + limit, ok := l.(int) + if !ok { + panic("tag lookup concurrency limit config key must have a integer value") + } + if limit < 0 { + panic("tag lookup concurrency limit should be a non-negative integer value") + } + options = append(options, storage.TagLookupConcurrencyLimit(limit)) + } + } + // configure redirects var redirectDisabled bool if redirectConfig, ok := config.Storage["redirect"]; ok { @@ -236,6 +257,21 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App { options = append(options, storage.ManifestURLsDenyRegexp(re)) } } + + switch config.Validation.Manifests.Indexes.Platforms { + case "list": + options = append(options, storage.EnableValidateImageIndexImagesExist) + for _, platform := range config.Validation.Manifests.Indexes.PlatformList { + options = append(options, storage.AddValidateImageIndexImagesExistPlatform(platform.Architecture, platform.OS)) + } + fallthrough + case "none": + dcontext.GetLogger(app).Warn("Image index completeness validation has been disabled, which is an experimental option because other container tooling might expect all image indexes to be complete") + case "all": + fallthrough + default: + options = append(options, storage.EnableValidateImageIndexImagesExist) + } } // configure storage caches @@ -411,6 +447,14 @@ func (app *App) RegisterHealthChecks(healthRegistries ...*health.Registry) { } } +// Shutdown close the underlying registry +func (app *App) Shutdown() error { + if r, ok := app.registry.(proxy.Closer); ok { + return r.Close() + } + return nil +} + // register a handler with the application, by route name. The handler will be // passed through the application filters and context will be constructed at // request time. @@ -487,12 +531,41 @@ func (app *App) configureEvents(configuration *configuration.Configuration) { } func (app *App) configureRedis(cfg *configuration.Configuration) { - if cfg.Redis.Addr == "" { + if len(cfg.Redis.Options.Addrs) == 0 { dcontext.GetLogger(app).Infof("redis not configured") return } - app.redis = app.createPool(cfg.Redis) + // redis TLS config + if cfg.Redis.TLS.Certificate != "" || cfg.Redis.TLS.Key != "" { + var err error + tlsConf := &tls.Config{} + tlsConf.Certificates = make([]tls.Certificate, 1) + tlsConf.Certificates[0], err = tls.LoadX509KeyPair(cfg.Redis.TLS.Certificate, cfg.Redis.TLS.Key) + if err != nil { + panic(err) + } + if len(cfg.Redis.TLS.ClientCAs) != 0 { + pool := x509.NewCertPool() + for _, ca := range cfg.Redis.TLS.ClientCAs { + caPem, err := os.ReadFile(ca) + if err != nil { + dcontext.GetLogger(app).Errorf("failed reading redis client CA: %v", err) + return + } + + if ok := pool.AppendCertsFromPEM(caPem); !ok { + dcontext.GetLogger(app).Error("could not add CA to pool") + return + } + } + tlsConf.ClientAuth = tls.RequireAndVerifyClientCert + tlsConf.ClientCAs = pool + } + cfg.Redis.Options.TLSConfig = tlsConf + } + + app.redis = app.createPool(cfg.Redis.Options) // Enable metrics instrumentation. if err := redisotel.InstrumentMetrics(app.redis); err != nil { @@ -514,25 +587,12 @@ func (app *App) configureRedis(cfg *configuration.Configuration) { })) } -func (app *App) createPool(cfg configuration.Redis) *redis.Client { - return redis.NewClient(&redis.Options{ - Addr: cfg.Addr, - OnConnect: func(ctx context.Context, cn *redis.Conn) error { - res := cn.Ping(ctx) - return res.Err() - }, - Username: cfg.Username, - Password: cfg.Password, - DB: cfg.DB, - MaxRetries: 3, - DialTimeout: cfg.DialTimeout, - ReadTimeout: cfg.ReadTimeout, - WriteTimeout: cfg.WriteTimeout, - PoolFIFO: false, - MaxIdleConns: cfg.Pool.MaxIdle, - PoolSize: cfg.Pool.MaxActive, - ConnMaxIdleTime: cfg.Pool.IdleTimeout, - }) +func (app *App) createPool(cfg redis.UniversalOptions) redis.UniversalClient { + cfg.OnConnect = func(ctx context.Context, cn *redis.Conn) error { + res := cn.Ping(ctx) + return res.Err() + } + return redis.NewUniversalClient(&cfg) } // configureLogHook prepares logging hook parameters. diff --git a/registry/handlers/manifests.go b/registry/handlers/manifests.go index 0778db3f2..144c04482 100644 --- a/registry/handlers/manifests.go +++ b/registry/handlers/manifests.go @@ -6,6 +6,7 @@ import ( "mime" "net/http" "strings" + "sync" "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/internal/dcontext" @@ -13,11 +14,13 @@ import ( "github.com/distribution/distribution/v3/manifest/ocischema" "github.com/distribution/distribution/v3/manifest/schema2" "github.com/distribution/distribution/v3/registry/api/errcode" + "github.com/distribution/distribution/v3/registry/storage" "github.com/distribution/distribution/v3/registry/storage/driver" "github.com/distribution/reference" "github.com/gorilla/handlers" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" + "golang.org/x/sync/errgroup" ) const ( @@ -212,6 +215,11 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request) w.Header().Set("Content-Length", fmt.Sprint(len(p))) w.Header().Set("Docker-Content-Digest", imh.Digest.String()) w.Header().Set("Etag", fmt.Sprintf(`"%s"`, imh.Digest)) + + if r.Method == http.MethodHead { + return + } + if _, err := w.Write(p); err != nil { w.WriteHeader(http.StatusInternalServerError) } @@ -476,12 +484,26 @@ func (imh *manifestHandler) DeleteManifest(w http.ResponseWriter, r *http.Reques return } + var ( + errs []error + mu sync.Mutex + ) + g := errgroup.Group{} + g.SetLimit(storage.DefaultConcurrencyLimit) for _, tag := range referencedTags { - if err := tagService.Untag(imh, tag); err != nil { - imh.Errors = append(imh.Errors, err) - return - } + tag := tag + + g.Go(func() error { + if err := tagService.Untag(imh, tag); err != nil { + mu.Lock() + errs = append(errs, err) + mu.Unlock() + } + return nil + }) } + _ = g.Wait() // imh will record all errors, so ignore the error of Wait() + imh.Errors = errs w.WriteHeader(http.StatusAccepted) } diff --git a/registry/proxy/proxyauth.go b/registry/proxy/proxyauth.go index 8cdc3ebff..adf60cb9c 100644 --- a/registry/proxy/proxyauth.go +++ b/registry/proxy/proxyauth.go @@ -17,14 +17,23 @@ type userpass struct { password string } +func (u userpass) Basic(_ *url.URL) (string, string) { + return u.username, u.password +} + +func (u userpass) RefreshToken(_ *url.URL, service string) string { + return "" +} + +func (u userpass) SetRefreshToken(_ *url.URL, service, token string) { +} + type credentials struct { creds map[string]userpass } func (c credentials) Basic(u *url.URL) (string, string) { - up := c.creds[u.String()] - - return up.username, up.password + return c.creds[u.String()].Basic(u) } func (c credentials) RefreshToken(u *url.URL, service string) string { @@ -35,12 +44,12 @@ func (c credentials) SetRefreshToken(u *url.URL, service, token string) { } // configureAuth stores credentials for challenge responses -func configureAuth(username, password, remoteURL string) (auth.CredentialStore, error) { +func configureAuth(username, password, remoteURL string) (auth.CredentialStore, auth.CredentialStore, error) { creds := map[string]userpass{} authURLs, err := getAuthURLs(remoteURL) if err != nil { - return nil, err + return nil, nil, err } for _, url := range authURLs { @@ -51,7 +60,7 @@ func configureAuth(username, password, remoteURL string) (auth.CredentialStore, } } - return credentials{creds: creds}, nil + return credentials{creds: creds}, userpass{username: username, password: password}, nil } func getAuthURLs(remoteURL string) ([]string, error) { diff --git a/registry/proxy/proxyblobstore.go b/registry/proxy/proxyblobstore.go index 0a082888e..7dc3a1c42 100644 --- a/registry/proxy/proxyblobstore.go +++ b/registry/proxy/proxyblobstore.go @@ -33,22 +33,20 @@ var inflight = make(map[digest.Digest]struct{}) // mu protects inflight var mu sync.Mutex -func setResponseHeaders(w http.ResponseWriter, length int64, mediaType string, digest digest.Digest) { - w.Header().Set("Content-Length", strconv.FormatInt(length, 10)) - w.Header().Set("Content-Type", mediaType) - w.Header().Set("Docker-Content-Digest", digest.String()) - w.Header().Set("Etag", digest.String()) +func setResponseHeaders(h http.Header, length int64, mediaType string, digest digest.Digest) { + h.Set("Content-Length", strconv.FormatInt(length, 10)) + h.Set("Content-Type", mediaType) + h.Set("Docker-Content-Digest", digest.String()) + h.Set("Etag", digest.String()) } -func (pbs *proxyBlobStore) copyContent(ctx context.Context, dgst digest.Digest, writer io.Writer) (distribution.Descriptor, error) { +func (pbs *proxyBlobStore) copyContent(ctx context.Context, dgst digest.Digest, writer io.Writer, h http.Header) (distribution.Descriptor, error) { desc, err := pbs.remoteStore.Stat(ctx, dgst) if err != nil { return distribution.Descriptor{}, err } - if w, ok := writer.(http.ResponseWriter); ok { - setResponseHeaders(w, desc.Size, desc.MediaType, dgst) - } + setResponseHeaders(h, desc.Size, desc.MediaType, dgst) remoteReader, err := pbs.remoteStore.Open(ctx, dgst) if err != nil { @@ -102,7 +100,7 @@ func (pbs *proxyBlobStore) ServeBlob(ctx context.Context, w http.ResponseWriter, // Will return the blob from the remote store directly. // TODO Maybe we could reuse the these blobs are serving remotely and caching locally. mu.Unlock() - _, err := pbs.copyContent(ctx, dgst, w) + _, err := pbs.copyContent(ctx, dgst, w, w.Header()) return err } inflight[dgst] = struct{}{} @@ -122,7 +120,7 @@ func (pbs *proxyBlobStore) ServeBlob(ctx context.Context, w http.ResponseWriter, // Serving client and storing locally over same fetching request. // This can prevent a redundant blob fetching. multiWriter := io.MultiWriter(w, bw) - desc, err := pbs.copyContent(ctx, dgst, multiWriter) + desc, err := pbs.copyContent(ctx, dgst, multiWriter, w.Header()) if err != nil { return err } diff --git a/registry/proxy/proxyblobstore_test.go b/registry/proxy/proxyblobstore_test.go index e7dccc09e..91218a401 100644 --- a/registry/proxy/proxyblobstore_test.go +++ b/registry/proxy/proxyblobstore_test.go @@ -448,12 +448,22 @@ func testProxyStoreServe(t *testing.T, te *testEnv, numClients int) { return } - bodyBytes := w.Body.Bytes() + resp := w.Result() + bodyBytes, err := io.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + t.Errorf(err.Error()) + return + } localDigest := digest.FromBytes(bodyBytes) if localDigest != remoteBlob.Digest { t.Errorf("Mismatching blob fetch from proxy") return } + if resp.Header.Get("Docker-Content-Digest") != localDigest.String() { + t.Errorf("Mismatching digest in response header") + return + } desc, err := te.store.localStore.Stat(te.ctx, remoteBlob.Digest) if err != nil { diff --git a/registry/proxy/proxymetrics.go b/registry/proxy/proxymetrics.go index e2c5d2429..0b492fe12 100644 --- a/registry/proxy/proxymetrics.go +++ b/registry/proxy/proxymetrics.go @@ -62,6 +62,16 @@ func init() { })) metrics.Register(prometheus.ProxyNamespace) + initPrometheusMetrics("blob") + initPrometheusMetrics("manifest") +} + +func initPrometheusMetrics(value string) { + requests.WithValues(value).Inc(0) + hits.WithValues(value).Inc(0) + misses.WithValues(value).Inc(0) + pulledBytes.WithValues(value).Inc(0) + pushedBytes.WithValues(value).Inc(0) } // BlobPull tracks metrics about blobs pulled into the cache diff --git a/registry/proxy/proxyregistry.go b/registry/proxy/proxyregistry.go index 33dcc4afa..e8bbe6bdf 100644 --- a/registry/proxy/proxyregistry.go +++ b/registry/proxy/proxyregistry.go @@ -8,6 +8,8 @@ import ( "sync" "time" + "github.com/distribution/reference" + "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/configuration" "github.com/distribution/distribution/v3/internal/client" @@ -18,7 +20,6 @@ import ( "github.com/distribution/distribution/v3/registry/proxy/scheduler" "github.com/distribution/distribution/v3/registry/storage" "github.com/distribution/distribution/v3/registry/storage/driver" - "github.com/distribution/reference" ) var repositoryTTL = 24 * 7 * time.Hour @@ -30,6 +31,7 @@ type proxyingRegistry struct { ttl *time.Duration remoteURL url.URL authChallenger authChallenger + basicAuth auth.CredentialStore } // NewRegistryPullThroughCache creates a registry acting as a pull through cache @@ -112,7 +114,7 @@ func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Name } } - cs, err := configureAuth(config.Username, config.Password, config.RemoteURL) + cs, b, err := configureAuth(config.Username, config.Password, config.RemoteURL) if err != nil { return nil, err } @@ -127,6 +129,7 @@ func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Name cm: challenge.NewSimpleManager(), cs: cs, }, + basicAuth: b, }, nil } @@ -155,7 +158,8 @@ func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named tr := transport.NewTransport(http.DefaultTransport, auth.NewAuthorizer(c.challengeManager(), - auth.NewTokenHandlerWithOptions(tkopts))) + auth.NewTokenHandlerWithOptions(tkopts), + auth.NewBasicHandler(pr.basicAuth))) localRepo, err := pr.embedded.Repository(ctx, name) if err != nil { @@ -211,6 +215,15 @@ func (pr *proxyingRegistry) BlobStatter() distribution.BlobStatter { return pr.embedded.BlobStatter() } +type Closer interface { + // Close release all resources used by this object + Close() error +} + +func (pr *proxyingRegistry) Close() error { + return pr.scheduler.Stop() +} + // authChallenger encapsulates a request to the upstream to establish credential challenges type authChallenger interface { tryEstablishChallenges(context.Context) error diff --git a/registry/proxy/scheduler/scheduler.go b/registry/proxy/scheduler/scheduler.go index ed1d9d419..78366c2ff 100644 --- a/registry/proxy/scheduler/scheduler.go +++ b/registry/proxy/scheduler/scheduler.go @@ -206,12 +206,13 @@ func (ttles *TTLExpirationScheduler) startTimer(entry *schedulerEntry, ttl time. } // Stop stops the scheduler. -func (ttles *TTLExpirationScheduler) Stop() { +func (ttles *TTLExpirationScheduler) Stop() error { ttles.Lock() defer ttles.Unlock() - if err := ttles.writeState(); err != nil { - dcontext.GetLogger(ttles.ctx).Errorf("Error writing scheduler state: %s", err) + err := ttles.writeState() + if err != nil { + err = fmt.Errorf("error writing scheduler state: %w", err) } for _, entry := range ttles.entries { @@ -221,6 +222,7 @@ func (ttles *TTLExpirationScheduler) Stop() { close(ttles.doneChan) ttles.saveTimer.Stop() ttles.stopped = true + return err } func (ttles *TTLExpirationScheduler) writeState() error { diff --git a/registry/proxy/scheduler/scheduler_test.go b/registry/proxy/scheduler/scheduler_test.go index 38fa0f580..fb0869c2d 100644 --- a/registry/proxy/scheduler/scheduler_test.go +++ b/registry/proxy/scheduler/scheduler_test.go @@ -136,7 +136,12 @@ func TestRestoreOld(t *testing.T) { if err != nil { t.Fatalf("Error starting ttlExpirationScheduler: %s", err) } - defer s.Stop() + defer func(s *TTLExpirationScheduler) { + err := s.Stop() + if err != nil { + t.Fatalf("Error stopping ttlExpirationScheduler: %s", err) + } + }(s) wg.Wait() mu.Lock() @@ -177,7 +182,10 @@ func TestStopRestore(t *testing.T) { // Start and stop before all operations complete // state will be written to fs - s.Stop() + err = s.Stop() + if err != nil { + t.Fatalf(err.Error()) + } time.Sleep(10 * time.Millisecond) // v2 will restore state from fs diff --git a/registry/registry.go b/registry/registry.go index 6fb79f189..3d3bf1eb1 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "errors" "fmt" "net/http" "os" @@ -20,6 +21,8 @@ import ( "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "golang.org/x/crypto/acme" "golang.org/x/crypto/acme/autocert" + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" "github.com/distribution/distribution/v3/configuration" "github.com/distribution/distribution/v3/health" @@ -79,9 +82,6 @@ var tlsVersions = map[string]uint16{ // defaultLogFormatter is the default formatter to use for logs. const defaultLogFormatter = "text" -// this channel gets notified when process receives signal. It is global to ease unit testing -var quit = make(chan os.Signal, 1) - // HandlerFunc defines an http middleware type HandlerFunc func(config *configuration.Configuration, handler http.Handler) http.Handler @@ -99,7 +99,7 @@ var ServeCmd = &cobra.Command{ Long: "`serve` stores and distributes Docker images.", Run: func(cmd *cobra.Command, args []string) { // setup context - ctx := dcontext.WithVersion(dcontext.Background(), version.Version) + ctx := dcontext.WithVersion(dcontext.Background(), version.Version()) config, err := resolveConfiguration(args) if err != nil { @@ -128,6 +128,7 @@ type Registry struct { config *configuration.Configuration app *handlers.App server *http.Server + quit chan os.Signal } // NewRegistry creates a new registry from a context and configuration struct. @@ -158,6 +159,9 @@ func NewRegistry(ctx context.Context, config *configuration.Configuration) (*Reg if err != nil { return nil, fmt.Errorf("error during open telemetry initialization: %v", err) } + if config.HTTP.H2C.Enabled { + handler = h2c.NewHandler(handler, &http2.Server{}) + } handler = otelHandler(handler) server := &http.Server{ @@ -168,6 +172,7 @@ func NewRegistry(ctx context.Context, config *configuration.Configuration) (*Reg app: app, config: config, server: server, + quit: make(chan os.Signal, 1), }, nil } @@ -308,7 +313,7 @@ func (registry *Registry) ListenAndServe() error { } // setup channel to get notified on SIGTERM signal - signal.Notify(quit, syscall.SIGTERM) + signal.Notify(registry.quit, os.Interrupt, syscall.SIGTERM) serveErr := make(chan error) // Start serving in goroutine and listen for stop signal in main thread @@ -319,15 +324,24 @@ func (registry *Registry) ListenAndServe() error { select { case err := <-serveErr: return err - case <-quit: + case <-registry.quit: dcontext.GetLogger(registry.app).Info("stopping server gracefully. Draining connections for ", config.HTTP.DrainTimeout) // shutdown the server with a grace period of configured timeout c, cancel := context.WithTimeout(context.Background(), config.HTTP.DrainTimeout) defer cancel() - return registry.server.Shutdown(c) + return registry.Shutdown(c) } } +// Shutdown gracefully shuts down the registry's HTTP server and application object. +func (registry *Registry) Shutdown(ctx context.Context) error { + err := registry.server.Shutdown(ctx) + if appErr := registry.app.Shutdown(); appErr != nil { + err = errors.Join(err, appErr) + } + return err +} + func configureDebugServer(config *configuration.Configuration) { if config.HTTP.Debug.Addr != "" { go func(addr string) { diff --git a/registry/registry_test.go b/registry/registry_test.go index 98dd6c94d..ea23b9db1 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -103,7 +103,7 @@ func TestGracefulShutdown(t *testing.T) { fmt.Fprintf(conn, "GET /v2/ ") // send stop signal - quit <- os.Interrupt + registry.quit <- os.Interrupt time.Sleep(100 * time.Millisecond) // try connecting again. it shouldn't @@ -325,7 +325,7 @@ func TestRegistrySupportedCipherSuite(t *testing.T) { } // send stop signal - quit <- os.Interrupt + registry.quit <- os.Interrupt time.Sleep(100 * time.Millisecond) } @@ -369,7 +369,7 @@ func TestRegistryUnsupportedCipherSuite(t *testing.T) { } // send stop signal - quit <- os.Interrupt + registry.quit <- os.Interrupt time.Sleep(100 * time.Millisecond) } diff --git a/registry/storage/blob_test.go b/registry/storage/blob_test.go index 768cd65c3..25ac325c6 100644 --- a/registry/storage/blob_test.go +++ b/registry/storage/blob_test.go @@ -40,7 +40,7 @@ func TestWriteSeek(t *testing.T) { } contents := []byte{1, 2, 3} if _, err := blobUpload.Write(contents); err != nil { - t.Fatalf("unexpected error writing contets: %v", err) + t.Fatalf("unexpected error writing contents: %v", err) } blobUpload.Close() offset := blobUpload.Size() diff --git a/registry/storage/blobwriter.go b/registry/storage/blobwriter.go index 9de3b0c2f..624098127 100644 --- a/registry/storage/blobwriter.go +++ b/registry/storage/blobwriter.go @@ -230,7 +230,7 @@ func (bw *blobWriter) validateBlob(ctx context.Context, desc distribution.Descri } if fullHash { - // a fantastic optimization: if the the written data and the size are + // a fantastic optimization: if the written data and the size are // the same, we don't need to read the data from the backend. This is // because we've written the entire file in the lifecycle of the // current instance. diff --git a/registry/storage/cache/redis/redis.go b/registry/storage/cache/redis/redis.go index d2596b7bc..baae8e1be 100644 --- a/registry/storage/cache/redis/redis.go +++ b/registry/storage/cache/redis/redis.go @@ -25,7 +25,7 @@ import ( // Note that there is no implied relationship between these two caches. The // layer may exist in one, both or none and the code must be written this way. type redisBlobDescriptorService struct { - pool *redis.Client + pool redis.UniversalClient // TODO(stevvooe): We use a pool because we don't have great control over // the cache lifecycle to manage connections. A new connection if fetched @@ -37,7 +37,7 @@ var _ distribution.BlobDescriptorService = &redisBlobDescriptorService{} // NewRedisBlobDescriptorCacheProvider returns a new redis-based // BlobDescriptorCacheProvider using the provided redis connection pool. -func NewRedisBlobDescriptorCacheProvider(pool *redis.Client) cache.BlobDescriptorCacheProvider { +func NewRedisBlobDescriptorCacheProvider(pool redis.UniversalClient) cache.BlobDescriptorCacheProvider { return metrics.NewPrometheusCacheProvider( &redisBlobDescriptorService{ pool: pool, diff --git a/registry/storage/cache/redis/redis_test.go b/registry/storage/cache/redis/redis_test.go index 328a2ec93..2b057ca82 100644 --- a/registry/storage/cache/redis/redis_test.go +++ b/registry/storage/cache/redis/redis_test.go @@ -20,7 +20,7 @@ func init() { // implementation. func TestRedisBlobDescriptorCacheProvider(t *testing.T) { if redisAddr == "" { - // fallback to an environement variable + // fallback to an environment variable redisAddr = os.Getenv("TEST_REGISTRY_STORAGE_CACHE_REDIS_ADDR") } diff --git a/registry/storage/driver/base/base.go b/registry/storage/driver/base/base.go index 32c9037ba..9dbb609ea 100644 --- a/registry/storage/driver/base/base.go +++ b/registry/storage/driver/base/base.go @@ -46,12 +46,20 @@ import ( "github.com/distribution/distribution/v3/internal/dcontext" prometheus "github.com/distribution/distribution/v3/metrics" storagedriver "github.com/distribution/distribution/v3/registry/storage/driver" + "github.com/distribution/distribution/v3/tracing" "github.com/docker/go-metrics" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) // storageAction is the metrics of blob related operations var storageAction = prometheus.StorageNamespace.NewLabeledTimer("action", "The number of seconds that the storage action takes", "driver", "action") +// tracer is the OpenTelemetry tracer utilized for tracing operations within +// this package's code. +var tracer = otel.Tracer("github.com/distribution/distribution/v3/registry/storage/driver/base") + func init() { metrics.Register(prometheus.StorageNamespace) } @@ -89,8 +97,16 @@ func (base *Base) setDriverName(e error) error { // GetContent wraps GetContent of underlying storage driver. func (base *Base) GetContent(ctx context.Context, path string) ([]byte, error) { - ctx, done := dcontext.WithTrace(ctx) - defer done("%s.GetContent(%q)", base.Name(), path) + attrs := []attribute.KeyValue{ + attribute.String(tracing.AttributePrefix+"storage.driver.name", base.Name()), + attribute.String(tracing.AttributePrefix+"storage.path", path), + } + ctx, span := tracer.Start( + ctx, + "GetContent", + trace.WithAttributes(attrs...)) + + defer span.End() if !storagedriver.PathRegexp.MatchString(path) { return nil, storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()} @@ -104,8 +120,17 @@ func (base *Base) GetContent(ctx context.Context, path string) ([]byte, error) { // PutContent wraps PutContent of underlying storage driver. func (base *Base) PutContent(ctx context.Context, path string, content []byte) error { - ctx, done := dcontext.WithTrace(ctx) - defer done("%s.PutContent(%q)", base.Name(), path) + attrs := []attribute.KeyValue{ + attribute.String(tracing.AttributePrefix+"storage.driver.name", base.Name()), + attribute.String(tracing.AttributePrefix+"storage.path", path), + attribute.Int(tracing.AttributePrefix+"storage.content.length", len(content)), + } + ctx, span := tracer.Start( + ctx, + "PutContent", + trace.WithAttributes(attrs...)) + + defer span.End() if !storagedriver.PathRegexp.MatchString(path) { return storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()} @@ -119,8 +144,17 @@ func (base *Base) PutContent(ctx context.Context, path string, content []byte) e // Reader wraps Reader of underlying storage driver. func (base *Base) Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { - ctx, done := dcontext.WithTrace(ctx) - defer done("%s.Reader(%q, %d)", base.Name(), path, offset) + attrs := []attribute.KeyValue{ + attribute.String(tracing.AttributePrefix+"storage.driver.name", base.Name()), + attribute.String(tracing.AttributePrefix+"storage.path", path), + attribute.Int64(tracing.AttributePrefix+"storage.offset", offset), + } + ctx, span := tracer.Start( + ctx, + "Reader", + trace.WithAttributes(attrs...)) + + defer span.End() if offset < 0 { return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset, DriverName: base.StorageDriver.Name()} @@ -136,8 +170,17 @@ func (base *Base) Reader(ctx context.Context, path string, offset int64) (io.Rea // Writer wraps Writer of underlying storage driver. func (base *Base) Writer(ctx context.Context, path string, append bool) (storagedriver.FileWriter, error) { - ctx, done := dcontext.WithTrace(ctx) - defer done("%s.Writer(%q, %v)", base.Name(), path, append) + attrs := []attribute.KeyValue{ + attribute.String(tracing.AttributePrefix+"storage.driver.name", base.Name()), + attribute.String(tracing.AttributePrefix+"storage.path", path), + attribute.Bool(tracing.AttributePrefix+"storage.append", append), + } + ctx, span := tracer.Start( + ctx, + "Writer", + trace.WithAttributes(attrs...)) + + defer span.End() if !storagedriver.PathRegexp.MatchString(path) { return nil, storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()} @@ -149,8 +192,16 @@ func (base *Base) Writer(ctx context.Context, path string, append bool) (storage // Stat wraps Stat of underlying storage driver. func (base *Base) Stat(ctx context.Context, path string) (storagedriver.FileInfo, error) { - ctx, done := dcontext.WithTrace(ctx) - defer done("%s.Stat(%q)", base.Name(), path) + attrs := []attribute.KeyValue{ + attribute.String(tracing.AttributePrefix+"storage.driver.name", base.Name()), + attribute.String(tracing.AttributePrefix+"storage.path", path), + } + ctx, span := tracer.Start( + ctx, + "Stat", + trace.WithAttributes(attrs...)) + + defer span.End() if !storagedriver.PathRegexp.MatchString(path) && path != "/" { return nil, storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()} @@ -164,8 +215,16 @@ func (base *Base) Stat(ctx context.Context, path string) (storagedriver.FileInfo // List wraps List of underlying storage driver. func (base *Base) List(ctx context.Context, path string) ([]string, error) { - ctx, done := dcontext.WithTrace(ctx) - defer done("%s.List(%q)", base.Name(), path) + attrs := []attribute.KeyValue{ + attribute.String(tracing.AttributePrefix+"storage.driver.name", base.Name()), + attribute.String(tracing.AttributePrefix+"storage.path", path), + } + ctx, span := tracer.Start( + ctx, + "List", + trace.WithAttributes(attrs...)) + + defer span.End() if !storagedriver.PathRegexp.MatchString(path) && path != "/" { return nil, storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()} @@ -179,6 +238,18 @@ func (base *Base) List(ctx context.Context, path string) ([]string, error) { // Move wraps Move of underlying storage driver. func (base *Base) Move(ctx context.Context, sourcePath string, destPath string) error { + attrs := []attribute.KeyValue{ + attribute.String(tracing.AttributePrefix+"storage.driver.name", base.Name()), + attribute.String(tracing.AttributePrefix+"storage.source.path", sourcePath), + attribute.String(tracing.AttributePrefix+"storage.dest.path", destPath), + } + ctx, span := tracer.Start( + ctx, + "Move", + trace.WithAttributes(attrs...)) + + defer span.End() + ctx, done := dcontext.WithTrace(ctx) defer done("%s.Move(%q, %q", base.Name(), sourcePath, destPath) @@ -196,8 +267,16 @@ func (base *Base) Move(ctx context.Context, sourcePath string, destPath string) // Delete wraps Delete of underlying storage driver. func (base *Base) Delete(ctx context.Context, path string) error { - ctx, done := dcontext.WithTrace(ctx) - defer done("%s.Delete(%q)", base.Name(), path) + attrs := []attribute.KeyValue{ + attribute.String(tracing.AttributePrefix+"storage.driver.name", base.Name()), + attribute.String(tracing.AttributePrefix+"storage.path", path), + } + ctx, span := tracer.Start( + ctx, + "Delete", + trace.WithAttributes(attrs...)) + + defer span.End() if !storagedriver.PathRegexp.MatchString(path) { return storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()} @@ -211,8 +290,16 @@ func (base *Base) Delete(ctx context.Context, path string) error { // RedirectURL wraps RedirectURL of the underlying storage driver. func (base *Base) RedirectURL(r *http.Request, path string) (string, error) { - ctx, done := dcontext.WithTrace(r.Context()) - defer done("%s.RedirectURL(%q)", base.Name(), path) + attrs := []attribute.KeyValue{ + attribute.String(tracing.AttributePrefix+"storage.driver.name", base.Name()), + attribute.String(tracing.AttributePrefix+"storage.path", path), + } + ctx, span := tracer.Start( + r.Context(), + "RedirectURL", + trace.WithAttributes(attrs...)) + + defer span.End() if !storagedriver.PathRegexp.MatchString(path) { return "", storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()} @@ -226,8 +313,16 @@ func (base *Base) RedirectURL(r *http.Request, path string) (string, error) { // Walk wraps Walk of underlying storage driver. func (base *Base) Walk(ctx context.Context, path string, f storagedriver.WalkFn, options ...func(*storagedriver.WalkOptions)) error { - ctx, done := dcontext.WithTrace(ctx) - defer done("%s.Walk(%q)", base.Name(), path) + attrs := []attribute.KeyValue{ + attribute.String(tracing.AttributePrefix+"storage.driver.name", base.Name()), + attribute.String(tracing.AttributePrefix+"storage.path", path), + } + ctx, span := tracer.Start( + ctx, + "Walk", + trace.WithAttributes(attrs...)) + + defer span.End() if !storagedriver.PathRegexp.MatchString(path) && path != "/" { return storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()} diff --git a/registry/storage/driver/gcs/gcs.go b/registry/storage/driver/gcs/gcs.go index beb9261ed..9676f7a09 100644 --- a/registry/storage/driver/gcs/gcs.go +++ b/registry/storage/driver/gcs/gcs.go @@ -155,6 +155,7 @@ func FromParameters(ctx context.Context, parameters map[string]interface{}) (sto jwtConf := new(jwt.Config) var err error var gcs *storage.Client + var options []option.ClientOption if keyfile, ok := parameters["keyfile"]; ok { jsonKey, err := os.ReadFile(fmt.Sprint(keyfile)) if err != nil { @@ -165,10 +166,7 @@ func FromParameters(ctx context.Context, parameters map[string]interface{}) (sto return nil, err } ts = jwtConf.TokenSource(ctx) - gcs, err = storage.NewClient(ctx, option.WithCredentialsFile(fmt.Sprint(keyfile))) - if err != nil { - return nil, err - } + options = append(options, option.WithCredentialsFile(fmt.Sprint(keyfile))) } else if credentials, ok := parameters["credentials"]; ok { credentialMap, ok := credentials.(map[interface{}]interface{}) if !ok { @@ -194,10 +192,7 @@ func FromParameters(ctx context.Context, parameters map[string]interface{}) (sto return nil, err } ts = jwtConf.TokenSource(ctx) - gcs, err = storage.NewClient(ctx, option.WithCredentialsJSON(data)) - if err != nil { - return nil, err - } + options = append(options, option.WithCredentialsJSON(data)) } else { var err error // DefaultTokenSource is a convenience method. It first calls FindDefaultCredentials, @@ -207,12 +202,19 @@ func FromParameters(ctx context.Context, parameters map[string]interface{}) (sto if err != nil { return nil, err } - gcs, err = storage.NewClient(ctx) - if err != nil { - return nil, err + } + + if userAgent, ok := parameters["useragent"]; ok { + if ua, ok := userAgent.(string); ok && ua != "" { + options = append(options, option.WithUserAgent(ua)) } } + gcs, err = storage.NewClient(ctx, options...) + if err != nil { + return nil, err + } + maxConcurrency, err := base.GetLimitFromParameter(parameters["maxconcurrency"], minConcurrency, defaultMaxConcurrency) if err != nil { return nil, fmt.Errorf("maxconcurrency config error: %s", err) @@ -783,10 +785,6 @@ func (d *driver) Delete(ctx context.Context, path string) error { // RedirectURL returns a URL which may be used to retrieve the content stored at // the given path, possibly using the given options. func (d *driver) RedirectURL(r *http.Request, path string) (string, error) { - if d.privateKey == nil { - return "", nil - } - if r.Method != http.MethodGet && r.Method != http.MethodHead { return "", nil } diff --git a/registry/storage/driver/gcs/gcs_test.go b/registry/storage/driver/gcs/gcs_test.go index e04230bd7..b8e54b802 100644 --- a/registry/storage/driver/gcs/gcs_test.go +++ b/registry/storage/driver/gcs/gcs_test.go @@ -34,40 +34,40 @@ func init() { } } - jsonKey, err := os.ReadFile(credentials) - if err != nil { - panic(fmt.Sprintf("Error reading JSON key : %v", err)) - } - - var ts oauth2.TokenSource - var email string - var privateKey []byte - - ts, err = google.DefaultTokenSource(dcontext.Background(), storage.ScopeFullControl) - if err != nil { - // Assume that the file contents are within the environment variable since it exists - // but does not contain a valid file path - jwtConfig, err := google.JWTConfigFromJSON(jsonKey, storage.ScopeFullControl) - if err != nil { - panic(fmt.Sprintf("Error reading JWT config : %s", err)) - } - email = jwtConfig.Email - privateKey = jwtConfig.PrivateKey - if len(privateKey) == 0 { - panic("Error reading JWT config : missing private_key property") - } - if email == "" { - panic("Error reading JWT config : missing client_email property") - } - ts = jwtConfig.TokenSource(dcontext.Background()) - } - - gcs, err := storage.NewClient(dcontext.Background(), option.WithCredentialsJSON(jsonKey)) - if err != nil { - panic(fmt.Sprintf("Error initializing gcs client : %v", err)) - } - gcsDriverConstructor = func(rootDirectory string) (storagedriver.StorageDriver, error) { + jsonKey, err := os.ReadFile(credentials) + if err != nil { + panic(fmt.Sprintf("Error reading JSON key : %v", err)) + } + + var ts oauth2.TokenSource + var email string + var privateKey []byte + + ts, err = google.DefaultTokenSource(dcontext.Background(), storage.ScopeFullControl) + if err != nil { + // Assume that the file contents are within the environment variable since it exists + // but does not contain a valid file path + jwtConfig, err := google.JWTConfigFromJSON(jsonKey, storage.ScopeFullControl) + if err != nil { + panic(fmt.Sprintf("Error reading JWT config : %s", err)) + } + email = jwtConfig.Email + privateKey = jwtConfig.PrivateKey + if len(privateKey) == 0 { + panic("Error reading JWT config : missing private_key property") + } + if email == "" { + panic("Error reading JWT config : missing client_email property") + } + ts = jwtConfig.TokenSource(dcontext.Background()) + } + + gcs, err := storage.NewClient(dcontext.Background(), option.WithCredentialsJSON(jsonKey)) + if err != nil { + panic(fmt.Sprintf("Error initializing gcs client : %v", err)) + } + parameters := driverParameters{ bucket: bucket, rootDirectory: rootDirectory, diff --git a/registry/storage/driver/middleware/cloudfront/middleware_test.go b/registry/storage/driver/middleware/cloudfront/middleware_test.go index 9d1e11a22..2fa8243d8 100644 --- a/registry/storage/driver/middleware/cloudfront/middleware_test.go +++ b/registry/storage/driver/middleware/cloudfront/middleware_test.go @@ -50,6 +50,6 @@ pZeMRablbPQdp8/1NyIwimq1VlG0ohQ4P6qhW7E09ZMC t.Fatal(err) } if storageDriver == nil { - t.Fatal("Driver couldnt be initialized.") + t.Fatal("Driver could not be initialized") } } diff --git a/registry/storage/driver/middleware/rewrite/middleware.go b/registry/storage/driver/middleware/rewrite/middleware.go new file mode 100644 index 000000000..7baef518d --- /dev/null +++ b/registry/storage/driver/middleware/rewrite/middleware.go @@ -0,0 +1,86 @@ +package middleware + +import ( + "context" + "fmt" + "net/http" + "net/url" + "strings" + + storagedriver "github.com/distribution/distribution/v3/registry/storage/driver" + storagemiddleware "github.com/distribution/distribution/v3/registry/storage/driver/middleware" + "github.com/sirupsen/logrus" +) + +func init() { + if err := storagemiddleware.Register("rewrite", newRewriteStorageMiddleware); err != nil { + logrus.Errorf("tailed to register redirect storage middleware: %v", err) + } +} + +type rewriteStorageMiddleware struct { + storagedriver.StorageDriver + overrideScheme string + overrideHost string + trimPathPrefix string +} + +var _ storagedriver.StorageDriver = &rewriteStorageMiddleware{} + +func getStringOption(key string, options map[string]interface{}) (string, error) { + o, ok := options[key] + if !ok { + return "", nil + } + s, ok := o.(string) + if !ok { + return "", fmt.Errorf("%s must be a string", key) + } + return s, nil +} + +func newRewriteStorageMiddleware(ctx context.Context, sd storagedriver.StorageDriver, options map[string]interface{}) (storagedriver.StorageDriver, error) { + var err error + + r := &rewriteStorageMiddleware{StorageDriver: sd} + + if r.overrideScheme, err = getStringOption("scheme", options); err != nil { + return nil, err + } + + if r.overrideHost, err = getStringOption("host", options); err != nil { + return nil, err + } + + if r.trimPathPrefix, err = getStringOption("trimpathprefix", options); err != nil { + return nil, err + } + + return r, nil +} + +func (r *rewriteStorageMiddleware) RedirectURL(req *http.Request, path string) (string, error) { + storagePath, err := r.StorageDriver.RedirectURL(req, path) + if err != nil { + return "", err + } + + u, err := url.Parse(storagePath) + if err != nil { + return "", err + } + + if r.overrideScheme != "" { + u.Scheme = r.overrideScheme + } + + if r.overrideHost != "" { + u.Host = r.overrideHost + } + + if r.trimPathPrefix != "" { + u.Path = strings.TrimPrefix(u.Path, r.trimPathPrefix) + } + + return u.String(), nil +} diff --git a/registry/storage/driver/middleware/rewrite/middleware_test.go b/registry/storage/driver/middleware/rewrite/middleware_test.go new file mode 100644 index 000000000..651866cbc --- /dev/null +++ b/registry/storage/driver/middleware/rewrite/middleware_test.go @@ -0,0 +1,75 @@ +package middleware + +import ( + "context" + "net/http" + "testing" + + "github.com/distribution/distribution/v3/registry/storage/driver/base" + "github.com/stretchr/testify/require" +) + +type mockSD struct { + base.Base +} + +func (*mockSD) RedirectURL(_ *http.Request, urlPath string) (string, error) { + return "http://some.host/some/path/file", nil +} + +func TestNoConfig(t *testing.T) { + options := make(map[string]interface{}) + middleware, err := newRewriteStorageMiddleware(context.Background(), &mockSD{}, options) + require.NoError(t, err) + + _, ok := middleware.(*rewriteStorageMiddleware) + require.True(t, ok) + + url, err := middleware.RedirectURL(nil, "") + require.NoError(t, err) + require.Equal(t, "http://some.host/some/path/file", url) +} + +func TestWrongType(t *testing.T) { + options := map[string]interface{}{ + "scheme": 1, + } + _, err := newRewriteStorageMiddleware(context.TODO(), nil, options) + require.ErrorContains(t, err, "scheme must be a string") +} + +func TestRewriteHostsScheme(t *testing.T) { + options := map[string]interface{}{ + "scheme": "https", + "host": "example.com", + } + + middleware, err := newRewriteStorageMiddleware(context.TODO(), &mockSD{}, options) + require.NoError(t, err) + + m, ok := middleware.(*rewriteStorageMiddleware) + require.True(t, ok) + require.Equal(t, "https", m.overrideScheme) + require.Equal(t, "example.com", m.overrideHost) + + url, err := middleware.RedirectURL(nil, "") + require.NoError(t, err) + require.Equal(t, "https://example.com/some/path/file", url) +} + +func TestTrimPrefix(t *testing.T) { + options := map[string]interface{}{ + "trimpathprefix": "/some/path", + } + + middleware, err := newRewriteStorageMiddleware(context.TODO(), &mockSD{}, options) + require.NoError(t, err) + + m, ok := middleware.(*rewriteStorageMiddleware) + require.True(t, ok) + require.Equal(t, "/some/path", m.trimPathPrefix) + + url, err := middleware.RedirectURL(nil, "") + require.NoError(t, err) + require.Equal(t, "http://some.host/file", url) +} diff --git a/registry/storage/driver/s3-aws/s3.go b/registry/storage/driver/s3-aws/s3.go index 60e205dbc..71b7b793a 100644 --- a/registry/storage/driver/s3-aws/s3.go +++ b/registry/storage/driver/s3-aws/s3.go @@ -205,7 +205,7 @@ func FromParameters(ctx context.Context, parameters map[string]interface{}) (*Dr regionEndpoint = "" } - forcePathStyleBool := true + forcePathStyleBool := false forcePathStyle := parameters["forcepathstyle"] switch forcePathStyle := forcePathStyle.(type) { case string: @@ -529,9 +529,9 @@ func New(ctx context.Context, params DriverParameters) (*Driver, error) { if params.RegionEndpoint != "" { awsConfig.WithEndpoint(params.RegionEndpoint) - awsConfig.WithS3ForcePathStyle(params.ForcePathStyle) } + awsConfig.WithS3ForcePathStyle(params.ForcePathStyle) awsConfig.WithS3UseAccelerate(params.Accelerate) awsConfig.WithRegion(params.Region) awsConfig.WithDisableSSL(!params.Secure) @@ -1128,16 +1128,14 @@ func (d *driver) doWalk(parentCtx context.Context, objectCount *int64, from stri // get a list of all inferred directories between the previous directory and this file dirs := directoryDiff(prevDir, filePath) - if len(dirs) > 0 { - for _, dir := range dirs { - walkInfos = append(walkInfos, storagedriver.FileInfoInternal{ - FileInfoFields: storagedriver.FileInfoFields{ - IsDir: true, - Path: dir, - }, - }) - prevDir = dir - } + for _, dir := range dirs { + walkInfos = append(walkInfos, storagedriver.FileInfoInternal{ + FileInfoFields: storagedriver.FileInfoFields{ + IsDir: true, + Path: dir, + }, + }) + prevDir = dir } walkInfos = append(walkInfos, storagedriver.FileInfoInternal{ diff --git a/registry/storage/driver/s3-aws/s3_test.go b/registry/storage/driver/s3-aws/s3_test.go index d79898011..a00666aed 100644 --- a/registry/storage/driver/s3-aws/s3_test.go +++ b/registry/storage/driver/s3-aws/s3_test.go @@ -80,7 +80,7 @@ func init() { return nil, err } } - forcePathStyleBool := true + forcePathStyleBool := false if forcePathStyle != "" { forcePathStyleBool, err = strconv.ParseBool(forcePathStyle) if err != nil { @@ -203,6 +203,8 @@ func TestEmptyRootList(t *testing.T) { } func TestClientTransport(t *testing.T) { + skipCheck(t) + testCases := []struct { skipverify bool }{ @@ -243,7 +245,7 @@ func TestClientTransport(t *testing.T) { return } // if tc.skipverify is false we do not override the driver - // HTTP clien transport and leave it to the AWS SDK. + // HTTP client transport and leave it to the AWS SDK. if s3drv.S3.Client.Config.HTTPClient.Transport != nil { t.Errorf("unexpected S3 driver client transport") } diff --git a/registry/storage/garbagecollect.go b/registry/storage/garbagecollect.go index 7895c9b51..aa5a494d3 100644 --- a/registry/storage/garbagecollect.go +++ b/registry/storage/garbagecollect.go @@ -2,6 +2,7 @@ package storage import ( "context" + "errors" "fmt" "github.com/distribution/distribution/v3" @@ -36,6 +37,7 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis // mark markSet := make(map[digest.Digest]struct{}) + deleteLayerSet := make(map[string][]digest.Digest) manifestArr := make([]ManifestDel, 0) err := repositoryEnumerator.Enumerate(ctx, func(repoName string) error { emit(repoName) @@ -68,12 +70,15 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis return fmt.Errorf("failed to retrieve tags for digest %v: %v", dgst, err) } if len(tags) == 0 { - emit("manifest eligible for deletion: %s", dgst) // fetch all tags from repository // all of these tags could contain manifest in history // which means that we need check (and delete) those references when deleting manifest allTags, err := repository.Tags(ctx).All(ctx) if err != nil { + if _, ok := err.(distribution.ErrRepositoryUnknown); ok { + emit("manifest tags path of repository %s does not exist", repoName) + return nil + } return fmt.Errorf("failed to retrieve tags %v", err) } manifestArr = append(manifestArr, ManifestDel{Name: repoName, Digest: dgst, Tags: allTags}) @@ -84,35 +89,50 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis emit("%s: marking manifest %s ", repoName, dgst) markSet[dgst] = struct{}{} - manifest, err := manifestService.Get(ctx, dgst) - if err != nil { - return fmt.Errorf("failed to retrieve manifest for digest %v: %v", dgst, err) - } - - descriptors := manifest.References() - for _, descriptor := range descriptors { - markSet[descriptor.Digest] = struct{}{} - emit("%s: marking blob %s", repoName, descriptor.Digest) - } - - return nil + return markManifestReferences(dgst, manifestService, ctx, func(d digest.Digest) bool { + _, marked := markSet[d] + if !marked { + markSet[d] = struct{}{} + emit("%s: marking blob %s", repoName, d) + } + return marked + }) }) - // In certain situations such as unfinished uploads, deleting all - // tags in S3 or removing the _manifests folder manually, this - // error may be of type PathNotFound. - // - // In these cases we can continue marking other manifests safely. - if _, ok := err.(driver.PathNotFoundError); ok { - return nil + if err != nil { + // In certain situations such as unfinished uploads, deleting all + // tags in S3 or removing the _manifests folder manually, this + // error may be of type PathNotFound. + // + // In these cases we can continue marking other manifests safely. + if _, ok := err.(driver.PathNotFoundError); !ok { + return err + } + } + blobService := repository.Blobs(ctx) + layerEnumerator, ok := blobService.(distribution.ManifestEnumerator) + if !ok { + return errors.New("unable to convert BlobService into ManifestEnumerator") } + var deleteLayers []digest.Digest + err = layerEnumerator.Enumerate(ctx, func(dgst digest.Digest) error { + if _, ok := markSet[dgst]; !ok { + deleteLayers = append(deleteLayers, dgst) + } + return nil + }) + if len(deleteLayers) > 0 { + deleteLayerSet[repoName] = deleteLayers + } return err }) if err != nil { return fmt.Errorf("failed to mark: %v", err) } + manifestArr = unmarkReferencedManifest(manifestArr, markSet) + // sweep vacuum := NewVacuum(ctx, storageDriver) if !opts.DryRun { @@ -147,5 +167,51 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis } } + for repo, dgsts := range deleteLayerSet { + for _, dgst := range dgsts { + err = vacuum.RemoveLayer(repo, dgst) + if err != nil { + return fmt.Errorf("failed to delete layer link %s of repo %s: %v", dgst, repo, err) + } + } + } + return err } + +// unmarkReferencedManifest filters out manifest present in markSet +func unmarkReferencedManifest(manifestArr []ManifestDel, markSet map[digest.Digest]struct{}) []ManifestDel { + filtered := make([]ManifestDel, 0) + for _, obj := range manifestArr { + if _, ok := markSet[obj.Digest]; !ok { + emit("manifest eligible for deletion: %s", obj) + filtered = append(filtered, obj) + } + } + return filtered +} + +// markManifestReferences marks the manifest references +func markManifestReferences(dgst digest.Digest, manifestService distribution.ManifestService, ctx context.Context, ingester func(digest.Digest) bool) error { + manifest, err := manifestService.Get(ctx, dgst) + if err != nil { + return fmt.Errorf("failed to retrieve manifest for digest %v: %v", dgst, err) + } + + descriptors := manifest.References() + for _, descriptor := range descriptors { + + // do not visit references if already marked + if ingester(descriptor.Digest) { + continue + } + + if ok, _ := manifestService.Exists(ctx, descriptor.Digest); ok { + err := markManifestReferences(descriptor.Digest, manifestService, ctx, ingester) + if err != nil { + return err + } + } + } + return nil +} diff --git a/registry/storage/garbagecollect_test.go b/registry/storage/garbagecollect_test.go index be67ce7a6..15203cde6 100644 --- a/registry/storage/garbagecollect_test.go +++ b/registry/storage/garbagecollect_test.go @@ -7,7 +7,9 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/internal/dcontext" + "github.com/distribution/distribution/v3/manifest/ocischema" "github.com/distribution/distribution/v3/registry/storage/driver" + storagedriver "github.com/distribution/distribution/v3/registry/storage/driver" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" "github.com/distribution/distribution/v3/testutil" "github.com/distribution/reference" @@ -129,6 +131,29 @@ func uploadRandomSchema2Image(t *testing.T, repository distribution.Repository) } } +func uploadRandomOCIImage(t *testing.T, repository distribution.Repository) image { + randomLayers, err := testutil.CreateRandomLayers(2) + if err != nil { + t.Fatalf("%v", err) + } + + digests := []digest.Digest{} + for digest := range randomLayers { + digests = append(digests, digest) + } + manifest, err := testutil.MakeOCIManifest(repository, digests) + if err != nil { + t.Fatalf("%v", err) + } + + manifestDigest := uploadImage(t, repository, image{manifest: manifest, layers: randomLayers}) + return image{ + manifest: manifest, + manifestDigest: manifestDigest, + layers: randomLayers, + } +} + func TestNoDeletionNoEffect(t *testing.T) { ctx := dcontext.Background() inmemoryDriver := inmemory.New() @@ -270,6 +295,202 @@ func TestDeleteManifestIfTagNotFound(t *testing.T) { } } +func TestDeleteManifestIndexWithDanglingReferences(t *testing.T) { + ctx := dcontext.Background() + inmemoryDriver := inmemory.New() + + registry := createRegistry(t, inmemoryDriver) + repo := makeRepository(t, registry, "deletemanifests") + manifestService, _ := repo.Manifests(ctx) + + image1 := uploadRandomOCIImage(t, repo) + image2 := uploadRandomOCIImage(t, repo) + + ii, _ := ocischema.FromDescriptors([]distribution.Descriptor{ + {Digest: image1.manifestDigest}, {Digest: image2.manifestDigest}, + }, map[string]string{}) + + id, err := manifestService.Put(ctx, ii) + if err != nil { + t.Fatalf("manifest upload failed: %v", err) + } + + err = repo.Tags(ctx).Tag(ctx, "test", distribution.Descriptor{Digest: id}) + if err != nil { + t.Fatalf("Failed to delete tag: %v", err) + } + + // delete image2 => ii has a dangling reference + err = manifestService.Delete(ctx, image2.manifestDigest) + if err != nil { + t.Fatalf("Failed to delete image: %v", err) + } + + before1 := allBlobs(t, registry) + before2 := allManifests(t, manifestService) + + // run GC (should not remove anything because of tag) + err = MarkAndSweep(dcontext.Background(), inmemoryDriver, registry, GCOpts{ + DryRun: false, + RemoveUntagged: true, + }) + if err != nil { + t.Fatalf("Failed mark and sweep: %v", err) + } + + after1 := allBlobs(t, registry) + after2 := allManifests(t, manifestService) + if len(before1) == len(after1) { + t.Fatalf("Garbage collection did not affect blobs storage: %d == %d", len(before1), len(after1)) + } + if len(before2) != len(after2) { + t.Fatalf("Garbage collection affected manifest storage: %d != %d", len(before2), len(after2)) + } +} + +func TestDeleteManifestIndexIfTagNotFound(t *testing.T) { + ctx := dcontext.Background() + inmemoryDriver := inmemory.New() + + registry := createRegistry(t, inmemoryDriver) + repo := makeRepository(t, registry, "deletemanifests") + manifestService, _ := repo.Manifests(ctx) + + image1 := uploadRandomOCIImage(t, repo) + image2 := uploadRandomOCIImage(t, repo) + + ii, _ := ocischema.FromDescriptors([]distribution.Descriptor{ + {Digest: image1.manifestDigest}, {Digest: image2.manifestDigest}, + }, map[string]string{}) + + d4, err := manifestService.Put(ctx, ii) + if err != nil { + t.Fatalf("manifest upload failed: %v", err) + } + + err = repo.Tags(ctx).Tag(ctx, "test", distribution.Descriptor{Digest: d4}) + if err != nil { + t.Fatalf("Failed to delete tag: %v", err) + } + + before1 := allBlobs(t, registry) + before2 := allManifests(t, manifestService) + + // run GC (should not remove anything because of tag) + err = MarkAndSweep(dcontext.Background(), inmemoryDriver, registry, GCOpts{ + DryRun: false, + RemoveUntagged: true, + }) + if err != nil { + t.Fatalf("Failed mark and sweep: %v", err) + } + beforeUntag1 := allBlobs(t, registry) + beforeUntag2 := allManifests(t, manifestService) + if len(before1) != len(beforeUntag1) { + t.Fatalf("Garbage collection affected blobs storage: %d != %d", len(before1), len(beforeUntag1)) + } + if len(before2) != len(beforeUntag2) { + t.Fatalf("Garbage collection affected manifest storage: %d != %d", len(before2), len(beforeUntag2)) + } + + err = repo.Tags(ctx).Untag(ctx, "test") + if err != nil { + t.Fatalf("Failed to delete tag: %v", err) + } + + // Run GC (removes everything because no manifests with tags exist) + err = MarkAndSweep(dcontext.Background(), inmemoryDriver, registry, GCOpts{ + DryRun: false, + RemoveUntagged: true, + }) + if err != nil { + t.Fatalf("Failed mark and sweep: %v", err) + } + + after1 := allBlobs(t, registry) + after2 := allManifests(t, manifestService) + if len(beforeUntag1) == len(after1) { + t.Fatalf("Garbage collection did not affect blobs storage: %d == %d", len(beforeUntag1), len(after1)) + } + if len(beforeUntag2) == len(after2) { + t.Fatalf("Garbage collection did not affect manifest storage: %d == %d", len(beforeUntag2), len(after2)) + } +} + +func TestGCWithUnusedLayerLinkPath(t *testing.T) { + ctx := dcontext.Background() + d := inmemory.New() + + registry := createRegistry(t, d) + repo := makeRepository(t, registry, "unusedlayerlink") + image := uploadRandomSchema2Image(t, repo) + + for dgst := range image.layers { + layerLinkPath, err := pathFor(layerLinkPathSpec{name: "unusedlayerlink", digest: dgst}) + if err != nil { + t.Fatal(err) + } + fileInfo, err := d.Stat(ctx, layerLinkPath) + if err != nil { + t.Fatal(err) + } + if fileInfo == nil { + t.Fatalf("layer link path %s not found", layerLinkPath) + } + } + + err := MarkAndSweep(dcontext.Background(), d, registry, GCOpts{ + DryRun: false, + RemoveUntagged: true, + }) + if err != nil { + t.Fatalf("got error: %v, expected nil", err) + } + for dgst := range image.layers { + layerLinkPath, err := pathFor(layerLinkPathSpec{name: "unusedlayerlink", digest: dgst}) + if err != nil { + t.Fatal(err) + } + _, err = d.Stat(ctx, layerLinkPath) + if _, ok := err.(storagedriver.PathNotFoundError); !ok { + t.Fatalf("layer link path %s should be not found", layerLinkPath) + } + } +} + +func TestGCWithUnknownRepository(t *testing.T) { + ctx := dcontext.Background() + d := inmemory.New() + + registry := createRegistry(t, d) + repo := makeRepository(t, registry, "nonexistentrepo") + image := uploadRandomSchema2Image(t, repo) + + err := repo.Tags(ctx).Tag(ctx, "image", distribution.Descriptor{Digest: image.manifestDigest}) + if err != nil { + t.Fatalf("Failed to tag descriptor: %v", err) + } + + // Simulate a missing _manifests/tags directory + manifestTagsPath, err := pathFor(manifestTagsPathSpec{"nonexistentrepo"}) + if err != nil { + t.Fatal(err) + } + + err = d.Delete(ctx, manifestTagsPath) + if err != nil { + t.Fatal(err) + } + + err = MarkAndSweep(dcontext.Background(), d, registry, GCOpts{ + DryRun: false, + RemoveUntagged: true, + }) + if err != nil { + t.Fatalf("got error: %v, expected nil", err) + } +} + func TestGCWithMissingManifests(t *testing.T) { ctx := dcontext.Background() d := inmemory.New() @@ -479,3 +700,225 @@ func TestOrphanBlobDeleted(t *testing.T) { } } } + +func TestTaggedManifestlistWithUntaggedManifest(t *testing.T) { + ctx := dcontext.Background() + inmemoryDriver := inmemory.New() + + registry := createRegistry(t, inmemoryDriver) + repo := makeRepository(t, registry, "foo/taggedlist/untaggedmanifest") + manifestService, err := repo.Manifests(ctx) + if err != nil { + t.Fatalf("%v", err) + } + + image1 := uploadRandomSchema2Image(t, repo) + image2 := uploadRandomSchema2Image(t, repo) + + // construct a manifestlist to reference manifests that is not tagged. + blobstatter := registry.BlobStatter() + manifestList, err := testutil.MakeManifestList(blobstatter, []digest.Digest{ + image1.manifestDigest, image2.manifestDigest, + }) + if err != nil { + t.Fatalf("Failed to make manifest list: %v", err) + } + + dgst, err := manifestService.Put(ctx, manifestList) + if err != nil { + t.Fatalf("Failed to add manifest list: %v", err) + } + + err = repo.Tags(ctx).Tag(ctx, "test", distribution.Descriptor{Digest: dgst}) + if err != nil { + t.Fatalf("Failed to delete tag: %v", err) + } + + before := allBlobs(t, registry) + + // Run GC + err = MarkAndSweep(dcontext.Background(), inmemoryDriver, registry, GCOpts{ + DryRun: false, + RemoveUntagged: true, + }) + if err != nil { + t.Fatalf("Failed mark and sweep: %v", err) + } + + after := allBlobs(t, registry) + if len(before) != len(after) { + t.Fatalf("Garbage collection affected storage: %d != %d", len(before), len(after)) + } + + if _, ok := after[image1.manifestDigest]; !ok { + t.Fatalf("First manifest is missing") + } + + if _, ok := after[image2.manifestDigest]; !ok { + t.Fatalf("Second manifest is missing") + } + + if _, ok := after[dgst]; !ok { + t.Fatalf("Manifest list is missing") + } +} + +func TestUnTaggedManifestlistWithUntaggedManifest(t *testing.T) { + ctx := dcontext.Background() + inmemoryDriver := inmemory.New() + + registry := createRegistry(t, inmemoryDriver) + repo := makeRepository(t, registry, "foo/untaggedlist/untaggedmanifest") + manifestService, err := repo.Manifests(ctx) + if err != nil { + t.Fatalf("%v", err) + } + + image1 := uploadRandomSchema2Image(t, repo) + image2 := uploadRandomSchema2Image(t, repo) + + // construct a manifestlist to reference manifests that is not tagged. + blobstatter := registry.BlobStatter() + manifestList, err := testutil.MakeManifestList(blobstatter, []digest.Digest{ + image1.manifestDigest, image2.manifestDigest, + }) + if err != nil { + t.Fatalf("Failed to make manifest list: %v", err) + } + + _, err = manifestService.Put(ctx, manifestList) + if err != nil { + t.Fatalf("Failed to add manifest list: %v", err) + } + + // Run GC + err = MarkAndSweep(dcontext.Background(), inmemoryDriver, registry, GCOpts{ + DryRun: false, + RemoveUntagged: true, + }) + if err != nil { + t.Fatalf("Failed mark and sweep: %v", err) + } + + after := allBlobs(t, registry) + if len(after) != 0 { + t.Fatalf("Garbage collection affected storage: %d != %d", len(after), 0) + } + +} + +func TestUnTaggedManifestlistWithTaggedManifest(t *testing.T) { + ctx := dcontext.Background() + inmemoryDriver := inmemory.New() + + registry := createRegistry(t, inmemoryDriver) + repo := makeRepository(t, registry, "foo/untaggedlist/taggedmanifest") + manifestService, err := repo.Manifests(ctx) + if err != nil { + t.Fatalf("%v", err) + } + + image1 := uploadRandomSchema2Image(t, repo) + image2 := uploadRandomSchema2Image(t, repo) + + err = repo.Tags(ctx).Tag(ctx, "image1", distribution.Descriptor{Digest: image1.manifestDigest}) + if err != nil { + t.Fatalf("Failed to delete tag: %v", err) + } + + err = repo.Tags(ctx).Tag(ctx, "image2", distribution.Descriptor{Digest: image2.manifestDigest}) + if err != nil { + t.Fatalf("Failed to delete tag: %v", err) + } + + // construct a manifestlist to reference manifests that is tagged. + blobstatter := registry.BlobStatter() + manifestList, err := testutil.MakeManifestList(blobstatter, []digest.Digest{ + image1.manifestDigest, image2.manifestDigest, + }) + if err != nil { + t.Fatalf("Failed to make manifest list: %v", err) + } + + dgst, err := manifestService.Put(ctx, manifestList) + if err != nil { + t.Fatalf("Failed to add manifest list: %v", err) + } + + // Run GC + err = MarkAndSweep(dcontext.Background(), inmemoryDriver, registry, GCOpts{ + DryRun: false, + RemoveUntagged: true, + }) + if err != nil { + t.Fatalf("Failed mark and sweep: %v", err) + } + + after := allBlobs(t, registry) + afterManifests := allManifests(t, manifestService) + + if _, ok := after[dgst]; ok { + t.Fatalf("Untagged manifestlist still exists") + } + + if _, ok := afterManifests[image1.manifestDigest]; !ok { + t.Fatalf("First manifest is missing") + } + + if _, ok := afterManifests[image2.manifestDigest]; !ok { + t.Fatalf("Second manifest is missing") + } +} + +func TestTaggedManifestlistWithDeletedReference(t *testing.T) { + ctx := dcontext.Background() + inmemoryDriver := inmemory.New() + + registry := createRegistry(t, inmemoryDriver) + repo := makeRepository(t, registry, "foo/untaggedlist/deleteref") + manifestService, err := repo.Manifests(ctx) + if err != nil { + t.Fatalf("%v", err) + } + + image1 := uploadRandomSchema2Image(t, repo) + image2 := uploadRandomSchema2Image(t, repo) + + // construct a manifestlist to reference manifests that is deleted. + blobstatter := registry.BlobStatter() + manifestList, err := testutil.MakeManifestList(blobstatter, []digest.Digest{ + image1.manifestDigest, image2.manifestDigest, + }) + if err != nil { + t.Fatalf("Failed to make manifest list: %v", err) + } + + _, err = manifestService.Put(ctx, manifestList) + if err != nil { + t.Fatalf("Failed to add manifest list: %v", err) + } + + err = manifestService.Delete(ctx, image1.manifestDigest) + if err != nil { + t.Fatalf("Failed to delete image: %v", err) + } + + err = manifestService.Delete(ctx, image2.manifestDigest) + if err != nil { + t.Fatalf("Failed to delete image: %v", err) + } + + // Run GC + err = MarkAndSweep(dcontext.Background(), inmemoryDriver, registry, GCOpts{ + DryRun: false, + RemoveUntagged: true, + }) + if err != nil { + t.Fatalf("Failed mark and sweep: %v", err) + } + + after := allBlobs(t, registry) + if len(after) != 0 { + t.Fatalf("Garbage collection affected storage: %d != %d", len(after), 0) + } +} diff --git a/registry/storage/manifestlisthandler.go b/registry/storage/manifestlisthandler.go index caaf76bb0..a1da46322 100644 --- a/registry/storage/manifestlisthandler.go +++ b/registry/storage/manifestlisthandler.go @@ -13,9 +13,10 @@ import ( // manifestListHandler is a ManifestHandler that covers schema2 manifest lists. type manifestListHandler struct { - repository distribution.Repository - blobStore distribution.BlobStore - ctx context.Context + repository distribution.Repository + blobStore distribution.BlobStore + ctx context.Context + validateImageIndexes validateImageIndexes } var _ ManifestHandler = &manifestListHandler{} @@ -74,24 +75,24 @@ func (ms *manifestListHandler) Put(ctx context.Context, manifestList distributio func (ms *manifestListHandler) verifyManifest(ctx context.Context, mnfst distribution.Manifest, skipDependencyVerification bool) error { var errs distribution.ErrManifestVerification - if !skipDependencyVerification { - // This manifest service is different from the blob service - // returned by Blob. It uses a linked blob store to ensure that - // only manifests are accessible. - + // Check if we should be validating the existence of any child images in images indexes + if ms.validateImageIndexes.imagesExist && !skipDependencyVerification { + // Get the manifest service we can use to check for the existence of child images manifestService, err := ms.repository.Manifests(ctx) if err != nil { return err } for _, manifestDescriptor := range mnfst.References() { - exists, err := manifestService.Exists(ctx, manifestDescriptor.Digest) - if err != nil && err != distribution.ErrBlobUnknown { - errs = append(errs, err) - } - if err != nil || !exists { - // On error here, we always append unknown blob errors. - errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: manifestDescriptor.Digest}) + if ms.platformMustExist(manifestDescriptor) { + exists, err := manifestService.Exists(ctx, manifestDescriptor.Digest) + if err != nil && err != distribution.ErrBlobUnknown { + errs = append(errs, err) + } + if err != nil || !exists { + // On error here, we always append unknown blob errors. + errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: manifestDescriptor.Digest}) + } } } } @@ -101,3 +102,24 @@ func (ms *manifestListHandler) verifyManifest(ctx context.Context, mnfst distrib return nil } + +// platformMustExist checks if a descriptor within an index should be validated as existing before accepting the manifest into the registry. +func (ms *manifestListHandler) platformMustExist(descriptor distribution.Descriptor) bool { + // If there are no image platforms configured to validate, we must check the existence of all child images. + if len(ms.validateImageIndexes.imagePlatforms) == 0 { + return true + } + + imagePlatform := descriptor.Platform + + // If the platform matches a platform that is configured to validate, we must check the existence. + for _, platform := range ms.validateImageIndexes.imagePlatforms { + if imagePlatform.Architecture == platform.architecture && + imagePlatform.OS == platform.os { + return true + } + } + + // If the platform doesn't match a platform configured to validate, we don't need to check the existence. + return false +} diff --git a/registry/storage/manifeststore_test.go b/registry/storage/manifeststore_test.go index 3c1cb9dde..1b071df25 100644 --- a/registry/storage/manifeststore_test.go +++ b/registry/storage/manifeststore_test.go @@ -10,6 +10,7 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/manifest" + "github.com/distribution/distribution/v3/manifest/manifestlist" "github.com/distribution/distribution/v3/manifest/ocischema" "github.com/distribution/distribution/v3/manifest/schema2" "github.com/distribution/distribution/v3/registry/storage/cache/memory" @@ -54,7 +55,7 @@ func newManifestStoreTestEnv(t *testing.T, name reference.Named, tag string, opt } func TestManifestStorage(t *testing.T) { - testManifestStorage(t, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableDelete, EnableRedirect) + testManifestStorage(t, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableDelete, EnableRedirect, EnableValidateImageIndexImagesExist) } func testManifestStorage(t *testing.T, options ...RegistryOption) { @@ -314,7 +315,7 @@ func testOCIManifestStorage(t *testing.T, testname string, includeMediaTypes boo repoName, _ := reference.WithName("foo/bar") env := newManifestStoreTestEnv(t, repoName, "thetag", BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), - EnableDelete, EnableRedirect) + EnableDelete, EnableRedirect, EnableValidateImageIndexImagesExist) ctx := context.Background() ms, err := env.repository.Manifests(ctx) @@ -322,46 +323,36 @@ func testOCIManifestStorage(t *testing.T, testname string, includeMediaTypes boo t.Fatal(err) } - // Build a manifest and store it and its layers in the registry + // Build a manifest and store its layers in the registry blobStore := env.repository.Blobs(ctx) - builder := ocischema.NewManifestBuilder(blobStore, []byte{}, map[string]string{}) - err = builder.(*ocischema.Builder).SetMediaType(imageMediaType) + mfst, err := createRandomImage(t, testname, imageMediaType, blobStore) if err != nil { - t.Fatal(err) + t.Fatalf("%s: unexpected error generating random image: %v", testname, err) } - // Add some layers - for i := 0; i < 2; i++ { - rs, dgst, err := testutil.CreateRandomTarFile() - if err != nil { - t.Fatalf("%s: unexpected error generating test layer file", testname) - } + // create an image index - wr, err := env.repository.Blobs(env.ctx).Create(env.ctx) - if err != nil { - t.Fatalf("%s: unexpected error creating test upload: %v", testname, err) - } - - if _, err := io.Copy(wr, rs); err != nil { - t.Fatalf("%s: unexpected error copying to upload: %v", testname, err) - } - - if _, err := wr.Commit(env.ctx, distribution.Descriptor{Digest: dgst}); err != nil { - t.Fatalf("%s: unexpected error finishing upload: %v", testname, err) - } - - if err := builder.AppendReference(distribution.Descriptor{Digest: dgst, MediaType: v1.MediaTypeImageLayer}); err != nil { - t.Fatalf("%s unexpected error appending references: %v", testname, err) - } + platformSpec := &v1.Platform{ + Architecture: "atari2600", + OS: "CP/M", } - mfst, err := builder.Build(ctx) + mfstDescriptors := []distribution.Descriptor{ + createOciManifestDescriptor(t, testname, mfst, platformSpec), + } + + imageIndex, err := ociIndexFromDesriptorsWithMediaType(mfstDescriptors, indexMediaType) if err != nil { - t.Fatalf("%s: unexpected error generating manifest: %v", testname, err) + t.Fatalf("%s: unexpected error creating image index: %v", testname, err) } - // before putting the manifest test for proper handling of SchemaVersion + _, err = ms.Put(ctx, imageIndex) + if err == nil { + t.Fatalf("%s: expected error putting image index without child manifests in the registry: %v", testname, err) + } + + // Test for proper handling of SchemaVersion for the image if mfst.(*ocischema.DeserializedManifest).Manifest.SchemaVersion != 2 { t.Fatalf("%s: unexpected error generating default version for oci manifest", testname) @@ -379,22 +370,7 @@ func testOCIManifestStorage(t *testing.T, testname string, includeMediaTypes boo } } - // Also create an image index that contains the manifest - - descriptor, err := env.registry.BlobStatter().Stat(ctx, manifestDigest) - if err != nil { - t.Fatalf("%s: unexpected error getting manifest descriptor", testname) - } - descriptor.MediaType = v1.MediaTypeImageManifest - descriptor.Platform = &v1.Platform{ - Architecture: "atari2600", - OS: "CP/M", - } - - imageIndex, err := ociIndexFromDesriptorsWithMediaType([]distribution.Descriptor{descriptor}, indexMediaType) - if err != nil { - t.Fatalf("%s: unexpected error creating image index: %v", testname, err) - } + // We can now push the index var indexDigest digest.Digest if indexDigest, err = ms.Put(ctx, imageIndex); err != nil { @@ -456,6 +432,244 @@ func testOCIManifestStorage(t *testing.T, testname string, includeMediaTypes boo } } +func TestIndexManifestStorageWithoutImageCheck(t *testing.T) { + imageMediaType := v1.MediaTypeImageManifest + indexMediaType := v1.MediaTypeImageIndex + + repoName, _ := reference.WithName("foo/bar") + env := newManifestStoreTestEnv(t, repoName, "thetag", + BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), + EnableDelete, EnableRedirect) + + ctx := context.Background() + ms, err := env.repository.Manifests(ctx) + if err != nil { + t.Fatal(err) + } + + // Build a manifest and store its layers in the registry + + blobStore := env.repository.Blobs(ctx) + manifest, err := createRandomImage(t, t.Name(), imageMediaType, blobStore) + if err != nil { + t.Fatalf("unexpected error generating random image: %v", err) + } + + // create an image index + + ociPlatformSpec := &v1.Platform{ + Architecture: "atari2600", + OS: "CP/M", + } + + ociManifestDescriptors := []distribution.Descriptor{ + createOciManifestDescriptor(t, t.Name(), manifest, ociPlatformSpec), + } + + imageIndex, err := ociIndexFromDesriptorsWithMediaType(ociManifestDescriptors, indexMediaType) + if err != nil { + t.Fatalf("unexpected error creating image index: %v", err) + } + + // We should be able to put the index without having put the image + + _, err = ms.Put(ctx, imageIndex) + if err != nil { + t.Fatalf("unexpected error putting sparse OCI image index: %v", err) + } + + // same for a manifest list + + listPlatformSpec := &manifestlist.PlatformSpec{ + Architecture: "atari2600", + OS: "CP/M", + } + + listManifestDescriptors := []manifestlist.ManifestDescriptor{ + createManifestListDescriptor(t, t.Name(), manifest, listPlatformSpec), + } + + list, err := manifestlist.FromDescriptors(listManifestDescriptors) + if err != nil { + t.Fatalf("unexpected error creating manifest list: %v", err) + } + + // We should be able to put the list without having put the image + + _, err = ms.Put(ctx, list) + if err != nil { + t.Fatalf("unexpected error putting sparse manifest list: %v", err) + } +} + +func TestIndexManifestStorageWithSelectivePlatforms(t *testing.T) { + imageMediaType := v1.MediaTypeImageManifest + indexMediaType := v1.MediaTypeImageIndex + + repoName, _ := reference.WithName("foo/bar") + env := newManifestStoreTestEnv(t, repoName, "thetag", + BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), + EnableDelete, EnableRedirect, EnableValidateImageIndexImagesExist, + AddValidateImageIndexImagesExistPlatform("amd64", "linux")) + + ctx := context.Background() + ms, err := env.repository.Manifests(ctx) + if err != nil { + t.Fatal(err) + } + + // Build a manifests their layers in the registry + + blobStore := env.repository.Blobs(ctx) + amdManifest, err := createRandomImage(t, t.Name(), imageMediaType, blobStore) + if err != nil { + t.Fatalf("%s: unexpected error generating random image: %v", t.Name(), err) + } + armManifest, err := createRandomImage(t, t.Name(), imageMediaType, blobStore) + if err != nil { + t.Fatalf("%s: unexpected error generating random image: %v", t.Name(), err) + } + atariManifest, err := createRandomImage(t, t.Name(), imageMediaType, blobStore) + if err != nil { + t.Fatalf("%s: unexpected error generating random image: %v", t.Name(), err) + } + + // create an image index + + amdPlatformSpec := &v1.Platform{ + Architecture: "amd64", + OS: "linux", + } + armPlatformSpec := &v1.Platform{ + Architecture: "arm", + OS: "plan9", + } + atariPlatformSpec := &v1.Platform{ + Architecture: "atari2600", + OS: "CP/M", + } + + manifestDescriptors := []distribution.Descriptor{ + createOciManifestDescriptor(t, t.Name(), amdManifest, amdPlatformSpec), + createOciManifestDescriptor(t, t.Name(), armManifest, armPlatformSpec), + createOciManifestDescriptor(t, t.Name(), atariManifest, atariPlatformSpec), + } + + imageIndex, err := ociIndexFromDesriptorsWithMediaType(manifestDescriptors, indexMediaType) + if err != nil { + t.Fatalf("unexpected error creating image index: %v", err) + } + + // Test we can't push with no image manifests existing in the registry + + _, err = ms.Put(ctx, imageIndex) + if err == nil { + t.Fatalf("expected error putting image index without existing images: %v", err) + } + + // Test we can't push with a manifest but not the right one + + _, err = ms.Put(ctx, atariManifest) + if err != nil { + t.Fatalf("unexpected error putting manifest: %v", err) + } + + _, err = ms.Put(ctx, imageIndex) + if err == nil { + t.Fatalf("expected error putting image index without correct existing images: %v", err) + } + + // Test we can push with the right manifest + + _, err = ms.Put(ctx, amdManifest) + if err != nil { + t.Fatalf("unexpected error putting manifest: %v", err) + } + + _, err = ms.Put(ctx, imageIndex) + if err != nil { + t.Fatalf("unexpected error putting image index: %v", err) + } +} + +// createRandomImage builds an image manifest and store it and its layers in the registry +func createRandomImage(t *testing.T, testname string, imageMediaType string, blobStore distribution.BlobStore) (distribution.Manifest, error) { + builder := ocischema.NewManifestBuilder(blobStore, []byte{}, map[string]string{}) + err := builder.(*ocischema.Builder).SetMediaType(imageMediaType) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + // Add some layers + for i := 0; i < 2; i++ { + rs, dgst, err := testutil.CreateRandomTarFile() + if err != nil { + t.Fatalf("%s: unexpected error generating test layer file", testname) + } + + wr, err := blobStore.Create(ctx) + if err != nil { + t.Fatalf("%s: unexpected error creating test upload: %v", testname, err) + } + + if _, err := io.Copy(wr, rs); err != nil { + t.Fatalf("%s: unexpected error copying to upload: %v", testname, err) + } + + if _, err := wr.Commit(ctx, distribution.Descriptor{Digest: dgst}); err != nil { + t.Fatalf("%s: unexpected error finishing upload: %v", testname, err) + } + + if err := builder.AppendReference(distribution.Descriptor{Digest: dgst, MediaType: v1.MediaTypeImageLayer}); err != nil { + t.Fatalf("%s unexpected error appending references: %v", testname, err) + } + } + + return builder.Build(ctx) +} + +// createOciManifestDescriptor builds a manifest descriptor from a manifest and a platform descriptor +func createOciManifestDescriptor(t *testing.T, testname string, manifest distribution.Manifest, platformSpec *v1.Platform) distribution.Descriptor { + manifestMediaType, manifestPayload, err := manifest.Payload() + if err != nil { + t.Fatalf("%s: unexpected error getting manifest payload: %v", testname, err) + } + manifestDigest := digest.FromBytes(manifestPayload) + + return distribution.Descriptor{ + Digest: manifestDigest, + Size: int64(len(manifestPayload)), + MediaType: manifestMediaType, + Platform: &v1.Platform{ + Architecture: platformSpec.Architecture, + OS: platformSpec.OS, + }, + } +} + +// createManifestListDescriptor builds a manifest descriptor from a manifest and a platform descriptor +func createManifestListDescriptor(t *testing.T, testname string, manifest distribution.Manifest, platformSpec *manifestlist.PlatformSpec) manifestlist.ManifestDescriptor { + manifestMediaType, manifestPayload, err := manifest.Payload() + if err != nil { + t.Fatalf("%s: unexpected error getting manifest payload: %v", testname, err) + } + manifestDigest := digest.FromBytes(manifestPayload) + + return manifestlist.ManifestDescriptor{ + Descriptor: distribution.Descriptor{ + Digest: manifestDigest, + Size: int64(len(manifestPayload)), + MediaType: manifestMediaType, + }, + Platform: manifestlist.PlatformSpec{ + Architecture: platformSpec.Architecture, + OS: platformSpec.OS, + }, + } +} + // TestLinkPathFuncs ensures that the link path functions behavior are locked // down and implemented as expected. func TestLinkPathFuncs(t *testing.T) { diff --git a/registry/storage/purgeuploads.go b/registry/storage/purgeuploads.go index e1a876e7f..9e9407a12 100644 --- a/registry/storage/purgeuploads.go +++ b/registry/storage/purgeuploads.go @@ -93,7 +93,7 @@ func getOutstandingUploads(ctx context.Context, driver storageDriver.StorageDriv ud.containingDir = filePath } if file == "startedat" { - if t, err := readStartedAtFile(driver, filePath); err == nil { + if t, err := readStartedAtFile(ctx, driver, filePath); err == nil { ud.startedAt = t } else { errors = pushError(errors, filePath, err) @@ -124,9 +124,8 @@ func uuidFromPath(path string) (string, bool) { } // readStartedAtFile reads the date from an upload's startedAtFile -func readStartedAtFile(driver storageDriver.StorageDriver, path string) (time.Time, error) { - // todo:(richardscothern) - pass in a context - startedAtBytes, err := driver.GetContent(context.Background(), path) +func readStartedAtFile(ctx context.Context, driver storageDriver.StorageDriver, path string) (time.Time, error) { + startedAtBytes, err := driver.GetContent(ctx, path) if err != nil { return time.Now(), err } diff --git a/registry/storage/registry.go b/registry/storage/registry.go index ecf483bf9..ab8fb1f9a 100644 --- a/registry/storage/registry.go +++ b/registry/storage/registry.go @@ -3,6 +3,7 @@ package storage import ( "context" "regexp" + "runtime" "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/registry/storage/cache" @@ -10,6 +11,10 @@ import ( "github.com/distribution/reference" ) +var ( + DefaultConcurrencyLimit = runtime.GOMAXPROCS(0) +) + // registry is the top-level implementation of Registry for use in the storage // package. All instances should descend from this object. type registry struct { @@ -18,10 +23,14 @@ type registry struct { statter *blobStatter // global statter service. blobDescriptorCacheProvider cache.BlobDescriptorCacheProvider deleteEnabled bool + tagLookupConcurrencyLimit int resumableDigestEnabled bool blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory - manifestURLs manifestURLs driver storagedriver.StorageDriver + + // Validation + manifestURLs manifestURLs + validateImageIndexes validateImageIndexes } // manifestURLs holds regular expressions for controlling manifest URL whitelisting @@ -30,6 +39,20 @@ type manifestURLs struct { deny *regexp.Regexp } +// validateImageIndexImages holds configuration for validation of image indexes +type validateImageIndexes struct { + // exist can be used to disable checking that platform images exist entirely. Default true. + imagesExist bool + // platforms can be used to only validate the existence of images for a set of platforms. The empty array means validate all platforms. + imagePlatforms []platform +} + +// platform represents a platform to validate exists in the +type platform struct { + architecture string + os string +} + // RegistryOption is the type used for functional options for NewRegistry. type RegistryOption func(*registry) error @@ -40,6 +63,13 @@ func EnableRedirect(registry *registry) error { return nil } +func TagLookupConcurrencyLimit(concurrencyLimit int) RegistryOption { + return func(registry *registry) error { + registry.tagLookupConcurrencyLimit = concurrencyLimit + return nil + } +} + // EnableDelete is a functional option for NewRegistry. It enables deletion on // the registry. func EnableDelete(registry *registry) error { @@ -70,6 +100,28 @@ func ManifestURLsDenyRegexp(r *regexp.Regexp) RegistryOption { } } +// EnableValidateImageIndexImagesExist is a functional option for NewRegistry. It enables +// validation that references exist before an image index is accepted. +func EnableValidateImageIndexImagesExist(registry *registry) error { + registry.validateImageIndexes.imagesExist = true + return nil +} + +// AddValidateImageIndexImagesExistPlatform returns a functional option for NewRegistry. +// It adds a platform to check for existence before an image index is accepted. +func AddValidateImageIndexImagesExistPlatform(architecture string, os string) RegistryOption { + return func(registry *registry) error { + registry.validateImageIndexes.imagePlatforms = append( + registry.validateImageIndexes.imagePlatforms, + platform{ + architecture: architecture, + os: os, + }, + ) + return nil + } +} + // BlobDescriptorServiceFactory returns a functional option for NewRegistry. It sets the // factory to create BlobDescriptorServiceFactory middleware. func BlobDescriptorServiceFactory(factory distribution.BlobDescriptorServiceFactory) RegistryOption { @@ -184,9 +236,14 @@ func (repo *repository) Named() reference.Named { } func (repo *repository) Tags(ctx context.Context) distribution.TagService { + limit := DefaultConcurrencyLimit + if repo.tagLookupConcurrencyLimit > 0 { + limit = repo.tagLookupConcurrencyLimit + } tags := &tagStore{ - repository: repo, - blobStore: repo.registry.blobStore, + repository: repo, + blobStore: repo.registry.blobStore, + concurrencyLimit: limit, } return tags @@ -222,9 +279,10 @@ func (repo *repository) Manifests(ctx context.Context, options ...distribution.M } manifestListHandler := &manifestListHandler{ - ctx: ctx, - repository: repo, - blobStore: blobStore, + ctx: ctx, + repository: repo, + blobStore: blobStore, + validateImageIndexes: repo.validateImageIndexes, } ms := &manifestStore{ diff --git a/registry/storage/schema2manifesthandler_test.go b/registry/storage/schema2manifesthandler_test.go index 26c19ec12..6b918a807 100644 --- a/registry/storage/schema2manifesthandler_test.go +++ b/registry/storage/schema2manifesthandler_test.go @@ -18,7 +18,9 @@ func TestVerifyManifestForeignLayer(t *testing.T) { inmemoryDriver := inmemory.New() registry := createRegistry(t, inmemoryDriver, ManifestURLsAllowRegexp(regexp.MustCompile("^https?://foo")), - ManifestURLsDenyRegexp(regexp.MustCompile("^https?://foo/nope"))) + ManifestURLsDenyRegexp(regexp.MustCompile("^https?://foo/nope")), + EnableValidateImageIndexImagesExist, + ) repo := makeRepository(t, registry, "test") manifestService := makeManifestService(t, repo) @@ -156,7 +158,9 @@ func TestVerifyManifestBlobLayerAndConfig(t *testing.T) { inmemoryDriver := inmemory.New() registry := createRegistry(t, inmemoryDriver, ManifestURLsAllowRegexp(regexp.MustCompile("^https?://foo")), - ManifestURLsDenyRegexp(regexp.MustCompile("^https?://foo/nope"))) + ManifestURLsDenyRegexp(regexp.MustCompile("^https?://foo/nope")), + EnableValidateImageIndexImagesExist, + ) repo := makeRepository(t, registry, strings.ToLower(t.Name())) manifestService := makeManifestService(t, repo) diff --git a/registry/storage/tagstore.go b/registry/storage/tagstore.go index 766295e9b..a481df94d 100644 --- a/registry/storage/tagstore.go +++ b/registry/storage/tagstore.go @@ -4,10 +4,13 @@ import ( "context" "path" "sort" + "sync" + + "github.com/opencontainers/go-digest" + "golang.org/x/sync/errgroup" "github.com/distribution/distribution/v3" storagedriver "github.com/distribution/distribution/v3/registry/storage/driver" - "github.com/opencontainers/go-digest" ) var _ distribution.TagService = &tagStore{} @@ -18,8 +21,9 @@ var _ distribution.TagService = &tagStore{} // which only makes use of the Digest field of the returned distribution.Descriptor // but does not enable full roundtripping of Descriptor objects type tagStore struct { - repository *repository - blobStore *blobStore + repository *repository + blobStore *blobStore + concurrencyLimit int } // All returns all tags @@ -54,7 +58,7 @@ func (ts *tagStore) All(ctx context.Context) ([]string, error) { return tags, nil } -// Tag tags the digest with the given tag, updating the the store to point at +// Tag tags the digest with the given tag, updating the store to point at // the current tag. The digest must point to a manifest. func (ts *tagStore) Tag(ctx context.Context, tag string, desc distribution.Descriptor) error { currentPath, err := pathFor(manifestTagCurrentPathSpec{ @@ -145,26 +149,48 @@ func (ts *tagStore) Lookup(ctx context.Context, desc distribution.Descriptor) ([ return nil, err } - var tags []string + g, ctx := errgroup.WithContext(ctx) + g.SetLimit(ts.concurrencyLimit) + + var ( + tags []string + mu sync.Mutex + ) for _, tag := range allTags { - tagLinkPathSpec := manifestTagCurrentPathSpec{ - name: ts.repository.Named().Name(), - tag: tag, + if ctx.Err() != nil { + break } + tag := tag - tagLinkPath, _ := pathFor(tagLinkPathSpec) - tagDigest, err := ts.blobStore.readlink(ctx, tagLinkPath) - if err != nil { - switch err.(type) { - case storagedriver.PathNotFoundError: - continue + g.Go(func() error { + tagLinkPathSpec := manifestTagCurrentPathSpec{ + name: ts.repository.Named().Name(), + tag: tag, } - return nil, err - } - if tagDigest == desc.Digest { - tags = append(tags, tag) - } + tagLinkPath, _ := pathFor(tagLinkPathSpec) + tagDigest, err := ts.blobStore.readlink(ctx, tagLinkPath) + if err != nil { + switch err.(type) { + case storagedriver.PathNotFoundError: + return nil + } + return err + } + + if tagDigest == desc.Digest { + mu.Lock() + tags = append(tags, tag) + mu.Unlock() + } + + return nil + }) + } + + err = g.Wait() + if err != nil { + return nil, err } return tags, nil diff --git a/registry/storage/vacuum.go b/registry/storage/vacuum.go index 38ebbd67f..e074b45c9 100644 --- a/registry/storage/vacuum.go +++ b/registry/storage/vacuum.go @@ -100,3 +100,18 @@ func (v Vacuum) RemoveRepository(repoName string) error { return nil } + +// RemoveLayer removes a layer link path from the storage +func (v Vacuum) RemoveLayer(repoName string, dgst digest.Digest) error { + layerLinkPath, err := pathFor(layerLinkPathSpec{name: repoName, digest: dgst}) + if err != nil { + return err + } + dcontext.GetLogger(v.ctx).Infof("Deleting layer link path: %s", layerLinkPath) + err = v.driver.Delete(v.ctx, layerLinkPath) + if err != nil { + return err + } + + return nil +} diff --git a/releases/v3.0.0.toml b/releases/v3.0.0-alpha.1.toml similarity index 100% rename from releases/v3.0.0.toml rename to releases/v3.0.0-alpha.1.toml diff --git a/releases/v3.0.0-beta.1.toml b/releases/v3.0.0-beta.1.toml new file mode 100644 index 000000000..1fde550d0 --- /dev/null +++ b/releases/v3.0.0-beta.1.toml @@ -0,0 +1,284 @@ +# commit to be tagged for new release +commit = "HEAD" + +project_name = "registry" +github_repo = "distribution/distribution" + +# previous release +previous = "v3.0.0-alpha.1" + +pre_release = true + +preface = """\ +Welcome to the 3.0.0-beta.1 release of registry! + +This is the last major pre-release of registry. +It contains various updates and bug fixes with a few minor feature additions. + +See the changelog below for full list of changes. + +### Deprecated + +* the default configuration path has changed to `/etc/distribution/config.yml` + +### Notable Changes + +* Support for sparse indexes enables selective mirroring of platform images +* Auth config now requires explicit declaration of token signing algorithms +* Support for OpenTelemetry tracing has been added +* Redis cache now supports clustering and custom TLS config +* Various caching proxy bug fixes and minitor improvements +* Various garbage collection fixes and improvements +* Documentation has received various updates and fixes + +### Changes + +
181 commits +

+ + * [`60da1934`](https://github.com/distribution/distribution/commit/60da1934b6c5ca04a5e3abc820f288209b97bc5c) Bump Go and golang linter (#4389) + * [`83a071e9`](https://github.com/distribution/distribution/commit/83a071e98ac210b905890cf0740bf662b9c8f54b) Bump alpine version + * [`5316d3bd`](https://github.com/distribution/distribution/commit/5316d3bda28d090d4c3494d837a0419a0370691c) Bump Go and golang linter + * [`948a39d3`](https://github.com/distribution/distribution/commit/948a39d358e0a89e9704eff9270d76b9506393ca) Update docs: JWKS credentials and AZ identity (#4397) + * [`d3cc664f`](https://github.com/distribution/distribution/commit/d3cc664fa2219d5ac6e7a724dbb755f27b08a2e6) Update docs: JWKS credentials and AZ identity + * [`4dd0ac97`](https://github.com/distribution/distribution/commit/4dd0ac977e9674cc0c2f3b206edb43475dac91c1) feat: implement 'rewrite' storage middleware (#4146) + * [`558ace13`](https://github.com/distribution/distribution/commit/558ace139143057e0f8e3dbbcc5b695dda33462a) feat: implement 'rewrite' storage middleware + * [`306f4ff7`](https://github.com/distribution/distribution/commit/306f4ff71eae432a323723eb4ffaf9aa861ca603) Replace custom Redis config struct with go-redis UniversalOptions (adds sentinel & cluster support) (#4306) + * [`6d591190`](https://github.com/distribution/distribution/commit/6d5911900a793318e61324584a144d2b06b40b2b) Update Redis configuration docs with TLS options + * [`a008d360`](https://github.com/distribution/distribution/commit/a008d360b4cda5deff8bfcce10a518c98ca717d8) Create type alias for redis.UniversalOptions + * [`f27799d1`](https://github.com/distribution/distribution/commit/f27799d1aa6285241f13d62408cd0a576d46f253) Add custom TLS config to Redis + * [`b63cbb33`](https://github.com/distribution/distribution/commit/b63cbb33181f9a917c226c59f15e284b96d87bd8) Replace custom Redis config struct with go-redis UniversalOptions + * [`3a849954`](https://github.com/distribution/distribution/commit/3a8499541a8d624b909c1e16a8d41e22b756457e) docs: disable base element override (#4391) + * [`f36b44ff`](https://github.com/distribution/distribution/commit/f36b44ff73cd4e7c94436ec3fd89f9c1777265ae) docs: disable base element override + * [`10d90f72`](https://github.com/distribution/distribution/commit/10d90f72904b3f8869499570230c2b26d5e7a446) remove layer's link file by gc (#4344) + * [`d9050bb9`](https://github.com/distribution/distribution/commit/d9050bb917d8fde4406a127d1fed5a09af7fbcdc) remove layer's link file by gc + * [`2b036a9f`](https://github.com/distribution/distribution/commit/2b036a9fc14d8a38f79b04e0c20d68f785d1e15b) Update dockerhub.md (#4394) + * [`43a64480`](https://github.com/distribution/distribution/commit/43a64480ef634d75a70a29048facaa181bb27f19) Update dockerhub.md + * [`5f804a9d`](https://github.com/distribution/distribution/commit/5f804a9df72f86e05de6a7c99466539b8fa26d2c) build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/azidentity from 1.3.0 to 1.6.0 (#4380) + * [`050e1a3e`](https://github.com/distribution/distribution/commit/050e1a3ee7483fe1b779275ada6a23521e67f730) build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/azidentity + * [`e1ec19ae`](https://github.com/distribution/distribution/commit/e1ec19ae60b8628b564d4fb01ce09ef81047987c) New path for distribution config (#4365) + * [`975613d4`](https://github.com/distribution/distribution/commit/975613d4a0b01418f2869ec6f78a17c5b464f61a) New path for distribution config + * [`675d7e27`](https://github.com/distribution/distribution/commit/675d7e27f59c9d332418315adb20f02c20593cea) feature: Bump go-jose and require signing algorithms in auth (#4349) + * [`52d68216`](https://github.com/distribution/distribution/commit/52d68216c0a8a4bc06871e2afdd62c3a8c068313) feature: Bump go-jose and require signing algorithms in auth + * [`37b83869`](https://github.com/distribution/distribution/commit/37b83869a95a0a597cbe8a1fb8e1ea2819b45191) Add option to enable sparse indexes (#3536) + * [`c40c4b28`](https://github.com/distribution/distribution/commit/c40c4b289ad1575d450da47476f335a724db370b) Enable configuration of index dependency validation + * [`e0a54de7`](https://github.com/distribution/distribution/commit/e0a54de7fc1dd9046f1d6449cc615af209003cbb) Add a go.mod toolchain version (#4347) + * [`421a359b`](https://github.com/distribution/distribution/commit/421a359b26a52e2553a752a20ff002595c838ea2) Add a go.mod toolchain version + * [`ad69db3f`](https://github.com/distribution/distribution/commit/ad69db3fd5b4207aa066f304c9532a5cbddd40d0) docs: update location of `filesystem.md` (#4355) + * [`119c608f`](https://github.com/distribution/distribution/commit/119c608fad0d4adb00fd0c9a1c7c65a960493e46) docs: update location of `filesystem.md` + * [`2c6b6482`](https://github.com/distribution/distribution/commit/2c6b6482fcfc82bd8c59695485ae1050b1cd861a) Include headers when serving blob through proxy (#4273) + * [`04182455`](https://github.com/distribution/distribution/commit/041824555ca2a5967699e0281e48e1e7b65eec93) Include headers when serving blob through proxy + * [`6a9b0cfb`](https://github.com/distribution/distribution/commit/6a9b0cfb71149fc85e1ea1af27d7a7349ca95bf2) Add support for `Basic Authentication` to `proxyingRegistry` (#4263) + * [`1e8ea031`](https://github.com/distribution/distribution/commit/1e8ea031732b73b727f3a93ca2b3d8e3196fcfc4) Add support for `Basic Authentication` to `proxyingRegistry` + * [`56a020f7`](https://github.com/distribution/distribution/commit/56a020f7f1fbec903d785b552c97330c58fddae6) Stop proxy scheduler on system exit (#4293) + * [`062309c0`](https://github.com/distribution/distribution/commit/062309c08b47a911d4c4558315628d938423b165) Stop proxy scheduler on system exit + * [`c49220d4`](https://github.com/distribution/distribution/commit/c49220d492233615f22c1eea91e3121a681751eb) Fix #2902: ‘autoRedirect’ hardcode ‘https’ scheme (#2903) + * [`63eb22d7`](https://github.com/distribution/distribution/commit/63eb22d74b42df1ab96624dc3a1e78e5d3915cd8) Fix: ‘autoRedirect’ hardcode ‘https’ scheme + * [`cb3a2010`](https://github.com/distribution/distribution/commit/cb3a2010c458f8c177e4b5a4a85d96e0518025bc) Set readStartAtFile context aware for purge uploads (#4339) + * [`f1875862`](https://github.com/distribution/distribution/commit/f1875862cf98feeae883c7795d44d62f83a22e5a) Set readStartAtFile context aware for purge uploads + * [`c8e22f67`](https://github.com/distribution/distribution/commit/c8e22f672344679fe7ef66d40cd99096c7f9e5b1) Add Shutdown method to registry.Registry (#4338) + * [`16a305eb`](https://github.com/distribution/distribution/commit/16a305ebafcf62f361ad5f2ae6f2b33c2fe3e3e5) Add registry.Shutdown method for graceful shutdown of embedded registry + * [`e0795fcf`](https://github.com/distribution/distribution/commit/e0795fcfe34c9e7d0fc037fd6231dbab3d1bcccb) add bounded concurrency for tag lookup and untag (#4329) + * [`a2afe23f`](https://github.com/distribution/distribution/commit/a2afe23f386e827d1975530aab12010f0be2a774) add concurrency limits for tag lookup and untag + * [`a5882d66`](https://github.com/distribution/distribution/commit/a5882d66468fd477d3f7a909c2d93ee71b434467) vendor: update manifest dependencies + * [`47a9dac2`](https://github.com/distribution/distribution/commit/47a9dac250aa62a25ef5a0da56dcd16862d1f705) fix: ignore error of manifest tag path not found in gc (#4331) + * [`11215632`](https://github.com/distribution/distribution/commit/112156321fceeb4a6c845970d971cec84a3f0794) fix: ignore error of manifest tag path not found in gc + * [`e6d1d182`](https://github.com/distribution/distribution/commit/e6d1d182bf002e9cb782e8a77493ccef0d716d2a) Allow setting s3 forcepathstyle without regionendpoint (#4291) + * [`8654a0ee`](https://github.com/distribution/distribution/commit/8654a0ee456c84a8a3e89ddb58339ba205fccea8) Allow setting s3 forcepathstyle without regionendpoint + * [`03e58dfc`](https://github.com/distribution/distribution/commit/03e58dfcf80e802c207c2b3eaa6d9e75da4d1981) chore: fix some typos in comments (#4335) + * [`2fe34420`](https://github.com/distribution/distribution/commit/2fe3442035e01805e2117788f9facdbcce354f44) chore: fix some typos in comments + * [`d61d8ebc`](https://github.com/distribution/distribution/commit/d61d8ebc16770c0b257cb342f12576d59e2df272) build(deps): bump golang.org/x/net from 0.20.0 to 0.23.0 (#4333) + * [`2db0a598`](https://github.com/distribution/distribution/commit/2db0a598ccd7c5d3a943b3477a447d869f637c5d) build(deps): bump golang.org/x/net from 0.20.0 to 0.23.0 + * [`e8ea4e59`](https://github.com/distribution/distribution/commit/e8ea4e5951440f20ebc812c043a9329d974ce106) chore: fix some typos in comments (#4332) + * [`e0a1ce14`](https://github.com/distribution/distribution/commit/e0a1ce14a8e9ca691ac13959a17cd5f141645cf2) chore: fix some typos in comments + * [`bdd3d31f`](https://github.com/distribution/distribution/commit/bdd3d31faedad54834116db2c9f118eb45fcc735) proxy: Do not configure HTTP secret for proxy registry (#4305) + * [`127fa7e0`](https://github.com/distribution/distribution/commit/127fa7e0574cffe8a091a25e01f6a5dab5a9d8fd) proxy: Do not configure HTTP secret for proxy registry + * [`df983747`](https://github.com/distribution/distribution/commit/df983747649396a7377a4b428ad3a1aebf7a9001) Fix garbage-collect --delete-untagged to handle schema 2 manifest list and OCI image index (#4285) + * [`601b37d9`](https://github.com/distribution/distribution/commit/601b37d98b9330bd66137cdc89a01896cf7a481d) Handle OCI image index and V2 manifest list during garbage collection + * [`bc6e81e1`](https://github.com/distribution/distribution/commit/bc6e81e1b9a8017a66fc56a55913b92930941feb) Add Go 1.22 support to CI (#4314) + * [`21c718d5`](https://github.com/distribution/distribution/commit/21c718d58c71e0053b51be5b4f5c5e397b571efb) Add Go 1.22 support to CI + * [`0947c654`](https://github.com/distribution/distribution/commit/0947c654e9de35658ed3bf11daf7c8a7036143f0) chore: bump distriution/reference dependency (#4312) + * [`167d7996`](https://github.com/distribution/distribution/commit/167d7996bebe275715b919664756123e076e1925) chore: bump distriution/reference dependency + * [`dde4f2a6`](https://github.com/distribution/distribution/commit/dde4f2a6db018f4ab8250a2b0e05924b4a98dd80) chore: remove repetitive words in comments (#4313) + * [`2446e110`](https://github.com/distribution/distribution/commit/2446e1102d1d73cfd078c8998a245e16b31e6883) chore: remove repetitive words in comments + * [`0d1792f5`](https://github.com/distribution/distribution/commit/0d1792f55f3c5bd0380d6cac781aba75dd5f87c0) build(deps): bump fossa-contrib/fossa-action from 2 to 3 (#4232) + * [`87ae3eb8`](https://github.com/distribution/distribution/commit/87ae3eb8d45a978fd527e569258f2f3318c7cf3e) build(deps): bump fossa-contrib/fossa-action from 2 to 3 + * [`f525c27f`](https://github.com/distribution/distribution/commit/f525c27f55dd565bcdb7adc25681b4b336f5859e) build(deps): bump ossf/scorecard-action from 2.0.6 to 2.3.1 (#4231) + * [`053fd16a`](https://github.com/distribution/distribution/commit/053fd16ae95a1c53ebb9fcba6031feab5a5903a6) build(deps): bump ossf/scorecard-action from 2.0.6 to 2.3.1 + * [`9d366245`](https://github.com/distribution/distribution/commit/9d3662456396b95dec573f5f26b27b2310bbab98) Upgrade Scorecard Action version to fix error (#4311) + * [`fdbb3a82`](https://github.com/distribution/distribution/commit/fdbb3a828868ea9abf01920c7b8d0e8b851f5ed2) fix: upgrade scorecard version + * [`94146f53`](https://github.com/distribution/distribution/commit/94146f53d82e8e2095781062da7c3c996361e3d0) Don't try to parse error responses with no body (#4307) + * [`e8820b25`](https://github.com/distribution/distribution/commit/e8820b2564ef3cd3fade9df3c65321a3d4dfcfa6) Don't try to parse error responses with no body + * [`3cb985ca`](https://github.com/distribution/distribution/commit/3cb985cac0cc56c643d28083c867f47902a6aae9) Initialize proxy prometheus counters values to 0 (#4283) + * [`6ca646ca`](https://github.com/distribution/distribution/commit/6ca646caadcda2fbde6befdb2a2f1cac1cd3da59) Initialize proxy prometheus counters values to 0 to prevent gaps after registry restart + * [`1e3de582`](https://github.com/distribution/distribution/commit/1e3de5823161684f78c995c60cf00632d17bca6c) Update go versions (#4303) + * [`1cb89e3e`](https://github.com/distribution/distribution/commit/1cb89e3e0e08b1d357f093ab7c4d8466735884c4) Update go versions + * [`7c751749`](https://github.com/distribution/distribution/commit/7c7517493c7be01aa6a9c904034bda97a6f8c378) build(deps): bump github.com/go-jose/go-jose/v3 from 3.0.1 to 3.0.3 (#4297) + * [`1c5fe22d`](https://github.com/distribution/distribution/commit/1c5fe22dec3752d1f74768e17028020779b5a73f) build(deps): bump github.com/go-jose/go-jose/v3 from 3.0.1 to 3.0.3 + * [`3783a795`](https://github.com/distribution/distribution/commit/3783a79518be20bc3a000a2826c015e8969d5a20) build(deps): bump google.golang.org/protobuf from 1.31.0 to 1.33.0 (#4301) + * [`cb2b51ca`](https://github.com/distribution/distribution/commit/cb2b51cac9cee286fe6618a60360a8321ac1db70) build(deps): bump google.golang.org/protobuf from 1.31.0 to 1.33.0 + * [`d9815da9`](https://github.com/distribution/distribution/commit/d9815da9cba94fd9ca2e52c4781f0373e57323a8) Support redirects in gcs storage with default credentials (#4295) + * [`de450c90`](https://github.com/distribution/distribution/commit/de450c903af640bea467c5bbd66604612026578b) update: support redirects in gcs storage with default credentials + * [`663b430c`](https://github.com/distribution/distribution/commit/663b430ccc10e184ecb82eff77661a38f1f6f660) fix: typo (#4296) + * [`6465b4cd`](https://github.com/distribution/distribution/commit/6465b4cd084fdee552cd462753ec012d2779ee7f) fix: typo + * [`5c662eb1`](https://github.com/distribution/distribution/commit/5c662eb1c209bb8687738eeee2c73643cc93d1af) Standardize OTEL error logging format to match application logs (#4292) + * [`71a069dc`](https://github.com/distribution/distribution/commit/71a069dc38ee1239c39892a946ea7c04619557e5) Standardize OTEL error logging format to match application logs + * [`51a72c2a`](https://github.com/distribution/distribution/commit/51a72c2aef976bd55de3a7b8b0120f97b4169476) [otel-tracing] Added Tracing to Base package (driver) (#4196) + * [`f690b3eb`](https://github.com/distribution/distribution/commit/f690b3ebe26f14c5d83998b2fec73f7491ec14d2) Added Open Telemetry Tracing to Filesystem package + * [`95077fda`](https://github.com/distribution/distribution/commit/95077fda37479fe4e6b7d748cd4b095081ae2470) fix: typo (#4290) + * [`a4918b67`](https://github.com/distribution/distribution/commit/a4918b67bbe4f2bdf870bb6fad345529555f107e) fix: typo + * [`38beeee2`](https://github.com/distribution/distribution/commit/38beeee2c8d88c7a87e5563844aa157ea5615144) Update notifications.md (#4287) + * [`1c3d44ec`](https://github.com/distribution/distribution/commit/1c3d44eccd57ae51d83b466c3935a56aeca1d985) Update notifications.md + * [`a2b608a1`](https://github.com/distribution/distribution/commit/a2b608a15cc329df91bb21d388c93c8e4697f097) build(deps): bump codecov/codecov-action from 3 to 4 (#4271) + * [`939061d1`](https://github.com/distribution/distribution/commit/939061d102b2cc3422312a9dd788a429fadbf021) build(deps): bump codecov/codecov-action from 3 to 4 + * [`6a568c10`](https://github.com/distribution/distribution/commit/6a568c100fe130b083d945511f379ea8be52db26) Do not write manifests on HEAD requests (#4286) + * [`2763ba1e`](https://github.com/distribution/distribution/commit/2763ba1eae3620daf9c7536b721e7a630541edae) Do not write manifests on HEAD requests + * [`62aa44ed`](https://github.com/distribution/distribution/commit/62aa44edfde08456ea01e476e42d13377c358bb9) Add a trademarks and docs license link (#4276) + * [`5bebd152`](https://github.com/distribution/distribution/commit/5bebd152be0c9a40721d1b1f97195f909dad5d73) Add a trademarks and docs license link + * [`9b3eac8f`](https://github.com/distribution/distribution/commit/9b3eac8f08207cc088270cf4acabb6ebeee1e7a0) build(deps): bump peter-evans/dockerhub-description from 3 to 4 (#4267) + * [`e5f5ff7a`](https://github.com/distribution/distribution/commit/e5f5ff7a11ff4bd7009f6fcdcb1fe84dccc70e42) build(deps): bump peter-evans/dockerhub-description from 3 to 4 + * [`6bc70e64`](https://github.com/distribution/distribution/commit/6bc70e640d8e95ac026149410ab02104f0953c63) build(deps): bump actions/upload-artifact from 4.1.0 to 4.3.0 (#4265) + * [`ee58e343`](https://github.com/distribution/distribution/commit/ee58e3438f1c5475d0eeef5b9d1a26f834a6c731) build(deps): bump actions/upload-artifact from 4.1.0 to 4.3.0 + * [`945eed71`](https://github.com/distribution/distribution/commit/945eed71e117a50143650b8d45845dc047b95ed6) feat: Add HTTP2 for unencrypted HTTP (v3) (#4248) + * [`11f50c03`](https://github.com/distribution/distribution/commit/11f50c034e908fea9b8176f4fef9755853bbd518) feat: Add HTTP2 for unencrypted HTTP + * [`0b21cc06`](https://github.com/distribution/distribution/commit/0b21cc06b0960f24f0e39280e5fa85cc631b8be7) refactor(storage/s3): remove redundant len check (#4259) + * [`41161a6e`](https://github.com/distribution/distribution/commit/41161a6e12807bcc151bddc5f9fde6619108e228) refactor(storage/s3): remove redundant len check + * [`01b4555d`](https://github.com/distribution/distribution/commit/01b4555d59222ec9698cb1173b980cbbb1fb5289) docs: add rendering hook and fix broken links (#4247) + * [`5e75227f`](https://github.com/distribution/distribution/commit/5e75227fb213162564bab74b146300ffed9f0bbd) docs: fix broken links and improve link resolution + * [`1611bd2f`](https://github.com/distribution/distribution/commit/1611bd2fc41820a545106852bcc505eb5f60b317) chore: Migrate PR labeler config to v5 (#4258) + * [`c78c1561`](https://github.com/distribution/distribution/commit/c78c156139ad203b380588e716f6473579afc9e3) Update labeler.yml + * [`65c6a6d3`](https://github.com/distribution/distribution/commit/65c6a6d37775d1aa85b2921449f874548672af7e) Update .github/labeler.yml + * [`b1d1be8e`](https://github.com/distribution/distribution/commit/b1d1be8e879597b12a202f7bc3741717f85e7045) chore: Migrate PR labeler config to v5 + * [`969bc4a1`](https://github.com/distribution/distribution/commit/969bc4a12562d62940204d32b0edd5240d0de10d) chore: Remove `area/config` duplicate entry in `labeler.yml` (#4257) + * [`a626871f`](https://github.com/distribution/distribution/commit/a626871f127539bd0e2cd1a5af4711a4ed33b033) chore: Sort entries + * [`d2c57396`](https://github.com/distribution/distribution/commit/d2c57396e043dd84c79fa2027fdbc889227e4a1d) chore: Remove `area/config` duplicate entry in `labeler.yml` + * [`781d0368`](https://github.com/distribution/distribution/commit/781d03682c4fcf8c3c8b80a85c5fc618e2c593f6) chore: Remove duplicate `area/ci` entry in PR labeler (#4256) + * [`45cea887`](https://github.com/distribution/distribution/commit/45cea887eb7561ee80f876f6ad37bb4d812c162a) chore: Remove duplicate `area/ci` entry in PR labeler + * [`bf6f5c3f`](https://github.com/distribution/distribution/commit/bf6f5c3f748deab2aa17741a3b86f4a682b2c33d) fix: add labeler action (#4213) + * [`ea02d9c4`](https://github.com/distribution/distribution/commit/ea02d9c42e90c6e479aad82b745bfcd7feded963) fix: add labeler action + * [`dd32792b`](https://github.com/distribution/distribution/commit/dd32792bc0a2b86e7a98c8fed1421b15392804b7) fix: update Dockerfile version output (#4212) + * [`5bd45551`](https://github.com/distribution/distribution/commit/5bd45551b40abfc5fec37c7510ced32eb19996ad) fix: update Dockerfile version output + * [`6926aea0`](https://github.com/distribution/distribution/commit/6926aea0ee50996890257223ad86de65ba889d3e) vendor: github.com/gorilla/handlers v1.5.2 (#4211) + * [`4f9fe183`](https://github.com/distribution/distribution/commit/4f9fe183c3a1ebc762c274c564eb6abed4e06997) vendor: github.com/gorilla/handlers v1.5.2 + * [`435d1b94`](https://github.com/distribution/distribution/commit/435d1b94834ab77d5ba9099a24bfd81e2af2dca5) remove deprecated ReadSeekCloser interfaces (#4245) + * [`50332793`](https://github.com/distribution/distribution/commit/5033279355313edc81af518c427602bb107ed8d5) remove deprecated ReadSeekCloser interfaces + * [`0c13e046`](https://github.com/distribution/distribution/commit/0c13e046aeef3676120c5b5550aff3cdc23f9bac) build(deps): bump actions/upload-artifact from 3.0.0 to 4.1.0 (#4254) + * [`ef1db8ac`](https://github.com/distribution/distribution/commit/ef1db8ac266e566ab1e25866a8e65de29685576d) build(deps): bump actions/upload-artifact from 3.0.0 to 4.1.0 + * [`88d85426`](https://github.com/distribution/distribution/commit/88d854269fa67d48eb6496050d8d1f4765b250b2) build(deps): bump docker/bake-action from 2 to 4 (#4253) + * [`6b14735d`](https://github.com/distribution/distribution/commit/6b14735dbf4c9fd5e199d887da09b22fffd6426d) ci: disable provenance when generating docs + * [`f09bf31f`](https://github.com/distribution/distribution/commit/f09bf31f3ef2427f2c6b612ced905a8d8b55f32c) ci: handle provenance for built artifacts + * [`f4a3149a`](https://github.com/distribution/distribution/commit/f4a3149a2f4edafaacebea211d3c2cf7d923bfa6) build(deps): bump docker/bake-action from 2 to 4 + * [`14366a2d`](https://github.com/distribution/distribution/commit/14366a2dff6a8f595e39d258085381731b43cec6) fix: load gcs credentials and client inside DriverConstructor (#4218) + * [`5bd7f258`](https://github.com/distribution/distribution/commit/5bd7f2588057e58009c13db38db9168337c9d379) fix: load gcs credentials and client inside DriverConstructor + * [`9dfead3d`](https://github.com/distribution/distribution/commit/9dfead3d9a82b57028283195a586a01ae71b77e1) build(deps): bump docker/setup-buildx-action from 2 to 3 (#4230) + * [`f2342966`](https://github.com/distribution/distribution/commit/f2342966469aa2ab268a06391703badd13633326) build(deps): bump docker/setup-buildx-action from 2 to 3 + * [`e780c8bb`](https://github.com/distribution/distribution/commit/e780c8bb24b2157d7f74ecc079edcfc3d6ceb096) update to alpine 3.19 (#4210) + * [`5f397b87`](https://github.com/distribution/distribution/commit/5f397b877dc7dab2de3854fb9ba2f746c7a9bea7) update to alpine 3.19 + * [`9d04a0fc`](https://github.com/distribution/distribution/commit/9d04a0fcd1ca1be05df436799821e1dbf18d1d10) build(deps): bump docker/metadata-action from 4 to 5 (#4240) + * [`5c585db7`](https://github.com/distribution/distribution/commit/5c585db74ec89755e49d7907a869235dac618aad) build(deps): bump docker/metadata-action from 4 to 5 + * [`1d2895f2`](https://github.com/distribution/distribution/commit/1d2895f2bfee0071d1da65c75ed239cc952d20d5) build(deps): bump docker/login-action from 2 to 3 (#4239) + * [`5c5d8d3d`](https://github.com/distribution/distribution/commit/5c5d8d3ddd79825e9b2e0551f7db6501c2adb168) build(deps): bump docker/login-action from 2 to 3 + * [`2fcf2091`](https://github.com/distribution/distribution/commit/2fcf2091e21717c082016228ac25cfc69b7dba43) build(deps): bump actions/upload-pages-artifact from 2 to 3 (#4234) + * [`fc992dfe`](https://github.com/distribution/distribution/commit/fc992dfef7d19d53eaf467b3476dd0e594a7f017) build(deps): bump actions/upload-pages-artifact from 2 to 3 + * [`e9995cdb`](https://github.com/distribution/distribution/commit/e9995cdb3f105ca3c55de3b2c8fa609b3ac9bc36) chore: use no-cache-filter for outdated stage (#4216) + * [`55e91b39`](https://github.com/distribution/distribution/commit/55e91b39e407bde9b6137b8555ede1850e91dac4) chore: use no-cache-filter for outdated stage + * [`4382e4bb`](https://github.com/distribution/distribution/commit/4382e4bb20528fc06840cbf04138042844b05899) chore: generate authors and update mailmap (#4215) + * [`587f9e28`](https://github.com/distribution/distribution/commit/587f9e286d7505d1928e825cd4a83fd26de65763) chore: generate authors + * [`befbaa68`](https://github.com/distribution/distribution/commit/befbaa680ca097c4b584684b686186419bf19330) chore: update mailmap + * [`b2bd724b`](https://github.com/distribution/distribution/commit/b2bd724b52147b909267d08a1739263b3e0101f5) chore: sort and fix mailmap + * [`a808a5bb`](https://github.com/distribution/distribution/commit/a808a5bb0e67401bdec7338e474f6adae79ac2d6) build(deps): bump actions/configure-pages from 3 to 4 (#4227) + * [`f0a66954`](https://github.com/distribution/distribution/commit/f0a669540ead51317cc3fdea56834fa9600e8e54) build(deps): bump actions/configure-pages from 3 to 4 + * [`ec0a4773`](https://github.com/distribution/distribution/commit/ec0a477324f1f30fbe09caff86158e793cefb73d) build(deps): bump actions/setup-go from 3 to 5 (#4228) + * [`78a6be85`](https://github.com/distribution/distribution/commit/78a6be85ee9e759aec076b239479207e274ec108) build(deps): bump actions/setup-go from 3 to 5 + * [`51a7c2bd`](https://github.com/distribution/distribution/commit/51a7c2bdf84a767a58165bfdbcebc468d2e1b0cb) build(deps): bump actions/checkout from 3 to 4 (#4226) + * [`38a2d53c`](https://github.com/distribution/distribution/commit/38a2d53c7bfcf1b8ee52537dd7ecfd422c26f96c) build(deps): bump actions/checkout from 3 to 4 + * [`8ab33dd8`](https://github.com/distribution/distribution/commit/8ab33dd8add1725d2f36011a54e4922770ae66de) build(deps): bump actions/deploy-pages from 2 to 4 (#4224) + * [`af2fa0ff`](https://github.com/distribution/distribution/commit/af2fa0ff4d07feb4b83deed0a017ed9fcdd03ce2) build(deps): bump actions/deploy-pages from 2 to 4 + * [`f73bcf07`](https://github.com/distribution/distribution/commit/f73bcf07007f96bad261c0ed24d808278f60d8b2) build(deps): bump github/codeql-action from 1.0.26 to 3.22.12 (#4225) + * [`ba702e1d`](https://github.com/distribution/distribution/commit/ba702e1d7c1245808e3245cca25fe59124de7fcf) build(deps): bump github/codeql-action from 1.0.26 to 3.22.12 + * [`7a9e0ea0`](https://github.com/distribution/distribution/commit/7a9e0ea014dc1067bfaf9bd8186ff7d4e800f6a6) chore: dependabot to keep gha up to date (#4217) + * [`7838a369`](https://github.com/distribution/distribution/commit/7838a369a367aad88d6468f778114557b6807b4f) chore: dependabot to keep gha up to date + * [`2cc6bd73`](https://github.com/distribution/distribution/commit/2cc6bd73e629bdb7ca84b4491496d62f4dd506bf) vendor: github.com/mitchellh/mapstructure v1.5.0 (#4222) + * [`bdfa8324`](https://github.com/distribution/distribution/commit/bdfa8324a088bb45938f158d45aa6eafd5655275) vendor: github.com/mitchellh/mapstructure v1.5.0 + * [`316e4099`](https://github.com/distribution/distribution/commit/316e4099b1f9223e7b922e7a92d800d2c4f13ea2) fix: add missing skip in s3 driver test (#4219) + * [`6908e0d5`](https://github.com/distribution/distribution/commit/6908e0d5facd31ed32046df03a09040c964be0b3) fix: add missing skip in s3 driver test + * [`012adcae`](https://github.com/distribution/distribution/commit/012adcae7de005cc134dcad37e3809e52f6238ba) feat: add PR labeler (#4205) + * [`e96fce17`](https://github.com/distribution/distribution/commit/e96fce1703986d12e365f823205ab2c4cd11ed7f) feat: add PR labeler + * [`fb6ccc33`](https://github.com/distribution/distribution/commit/fb6ccc33d11f3eae2d76683b5c5ad39653d2baaa) update: readme cleanup and fxes (#4208) + * [`e29a5c8e`](https://github.com/distribution/distribution/commit/e29a5c8e68353705ea282d1e13f26937da199944) update: readme cleanup and fxes + * [`c8f17009`](https://github.com/distribution/distribution/commit/c8f17009c44560d4c07b2ae11414ae148cd23565) docs: remove legacy kramdown options from link (#4209) + * [`0e0d74b0`](https://github.com/distribution/distribution/commit/0e0d74b03763eabf1c2b6bf30d2d2203901f4195) docs: remove legacy kramdown options from link + * [`d830076a`](https://github.com/distribution/distribution/commit/d830076a49dc1b66129c3591a7d71f7f34109ea9) fix: build status badge (#4207) + * [`59929031`](https://github.com/distribution/distribution/commit/599290318264d3aa6e9b7f4ec24f9a56462bd168) fix: build status badge + * [`2306ab8a`](https://github.com/distribution/distribution/commit/2306ab8aed3d82a73f652693d121659b4319bfa4) feat: add GH issue template (#4206) + * [`535b6586`](https://github.com/distribution/distribution/commit/535b65869b31e776895b8e4484562ea35782f6e5) feat: add GH issue template + * [`c5a88721`](https://github.com/distribution/distribution/commit/c5a887217e74d55e3f62b2c4beb3b7909d5b97ed) version: export getter functions (#4204) + * [`a74cacff`](https://github.com/distribution/distribution/commit/a74cacff04e76ec91230ed407a10d50f64c028ef) version: export getter functions + * [`ab27c9d5`](https://github.com/distribution/distribution/commit/ab27c9d5f18cb080aff37770d1c55f2554c6b307) version: use go list -m + * [`ec617ca6`](https://github.com/distribution/distribution/commit/ec617ca6d20c7522b7fb994cfaf590fc7ac9adca) update: set User-Agent header in GCS storage driver (#4203) + * [`d59a570c`](https://github.com/distribution/distribution/commit/d59a570c3d9e91ab5e8e7ddc4282c2087d390f96) update: set User-Agent header in GCS storage driver +

+
+ +### Contributors + +* Milos Gajdos +* CrazyMax +* erezrokah +* Wang Yan +* Sebastiaan van Stijn +* Liang Zheng +* David Karlsson +* James Hewitt +* guoguangwu +* Cory Snider +* Dimitar Kostadinov +* Ismail Alidzhikov +* Paul Meyer +* gotgelf +* Anders Ingemann +* Andrey Smirnov +* Anthony Ramahay +* Austin Vazquez +* Benjamin Schanzel +* Chad Faragher +* Emmanuel Ferdman +* Eng Zer Jun +* Jaime Martinez +* Joyce Brum +* Kyle Squizzato +* Mahmoud Kandil +* Markus Thömmes +* Mikel Rychliski +* Robin Ketelbuters +* Steven Kalt +* Sylvain DESGRAIS +* Tadeusz Dudkiewicz +* goodactive +* icefed +* oliver-goetz +* xiaoxiangxianzi + +### Dependency Changes + +* **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.6.0 -> v1.11.1 +* **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.3.0 -> v1.6.0 +* **github.com/Azure/azure-sdk-for-go/sdk/internal** v1.3.0 -> v1.8.0 +* **github.com/AzureAD/microsoft-authentication-library-for-go** v1.0.0 -> v1.2.2 +* **github.com/distribution/reference** v0.5.0 -> v0.6.0 +* **github.com/go-jose/go-jose/v4** v4.0.2 **_new_** +* **github.com/golang-jwt/jwt/v5** v5.2.1 **_new_** +* **github.com/google/uuid** v1.3.1 -> v1.6.0 +* **github.com/gorilla/handlers** v1.5.1 -> v1.5.2 +* **github.com/mitchellh/mapstructure** v1.1.2 -> v1.5.0 +* **github.com/pkg/browser** 681adbf594b8 -> 5ac0b6a4141c +* **github.com/stretchr/testify** v1.8.4 -> v1.9.0 +* **golang.org/x/crypto** v0.17.0 -> v0.24.0 +* **golang.org/x/net** v0.18.0 -> v0.26.0 +* **golang.org/x/sync** v0.3.0 -> v0.7.0 +* **golang.org/x/sys** v0.15.0 -> v0.21.0 +* **golang.org/x/text** v0.14.0 -> v0.16.0 +* **google.golang.org/protobuf** v1.31.0 -> v1.33.0 + +Previous release can be found at [v3.0.0-alpha.1](https://github.com/distribution/distribution/tree/v3.0.0-alpha.1) +""" diff --git a/tests/conf-e2e-cloud-storage.yml b/tests/conf-e2e-cloud-storage.yml index 63a8778c7..b03697c21 100644 --- a/tests/conf-e2e-cloud-storage.yml +++ b/tests/conf-e2e-cloud-storage.yml @@ -17,15 +17,14 @@ log: formatter: text level: debug redis: - addr: redis:6379 + addrs: [redis:6379] db: 0 dialtimeout: 5s readtimeout: 10ms writetimeout: 10ms - pool: - idletimeout: 60s - maxactive: 64 - maxidle: 16 + maxidleconns: 16 + poolsize: 64 + connmaxidletime: 300s storage: redirect: disable: true @@ -41,6 +40,7 @@ storage: bucket: images-local rootdirectory: /registry-v2 regionendpoint: http://minio:9000 + forcepathstyle: true encrypt: false secure: false chunksize: 33554432 diff --git a/tests/docker-compose-e2e-cloud-storage.yml b/tests/docker-compose-e2e-cloud-storage.yml index 887c9db9f..58f949d44 100644 --- a/tests/docker-compose-e2e-cloud-storage.yml +++ b/tests/docker-compose-e2e-cloud-storage.yml @@ -43,7 +43,7 @@ services: dockerfile: ./Dockerfile command: - "serve" - - "/etc/docker/registry/config-test.yml" + - "/etc/distribution/config-test.yml" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5001/debug/health"] interval: 5s @@ -58,4 +58,4 @@ services: - "5000:5000" - "5001:5001" volumes: - - ./conf-e2e-cloud-storage.yml:/etc/docker/registry/config-test.yml + - ./conf-e2e-cloud-storage.yml:/etc/distribution/config-test.yml diff --git a/testutil/manifests.go b/testutil/manifests.go index 4f2134df1..0bcc4ddba 100644 --- a/testutil/manifests.go +++ b/testutil/manifests.go @@ -1,11 +1,13 @@ package testutil import ( + "context" "fmt" "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/internal/dcontext" "github.com/distribution/distribution/v3/manifest/manifestlist" + "github.com/distribution/distribution/v3/manifest/ocischema" "github.com/distribution/distribution/v3/manifest/schema2" "github.com/opencontainers/go-digest" ) @@ -49,6 +51,20 @@ func MakeSchema2Manifest(repository distribution.Repository, digests []digest.Di return nil, fmt.Errorf("unexpected error storing content in blobstore: %v", err) } builder := schema2.NewManifestBuilder(d, configJSON) + return makeManifest(ctx, builder, digests) +} + +func MakeOCIManifest(repository distribution.Repository, digests []digest.Digest) (distribution.Manifest, error) { + ctx := dcontext.Background() + blobStore := repository.Blobs(ctx) + + var configJSON []byte + + builder := ocischema.NewManifestBuilder(blobStore, configJSON, make(map[string]string)) + return makeManifest(ctx, builder, digests) +} + +func makeManifest(ctx context.Context, builder distribution.ManifestBuilder, digests []digest.Digest) (distribution.Manifest, error) { for _, digest := range digests { if err := builder.AppendReference(distribution.Descriptor{Digest: digest}); err != nil { return nil, fmt.Errorf("unexpected error building manifest: %v", err) diff --git a/tracing/exporter.go b/tracing/exporter.go new file mode 100644 index 000000000..483c9b124 --- /dev/null +++ b/tracing/exporter.go @@ -0,0 +1,42 @@ +package tracing + +import ( + "context" + + sdktrace "go.opentelemetry.io/otel/sdk/trace" +) + +// compositeExporter is a custom exporter that wraps multiple SpanExporters. +// It allows you to export spans to multiple destinations, e.g., different telemetry backends. +type compositeExporter struct { + exporters []sdktrace.SpanExporter +} + +func newCompositeExporter(exporters ...sdktrace.SpanExporter) *compositeExporter { + return &compositeExporter{exporters: exporters} +} + +// ExportSpans iterates over each SpanExporter in the compositeExporter and +// exports the spans. If any exporter returns an error, the process is stopped +// and the error is returned. This ensures that span exporting behaves correctly +// and reports errors as expected. +func (ce *compositeExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error { + for _, exporter := range ce.exporters { + if err := exporter.ExportSpans(ctx, spans); err != nil { + return err + } + } + return nil +} + +// Shutdown iterates over each SpanExporter in the compositeExporter and +// shuts them down. If any exporter returns an error during shutdown, the process +// is stopped and the error is returned. This ensures proper shutdown of all exporters. +func (ce *compositeExporter) Shutdown(ctx context.Context) error { + for _, exporter := range ce.exporters { + if err := exporter.Shutdown(ctx); err != nil { + return err + } + } + return nil +} diff --git a/tracing/exporter_test.go b/tracing/exporter_test.go new file mode 100644 index 000000000..ec5517355 --- /dev/null +++ b/tracing/exporter_test.go @@ -0,0 +1,82 @@ +package tracing + +import ( + "context" + "errors" + "testing" + + "go.opentelemetry.io/otel/sdk/trace" +) + +type mockSpanExporter struct { + exportSpansCalled bool + shutdownCalled bool + returnError bool +} + +func (m *mockSpanExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { + m.exportSpansCalled = true + if m.returnError { + return errors.New("export error") + } + return nil +} + +func (m *mockSpanExporter) Shutdown(ctx context.Context) error { + m.shutdownCalled = true + if m.returnError { + return errors.New("shutdown error") + } + return nil +} +func TestCompositeExporterExportSpans(t *testing.T) { + mockExporter1 := &mockSpanExporter{} + mockExporter2 := &mockSpanExporter{} + composite := newCompositeExporter(mockExporter1, mockExporter2) + + err := composite.ExportSpans(context.Background(), nil) + if err != nil { + t.Errorf("ExportSpans() error = %v", err) + } + + if !mockExporter1.exportSpansCalled || !mockExporter2.exportSpansCalled { + t.Error("ExportSpans was not called on all exporters") + } +} + +func TestCompositeExporterExportSpans_Error(t *testing.T) { + mockExporter1 := &mockSpanExporter{returnError: true} + mockExporter2 := &mockSpanExporter{} + composite := newCompositeExporter(mockExporter1, mockExporter2) + + err := composite.ExportSpans(context.Background(), nil) + if err == nil { + t.Error("Expected error from ExportSpans, but got none") + } +} + +func TestCompositeExporterShutdown(t *testing.T) { + mockExporter1 := &mockSpanExporter{} + mockExporter2 := &mockSpanExporter{} + composite := newCompositeExporter(mockExporter1, mockExporter2) + + err := composite.Shutdown(context.Background()) + if err != nil { + t.Errorf("Shutdown() error = %v", err) + } + + if !mockExporter1.shutdownCalled || !mockExporter2.shutdownCalled { + t.Error("Shutdown was not called on all exporters") + } +} + +func TestCompositeExporterShutdown_Error(t *testing.T) { + mockExporter1 := &mockSpanExporter{returnError: true} + mockExporter2 := &mockSpanExporter{} + composite := newCompositeExporter(mockExporter1, mockExporter2) + + err := composite.Shutdown(context.Background()) + if err == nil { + t.Error("Expected error from Shutdown, but got none") + } +} diff --git a/tracing/loggerwriter.go b/tracing/loggerwriter.go new file mode 100644 index 000000000..a0479426b --- /dev/null +++ b/tracing/loggerwriter.go @@ -0,0 +1,21 @@ +package tracing + +import "github.com/distribution/distribution/v3/internal/dcontext" + +// loggerWriter is a custom writer that implements the io.Writer interface. +// It is designed to redirect log messages to the Logger interface, specifically +// for use with OpenTelemetry's stdouttrace exporter. +type loggerWriter struct { + logger dcontext.Logger // Use the Logger interface +} + +// Write logs the data using the Debug level of the provided logger. +func (lw *loggerWriter) Write(p []byte) (n int, err error) { + lw.logger.Debug(string(p)) + return len(p), nil +} + +// Handle logs the error using the Error level of the provided logger. +func (lw *loggerWriter) Handle(err error) { + lw.logger.Error(err) +} diff --git a/tracing/tracing.go b/tracing/tracing.go index 8a8559e7b..dca9d584f 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -3,9 +3,11 @@ package tracing import ( "context" + "github.com/distribution/distribution/v3/internal/dcontext" "github.com/distribution/distribution/v3/version" "go.opentelemetry.io/contrib/exporters/autoexport" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" @@ -18,6 +20,10 @@ const ( // DefaultSamplingRatio default sample ratio defaultSamplingRatio = 1 + + // AttributePrefix defines a standardized prefix for custom telemetry attributes + // associated with the CNCF Distribution project. + AttributePrefix = "io.cncf.distribution." ) // InitOpenTelemetry initializes OpenTelemetry for the application. This function sets up the @@ -26,21 +32,33 @@ func InitOpenTelemetry(ctx context.Context) error { res := resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String(serviceName), - semconv.ServiceVersionKey.String(version.Version), + semconv.ServiceVersionKey.String(version.Version()), ) - exp, err := autoexport.NewSpanExporter(ctx) + autoExp, err := autoexport.NewSpanExporter(ctx) if err != nil { return err } - sp := sdktrace.NewBatchSpanProcessor(exp) + lw := &loggerWriter{ + logger: dcontext.GetLogger(ctx), + } + + loggerExp, err := stdouttrace.New(stdouttrace.WithWriter(lw)) + if err != nil { + return err + } + + compositeExp := newCompositeExporter(autoExp, loggerExp) + + sp := sdktrace.NewBatchSpanProcessor(compositeExp) provider := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.TraceIDRatioBased(defaultSamplingRatio)), sdktrace.WithResource(res), sdktrace.WithSpanProcessor(sp), ) otel.SetTracerProvider(provider) + otel.SetErrorHandler(lw) pr := propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}) otel.SetTextMapPropagator(pr) diff --git a/version/print.go b/version/get.go similarity index 53% rename from version/print.go rename to version/get.go index 5529da85f..db68e6d46 100644 --- a/version/print.go +++ b/version/get.go @@ -6,6 +6,24 @@ import ( "os" ) +// Package returns the overall, canonical project import path under +// which the package was built. +func Package() string { + return mainpkg +} + +// Version returns returns the module version the running binary was +// built from. +func Version() string { + return version +} + +// Revision returns the VCS (e.g. git) revision being used to build +// the program at linking time. +func Revision() string { + return revision +} + // FprintVersion outputs the version string to the writer, in the following // format, followed by a newline: // @@ -16,7 +34,7 @@ import ( // // registry github.com/distribution/distribution v2.0 func FprintVersion(w io.Writer) { - fmt.Fprintln(w, os.Args[0], Package, Version) + fmt.Fprintln(w, os.Args[0], Package(), Version()) } // PrintVersion outputs the version information, from Fprint, to stdout. diff --git a/version/version.go b/version/version.go index a99b77598..25338926b 100644 --- a/version/version.go +++ b/version/version.go @@ -1,15 +1,15 @@ package version -// Package is the overall, canonical project import path under which the +// mainpkg is the overall, canonical project import path under which the // package was built. -var Package = "github.com/distribution/distribution/v3" +var mainpkg = "github.com/distribution/distribution/v3" -// Version indicates which version of the binary is running. This is set to +// version indicates which version of the binary is running. This is set to // the latest release tag by hand, always suffixed by "+unknown". During // build, it will be replaced by the actual version. The value here will be // used if the registry is run after a go get based install. -var Version = "v3.0.0-alpha.1" +var version = "v3.0.0-beta.1.m+unknown" -// Revision is filled with the VCS (e.g. git) revision being used to build +// revision is filled with the VCS (e.g. git) revision being used to build // the program at linking time. -var Revision = "" +var revision = "" diff --git a/version/version.sh b/version/version.sh index 75dca7847..176a370a2 100755 --- a/version/version.sh +++ b/version/version.sh @@ -10,17 +10,17 @@ set -e cat <